github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/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 35 fsMagic, err := graphdriver.GetFSMagic(home) 36 if err != nil { 37 return nil, err 38 } 39 40 if fsMagic != 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 fullPath := path.Join(dirpath, name) 191 192 var args C.struct_btrfs_ioctl_vol_args 193 194 // walk the btrfs subvolumes 195 walkSubvolumes := func(p string, f os.FileInfo, err error) error { 196 if err != nil { 197 if os.IsNotExist(err) && p != fullPath { 198 // missing most likely because the path was a subvolume that got removed in the previous iteration 199 // since it's gone anyway, we don't care 200 return nil 201 } 202 return fmt.Errorf("error walking subvolumes: %v", err) 203 } 204 // we want to check children only so skip itself 205 // it will be removed after the filepath walk anyways 206 if f.IsDir() && p != fullPath { 207 sv, err := isSubvolume(p) 208 if err != nil { 209 return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err) 210 } 211 if sv { 212 if err := subvolDelete(path.Dir(p), f.Name()); err != nil { 213 return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err) 214 } 215 } 216 } 217 return nil 218 } 219 if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil { 220 return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err) 221 } 222 223 // all subvolumes have been removed 224 // now remove the one originally passed in 225 for i, c := range []byte(name) { 226 args.name[i] = C.char(c) 227 } 228 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, 229 uintptr(unsafe.Pointer(&args))) 230 if errno != 0 { 231 return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error()) 232 } 233 return nil 234 } 235 236 func (d *Driver) subvolumesDir() string { 237 return path.Join(d.home, "subvolumes") 238 } 239 240 func (d *Driver) subvolumesDirID(id string) string { 241 return path.Join(d.subvolumesDir(), id) 242 } 243 244 // Create the filesystem with given id. 245 func (d *Driver) Create(id, parent, mountLabel string) error { 246 subvolumes := path.Join(d.home, "subvolumes") 247 rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) 248 if err != nil { 249 return err 250 } 251 if err := idtools.MkdirAllAs(subvolumes, 0700, rootUID, rootGID); err != nil { 252 return err 253 } 254 if parent == "" { 255 if err := subvolCreate(subvolumes, id); err != nil { 256 return err 257 } 258 } else { 259 parentDir := d.subvolumesDirID(parent) 260 st, err := os.Stat(parentDir) 261 if err != nil { 262 return err 263 } 264 if !st.IsDir() { 265 return fmt.Errorf("%s: not a directory", parentDir) 266 } 267 if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { 268 return err 269 } 270 } 271 272 // if we have a remapped root (user namespaces enabled), change the created snapshot 273 // dir ownership to match 274 if rootUID != 0 || rootGID != 0 { 275 if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil { 276 return err 277 } 278 } 279 280 return label.Relabel(path.Join(subvolumes, id), mountLabel, false) 281 } 282 283 // Remove the filesystem with given id. 284 func (d *Driver) Remove(id string) error { 285 dir := d.subvolumesDirID(id) 286 if _, err := os.Stat(dir); err != nil { 287 return err 288 } 289 if err := subvolDelete(d.subvolumesDir(), id); err != nil { 290 return err 291 } 292 if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { 293 return err 294 } 295 return nil 296 } 297 298 // Get the requested filesystem id. 299 func (d *Driver) Get(id, mountLabel string) (string, error) { 300 dir := d.subvolumesDirID(id) 301 st, err := os.Stat(dir) 302 if err != nil { 303 return "", err 304 } 305 306 if !st.IsDir() { 307 return "", fmt.Errorf("%s: not a directory", dir) 308 } 309 310 return dir, nil 311 } 312 313 // Put is not implemented for BTRFS as there is no cleanup required for the id. 314 func (d *Driver) Put(id string) error { 315 // Get() creates no runtime resources (like e.g. mounts) 316 // so this doesn't need to do anything. 317 return nil 318 } 319 320 // Exists checks if the id exists in the filesystem. 321 func (d *Driver) Exists(id string) bool { 322 dir := d.subvolumesDirID(id) 323 _, err := os.Stat(dir) 324 return err == nil 325 }