Browse code

cleanup and fix btrfs subvolume recursion deletion

Signed-off-by: Jessica Frazelle <acidburn@docker.com>

Jessica Frazelle authored on 2015/08/25 08:18:01
Showing 4 changed files
... ...
@@ -4,7 +4,7 @@
4 4
 
5 5
 FROM debian:wheezy-backports
6 6
 
7
-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/*
7
+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/*
8 8
 
9 9
 ENV GO_VERSION 1.4.2
10 10
 RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
... ...
@@ -73,6 +73,12 @@ for version in "${versions[@]}"; do
73 73
 		extraBuildTags+=' exclude_graphdriver_btrfs'
74 74
 	fi
75 75
 
76
+	if [ "$suite" = 'wheezy' ]; then
77
+		# pull btrfs-toold from backports
78
+		backports="/$suite-backports"
79
+		packages=( "${packages[@]/btrfs-tools/btrfs-tools$backports}" )
80
+	fi
81
+
76 82
 	echo "RUN apt-get update && apt-get install -y ${packages[*]} --no-install-recommends && rm -rf /var/lib/apt/lists/*" >> "$version/Dockerfile"
77 83
 
78 84
 	echo >> "$version/Dockerfile"
... ...
@@ -26,21 +26,6 @@ func init() {
26 26
 	graphdriver.Register("btrfs", Init)
27 27
 }
28 28
 
29
-func is_subvolume(dirpath string) (bool, error) {
30
-	rootdir := path.Dir(dirpath)
31
-
32
-	var bufStat syscall.Stat_t
33
-	if err := syscall.Lstat(rootdir, &bufStat); err != nil {
34
-		return false, err
35
-	}
36
-
37
-	if bufStat.Ino != C.BTRFS_FIRST_FREE_OBJECTID {
38
-		return false, nil
39
-	}
40
-
41
-	return true, nil
42
-}
43
-
44 29
 // Init returns a new BTRFS driver.
45 30
 // An error is returned if BTRFS is not supported.
46 31
 func Init(home string, options []string) (graphdriver.Driver, error) {
... ...
@@ -177,6 +162,16 @@ func subvolSnapshot(src, dest, name string) error {
177 177
 	return nil
178 178
 }
179 179
 
180
+func isSubvolume(p string) (bool, error) {
181
+	var bufStat syscall.Stat_t
182
+	if err := syscall.Lstat(p, &bufStat); err != nil {
183
+		return false, err
184
+	}
185
+
186
+	// return true if it is a btrfs subvolume
187
+	return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
188
+}
189
+
180 190
 func subvolDelete(dirpath, name string) error {
181 191
 	dir, err := openDir(dirpath)
182 192
 	if err != nil {
... ...
@@ -186,35 +181,36 @@ func subvolDelete(dirpath, name string) error {
186 186
 
187 187
 	var args C.struct_btrfs_ioctl_vol_args
188 188
 
189
-	filepath.Walk(dirpath,
190
-		func(dirpath string, f os.FileInfo, err error) error {
191
-			if f.IsDir() {
192
-				isSubvolumes, err := is_subvolume(path.Join(dirpath, f.Name()))
193
-				if err != nil {
194
-					return err
195
-				}
196
-				if isSubvolumes {
197
-					for i, c := range []byte(f.Name()) {
198
-						args.name[i] = C.char(c)
199
-					}
200
-					_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
201
-						uintptr(unsafe.Pointer(&args)))
202
-					if errno != 0 {
203
-						return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
204
-					}
189
+	// walk the btrfs subvolumes
190
+	walkSubvolumes := func(p string, f os.FileInfo, err error) error {
191
+		// we want to check children only so skip itself
192
+		// it will be removed after the filepath walk anyways
193
+		if f.IsDir() && p != path.Join(dirpath, name) {
194
+			sv, err := isSubvolume(p)
195
+			if err != nil {
196
+				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
197
+			}
198
+			if sv {
199
+				if err := subvolDelete(p, f.Name()); err != nil {
200
+					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
205 201
 				}
206
-				return nil
207 202
 			}
208
-			return nil
209
-		})
203
+		}
204
+		return nil
205
+	}
206
+	if err := filepath.Walk(path.Join(dirpath, name), walkSubvolumes); err != nil {
207
+		return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
208
+	}
210 209
 
210
+	// all subvolumes have been removed
211
+	// now remove the one originally passed in
211 212
 	for i, c := range []byte(name) {
212 213
 		args.name[i] = C.char(c)
213 214
 	}
214 215
 	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_SNAP_DESTROY,
215 216
 		uintptr(unsafe.Pointer(&args)))
216 217
 	if errno != 0 {
217
-		return fmt.Errorf("Failed to destroy btrfs snapshot: %v", errno.Error())
218
+		return fmt.Errorf("Failed to destroy btrfs snapshot %s for %s: %v", dirpath, name, errno.Error())
218 219
 	}
219 220
 	return nil
220 221
 }
... ...
@@ -3,8 +3,9 @@
3 3
 package btrfs
4 4
 
5 5
 import (
6
-	"github.com/docker/docker/daemon/graphdriver/graphtest"
7 6
 	"testing"
7
+
8
+	"github.com/docker/docker/daemon/graphdriver/graphtest"
8 9
 )
9 10
 
10 11
 // This avoids creating a new driver for each test if all tests are run