Преглед изворни кода

cleanup and fix btrfs subvolume recursion deletion

Signed-off-by: Jessica Frazelle <acidburn@docker.com>
Jessica Frazelle пре 10 година
родитељ
комит
bd06432ba3

+ 1 - 1
contrib/builder/deb/debian-wheezy/Dockerfile

@@ -4,7 +4,7 @@
 
 FROM debian:wheezy-backports
 
-RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && apt-get install -y bash-completion btrfs-tools/wheezy-backports build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
 
 ENV GO_VERSION 1.4.2
 RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local

+ 6 - 0
contrib/builder/deb/generate.sh

@@ -73,6 +73,12 @@ for version in "${versions[@]}"; do
 		extraBuildTags+=' exclude_graphdriver_btrfs'
 	fi
 
+	if [ "$suite" = 'wheezy' ]; then
+		# pull btrfs-toold from backports
+		backports="/$suite-backports"
+		packages=( "${packages[@]/btrfs-tools/btrfs-tools$backports}" )
+	fi
+
 	echo "RUN apt-get update && apt-get install -y ${packages[*]} --no-install-recommends && rm -rf /var/lib/apt/lists/*" >> "$version/Dockerfile"
 
 	echo >> "$version/Dockerfile"

+ 31 - 35
daemon/graphdriver/btrfs/btrfs.go

@@ -26,21 +26,6 @@ func init() {
 	graphdriver.Register("btrfs", Init)
 }
 
-func is_subvolume(dirpath string) (bool, error) {
-	rootdir := path.Dir(dirpath)
-
-	var bufStat syscall.Stat_t
-	if err := syscall.Lstat(rootdir, &bufStat); err != nil {
-		return false, err
-	}
-
-	if bufStat.Ino != C.BTRFS_FIRST_FREE_OBJECTID {
-		return false, nil
-	}
-
-	return true, nil
-}
-
 // Init returns a new BTRFS driver.
 // An error is returned if BTRFS is not supported.
 func Init(home string, options []string) (graphdriver.Driver, error) {
@@ -177,6 +162,16 @@ func subvolSnapshot(src, dest, name string) error {
 	return nil
 }
 
+func isSubvolume(p string) (bool, error) {
+	var bufStat syscall.Stat_t
+	if err := syscall.Lstat(p, &bufStat); err != nil {
+		return false, err
+	}
+
+	// return true if it is a btrfs subvolume
+	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
+}
+
 func subvolDelete(dirpath, name string) error {
 	dir, err := openDir(dirpath)
 	if err != nil {
@@ -186,35 +181,36 @@ func subvolDelete(dirpath, name string) error {
 
 	var args C.struct_btrfs_ioctl_vol_args
 
-	filepath.Walk(dirpath,
-		func(dirpath string, f os.FileInfo, err error) error {
-			if f.IsDir() {
-				isSubvolumes, err := is_subvolume(path.Join(dirpath, f.Name()))
-				if err != nil {
-					return err
-				}
-				if isSubvolumes {
-					for i, c := range []byte(f.Name()) {
-						args.name[i] = C.char(c)
-					}
-					_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
-						uintptr(unsafe.Pointer(&args)))
-					if errno != 0 {
-						return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
-					}
+	// walk the btrfs subvolumes
+	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
+		// we want to check children only so skip itself
+		// it will be removed after the filepath walk anyways
+		if f.IsDir() && p != path.Join(dirpath, name) {
+			sv, err := isSubvolume(p)
+			if err != nil {
+				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
+			}
+			if sv {
+				if err := subvolDelete(p, f.Name()); err != nil {
+					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
 				}
-				return nil
 			}
-			return nil
-		})
+		}
+		return nil
+	}
+	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
+		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
+	}
 
+	// all subvolumes have been removed
+	// now remove the one originally passed in
 	for i, c := range []byte(name) {
 		args.name[i] = C.char(c)
 	}
 	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
 		uintptr(unsafe.Pointer(&args)))
 	if errno != 0 {
-		return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
+		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
 	}
 	return nil
 }

+ 2 - 1
daemon/graphdriver/btrfs/btrfs_test.go

@@ -3,8 +3,9 @@
 package btrfs
 
 import (
-	"github.com/docker/docker/daemon/graphdriver/graphtest"
 	"testing"
+
+	"github.com/docker/docker/daemon/graphdriver/graphtest"
 )
 
 // This avoids creating a new driver for each test if all tests are run