//go:build linux && !exclude_disk_quota && cgo package quota // import "github.com/docker/docker/quota" import ( "os" "os/exec" "testing" "golang.org/x/sys/unix" ) // CanTestQuota - checks if xfs prjquota can be tested // returns a reason if not func CanTestQuota() (string, bool) { if os.Getuid() != 0 { return "requires mounts", false } _, err := exec.LookPath("mkfs.xfs") if err != nil { return "mkfs.xfs not found in PATH", false } return "", true } // PrepareQuotaTestImage - prepares an xfs prjquota test image // returns the path of the image on success func PrepareQuotaTestImage(t *testing.T) (string, error) { // imageSize is the size of the test-image. The minimum size allowed // is 300MB. // // See https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/commit/?id=6e0ed3d19c54603f0f7d628ea04b550151d8a262 const imageSize = 300 * 1024 * 1024 mkfs, err := exec.LookPath("mkfs.xfs") if err != nil { return "", err } // create a sparse image imageFile, err := os.CreateTemp("", "xfs-image") if err != nil { return "", err } imageFileName := imageFile.Name() if _, err = imageFile.Seek(imageSize-1, 0); err != nil { os.Remove(imageFileName) return "", err } if _, err = imageFile.Write([]byte{0}); err != nil { os.Remove(imageFileName) return "", err } if err = imageFile.Close(); err != nil { os.Remove(imageFileName) return "", err } // The reason for disabling these options is sometimes people run with a newer userspace // than kernelspace out, err := exec.Command(mkfs, "-m", "crc=0,finobt=0", imageFileName).CombinedOutput() if len(out) > 0 { t.Log(string(out)) } if err != nil { os.Remove(imageFileName) return "", err } return imageFileName, nil } // WrapMountTest - wraps a test function such that it has easy access to a mountPoint and testDir // with guaranteed prjquota or guaranteed no prjquota support. func WrapMountTest(imageFileName string, enableQuota bool, testFunc func(t *testing.T, mountPoint, backingFsDev, testDir string)) func(*testing.T) { return func(t *testing.T) { mountOptions := "loop" if enableQuota { mountOptions = mountOptions + ",prjquota" } mountPoint := t.TempDir() out, err := exec.Command("mount", "-o", mountOptions, imageFileName, mountPoint).CombinedOutput() if err != nil { _, err := os.Stat("/proc/fs/xfs") if os.IsNotExist(err) { t.Skip("no /proc/fs/xfs") } } if err != nil { t.Fatalf("assertion failed: error is not nil: %v: mount failed: %s", err, out) } defer func() { if err := unix.Unmount(mountPoint, 0); err != nil { t.Fatalf("assertion failed: error is not nil: %v", err) } }() backingFsDev, err := makeBackingFsDev(mountPoint) if err != nil { t.Fatalf("assertion failed: error is not nil: %v", err) } testDir, err := os.MkdirTemp(mountPoint, "per-test") if err != nil { t.Fatalf("assertion failed: error is not nil: %v", err) } defer os.RemoveAll(testDir) testFunc(t, mountPoint, backingFsDev, testDir) } } // WrapQuotaTest - wraps a test function such that is has easy and guaranteed access to a quota Control // instance with a quota test dir under its control. func WrapQuotaTest(testFunc func(t *testing.T, ctrl *Control, mountPoint, testDir, testSubDir string)) func(t *testing.T, mountPoint, backingFsDev, testDir string) { return func(t *testing.T, mountPoint, backingFsDev, testDir string) { ctrl, err := NewControl(testDir) if err != nil { t.Fatalf("assertion failed: error is not nil: %v", err) } testSubDir, err := os.MkdirTemp(testDir, "quota-test") if err != nil { t.Fatalf("assertion failed: error is not nil: %v", err) } testFunc(t, ctrl, mountPoint, testDir, testSubDir) } }