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