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  }