github.com/afein/docker@v1.8.2/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 */ 10 import "C" 11 12 import ( 13 "fmt" 14 "os" 15 "path" 16 "syscall" 17 "unsafe" 18 19 "github.com/docker/docker/daemon/graphdriver" 20 "github.com/docker/docker/pkg/mount" 21 ) 22 23 func init() { 24 graphdriver.Register("btrfs", Init) 25 } 26 27 func Init(home string, options []string) (graphdriver.Driver, error) { 28 rootdir := path.Dir(home) 29 30 var buf syscall.Statfs_t 31 if err := syscall.Statfs(rootdir, &buf); err != nil { 32 return nil, err 33 } 34 35 if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicBtrfs { 36 return nil, graphdriver.ErrPrerequisites 37 } 38 39 if err := os.MkdirAll(home, 0700); err != nil { 40 return nil, err 41 } 42 43 if err := mount.MakePrivate(home); err != nil { 44 return nil, err 45 } 46 47 driver := &Driver{ 48 home: home, 49 } 50 51 return graphdriver.NaiveDiffDriver(driver), nil 52 } 53 54 type Driver struct { 55 home string 56 } 57 58 func (d *Driver) String() string { 59 return "btrfs" 60 } 61 62 func (d *Driver) Status() [][2]string { 63 status := [][2]string{} 64 if bv := BtrfsBuildVersion(); bv != "-" { 65 status = append(status, [2]string{"Build Version", bv}) 66 } 67 if lv := BtrfsLibVersion(); lv != -1 { 68 status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)}) 69 } 70 return status 71 } 72 73 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 74 return nil, nil 75 } 76 77 func (d *Driver) Cleanup() error { 78 return mount.Unmount(d.home) 79 } 80 81 func free(p *C.char) { 82 C.free(unsafe.Pointer(p)) 83 } 84 85 func openDir(path string) (*C.DIR, error) { 86 Cpath := C.CString(path) 87 defer free(Cpath) 88 89 dir := C.opendir(Cpath) 90 if dir == nil { 91 return nil, fmt.Errorf("Can't open dir") 92 } 93 return dir, nil 94 } 95 96 func closeDir(dir *C.DIR) { 97 if dir != nil { 98 C.closedir(dir) 99 } 100 } 101 102 func getDirFd(dir *C.DIR) uintptr { 103 return uintptr(C.dirfd(dir)) 104 } 105 106 func subvolCreate(path, name string) error { 107 dir, err := openDir(path) 108 if err != nil { 109 return err 110 } 111 defer closeDir(dir) 112 113 var args C.struct_btrfs_ioctl_vol_args 114 for i, c := range []byte(name) { 115 args.name[i] = C.char(c) 116 } 117 118 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE, 119 uintptr(unsafe.Pointer(&args))) 120 if errno != 0 { 121 return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error()) 122 } 123 return nil 124 } 125 126 func subvolSnapshot(src, dest, name string) error { 127 srcDir, err := openDir(src) 128 if err != nil { 129 return err 130 } 131 defer closeDir(srcDir) 132 133 destDir, err := openDir(dest) 134 if err != nil { 135 return err 136 } 137 defer closeDir(destDir) 138 139 var args C.struct_btrfs_ioctl_vol_args_v2 140 args.fd = C.__s64(getDirFd(srcDir)) 141 for i, c := range []byte(name) { 142 args.name[i] = C.char(c) 143 } 144 145 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2, 146 uintptr(unsafe.Pointer(&args))) 147 if errno != 0 { 148 return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error()) 149 } 150 return nil 151 } 152 153 func subvolDelete(path, name string) error { 154 dir, err := openDir(path) 155 if err != nil { 156 return err 157 } 158 defer closeDir(dir) 159 160 var args C.struct_btrfs_ioctl_vol_args 161 for i, c := range []byte(name) { 162 args.name[i] = C.char(c) 163 } 164 165 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, 166 uintptr(unsafe.Pointer(&args))) 167 if errno != 0 { 168 return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error()) 169 } 170 return nil 171 } 172 173 func (d *Driver) subvolumesDir() string { 174 return path.Join(d.home, "subvolumes") 175 } 176 177 func (d *Driver) subvolumesDirId(id string) string { 178 return path.Join(d.subvolumesDir(), id) 179 } 180 181 func (d *Driver) Create(id string, parent string) error { 182 subvolumes := path.Join(d.home, "subvolumes") 183 if err := os.MkdirAll(subvolumes, 0700); err != nil { 184 return err 185 } 186 if parent == "" { 187 if err := subvolCreate(subvolumes, id); err != nil { 188 return err 189 } 190 } else { 191 parentDir, err := d.Get(parent, "") 192 if err != nil { 193 return err 194 } 195 if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { 196 return err 197 } 198 } 199 return nil 200 } 201 202 func (d *Driver) Remove(id string) error { 203 dir := d.subvolumesDirId(id) 204 if _, err := os.Stat(dir); err != nil { 205 return err 206 } 207 if err := subvolDelete(d.subvolumesDir(), id); err != nil { 208 return err 209 } 210 return os.RemoveAll(dir) 211 } 212 213 func (d *Driver) Get(id, mountLabel string) (string, error) { 214 dir := d.subvolumesDirId(id) 215 st, err := os.Stat(dir) 216 if err != nil { 217 return "", err 218 } 219 220 if !st.IsDir() { 221 return "", fmt.Errorf("%s: not a directory", dir) 222 } 223 224 return dir, nil 225 } 226 227 func (d *Driver) Put(id string) error { 228 // Get() creates no runtime resources (like e.g. mounts) 229 // so this doesn't need to do anything. 230 return nil 231 } 232 233 func (d *Driver) Exists(id string) bool { 234 dir := d.subvolumesDirId(id) 235 _, err := os.Stat(dir) 236 return err == nil 237 }