mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Remove btrfs quota groups after containers destroyed
This fix tries to address the issue raised in 29325 where btrfs quota groups are not clean up even after containers have been destroyed. The reason for the issue is that btrfs quota groups have to be explicitly destroyed. This fix fixes this issue. This fix is tested manually in Ubuntu 16.04, with steps specified in 29325. This fix fixes 29325. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
986510951a
commit
e907c6418a
1 changed files with 79 additions and 20 deletions
|
@ -16,13 +16,16 @@ import "C"
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
|
@ -119,6 +122,7 @@ type Driver struct {
|
|||
gidMaps []idtools.IDMap
|
||||
options btrfsOptions
|
||||
quotaEnabled bool
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// String prints the name of the driver (btrfs).
|
||||
|
@ -237,7 +241,7 @@ func isSubvolume(p string) (bool, error) {
|
|||
return bufStat.Ino == C.BTRFS_FIRST_FREE_OBJECTID, nil
|
||||
}
|
||||
|
||||
func subvolDelete(dirpath, name string) error {
|
||||
func subvolDelete(dirpath, name string, quotaEnabled bool) error {
|
||||
dir, err := openDir(dirpath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -265,7 +269,7 @@ func subvolDelete(dirpath, name string) error {
|
|||
return fmt.Errorf("Failed to test if %s is a btrfs subvolume: %v", p, err)
|
||||
}
|
||||
if sv {
|
||||
if err := subvolDelete(path.Dir(p), f.Name()); err != nil {
|
||||
if err := subvolDelete(path.Dir(p), f.Name(), quotaEnabled); err != nil {
|
||||
return fmt.Errorf("Failed to destroy btrfs child subvolume (%s) of parent (%s): %v", p, dirpath, err)
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +280,21 @@ func subvolDelete(dirpath, name string) error {
|
|||
return fmt.Errorf("Recursively walking subvolumes for %s failed: %v", dirpath, err)
|
||||
}
|
||||
|
||||
if quotaEnabled {
|
||||
if qgroupid, err := subvolLookupQgroup(fullPath); err == nil {
|
||||
var args C.struct_btrfs_ioctl_qgroup_create_args
|
||||
args.qgroupid = C.__u64(qgroupid)
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QGROUP_CREATE,
|
||||
uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
logrus.Errorf("Failed to delete btrfs qgroup %v for %s: %v", qgroupid, fullPath, errno.Error())
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("Failed to lookup btrfs qgroup for %s: %v", fullPath, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// all subvolumes have been removed
|
||||
// now remove the one originally passed in
|
||||
for i, c := range []byte(name) {
|
||||
|
@ -289,15 +308,25 @@ func subvolDelete(dirpath, name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) updateQuotaStatus() {
|
||||
d.once.Do(func() {
|
||||
if !d.quotaEnabled {
|
||||
// In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed
|
||||
if err := subvolQgroupStatus(d.home); err != nil {
|
||||
// quota is still not enabled
|
||||
return
|
||||
}
|
||||
d.quotaEnabled = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Driver) subvolEnableQuota() error {
|
||||
d.updateQuotaStatus()
|
||||
|
||||
if d.quotaEnabled {
|
||||
return nil
|
||||
}
|
||||
// In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed
|
||||
if _, err := subvolLookupQgroup(d.home); err == nil {
|
||||
d.quotaEnabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
dir, err := openDir(d.home)
|
||||
if err != nil {
|
||||
|
@ -319,13 +348,10 @@ func (d *Driver) subvolEnableQuota() error {
|
|||
}
|
||||
|
||||
func (d *Driver) subvolDisableQuota() error {
|
||||
d.updateQuotaStatus()
|
||||
|
||||
if !d.quotaEnabled {
|
||||
// In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed
|
||||
if _, err := subvolLookupQgroup(d.home); err != nil {
|
||||
// quota is still not enabled
|
||||
return nil
|
||||
}
|
||||
d.quotaEnabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
dir, err := openDir(d.home)
|
||||
|
@ -348,13 +374,10 @@ func (d *Driver) subvolDisableQuota() error {
|
|||
}
|
||||
|
||||
func (d *Driver) subvolRescanQuota() error {
|
||||
d.updateQuotaStatus()
|
||||
|
||||
if !d.quotaEnabled {
|
||||
// In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed
|
||||
if _, err := subvolLookupQgroup(d.home); err != nil {
|
||||
// quota is still not enabled
|
||||
return nil
|
||||
}
|
||||
d.quotaEnabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
dir, err := openDir(d.home)
|
||||
|
@ -392,6 +415,38 @@ func subvolLimitQgroup(path string, size uint64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// subvolQgroupStatus performs a BTRFS_IOC_TREE_SEARCH on the root path
|
||||
// with search key of BTRFS_QGROUP_STATUS_KEY.
|
||||
// In case qgroup is enabled, the retuned key type will match BTRFS_QGROUP_STATUS_KEY.
|
||||
// For more details please see https://github.com/kdave/btrfs-progs/blob/v4.9/qgroup.c#L1035
|
||||
func subvolQgroupStatus(path string) error {
|
||||
dir, err := openDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDir(dir)
|
||||
|
||||
var args C.struct_btrfs_ioctl_search_args
|
||||
args.key.tree_id = C.BTRFS_QUOTA_TREE_OBJECTID
|
||||
args.key.min_type = C.BTRFS_QGROUP_STATUS_KEY
|
||||
args.key.max_type = C.BTRFS_QGROUP_STATUS_KEY
|
||||
args.key.max_objectid = C.__u64(math.MaxUint64)
|
||||
args.key.max_offset = C.__u64(math.MaxUint64)
|
||||
args.key.max_transid = C.__u64(math.MaxUint64)
|
||||
args.key.nr_items = 4096
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_TREE_SEARCH,
|
||||
uintptr(unsafe.Pointer(&args)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to search qgroup for %s: %v", path, errno.Error())
|
||||
}
|
||||
sh := (*C.struct_btrfs_ioctl_search_header)(unsafe.Pointer(&args.buf))
|
||||
if sh._type != C.BTRFS_QGROUP_STATUS_KEY {
|
||||
return fmt.Errorf("Invalid qgroup search header type for %s: %v", path, sh._type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func subvolLookupQgroup(path string) (uint64, error) {
|
||||
dir, err := openDir(path)
|
||||
if err != nil {
|
||||
|
@ -533,7 +588,11 @@ func (d *Driver) Remove(id string) error {
|
|||
if _, err := os.Stat(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := subvolDelete(d.subvolumesDir(), id); err != nil {
|
||||
|
||||
// Call updateQuotaStatus() to invoke status update
|
||||
d.updateQuotaStatus()
|
||||
|
||||
if err := subvolDelete(d.subvolumesDir(), id, d.quotaEnabled); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := system.EnsureRemoveAll(dir); err != nil {
|
||||
|
|
Loading…
Reference in a new issue