github.com/amylindburg/docker@v1.7.0/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) error {
   224  	// Get() creates no runtime resources (like e.g. mounts)
   225  	// so this doesn't need to do anything.
   226  	return nil
   227  }
   228  
   229  func (d *Driver) Exists(id string) bool {
   230  	dir := d.subvolumesDirId(id)
   231  	_, err := os.Stat(dir)
   232  	return err == nil
   233  }