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