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