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  }