Skip to content

Commit

Permalink
Recreate missing backingFsBlockDev on setting xfs project quota
Browse files Browse the repository at this point in the history
There is the rare case that something removed the backingFsBlockDev,
which usually points to
`/var/lib/containers/storage/overlay/backingFsBlockDev`. In that case,
all `SetQuota` operations would fail on overlay layer creation.

We now work around that error by recreating the backingFsBlockDev.

Cherry-picked: aa0d394
Signed-off-by: Sascha Grunert <[email protected]>
  • Loading branch information
saschagrunert committed Nov 22, 2022
1 parent 662b85d commit 5cac318
Showing 1 changed file with 42 additions and 19 deletions.
61 changes: 42 additions & 19 deletions drivers/quota/projectquota.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build linux && !exclude_disk_quota && cgo
// +build linux,!exclude_disk_quota,cgo

//
Expand Down Expand Up @@ -50,6 +51,7 @@ struct fsxattr {
*/
import "C"
import (
"errors"
"fmt"
"io/ioutil"
"math"
Expand Down Expand Up @@ -78,6 +80,7 @@ type Control struct {
backingFsBlockDev string
nextProjectID uint32
quotas map[string]uint32
basePath string
}

// Attempt to generate a unigue projectid. Multiple directories
Expand Down Expand Up @@ -122,11 +125,9 @@ func generateUniqueProjectID(path string) (uint32, error) {
// This is a way to prevent xfs_quota management from conflicting with
// containers/storage.

//
// Then try to create a test directory with the next project id and set a quota
// on it. If that works, continue to scan existing containers to map allocated
// project ids.
//
func NewControl(basePath string) (*Control, error) {
//
// Get project id of parent dir as minimal id to be used by driver
Expand Down Expand Up @@ -160,20 +161,22 @@ func NewControl(basePath string) (*Control, error) {
Size: 0,
Inodes: 0,
}
if err := setProjectQuota(backingFsBlockDev, minProjectID, quota); err != nil {
return nil, err
}

q := Control{
backingFsBlockDev: backingFsBlockDev,
nextProjectID: minProjectID + 1,
quotas: make(map[string]uint32),
basePath: basePath,
}

if err := q.setProjectQuota(minProjectID, quota); err != nil {
return nil, err
}

//
// get first project id to be used for next container
//
err = q.findNextProjectID(basePath)
err = q.findNextProjectID()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -206,11 +209,11 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error {
// set the quota limit for the container's project id
//
logrus.Debugf("SetQuota path=%s, size=%d, inodes=%d, projectID=%d", targetPath, quota.Size, quota.Inodes, projectID)
return setProjectQuota(q.backingFsBlockDev, projectID, quota)
return q.setProjectQuota(projectID, quota)
}

// setProjectQuota - set the quota for project id on xfs block device
func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error {
func (q *Control) setProjectQuota(projectID uint32, quota Quota) error {
var d C.fs_disk_quota_t
d.d_version = C.FS_DQUOT_VERSION
d.d_id = C.__u32(projectID)
Expand All @@ -227,15 +230,35 @@ func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) er
d.d_ino_softlimit = d.d_ino_hardlimit
}

var cs = C.CString(backingFsBlockDev)
var cs = C.CString(q.backingFsBlockDev)
defer C.free(unsafe.Pointer(cs))

_, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM,
uintptr(unsafe.Pointer(cs)), uintptr(d.d_id),
uintptr(unsafe.Pointer(&d)), 0, 0)
if errno != 0 {
return fmt.Errorf("Failed to set quota limit for projid %d on %s: %v",
projectID, backingFsBlockDev, errno.Error())
runQuotactl := func() syscall.Errno {
_, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM,
uintptr(unsafe.Pointer(cs)), uintptr(d.d_id),
uintptr(unsafe.Pointer(&d)), 0, 0)
return errno
}

errno := runQuotactl()

// If the backingFsBlockDev does not exist any more then try to recreate it.
if errors.Is(errno, unix.ENOENT) {
if _, err := makeBackingFsDev(q.basePath); err != nil {
return fmt.Errorf(
"failed to recreate missing backingFsBlockDev %s for projid %d: %w",
q.backingFsBlockDev, projectID, err,
)
}

if errno := runQuotactl(); errno != 0 {
return fmt.Errorf("failed to set quota limit for projid %d on %s after backingFsBlockDev recreation: %w",
projectID, q.backingFsBlockDev, errno)
}

} else if errno != 0 {
return fmt.Errorf("failed to set quota limit for projid %d on %s: %w",
projectID, q.backingFsBlockDev, errno)
}

return nil
Expand Down Expand Up @@ -334,16 +357,16 @@ func setProjectID(targetPath string, projectID uint32) error {

// findNextProjectID - find the next project id to be used for containers
// by scanning driver home directory to find used project ids
func (q *Control) findNextProjectID(home string) error {
files, err := ioutil.ReadDir(home)
func (q *Control) findNextProjectID() error {
files, err := ioutil.ReadDir(q.basePath)
if err != nil {
return fmt.Errorf("read directory failed : %s", home)
return fmt.Errorf("read directory failed : %s", q.basePath)
}
for _, file := range files {
if !file.IsDir() {
continue
}
path := filepath.Join(home, file.Name())
path := filepath.Join(q.basePath, file.Name())
projid, err := getProjectID(path)
if err != nil {
return err
Expand Down

0 comments on commit 5cac318

Please sign in to comment.