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