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