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 c8a56fe094..d0ad27d370 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" @@ -160,22 +162,55 @@ func subvolSnapshot(src, dest, name string) error { return nil } -func subvolDelete(path, name string) error { - dir, err := openDir(path) +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 { return err } defer closeDir(dir) var args C.struct_btrfs_ioctl_vol_args + + // 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 + } + 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