From 25a6eac24a99570772cf9a28f58a41ed82cf784b Mon Sep 17 00:00:00 2001 From: TanYiFeng Date: Tue, 9 Apr 2019 01:32:27 -0400 Subject: [PATCH 03/39] vendor: support setting rootfs quota Signed-off-by: TanYiFeng --- .../storage/drivers/overlay/overlay.go | 42 ++++- .../storage/drivers/quota/projectquota.go | 173 ++++++++++++++++-- vendor/github.com/containers/storage/store.go | 9 +- vendor/github.com/docker/go-units/size.go | 10 +- 4 files changed, 199 insertions(+), 35 deletions(-) diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 6b7e67f..5561658 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -87,6 +87,7 @@ type overlayOptions struct { overrideKernelCheck bool imageStores []string quota quota.Quota + quotaBaseSize uint64 mountProgram string ostreeRepo string skipMountHome bool @@ -195,16 +196,17 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, d) - if backingFs == "xfs" { - // Try to enable project quota support over xfs. - if d.quotaCtl, err = quota.NewControl(home); err == nil { + if backingFs == "xfs" || backingFs == "extfs" { + // Try to enable project quota support over xfs and extfs. + if d.quotaCtl, err = quota.NewControl(home, backingFs); err == nil { projectQuotaSupported = true + d.options.quotaBaseSize = opts.quotaBaseSize } else if opts.quota.Size > 0 { return nil, fmt.Errorf("Storage option overlay.size not supported. Filesystem does not support Project Quota: %v", err) } } else if opts.quota.Size > 0 { // if xfs is not the backing fs then error out if the storage-opt overlay.size is used. - return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs) + return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS or ext4. Found %v", backingFs) } go d.cleanupLinkDir() @@ -248,13 +250,13 @@ func parseOptions(options []string) (*overlayOptions, error) { } case ".mountopt", "overlay.mountopt", "overlay2.mountopt": o.mountOptions = val - case ".size", "overlay.size", "overlay2.size": + case ".size", "overlay.size", "overlay2.size", "overlay.basesize", "overlay2.basesize": logrus.Debugf("overlay: size=%s", val) size, err := units.RAMInBytes(val) if err != nil { return nil, err } - o.quota.Size = uint64(size) + o.quotaBaseSize = uint64(size) case ".imagestore", "overlay.imagestore", "overlay2.imagestore": logrus.Debugf("overlay: imagestore=%s", val) // Additional read only image stores to use for lower paths @@ -422,7 +424,7 @@ func (d *Driver) Cleanup() error { // file system. func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { if opts != nil && len(opts.StorageOpt) != 0 && !projectQuotaSupported { - return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") + return fmt.Errorf("--storage-opt is supported only for overlay over xfs or ext4 with 'pquota' mount option") } if opts == nil { @@ -486,15 +488,27 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr } }() - if opts != nil && len(opts.StorageOpt) > 0 { + if opts != nil && (len(opts.StorageOpt) > 0 || d.options.quotaBaseSize > 0) { driver := &Driver{} if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil { return err } + if driver.options.quota.Size == 0 && d.options.quotaBaseSize > 0 { + driver.options.quota.Size = d.options.quotaBaseSize + } if driver.options.quota.Size > 0 { - // Set container disk quota limit - if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil { + if d.quotaCtl != nil { + // Set container disk quota limit + if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil { + return err + } + } + } + } else if d.options.quota.Size > 0 { + if d.quotaCtl != nil { + // storage-opt not specified quota size, but graphdriver-options does, so limits it also + if err := d.quotaCtl.SetQuota(dir, d.options.quota); err != nil { return err } } @@ -550,6 +564,14 @@ func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) e if err != nil { return err } + // deal with negative and super large number + if size < 0 { + return fmt.Errorf("Illegal storage size(%s): numerical result out of range", val) + } + // for overlay (0-1024) means no limit + if size < 1024 && size > 0 { + return fmt.Errorf("Illegal storage size:%d, 1024 at least", size) + } driver.options.quota.Size = uint64(size) default: return fmt.Errorf("Unknown option %s", key) diff --git a/vendor/github.com/containers/storage/drivers/quota/projectquota.go b/vendor/github.com/containers/storage/drivers/quota/projectquota.go index 93e7443..844e02a 100644 --- a/vendor/github.com/containers/storage/drivers/quota/projectquota.go +++ b/vendor/github.com/containers/storage/drivers/quota/projectquota.go @@ -38,8 +38,8 @@ struct fsxattr { #ifndef PRJQUOTA #define PRJQUOTA 2 #endif -#ifndef XFS_PROJ_QUOTA -#define XFS_PROJ_QUOTA 2 +#ifndef PROJ_QUOTA +#define PROJ_QUOTA 2 #endif #ifndef Q_XSETPQLIM #define Q_XSETPQLIM QCMD(Q_XSETQLIM, PRJQUOTA) @@ -47,6 +47,28 @@ struct fsxattr { #ifndef Q_XGETPQUOTA #define Q_XGETPQUOTA QCMD(Q_XGETQUOTA, PRJQUOTA) #endif + +#ifndef Q_XGETPQSTAT +#define Q_XGETPQSTAT QCMD(Q_XGETQSTAT, PRJQUOTA) +#endif + +#ifndef Q_SETPQUOTA +#define Q_SETPQUOTA (unsigned int)QCMD(Q_SETQUOTA, PRJQUOTA) +#endif + +#ifndef Q_GETPQUOTA +#define Q_GETPQUOTA (unsigned int)QCMD(Q_GETQUOTA, PRJQUOTA) +#endif + +#define PDQ_ACCT_BIT 4 +#define PDQ_ENFD_BIT 5 + +#ifndef QUOTA_PDQ_ACCT +#define QUOTA_PDQ_ACCT (1<>C.PDQ_ACCT_BIT + (info.qs_flags&C.QUOTA_PDQ_ENFD)>>C.PDQ_ENFD_BIT), nil +} + +// GetQuota - get the quota limits of a directory that was configured with SetQuota +func (q *Control) GetQuota(targetPath string, quota *Quota) error { + q.lock.Lock() + projectID, ok := q.quotas[targetPath] + q.lock.Unlock() + if !ok { + return fmt.Errorf("quota not found for path : %s", targetPath) + } + + // + // get the quota limit for the container's project id + // + + return q.quotaOps.GetProjectQuota(q.backingFsBlockDev, projectID, quota) +} + // getProjectID - get the project id of path on xfs func getProjectID(targetPath string) (uint32, error) { dir, err := openDir(targetPath) diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index b177cb4..df4205f 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -15,7 +15,6 @@ import ( // register all of the built-in drivers _ "github.com/containers/storage/drivers/register" - "github.com/BurntSushi/toml" drivers "github.com/containers/storage/drivers" "github.com/containers/storage/pkg/archive" @@ -1218,7 +1217,13 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat options.Flags["MountLabel"] = mountLabel } - clayer, err := rlstore.Create(layer, imageTopLayer, nil, options.Flags["MountLabel"].(string), nil, layerOptions, true) + storageOpts := make(map[string]string) + storageOptions, _ := options.Flags["StorageOpts"] + if storageOptions != nil { + storageOpts = storageOptions.(map[string]string) + } + + clayer, err := rlstore.Create(layer, imageTopLayer, nil, options.Flags["MountLabel"].(string), storageOpts, layerOptions, true) if err != nil { return nil, err } diff --git a/vendor/github.com/docker/go-units/size.go b/vendor/github.com/docker/go-units/size.go index b6485ed..2b47b66 100644 --- a/vendor/github.com/docker/go-units/size.go +++ b/vendor/github.com/docker/go-units/size.go @@ -31,7 +31,7 @@ type unitMap map[string]int64 var ( decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} - sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`) + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`) ) var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} @@ -58,7 +58,7 @@ func CustomSize(format string, size float64, base float64, _map []string) string // instead of 4 digit precision used in units.HumanSize. func HumanSizeWithPrecision(size float64, precision int) string { size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) - return fmt.Sprintf("%.*g %s", precision, size, unit) + return fmt.Sprintf("%.*g%s", precision, size, unit) } // HumanSize returns a human-readable approximation of a size @@ -70,7 +70,7 @@ func HumanSize(size float64) string { // BytesSize returns a human-readable size in bytes, kibibytes, // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). func BytesSize(size float64) string { - return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) + return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) } // FromHumanSize returns an integer from a human-readable specification of a @@ -104,5 +104,9 @@ func parseSize(sizeStr string, uMap unitMap) (int64, error) { size *= float64(mul) } + if int64(size) < 0 { + return -1, fmt.Errorf("%s converted to int64 overflowed!", sizeStr) + } + return int64(size), nil } -- 2.19.1