12 "github.com/Sirupsen/logrus"
14 "github.com/docker/docker/daemon/graphdriver"
15 "github.com/docker/docker/pkg/devicemapper"
16 "github.com/docker/docker/pkg/idtools"
17 "github.com/docker/docker/pkg/locker"
18 "github.com/docker/docker/pkg/mount"
19 "github.com/docker/docker/pkg/system"
20 units "github.com/docker/go-units"
24 graphdriver.Register("devicemapper", Init)
27 // Driver contains the device set mounted and the home directory
31 uidMaps []idtools.IDMap
32 gidMaps []idtools.IDMap
33 ctr *graphdriver.RefCounter
37 // Init creates a driver with the given home and the set of options.
38 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
39 deviceSet, err := NewDeviceSet(home, true, options, uidMaps, gidMaps)
44 if err := mount.MakePrivate(home); err != nil {
53 ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
57 return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
60 func (d *Driver) String() string {
64 // Status returns the status about the driver in a printable format.
65 // Information returned contains Pool Name, Data File, Metadata file, disk usage by
66 // the data and metadata, etc.
67 func (d *Driver) Status() [][2]string {
68 s := d.DeviceSet.Status()
70 status := [][2]string{
71 {"Pool Name", s.PoolName},
72 {"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
73 {"Base Device Size", fmt.Sprintf("%s", units.HumanSize(float64(s.BaseDeviceSize)))},
74 {"Backing Filesystem", s.BaseDeviceFS},
75 {"Data file", s.DataFile},
76 {"Metadata file", s.MetadataFile},
77 {"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
78 {"Data Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Total)))},
79 {"Data Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Available)))},
80 {"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Used)))},
81 {"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))},
82 {"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))},
83 {"Thin Pool Minimum Free Space", fmt.Sprintf("%s", units.HumanSize(float64(s.MinFreeSpace)))},
84 {"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)},
85 {"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)},
86 {"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)},
87 {"Deferred Deleted Device Count", fmt.Sprintf("%v", s.DeferredDeletedDeviceCount)},
89 if len(s.DataLoopback) > 0 {
90 status = append(status, [2]string{"Data loop file", s.DataLoopback})
92 if len(s.MetadataLoopback) > 0 {
93 status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback})
95 if vStr, err := devicemapper.GetLibraryVersion(); err == nil {
96 status = append(status, [2]string{"Library Version", vStr})
101 // GetMetadata returns a map of information about the device.
102 func (d *Driver) GetMetadata(id string) (map[string]string, error) {
103 m, err := d.DeviceSet.exportDeviceMetadata(id)
109 metadata := make(map[string]string)
110 metadata["DeviceId"] = strconv.Itoa(m.deviceID)
111 metadata["DeviceSize"] = strconv.FormatUint(m.deviceSize, 10)
112 metadata["DeviceName"] = m.deviceName
116 // Cleanup unmounts a device.
117 func (d *Driver) Cleanup() error {
118 err := d.DeviceSet.Shutdown(d.home)
120 if err2 := mount.Unmount(d.home); err == nil {
127 // CreateReadWrite creates a layer that is writable for use as a container
129 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
130 return d.Create(id, parent, opts)
133 // Create adds a device with a given id and the parent.
134 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
135 var storageOpt map[string]string
137 storageOpt = opts.StorageOpt
140 if err := d.DeviceSet.AddDevice(id, parent, storageOpt); err != nil {
147 // Remove removes a device with a given id, unmounts the filesystem.
148 func (d *Driver) Remove(id string) error {
150 defer d.locker.Unlock(id)
151 if !d.DeviceSet.HasDevice(id) {
152 // Consider removing a non-existing device a no-op
153 // This is useful to be able to progress on container removal
154 // if the underlying device has gone away due to earlier errors
158 // This assumes the device has been properly Get/Put:ed and thus is unmounted
159 if err := d.DeviceSet.DeleteDevice(id, false); err != nil {
160 return fmt.Errorf("failed to remove device %s: %v", id, err)
163 mp := path.Join(d.home, "mnt", id)
164 if err := system.EnsureRemoveAll(mp); err != nil {
171 // Get mounts a device with given id into the root filesystem
172 func (d *Driver) Get(id, mountLabel string) (string, error) {
174 defer d.locker.Unlock(id)
175 mp := path.Join(d.home, "mnt", id)
176 rootFs := path.Join(mp, "rootfs")
177 if count := d.ctr.Increment(mp); count > 1 {
181 uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
187 // Create the target directories if they don't exist
188 if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
192 if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
198 if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
203 if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
205 d.DeviceSet.UnmountDevice(id, mp)
209 idFile := path.Join(mp, "id")
210 if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) {
211 // Create an "id" file with the container/image id in it to help reconstruct this in case
213 if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
215 d.DeviceSet.UnmountDevice(id, mp)
223 // Put unmounts a device and removes it.
224 func (d *Driver) Put(id string) error {
226 defer d.locker.Unlock(id)
227 mp := path.Join(d.home, "mnt", id)
228 if count := d.ctr.Decrement(mp); count > 0 {
231 err := d.DeviceSet.UnmountDevice(id, mp)
233 logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)
238 // Exists checks to see if the device exists.
239 func (d *Driver) Exists(id string) bool {
240 return d.DeviceSet.HasDevice(id)