github.com/jaegerpicker/docker@v0.7.7-0.20150325003727-22dba32b4dab/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 lv := BtrfsLibVersion(); lv != -1 { 65 status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)}) 66 } 67 return status 68 } 69 70 func (d *Driver) Cleanup() error { 71 return mount.Unmount(d.home) 72 } 73 74 func free(p *C.char) { 75 C.free(unsafe.Pointer(p)) 76 } 77 78 func openDir(path string) (*C.DIR, error) { 79 Cpath := C.CString(path) 80 defer free(Cpath) 81 82 dir := C.opendir(Cpath) 83 if dir == nil { 84 return nil, fmt.Errorf("Can't open dir") 85 } 86 return dir, nil 87 } 88 89 func closeDir(dir *C.DIR) { 90 if dir != nil { 91 C.closedir(dir) 92 } 93 } 94 95 func getDirFd(dir *C.DIR) uintptr { 96 return uintptr(C.dirfd(dir)) 97 } 98 99 func subvolCreate(path, name string) error { 100 dir, err := openDir(path) 101 if err != nil { 102 return err 103 } 104 defer closeDir(dir) 105 106 var args C.struct_btrfs_ioctl_vol_args 107 for i, c := range []byte(name) { 108 args.name[i] = C.char(c) 109 } 110 111 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE, 112 uintptr(unsafe.Pointer(&args))) 113 if errno != 0 { 114 return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error()) 115 } 116 return nil 117 } 118 119 func subvolSnapshot(src, dest, name string) error { 120 srcDir, err := openDir(src) 121 if err != nil { 122 return err 123 } 124 defer closeDir(srcDir) 125 126 destDir, err := openDir(dest) 127 if err != nil { 128 return err 129 } 130 defer closeDir(destDir) 131 132 var args C.struct_btrfs_ioctl_vol_args_v2 133 args.fd = C.__s64(getDirFd(srcDir)) 134 for i, c := range []byte(name) { 135 args.name[i] = C.char(c) 136 } 137 138 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2, 139 uintptr(unsafe.Pointer(&args))) 140 if errno != 0 { 141 return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error()) 142 } 143 return nil 144 } 145 146 func subvolDelete(path, name string) error { 147 dir, err := openDir(path) 148 if err != nil { 149 return err 150 } 151 defer closeDir(dir) 152 153 var args C.struct_btrfs_ioctl_vol_args 154 for i, c := range []byte(name) { 155 args.name[i] = C.char(c) 156 } 157 158 _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY, 159 uintptr(unsafe.Pointer(&args))) 160 if errno != 0 { 161 return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error()) 162 } 163 return nil 164 } 165 166 func (d *Driver) subvolumesDir() string { 167 return path.Join(d.home, "subvolumes") 168 } 169 170 func (d *Driver) subvolumesDirId(id string) string { 171 return path.Join(d.subvolumesDir(), id) 172 } 173 174 func (d *Driver) Create(id string, parent string) error { 175 subvolumes := path.Join(d.home, "subvolumes") 176 if err := os.MkdirAll(subvolumes, 0700); err != nil { 177 return err 178 } 179 if parent == "" { 180 if err := subvolCreate(subvolumes, id); err != nil { 181 return err 182 } 183 } else { 184 parentDir, err := d.Get(parent, "") 185 if err != nil { 186 return err 187 } 188 if err := subvolSnapshot(parentDir, subvolumes, id); err != nil { 189 return err 190 } 191 } 192 return nil 193 } 194 195 func (d *Driver) Remove(id string) error { 196 dir := d.subvolumesDirId(id) 197 if _, err := os.Stat(dir); err != nil { 198 return err 199 } 200 if err := subvolDelete(d.subvolumesDir(), id); err != nil { 201 return err 202 } 203 return os.RemoveAll(dir) 204 } 205 206 func (d *Driver) Get(id, mountLabel string) (string, error) { 207 dir := d.subvolumesDirId(id) 208 st, err := os.Stat(dir) 209 if err != nil { 210 return "", err 211 } 212 213 if !st.IsDir() { 214 return "", fmt.Errorf("%s: not a directory", dir) 215 } 216 217 return dir, nil 218 } 219 220 func (d *Driver) Put(id string) error { 221 // Get() creates no runtime resources (like e.g. mounts) 222 // so this doesn't need to do anything. 223 return nil 224 } 225 226 func (d *Driver) Exists(id string) bool { 227 dir := d.subvolumesDirId(id) 228 _, err := os.Stat(dir) 229 return err == nil 230 }