github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/graphdriver/devmapper/driver.go (about) 1 //go:build linux 2 // +build linux 3 4 package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" 5 6 import ( 7 "fmt" 8 "os" 9 "path" 10 "strconv" 11 12 "github.com/docker/docker/daemon/graphdriver" 13 "github.com/docker/docker/pkg/devicemapper" 14 "github.com/docker/docker/pkg/idtools" 15 units "github.com/docker/go-units" 16 "github.com/moby/locker" 17 "github.com/moby/sys/mount" 18 "github.com/sirupsen/logrus" 19 "golang.org/x/sys/unix" 20 ) 21 22 func init() { 23 graphdriver.Register("devicemapper", Init) 24 } 25 26 // Driver contains the device set mounted and the home directory 27 type Driver struct { 28 *DeviceSet 29 home string 30 ctr *graphdriver.RefCounter 31 locker *locker.Locker 32 } 33 34 // Init creates a driver with the given home and the set of options. 35 func Init(home string, options []string, idMap idtools.IdentityMapping) (graphdriver.Driver, error) { 36 deviceSet, err := NewDeviceSet(home, true, options, idMap) 37 if err != nil { 38 return nil, err 39 } 40 41 d := &Driver{ 42 DeviceSet: deviceSet, 43 home: home, 44 ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), 45 locker: locker.New(), 46 } 47 48 return graphdriver.NewNaiveDiffDriver(d, d.idMap), nil 49 } 50 51 func (d *Driver) String() string { 52 return "devicemapper" 53 } 54 55 // Status returns the status about the driver in a printable format. 56 // Information returned contains Pool Name, Data File, Metadata file, disk usage by 57 // the data and metadata, etc. 58 func (d *Driver) Status() [][2]string { 59 s := d.DeviceSet.Status() 60 61 status := [][2]string{ 62 {"Pool Name", s.PoolName}, 63 {"Pool Blocksize", units.HumanSize(float64(s.SectorSize))}, 64 {"Base Device Size", units.HumanSize(float64(s.BaseDeviceSize))}, 65 {"Backing Filesystem", s.BaseDeviceFS}, 66 {"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)}, 67 } 68 69 if len(s.DataFile) > 0 { 70 status = append(status, [2]string{"Data file", s.DataFile}) 71 } 72 if len(s.MetadataFile) > 0 { 73 status = append(status, [2]string{"Metadata file", s.MetadataFile}) 74 } 75 if len(s.DataLoopback) > 0 { 76 status = append(status, [2]string{"Data loop file", s.DataLoopback}) 77 } 78 if len(s.MetadataLoopback) > 0 { 79 status = append(status, [2]string{"Metadata loop file", s.MetadataLoopback}) 80 } 81 82 status = append(status, [][2]string{ 83 {"Data Space Used", units.HumanSize(float64(s.Data.Used))}, 84 {"Data Space Total", units.HumanSize(float64(s.Data.Total))}, 85 {"Data Space Available", units.HumanSize(float64(s.Data.Available))}, 86 {"Metadata Space Used", units.HumanSize(float64(s.Metadata.Used))}, 87 {"Metadata Space Total", units.HumanSize(float64(s.Metadata.Total))}, 88 {"Metadata Space Available", units.HumanSize(float64(s.Metadata.Available))}, 89 {"Thin Pool Minimum Free Space", units.HumanSize(float64(s.MinFreeSpace))}, 90 {"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)}, 91 {"Deferred Deletion Enabled", fmt.Sprintf("%v", s.DeferredDeleteEnabled)}, 92 {"Deferred Deleted Device Count", fmt.Sprintf("%v", s.DeferredDeletedDeviceCount)}, 93 }...) 94 95 if vStr, err := devicemapper.GetLibraryVersion(); err == nil { 96 status = append(status, [2]string{"Library Version", vStr}) 97 } 98 return status 99 } 100 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) 104 105 if err != nil { 106 return nil, err 107 } 108 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 113 return metadata, nil 114 } 115 116 // Cleanup unmounts a device. 117 func (d *Driver) Cleanup() error { 118 err := d.DeviceSet.Shutdown(d.home) 119 umountErr := mount.RecursiveUnmount(d.home) 120 121 // in case we have two errors, prefer the one from Shutdown() 122 if err != nil { 123 return err 124 } 125 126 return umountErr 127 } 128 129 // CreateReadWrite creates a layer that is writable for use as a container 130 // file system. 131 func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { 132 return d.Create(id, parent, opts) 133 } 134 135 // Create adds a device with a given id and the parent. 136 func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { 137 var storageOpt map[string]string 138 if opts != nil { 139 storageOpt = opts.StorageOpt 140 } 141 return d.DeviceSet.AddDevice(id, parent, storageOpt) 142 } 143 144 // Remove removes a device with a given id, unmounts the filesystem, and removes the mount point. 145 func (d *Driver) Remove(id string) error { 146 d.locker.Lock(id) 147 defer d.locker.Unlock(id) 148 if !d.DeviceSet.HasDevice(id) { 149 // Consider removing a non-existing device a no-op 150 // This is useful to be able to progress on container removal 151 // if the underlying device has gone away due to earlier errors 152 return nil 153 } 154 155 // This assumes the device has been properly Get/Put:ed and thus is unmounted 156 if err := d.DeviceSet.DeleteDevice(id, false); err != nil { 157 return fmt.Errorf("failed to remove device %s: %v", id, err) 158 } 159 160 // Most probably the mount point is already removed on Put() 161 // (see DeviceSet.UnmountDevice()), but just in case it was not 162 // let's try to remove it here as well, ignoring errors as 163 // an older kernel can return EBUSY if e.g. the mount was leaked 164 // to other mount namespaces. A failure to remove the container's 165 // mount point is not important and should not be treated 166 // as a failure to remove the container. 167 mp := path.Join(d.home, "mnt", id) 168 err := unix.Rmdir(mp) 169 if err != nil && !os.IsNotExist(err) { 170 logrus.WithField("storage-driver", "devicemapper").Warnf("unable to remove mount point %q: %s", mp, err) 171 } 172 173 return nil 174 } 175 176 // Get mounts a device with given id into the root filesystem 177 func (d *Driver) Get(id, mountLabel string) (string, error) { 178 d.locker.Lock(id) 179 defer d.locker.Unlock(id) 180 mp := path.Join(d.home, "mnt", id) 181 rootFs := path.Join(mp, "rootfs") 182 if count := d.ctr.Increment(mp); count > 1 { 183 return rootFs, nil 184 } 185 186 root := d.idMap.RootPair() 187 188 // Create the target directories if they don't exist 189 if err := idtools.MkdirAllAndChown(path.Join(d.home, "mnt"), 0755, root); err != nil { 190 d.ctr.Decrement(mp) 191 return "", err 192 } 193 if err := idtools.MkdirAndChown(mp, 0755, root); err != nil && !os.IsExist(err) { 194 d.ctr.Decrement(mp) 195 return "", err 196 } 197 198 // Mount the device 199 if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil { 200 d.ctr.Decrement(mp) 201 return "", err 202 } 203 204 if err := idtools.MkdirAllAndChown(rootFs, 0755, root); err != nil { 205 d.ctr.Decrement(mp) 206 d.DeviceSet.UnmountDevice(id, mp) 207 return "", err 208 } 209 210 idFile := path.Join(mp, "id") 211 if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) { 212 // Create an "id" file with the container/image id in it to help reconstruct this in case 213 // of later problems 214 if err := os.WriteFile(idFile, []byte(id), 0600); err != nil { 215 d.ctr.Decrement(mp) 216 d.DeviceSet.UnmountDevice(id, mp) 217 return "", err 218 } 219 } 220 221 return rootFs, nil 222 } 223 224 // Put unmounts a device and removes it. 225 func (d *Driver) Put(id string) error { 226 d.locker.Lock(id) 227 defer d.locker.Unlock(id) 228 mp := path.Join(d.home, "mnt", id) 229 if count := d.ctr.Decrement(mp); count > 0 { 230 return nil 231 } 232 233 err := d.DeviceSet.UnmountDevice(id, mp) 234 if err != nil { 235 logrus.WithField("storage-driver", "devicemapper").Errorf("Error unmounting device %s: %v", id, err) 236 } 237 238 return err 239 } 240 241 // Exists checks to see if the device exists. 242 func (d *Driver) Exists(id string) bool { 243 return d.DeviceSet.HasDevice(id) 244 }