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