Browse code

Fix btrfs recursive btrfs subvol delete

Really fixing 2 things:

1. Panic when any error is detected while walking the btrfs graph dir on
removal due to no error check.
2. Nested subvolumes weren't actually being removed due to passing in
the wrong path

On point 2, for a path detected as a nested subvolume, we were calling
`subvolDelete("/path/to/subvol", "subvol")`, where the last part of the
path was duplicated due to a logic error, and as such actually causing
point #1 since `subvolDelete` joins the two arguemtns, and
`/path/to/subvol/subvol` (the joined version) doesn't exist.

Also adds a test for nested subvol delete.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2015/12/16 05:16:06
Showing 2 changed files
... ...
@@ -188,20 +188,29 @@ func subvolDelete(dirpath, name string) error {
188 188
 		return err
189 189
 	}
190 190
 	defer closeDir(dir)
191
+	fullPath := path.Join(dirpath, name)
191 192
 
192 193
 	var args C.struct_btrfs_ioctl_vol_args
193 194
 
194 195
 	// walk the btrfs subvolumes
195 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
+		}
196 205
 		// we want to check children only so skip itself
197 206
 		// it will be removed after the filepath walk anyways
198
-		if f.IsDir() && p != path.Join(dirpath, name) {
207
+		if f.IsDir() && p != fullPath {
199 208
 			sv, err := isSubvolume(p)
200 209
 			if err != nil {
201 210
 				return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
202 211
 			}
203 212
 			if sv {
204
-				if err := subvolDelete(p, f.Name()); err != nil {
213
+				if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
205 214
 					return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
206 215
 				}
207 216
 			}
... ...
@@ -3,6 +3,8 @@
3 3
 package btrfs
4 4
 
5 5
 import (
6
+	"os"
7
+	"path"
6 8
 	"testing"
7 9
 
8 10
 	"github.com/docker/docker/daemon/graphdriver/graphtest"
... ...
@@ -26,6 +28,36 @@ func TestBtrfsCreateSnap(t *testing.T) {
26 26
 	graphtest.DriverTestCreateSnap(t, "btrfs")
27 27
 }
28 28
 
29
+func TestBtrfsSubvolDelete(t *testing.T) {
30
+	d := graphtest.GetDriver(t, "btrfs")
31
+	if err := d.Create("test", "", ""); err != nil {
32
+		t.Fatal(err)
33
+	}
34
+	defer graphtest.PutDriver(t)
35
+
36
+	dir, err := d.Get("test", "")
37
+	if err != nil {
38
+		t.Fatal(err)
39
+	}
40
+	defer d.Put("test")
41
+
42
+	if err := subvolCreate(dir, "subvoltest"); err != nil {
43
+		t.Fatal(err)
44
+	}
45
+
46
+	if _, err := os.Stat(path.Join(dir, "subvoltest")); err != nil {
47
+		t.Fatal(err)
48
+	}
49
+
50
+	if err := d.Remove("test"); err != nil {
51
+		t.Fatal(err)
52
+	}
53
+
54
+	if _, err := os.Stat(path.Join(dir, "subvoltest")); !os.IsNotExist(err) {
55
+		t.Fatalf("expected not exist error on nested subvol, got: %v", err)
56
+	}
57
+}
58
+
29 59
 func TestBtrfsTeardown(t *testing.T) {
30 60
 	graphtest.PutDriver(t)
31 61
 }