Skip to content

Commit f9befce

Browse files
committed
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>
1 parent 51ffc08 commit f9befce

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

daemon/graphdriver/btrfs/btrfs.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,20 +188,29 @@ func subvolDelete(dirpath, name string) error {
188188
return err
189189
}
190190
defer closeDir(dir)
191+
fullPath := path.Join(dirpath, name)
191192

192193
var args C.struct_btrfs_ioctl_vol_args
193194

194195
// walk the btrfs subvolumes
195196
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+
}
196205
// we want to check children only so skip itself
197206
// it will be removed after the filepath walk anyways
198-
if f.IsDir() && p != path.Join(dirpath, name) {
207+
if f.IsDir() && p != fullPath {
199208
sv, err := isSubvolume(p)
200209
if err != nil {
201210
return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
202211
}
203212
if sv {
204-
if err := subvolDelete(p, f.Name()); err != nil {
213+
if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
205214
return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
206215
}
207216
}

daemon/graphdriver/btrfs/btrfs_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
package btrfs
44

55
import (
6+
"os"
7+
"path"
68
"testing"
79

810
"github.com/docker/docker/daemon/graphdriver/graphtest"
@@ -26,6 +28,36 @@ func TestBtrfsCreateSnap(t *testing.T) {
2628
graphtest.DriverTestCreateSnap(t, "btrfs")
2729
}
2830

31+
func TestBtrfsSubvolDelete(t *testing.T) {
32+
d := graphtest.GetDriver(t, "btrfs")
33+
if err := d.Create("test", "", ""); err != nil {
34+
t.Fatal(err)
35+
}
36+
defer graphtest.PutDriver(t)
37+
38+
dir, err := d.Get("test", "")
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
defer d.Put("test")
43+
44+
if err := subvolCreate(dir, "subvoltest"); err != nil {
45+
t.Fatal(err)
46+
}
47+
48+
if _, err := os.Stat(path.Join(dir, "subvoltest")); err != nil {
49+
t.Fatal(err)
50+
}
51+
52+
if err := d.Remove("test"); err != nil {
53+
t.Fatal(err)
54+
}
55+
56+
if _, err := os.Stat(path.Join(dir, "subvoltest")); !os.IsNotExist(err) {
57+
t.Fatalf("expected not exist error on nested subvol, got: %v", err)
58+
}
59+
}
60+
2961
func TestBtrfsTeardown(t *testing.T) {
3062
graphtest.PutDriver(t)
3163
}

0 commit comments

Comments
 (0)