github.com/shishir-a412ed/docker@v1.3.2-0.20180103180333-fda904911d87/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  	"io/ioutil"
    20  	"math"
    21  	"os"
    22  	"path"
    23  	"path/filepath"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"unsafe"
    28  
    29  	"github.com/docker/docker/daemon/graphdriver"
    30  	"github.com/docker/docker/pkg/containerfs"
    31  	"github.com/docker/docker/pkg/idtools"
    32  	"github.com/docker/docker/pkg/mount"
    33  	"github.com/docker/docker/pkg/parsers"
    34  	"github.com/docker/docker/pkg/system"
    35  	"github.com/docker/go-units"
    36  	"github.com/opencontainers/selinux/go-selinux/label"
    37  	"github.com/sirupsen/logrus"
    38  	"golang.org/x/sys/unix"
    39  )
    40  
    41  func init() {
    42  	graphdriver.Register("btrfs", Init)
    43  }
    44  
    45  type btrfsOptions struct {
    46  	minSpace uint64
    47  	size     uint64
    48  }
    49  
    50  // Init returns a new BTRFS driver.
    51  // An error is returned if BTRFS is not supported.
    52  func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
    53  
    54  	// Perform feature detection on /var/lib/docker/btrfs if it's an existing directory.
    55  	// This covers situations where /var/lib/docker/btrfs is a mount, and on a different
    56  	// filesystem than /var/lib/docker.
    57  	// If the path does not exist, fall back to using /var/lib/docker for feature detection.
    58  	testdir := home
    59  	if _, err := os.Stat(testdir); os.IsNotExist(err) {
    60  		testdir = filepath.Dir(testdir)
    61  	}
    62  
    63  	fsMagic, err := graphdriver.GetFSMagic(testdir)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	if fsMagic != graphdriver.FsMagicBtrfs {
    69  		return nil, graphdriver.ErrPrerequisites
    70  	}
    71  
    72  	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	if err := idtools.MkdirAllAndChown(home, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	if err := mount.MakePrivate(home); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	opt, userDiskQuota, err := parseOptions(options)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	driver := &Driver{
    90  		home:    home,
    91  		uidMaps: uidMaps,
    92  		gidMaps: gidMaps,
    93  		options: opt,
    94  	}
    95  
    96  	if userDiskQuota {
    97  		if err := driver.subvolEnableQuota(); err != nil {
    98  			return nil, err
    99  		}
   100  	}
   101  
   102  	return graphdriver.NewNaiveDiffDriver(driver, uidMaps, gidMaps), nil
   103  }
   104  
   105  func parseOptions(opt []string) (btrfsOptions, bool, error) {
   106  	var options btrfsOptions
   107  	userDiskQuota := false
   108  	for _, option := range opt {
   109  		key, val, err := parsers.ParseKeyValueOpt(option)
   110  		if err != nil {
   111  			return options, userDiskQuota, err
   112  		}
   113  		key = strings.ToLower(key)
   114  		switch key {
   115  		case "btrfs.min_space":
   116  			minSpace, err := units.RAMInBytes(val)
   117  			if err != nil {
   118  				return options, userDiskQuota, err
   119  			}
   120  			userDiskQuota = true
   121  			options.minSpace = uint64(minSpace)
   122  		default:
   123  			return options, userDiskQuota, fmt.Errorf("Unknown option %s", key)
   124  		}
   125  	}
   126  	return options, userDiskQuota, nil
   127  }
   128  
   129  // Driver contains information about the filesystem mounted.
   130  type Driver struct {
   131  	//root of the file system
   132  	home         string
   133  	uidMaps      []idtools.IDMap
   134  	gidMaps      []idtools.IDMap
   135  	options      btrfsOptions
   136  	quotaEnabled bool
   137  	once         sync.Once
   138  }
   139  
   140  // String prints the name of the driver (btrfs).
   141  func (d *Driver) String() string {
   142  	return "btrfs"
   143  }
   144  
   145  // Status returns current driver information in a two dimensional string array.
   146  // Output contains "Build Version" and "Library Version" of the btrfs libraries used.
   147  // Version information can be used to check compatibility with your kernel.
   148  func (d *Driver) Status() [][2]string {
   149  	status := [][2]string{}
   150  	if bv := btrfsBuildVersion(); bv != "-" {
   151  		status = append(status, [2]string{"Build Version", bv})
   152  	}
   153  	if lv := btrfsLibVersion(); lv != -1 {
   154  		status = append(status, [2]string{"Library Version", fmt.Sprintf("%d", lv)})
   155  	}
   156  	return status
   157  }
   158  
   159  // GetMetadata returns empty metadata for this driver.
   160  func (d *Driver) GetMetadata(id string) (map[string]string, error) {
   161  	return nil, nil
   162  }
   163  
   164  // Cleanup unmounts the home directory.
   165  func (d *Driver) Cleanup() error {
   166  	if err := d.subvolDisableQuota(); err != nil {
   167  		return err
   168  	}
   169  
   170  	return mount.Unmount(d.home)
   171  }
   172  
   173  func free(p *C.char) {
   174  	C.free(unsafe.Pointer(p))
   175  }
   176  
   177  func openDir(path string) (*C.DIR, error) {
   178  	Cpath := C.CString(path)
   179  	defer free(Cpath)
   180  
   181  	dir := C.opendir(Cpath)
   182  	if dir == nil {
   183  		return nil, fmt.Errorf("Can't open dir")
   184  	}
   185  	return dir, nil
   186  }
   187  
   188  func closeDir(dir *C.DIR) {
   189  	if dir != nil {
   190  		C.closedir(dir)
   191  	}
   192  }
   193  
   194  func getDirFd(dir *C.DIR) uintptr {
   195  	return uintptr(C.dirfd(dir))
   196  }
   197  
   198  func subvolCreate(path, name string) error {
   199  	dir, err := openDir(path)
   200  	if err != nil {
   201  		return err
   202  	}
   203  	defer closeDir(dir)
   204  
   205  	var args C.struct_btrfs_ioctl_vol_args
   206  	for i, c := range []byte(name) {
   207  		args.name[i] = C.char(c)
   208  	}
   209  
   210  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SUBVOL_CREATE,
   211  		uintptr(unsafe.Pointer(&args)))
   212  	if errno != 0 {
   213  		return fmt.Errorf("Failed to create btrfs subvolume: %v", errno.Error())
   214  	}
   215  	return nil
   216  }
   217  
   218  func subvolSnapshot(src, dest, name string) error {
   219  	srcDir, err := openDir(src)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	defer closeDir(srcDir)
   224  
   225  	destDir, err := openDir(dest)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	defer closeDir(destDir)
   230  
   231  	var args C.struct_btrfs_ioctl_vol_args_v2
   232  	args.fd = C.__s64(getDirFd(srcDir))
   233  
   234  	var cs = C.CString(name)
   235  	C.set_name_btrfs_ioctl_vol_args_v2(&args, cs)
   236  	C.free(unsafe.Pointer(cs))
   237  
   238  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(destDir), C.BTRFS_IOC_SNAP_CREATE_V2,
   239  		uintptr(unsafe.Pointer(&args)))
   240  	if errno != 0 {
   241  		return fmt.Errorf("Failed to create btrfs snapshot: %v", errno.Error())
   242  	}
   243  	return nil
   244  }
   245  
   246  func isSubvolume(p string) (bool, error) {
   247  	var bufStat unix.Stat_t
   248  	if err := unix.Lstat(p, &bufStat); err != nil {
   249  		return false, err
   250  	}
   251  
   252  	// return true if it is a btrfs subvolume
   253  	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
   254  }
   255  
   256  func subvolDelete(dirpath, name string, quotaEnabled bool) error {
   257  	dir, err := openDir(dirpath)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	defer closeDir(dir)
   262  	fullPath := path.Join(dirpath, name)
   263  
   264  	var args C.struct_btrfs_ioctl_vol_args
   265  
   266  	// walk the btrfs subvolumes
   267  	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
   268  		if err != nil {
   269  			if os.IsNotExist(err) && p != fullPath {
   270  				// missing most likely because the path was a subvolume that got removed in the previous iteration
   271  				// since it's gone anyway, we don't care
   272  				return nil
   273  			}
   274  			return fmt.Errorf("error walking subvolumes: %v", err)
   275  		}
   276  		// we want to check children only so skip itself
   277  		// it will be removed after the filepath walk anyways
   278  		if f.IsDir() && p != fullPath {
   279  			sv, err := isSubvolume(p)
   280  			if err != nil {
   281  				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
   282  			}
   283  			if sv {
   284  				if err := subvolDelete(path.Dir(p), f.Name(), quotaEnabled); err != nil {
   285  					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
   286  				}
   287  			}
   288  		}
   289  		return nil
   290  	}
   291  	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
   292  		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
   293  	}
   294  
   295  	if quotaEnabled {
   296  		if qgroupid, err := subvolLookupQgroup(fullPath); err == nil {
   297  			var args C.struct_btrfs_ioctl_qgroup_create_args
   298  			args.qgroupid = C.__u64(qgroupid)
   299  
   300  			_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QGROUP_CREATE,
   301  				uintptr(unsafe.Pointer(&args)))
   302  			if errno != 0 {
   303  				logrus.Errorf("Failed to delete btrfs qgroup %v for %s: %v", qgroupid, fullPath, errno.Error())
   304  			}
   305  		} else {
   306  			logrus.Errorf("Failed to lookup btrfs qgroup for %s: %v", fullPath, err.Error())
   307  		}
   308  	}
   309  
   310  	// all subvolumes have been removed
   311  	// now remove the one originally passed in
   312  	for i, c := range []byte(name) {
   313  		args.name[i] = C.char(c)
   314  	}
   315  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
   316  		uintptr(unsafe.Pointer(&args)))
   317  	if errno != 0 {
   318  		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
   319  	}
   320  	return nil
   321  }
   322  
   323  func (d *Driver) updateQuotaStatus() {
   324  	d.once.Do(func() {
   325  		if !d.quotaEnabled {
   326  			// In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed
   327  			if err := subvolQgroupStatus(d.home); err != nil {
   328  				// quota is still not enabled
   329  				return
   330  			}
   331  			d.quotaEnabled = true
   332  		}
   333  	})
   334  }
   335  
   336  func (d *Driver) subvolEnableQuota() error {
   337  	d.updateQuotaStatus()
   338  
   339  	if d.quotaEnabled {
   340  		return nil
   341  	}
   342  
   343  	dir, err := openDir(d.home)
   344  	if err != nil {
   345  		return err
   346  	}
   347  	defer closeDir(dir)
   348  
   349  	var args C.struct_btrfs_ioctl_quota_ctl_args
   350  	args.cmd = C.BTRFS_QUOTA_CTL_ENABLE
   351  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QUOTA_CTL,
   352  		uintptr(unsafe.Pointer(&args)))
   353  	if errno != 0 {
   354  		return fmt.Errorf("Failed to enable btrfs quota for %s: %v", dir, errno.Error())
   355  	}
   356  
   357  	d.quotaEnabled = true
   358  
   359  	return nil
   360  }
   361  
   362  func (d *Driver) subvolDisableQuota() error {
   363  	d.updateQuotaStatus()
   364  
   365  	if !d.quotaEnabled {
   366  		return nil
   367  	}
   368  
   369  	dir, err := openDir(d.home)
   370  	if err != nil {
   371  		return err
   372  	}
   373  	defer closeDir(dir)
   374  
   375  	var args C.struct_btrfs_ioctl_quota_ctl_args
   376  	args.cmd = C.BTRFS_QUOTA_CTL_DISABLE
   377  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QUOTA_CTL,
   378  		uintptr(unsafe.Pointer(&args)))
   379  	if errno != 0 {
   380  		return fmt.Errorf("Failed to disable btrfs quota for %s: %v", dir, errno.Error())
   381  	}
   382  
   383  	d.quotaEnabled = false
   384  
   385  	return nil
   386  }
   387  
   388  func (d *Driver) subvolRescanQuota() error {
   389  	d.updateQuotaStatus()
   390  
   391  	if !d.quotaEnabled {
   392  		return nil
   393  	}
   394  
   395  	dir, err := openDir(d.home)
   396  	if err != nil {
   397  		return err
   398  	}
   399  	defer closeDir(dir)
   400  
   401  	var args C.struct_btrfs_ioctl_quota_rescan_args
   402  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QUOTA_RESCAN_WAIT,
   403  		uintptr(unsafe.Pointer(&args)))
   404  	if errno != 0 {
   405  		return fmt.Errorf("Failed to rescan btrfs quota for %s: %v", dir, errno.Error())
   406  	}
   407  
   408  	return nil
   409  }
   410  
   411  func subvolLimitQgroup(path string, size uint64) error {
   412  	dir, err := openDir(path)
   413  	if err != nil {
   414  		return err
   415  	}
   416  	defer closeDir(dir)
   417  
   418  	var args C.struct_btrfs_ioctl_qgroup_limit_args
   419  	args.lim.max_referenced = C.__u64(size)
   420  	args.lim.flags = C.BTRFS_QGROUP_LIMIT_MAX_RFER
   421  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QGROUP_LIMIT,
   422  		uintptr(unsafe.Pointer(&args)))
   423  	if errno != 0 {
   424  		return fmt.Errorf("Failed to limit qgroup for %s: %v", dir, errno.Error())
   425  	}
   426  
   427  	return nil
   428  }
   429  
   430  // subvolQgroupStatus performs a BTRFS_IOC_TREE_SEARCH on the root path
   431  // with search key of BTRFS_QGROUP_STATUS_KEY.
   432  // In case qgroup is enabled, the retuned key type will match BTRFS_QGROUP_STATUS_KEY.
   433  // For more details please see https://github.com/kdave/btrfs-progs/blob/v4.9/qgroup.c#L1035
   434  func subvolQgroupStatus(path string) error {
   435  	dir, err := openDir(path)
   436  	if err != nil {
   437  		return err
   438  	}
   439  	defer closeDir(dir)
   440  
   441  	var args C.struct_btrfs_ioctl_search_args
   442  	args.key.tree_id = C.BTRFS_QUOTA_TREE_OBJECTID
   443  	args.key.min_type = C.BTRFS_QGROUP_STATUS_KEY
   444  	args.key.max_type = C.BTRFS_QGROUP_STATUS_KEY
   445  	args.key.max_objectid = C.__u64(math.MaxUint64)
   446  	args.key.max_offset = C.__u64(math.MaxUint64)
   447  	args.key.max_transid = C.__u64(math.MaxUint64)
   448  	args.key.nr_items = 4096
   449  
   450  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_TREE_SEARCH,
   451  		uintptr(unsafe.Pointer(&args)))
   452  	if errno != 0 {
   453  		return fmt.Errorf("Failed to search qgroup for %s: %v", path, errno.Error())
   454  	}
   455  	sh := (*C.struct_btrfs_ioctl_search_header)(unsafe.Pointer(&args.buf))
   456  	if sh._type != C.BTRFS_QGROUP_STATUS_KEY {
   457  		return fmt.Errorf("Invalid qgroup search header type for %s: %v", path, sh._type)
   458  	}
   459  	return nil
   460  }
   461  
   462  func subvolLookupQgroup(path string) (uint64, error) {
   463  	dir, err := openDir(path)
   464  	if err != nil {
   465  		return 0, err
   466  	}
   467  	defer closeDir(dir)
   468  
   469  	var args C.struct_btrfs_ioctl_ino_lookup_args
   470  	args.objectid = C.BTRFS_FIRST_FREE_OBJECTID
   471  
   472  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_INO_LOOKUP,
   473  		uintptr(unsafe.Pointer(&args)))
   474  	if errno != 0 {
   475  		return 0, fmt.Errorf("Failed to lookup qgroup for %s: %v", dir, errno.Error())
   476  	}
   477  	if args.treeid == 0 {
   478  		return 0, fmt.Errorf("Invalid qgroup id for %s: 0", dir)
   479  	}
   480  
   481  	return uint64(args.treeid), nil
   482  }
   483  
   484  func (d *Driver) subvolumesDir() string {
   485  	return path.Join(d.home, "subvolumes")
   486  }
   487  
   488  func (d *Driver) subvolumesDirID(id string) string {
   489  	return path.Join(d.subvolumesDir(), id)
   490  }
   491  
   492  func (d *Driver) quotasDir() string {
   493  	return path.Join(d.home, "quotas")
   494  }
   495  
   496  func (d *Driver) quotasDirID(id string) string {
   497  	return path.Join(d.quotasDir(), id)
   498  }
   499  
   500  // CreateReadWrite creates a layer that is writable for use as a container
   501  // file system.
   502  func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
   503  	return d.Create(id, parent, opts)
   504  }
   505  
   506  // Create the filesystem with given id.
   507  func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
   508  	quotas := path.Join(d.home, "quotas")
   509  	subvolumes := path.Join(d.home, "subvolumes")
   510  	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
   511  	if err != nil {
   512  		return err
   513  	}
   514  	if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
   515  		return err
   516  	}
   517  	if parent == "" {
   518  		if err := subvolCreate(subvolumes, id); err != nil {
   519  			return err
   520  		}
   521  	} else {
   522  		parentDir := d.subvolumesDirID(parent)
   523  		st, err := os.Stat(parentDir)
   524  		if err != nil {
   525  			return err
   526  		}
   527  		if !st.IsDir() {
   528  			return fmt.Errorf("%s: not a directory", parentDir)
   529  		}
   530  		if err := subvolSnapshot(parentDir, subvolumes, id); err != nil {
   531  			return err
   532  		}
   533  	}
   534  
   535  	var storageOpt map[string]string
   536  	if opts != nil {
   537  		storageOpt = opts.StorageOpt
   538  	}
   539  
   540  	if _, ok := storageOpt["size"]; ok {
   541  		driver := &Driver{}
   542  		if err := d.parseStorageOpt(storageOpt, driver); err != nil {
   543  			return err
   544  		}
   545  
   546  		if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil {
   547  			return err
   548  		}
   549  		if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.IDPair{UID: rootUID, GID: rootGID}); err != nil {
   550  			return err
   551  		}
   552  		if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {
   553  			return err
   554  		}
   555  	}
   556  
   557  	// if we have a remapped root (user namespaces enabled), change the created snapshot
   558  	// dir ownership to match
   559  	if rootUID != 0 || rootGID != 0 {
   560  		if err := os.Chown(path.Join(subvolumes, id), rootUID, rootGID); err != nil {
   561  			return err
   562  		}
   563  	}
   564  
   565  	mountLabel := ""
   566  	if opts != nil {
   567  		mountLabel = opts.MountLabel
   568  	}
   569  
   570  	return label.Relabel(path.Join(subvolumes, id), mountLabel, false)
   571  }
   572  
   573  // Parse btrfs storage options
   574  func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error {
   575  	// Read size to change the subvolume disk quota per container
   576  	for key, val := range storageOpt {
   577  		key := strings.ToLower(key)
   578  		switch key {
   579  		case "size":
   580  			size, err := units.RAMInBytes(val)
   581  			if err != nil {
   582  				return err
   583  			}
   584  			driver.options.size = uint64(size)
   585  		default:
   586  			return fmt.Errorf("Unknown option %s", key)
   587  		}
   588  	}
   589  
   590  	return nil
   591  }
   592  
   593  // Set btrfs storage size
   594  func (d *Driver) setStorageSize(dir string, driver *Driver) error {
   595  	if driver.options.size <= 0 {
   596  		return fmt.Errorf("btrfs: invalid storage size: %s", units.HumanSize(float64(driver.options.size)))
   597  	}
   598  	if d.options.minSpace > 0 && driver.options.size < d.options.minSpace {
   599  		return fmt.Errorf("btrfs: storage size cannot be less than %s", units.HumanSize(float64(d.options.minSpace)))
   600  	}
   601  
   602  	if err := d.subvolEnableQuota(); err != nil {
   603  		return err
   604  	}
   605  
   606  	if err := subvolLimitQgroup(dir, driver.options.size); err != nil {
   607  		return err
   608  	}
   609  
   610  	return nil
   611  }
   612  
   613  // Remove the filesystem with given id.
   614  func (d *Driver) Remove(id string) error {
   615  	dir := d.subvolumesDirID(id)
   616  	if _, err := os.Stat(dir); err != nil {
   617  		return err
   618  	}
   619  	quotasDir := d.quotasDirID(id)
   620  	if _, err := os.Stat(quotasDir); err == nil {
   621  		if err := os.Remove(quotasDir); err != nil {
   622  			return err
   623  		}
   624  	} else if !os.IsNotExist(err) {
   625  		return err
   626  	}
   627  
   628  	// Call updateQuotaStatus() to invoke status update
   629  	d.updateQuotaStatus()
   630  
   631  	if err := subvolDelete(d.subvolumesDir(), id, d.quotaEnabled); err != nil {
   632  		return err
   633  	}
   634  	if err := system.EnsureRemoveAll(dir); err != nil {
   635  		return err
   636  	}
   637  	if err := d.subvolRescanQuota(); err != nil {
   638  		return err
   639  	}
   640  	return nil
   641  }
   642  
   643  // Get the requested filesystem id.
   644  func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
   645  	dir := d.subvolumesDirID(id)
   646  	st, err := os.Stat(dir)
   647  	if err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	if !st.IsDir() {
   652  		return nil, fmt.Errorf("%s: not a directory", dir)
   653  	}
   654  
   655  	if quota, err := ioutil.ReadFile(d.quotasDirID(id)); err == nil {
   656  		if size, err := strconv.ParseUint(string(quota), 10, 64); err == nil && size >= d.options.minSpace {
   657  			if err := d.subvolEnableQuota(); err != nil {
   658  				return nil, err
   659  			}
   660  			if err := subvolLimitQgroup(dir, size); err != nil {
   661  				return nil, err
   662  			}
   663  		}
   664  	}
   665  
   666  	return containerfs.NewLocalContainerFS(dir), nil
   667  }
   668  
   669  // Put is not implemented for BTRFS as there is no cleanup required for the id.
   670  func (d *Driver) Put(id string) error {
   671  	// Get() creates no runtime resources (like e.g. mounts)
   672  	// so this doesn't need to do anything.
   673  	return nil
   674  }
   675  
   676  // Exists checks if the id exists in the filesystem.
   677  func (d *Driver) Exists(id string) bool {
   678  	dir := d.subvolumesDirID(id)
   679  	_, err := os.Stat(dir)
   680  	return err == nil
   681  }