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