From dea78fc2ce9d7a2cf24b79f8625a06dbd74403a2 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Wed, 5 Aug 2015 15:10:40 +0800 Subject: [PATCH 1/2] fix 9939: docker does not remove btrfs subvolumes when destroying container Signed-off-by: Ma Shimiao --- daemon/graphdriver/btrfs/btrfs.go | 45 ++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index c8a56fe094..7ea4dafca9 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/daemon/graphdriver/btrfs/btrfs.go @@ -6,6 +6,7 @@ package btrfs #include #include #include +#include */ import "C" @@ -13,6 +14,7 @@ import ( "fmt" "os" "path" + "path/filepath" "syscall" "unsafe" @@ -24,6 +26,21 @@ 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) { @@ -160,18 +177,40 @@ func subvolSnapshot(src, dest, name string) error { return nil } -func subvolDelete(path, name string) error { - dir, err := openDir(path) +func subvolDelete(dirpath, name string) error { + dir, err := openDir(dirpath) if err != nil { return err } defer closeDir(dir) 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()) + } + } + return nil + } + return nil + }) + 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 { From bd06432ba32a6cc555de82e08b125992aaec25fb Mon Sep 17 00:00:00 2001 From: Jessica Frazelle Date: Mon, 24 Aug 2015 16:18:01 -0700 Subject: [PATCH 2/2] cleanup and fix btrfs subvolume recursion deletion Signed-off-by: Jessica Frazelle --- contrib/builder/deb/debian-wheezy/Dockerfile | 2 +- contrib/builder/deb/generate.sh | 6 ++ daemon/graphdriver/btrfs/btrfs.go | 68 +++++++++----------- daemon/graphdriver/btrfs/btrfs_test.go | 3 +- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/contrib/builder/deb/debian-wheezy/Dockerfile b/contrib/builder/deb/debian-wheezy/Dockerfile index 8c94a9fbdf..1b6d5330de 100644 --- a/contrib/builder/deb/debian-wheezy/Dockerfile +++ b/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 diff --git a/contrib/builder/deb/generate.sh b/contrib/builder/deb/generate.sh index aef49d0877..901993323a 100755 --- a/contrib/builder/deb/generate.sh +++ b/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" diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index 7ea4dafca9..d0ad27d370 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/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()) - } - } - return nil + // 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) } - return nil - }) + 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 + } + 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 } diff --git a/daemon/graphdriver/btrfs/btrfs_test.go b/daemon/graphdriver/btrfs/btrfs_test.go index bfd15e06f5..32a8bf1741 100644 --- a/daemon/graphdriver/btrfs/btrfs_test.go +++ b/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