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