github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/daemon/graphdriver/btrfs/btrfs.go (about) 1 // +build linux 2 3 package btrfs 4 5 /* 6 #include <stdlib.h> 7 #include <dirent.h> 8 #include <btrfs/ioctl.h> 9 #include <btrfs/ctree.h> 10 */ 11 import "C" 12 13 import ( 14 "fmt" 15 "os" 16 "path" 17 "path/filepath" 18 "syscall" 19 "unsafe" 20 21 "github.com/docker/docker/daemon/graphdriver" 22 "github.com/docker/docker/pkg/idtools" 23 "github.com/docker/docker/pkg/mount" 24 "github.com/opencontainers/runc/libcontainer/label" 25 ) 26 27 func init() { 28 graphdriver.Register("btrfs", Init) 29 } 30 31 // Init returns a new BTRFS driver. 32 // An error is returned if BTRFS is not supported. 33 func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 34 rootdir := path.Dir(home) 35 36 var buf syscall.Statfs_t 37 if err := syscall.Statfs(rootdir, &buf); err != nil { 38 return nil, err 39 } 40 41 if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicBtrfs { 42 return nil, graphdriver.ErrPrerequisites 43 } 44 45 rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) 46 if err != nil { 47 return nil, err 48 } 49 if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { 50 return nil, err 51 } 52 53 if err := mount.MakePrivate(home); err != nil { 54 return nil, err 55 } 56 57 driver := &Driver{ 58 home: home, 59 uidMaps: uidMaps, 60 gidMaps: gidMaps, 61 } 62 63 return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil 64 } 65 66 // Driver contains information about the filesystem mounted. 67 type Driver struct { 68 //root of the file system 69 home string 70 uidMaps []idtools.IDMap 71 gidMaps []idtools.IDMap 72 } 73 74 // String prints the name of the driver (btrfs). 75 func (d *Driver) String() string { 76 return "btrfs" 77 } 78 79 // Status returns current driver information in a two dimensional string array. 80 // Output contains "Build Version" and "Library Version" of the btrfs libraries used. 81 // Version information can be used to check compatibility with your kernel. 82 func (d *Driver) Status() [][2]string { 83 status := [][2]string{} 84 if bv := btrfsBuildVersion(); bv != "-" { 85 status = append(status, [2]string{"Build Version", bv}) 86 } 87 if lv := btrfsLibVersion(); lv != -1 { 88 status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)}) 89 } 90 return status 91 } 92 93 // GetMetadata returns empty metadata for this driver. 94 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 95 return nil, nil 96 } 97 98 // Cleanup unmounts the home directory. 99 func (d *Driver) Cleanup() error { 100 return mount.Unmount(d.home) 101 } 102 103 func free(p *C.char) { 104 C.free(unsafe.Pointer(p)) 105 } 106 107 func openDir(path string) (*C.DIR, error) { 108 Cpath := C.CString(path) 109 defer free(Cpath) 110 111 dir := C.opendir(Cpath) 112 if dir == nil { 113 return nil, fmt.Errorf("Can't open dir") 114 } 115 return dir, nil 116 } 117 118 func closeDir(dir *C.DIR) { 119 if dir != nil { 120 C.closedir(dir) 121 } 122 } 123 124 func getDirFd(dir *C.DIR) uintptr { 125 return uintptr(C.dirfd(dir)) 126 } 127 128 func subvolCreate(path, name string) error { 129 dir, err := openDir(path) 130 if err != nil { 131 return err 132 } 133 defer closeDir(dir) 134 135 var args C.struct_btrfs_ioctl_vol_args 136 for i, c := range []byte(name) { 137 args.name[i] = C.char(c) 138 } 139 140 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE, 141 uintptr(unsafe.Pointer(&args))) 142 if errno != 0 { 143 return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error()) 144 } 145 return nil 146 } 147 148 func subvolSnapshot(src, dest, name string) error { 149 srcDir, err := openDir(src) 150 if err != nil { 151 return err 152 } 153 defer closeDir(srcDir) 154 155 destDir, err := openDir(dest) 156 if err != nil { 157 return err 158 } 159 defer closeDir(destDir) 160 161 var args C.struct_btrfs_ioctl_vol_args_v2 162 args.fd = C.__s64(getDirFd(srcDir)) 163 for i, c := range []byte(name) { 164 args.name[i] = C.char(c) 165 } 166 167 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2, 168 uintptr(unsafe.Pointer(&args))) 169 if errno != 0 { 170 return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error()) 171 } 172 return nil 173 } 174 175 func isSubvolume(p string) (bool, error) { 176 var bufStat syscall.Stat_t 177 if err := syscall.Lstat(p, &bufStat); err != nil { 178 return false, err 179 } 180 181 // return true if it is a btrfs subvolume 182 return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil 183 } 184 185 func subvolDelete(dirpath, name string) error { 186 dir, err := openDir(dirpath) 187 if err != nil { 188 return err 189 } 190 defer closeDir(dir) 191 fullPath := path.Join(dirpath, name) 192 193 var args C.struct_btrfs_ioctl_vol_args 194 195 // walk the btrfs subvolumes 196 walkSubvolumes := func(p string, f os.FileInfo, err error) error { 197 if err != nil { 198 if os.IsNotExist(err) && p != fullPath { 199 // missing most likely because the path was a subvolume that got removed in the previous iteration 200 // since it's gone anyway, we don't care 201 return nil 202 } 203 return fmt.Errorf("error walking subvolumes: %v", err) 204 } 205 // we want to check children only so skip itself 206 // it will be removed after the filepath walk anyways 207 if f.IsDir() && p != fullPath { 208 sv, err := isSubvolume(p) 209 if err != nil { 210 return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err) 211 } 212 if sv { 213 if err := subvolDelete(path.Dir(p), f.Name()); err != nil { 214 return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err) 215 } 216 } 217 } 218 return nil 219 } 220 if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil { 221 return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err) 222 } 223 224 // all subvolumes have been removed 225 // now remove the one originally passed in 226 for i, c := range []byte(name) { 227 args.name[i] = C.char(c) 228 } 229 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, 230 uintptr(unsafe.Pointer(&args))) 231 if errno != 0 { 232 return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error()) 233 } 234 return nil 235 } 236 237 func (d *Driver) subvolumesDir() string { 238 return path.Join(d.home, "subvolumes") 239 } 240 241 func (d *Driver) subvolumesDirID(id string) string { 242 return path.Join(d.subvolumesDir(), id) 243 } 244 245 // Create the filesystem with given id. 246 func (d *Driver) Create(id, parent, mountLabel string) error { 247 subvolumes := path.Join(d.home, "subvolumes") 248 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 249 if err != nil { 250 return err 251 } 252 if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { 253 return err 254 } 255 if parent == "" { 256 if err := subvolCreate(subvolumes, id); err != nil { 257 return err 258 } 259 } else { 260 parentDir, err := d.Get(parent, "") 261 if err != nil { 262 return err 263 } 264 if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { 265 return err 266 } 267 } 268 269 // if we have a remapped root (user namespaces enabled), change the created snapshot 270 // dir ownership to match 271 if rootUID != 0 || rootGID != 0 { 272 if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { 273 return err 274 } 275 } 276 277 return label.Relabel(path.Join(subvolumes, id), mountLabel, false) 278 } 279 280 // Remove the filesystem with given id. 281 func (d *Driver) Remove(id string) error { 282 dir := d.subvolumesDirID(id) 283 if _, err := os.Stat(dir); err != nil { 284 return err 285 } 286 if err := subvolDelete(d.subvolumesDir(), id); err != nil { 287 return err 288 } 289 if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { 290 return err 291 } 292 return nil 293 } 294 295 // Get the requested filesystem id. 296 func (d *Driver) Get(id, mountLabel string) (string, error) { 297 dir := d.subvolumesDirID(id) 298 st, err := os.Stat(dir) 299 if err != nil { 300 return "", err 301 } 302 303 if !st.IsDir() { 304 return "", fmt.Errorf("%s: not a directory", dir) 305 } 306 307 return dir, nil 308 } 309 310 // Put is not implemented for BTRFS as there is no cleanup required for the id. 311 func (d *Driver) Put(id string) error { 312 // Get() creates no runtime resources (like e.g. mounts) 313 // so this doesn't need to do anything. 314 return nil 315 } 316 317 // Exists checks if the id exists in the filesystem. 318 func (d *Driver) Exists(id string) bool { 319 dir := d.subvolumesDirID(id) 320 _, err := os.Stat(dir) 321 return err == nil 322 }