github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/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  #include <btrfs/ctree.h>
    10  */
    11  import "C"
    12  
    13  import (
    14  	"fmt"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"syscall"
    19  	"unsafe"
    20  
    21  	"github.com/docker/docker/daemon/graphdriver"
    22  	"github.com/docker/docker/pkg/mount"
    23  )
    24  
    25  func init() {
    26  	graphdriver.Register("btrfs", Init)
    27  }
    28  
    29  // Init returns a new BTRFS driver.
    30  // An error is returned if BTRFS is not supported.
    31  func Init(home string, options []string) (graphdriver.Driver, error) {
    32  	rootdir := path.Dir(home)
    33  
    34  	var buf syscall.Statfs_t
    35  	if err := syscall.Statfs(rootdir, &buf); err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	if graphdriver.FsMagic(buf.Type) != graphdriver.FsMagicBtrfs {
    40  		return nil, graphdriver.ErrPrerequisites
    41  	}
    42  
    43  	if err := os.MkdirAll(home, 0700); err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	if err := mount.MakePrivate(home); err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	driver := &Driver{
    52  		home: home,
    53  	}
    54  
    55  	return graphdriver.NaiveDiffDriver(driver), nil
    56  }
    57  
    58  // Driver contains information about the filesystem mounted.
    59  type Driver struct {
    60  	//root of the file system
    61  	home string
    62  }
    63  
    64  // String prints the name of the driver (btrfs).
    65  func (d *Driver) String() string {
    66  	return "btrfs"
    67  }
    68  
    69  // Status returns current driver information in a two dimensional string array.
    70  // Output contains "Build Version" and "Library Version" of the btrfs libraries used.
    71  // Version information can be used to check compatibility with your kernel.
    72  func (d *Driver) Status() [][2]string {
    73  	status := [][2]string{}
    74  	if bv := btrfsBuildVersion(); bv != "-" {
    75  		status = append(status, [2]string{"Build Version", bv})
    76  	}
    77  	if lv := btrfsLibVersion(); lv != -1 {
    78  		status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
    79  	}
    80  	return status
    81  }
    82  
    83  // GetMetadata returns empty metadata for this driver.
    84  func (d *Driver) GetMetadata(id string) (map[string]string, error) {
    85  	return nil, nil
    86  }
    87  
    88  // Cleanup unmounts the home directory.
    89  func (d *Driver) Cleanup() error {
    90  	return mount.Unmount(d.home)
    91  }
    92  
    93  func free(p *C.char) {
    94  	C.free(unsafe.Pointer(p))
    95  }
    96  
    97  func openDir(path string) (*C.DIR, error) {
    98  	Cpath := C.CString(path)
    99  	defer free(Cpath)
   100  
   101  	dir := C.opendir(Cpath)
   102  	if dir == nil {
   103  		return nil, fmt.Errorf("Can't open dir")
   104  	}
   105  	return dir, nil
   106  }
   107  
   108  func closeDir(dir *C.DIR) {
   109  	if dir != nil {
   110  		C.closedir(dir)
   111  	}
   112  }
   113  
   114  func getDirFd(dir *C.DIR) uintptr {
   115  	return uintptr(C.dirfd(dir))
   116  }
   117  
   118  func subvolCreate(path, name string) error {
   119  	dir, err := openDir(path)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer closeDir(dir)
   124  
   125  	var args C.struct_btrfs_ioctl_vol_args
   126  	for i, c := range []byte(name) {
   127  		args.name[i] = C.char(c)
   128  	}
   129  
   130  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
   131  		uintptr(unsafe.Pointer(&args)))
   132  	if errno != 0 {
   133  		return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
   134  	}
   135  	return nil
   136  }
   137  
   138  func subvolSnapshot(src, dest, name string) error {
   139  	srcDir, err := openDir(src)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	defer closeDir(srcDir)
   144  
   145  	destDir, err := openDir(dest)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	defer closeDir(destDir)
   150  
   151  	var args C.struct_btrfs_ioctl_vol_args_v2
   152  	args.fd = C.__s64(getDirFd(srcDir))
   153  	for i, c := range []byte(name) {
   154  		args.name[i] = C.char(c)
   155  	}
   156  
   157  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
   158  		uintptr(unsafe.Pointer(&args)))
   159  	if errno != 0 {
   160  		return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
   161  	}
   162  	return nil
   163  }
   164  
   165  func isSubvolume(p string) (bool, error) {
   166  	var bufStat syscall.Stat_t
   167  	if err := syscall.Lstat(p, &bufStat); err != nil {
   168  		return false, err
   169  	}
   170  
   171  	// return true if it is a btrfs subvolume
   172  	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
   173  }
   174  
   175  func subvolDelete(dirpath, name string) error {
   176  	dir, err := openDir(dirpath)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	defer closeDir(dir)
   181  
   182  	var args C.struct_btrfs_ioctl_vol_args
   183  
   184  	// walk the btrfs subvolumes
   185  	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
   186  		// we want to check children only so skip itself
   187  		// it will be removed after the filepath walk anyways
   188  		if f.IsDir() && p != path.Join(dirpath, name) {
   189  			sv, err := isSubvolume(p)
   190  			if err != nil {
   191  				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
   192  			}
   193  			if sv {
   194  				if err := subvolDelete(p, f.Name()); err != nil {
   195  					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
   196  				}
   197  			}
   198  		}
   199  		return nil
   200  	}
   201  	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
   202  		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
   203  	}
   204  
   205  	// all subvolumes have been removed
   206  	// now remove the one originally passed in
   207  	for i, c := range []byte(name) {
   208  		args.name[i] = C.char(c)
   209  	}
   210  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
   211  		uintptr(unsafe.Pointer(&args)))
   212  	if errno != 0 {
   213  		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
   214  	}
   215  	return nil
   216  }
   217  
   218  func (d *Driver) subvolumesDir() string {
   219  	return path.Join(d.home, "subvolumes")
   220  }
   221  
   222  func (d *Driver) subvolumesDirID(id string) string {
   223  	return path.Join(d.subvolumesDir(), id)
   224  }
   225  
   226  // Create the filesystem with given id.
   227  func (d *Driver) Create(id string, parent string) error {
   228  	subvolumes := path.Join(d.home, "subvolumes")
   229  	if err := os.MkdirAll(subvolumes, 0700); err != nil {
   230  		return err
   231  	}
   232  	if parent == "" {
   233  		if err := subvolCreate(subvolumes, id); err != nil {
   234  			return err
   235  		}
   236  	} else {
   237  		parentDir, err := d.Get(parent, "")
   238  		if err != nil {
   239  			return err
   240  		}
   241  		if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
   242  			return err
   243  		}
   244  	}
   245  	return nil
   246  }
   247  
   248  // Remove the filesystem with given id.
   249  func (d *Driver) Remove(id string) error {
   250  	dir := d.subvolumesDirID(id)
   251  	if _, err := os.Stat(dir); err != nil {
   252  		return err
   253  	}
   254  	if err := subvolDelete(d.subvolumesDir(), id); err != nil {
   255  		return err
   256  	}
   257  	return os.RemoveAll(dir)
   258  }
   259  
   260  // Get the requested filesystem id.
   261  func (d *Driver) Get(id, mountLabel string) (string, error) {
   262  	dir := d.subvolumesDirID(id)
   263  	st, err := os.Stat(dir)
   264  	if err != nil {
   265  		return "", err
   266  	}
   267  
   268  	if !st.IsDir() {
   269  		return "", fmt.Errorf("%s: not a directory", dir)
   270  	}
   271  
   272  	return dir, nil
   273  }
   274  
   275  // Put is not implemented for BTRFS as there is no cleanup required for the id.
   276  func (d *Driver) Put(id string) error {
   277  	// Get() creates no runtime resources (like e.g. mounts)
   278  	// so this doesn't need to do anything.
   279  	return nil
   280  }
   281  
   282  // Exists checks if the id exists in the filesystem.
   283  func (d *Driver) Exists(id string) bool {
   284  	dir := d.subvolumesDirID(id)
   285  	_, err := os.Stat(dir)
   286  	return err == nil
   287  }