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