From a8f05692638bf50826ed9533f2a5282e2cde359d Mon Sep 17 00:00:00 2001 From: lujingxiao Date: Sat, 19 Jan 2019 11:13:41 +0800 Subject: [PATCH 004/111] prjquota: support set filesystem quota if ext4 support project quota reason:Support set filesystem quota if ext4 support project quota Change-Id: I99d28f248e758837cbf8b615e673ee7d8e36be7b Signed-off-by: Lei Jitang Signed-off-by: zhangyu235 Signed-off-by: lujingxiao --- components/engine/daemon/daemon_unix.go | 19 ++ .../daemon/graphdriver/overlay2/overlay.go | 10 +- .../daemon/graphdriver/quota/projectquota.go | 176 +++++++++++++++--- .../daemon/graphdriver/vfs/quota_linux.go | 13 +- 4 files changed, 191 insertions(+), 27 deletions(-) diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index b69eede21c..1b35df4950 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -633,6 +633,25 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. } } + if hostConfig.StorageOpt != nil && daemon.imageService.GraphDriverForOS(runtime.GOOS) == "overlay2" { + _, exist := hostConfig.StorageOpt["size"] + if exist { + status := daemon.imageService.LayerStoreStatus()[runtime.GOOS] + if status[0][0] == "Backing Filesystem" && status[0][1] == "extfs" { + if hostConfig.Privileged { + warnings = append(warnings, "filesystem quota for overlay2 over ext4 can't take affect with privileged container") + } else { + for _, cap := range hostConfig.CapAdd { + if cap == "SYS_RESOURCE" { + warnings = append(warnings, "filesystem quota for overlay2 over ext4 can't take affect with CAP_SYS_RESOURCE") + break + } + } + } + } + } + } + return warnings, nil } diff --git a/components/engine/daemon/graphdriver/overlay2/overlay.go b/components/engine/daemon/graphdriver/overlay2/overlay.go index 6b3236f8f3..36ae182bcd 100644 --- a/components/engine/daemon/graphdriver/overlay2/overlay.go +++ b/components/engine/daemon/graphdriver/overlay2/overlay.go @@ -216,16 +216,16 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) - 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 } else if opts.quota.Size > 0 { return nil, fmt.Errorf("Storage option overlay2.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 overlay2.size is used. - return nil, fmt.Errorf("Storage Option overlay2.size only supported for backingFS XFS. Found %v", backingFs) + return nil, fmt.Errorf("Storage Option overlay2.size only supported for backingFS XFS or ext4. Found %v", backingFs) } logger.Debugf("backingFs=%s, projectQuotaSupported=%v", backingFs, projectQuotaSupported) @@ -341,7 +341,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 { diff --git a/components/engine/daemon/graphdriver/quota/projectquota.go b/components/engine/daemon/graphdriver/quota/projectquota.go index 93e85823af..7d879eb81d 100644 --- a/components/engine/daemon/graphdriver/quota/projectquota.go +++ b/components/engine/daemon/graphdriver/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) @@ -49,6 +49,28 @@ struct fsxattr { #endif const int Q_XGETQSTAT_PRJQUOTA = QCMD(Q_XGETQSTAT, PRJQUOTA); + +#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/components/engine/daemon/graphdriver/vfs/quota_linux.go b/components/engine/daemon/graphdriver/vfs/quota_linux.go index 0d5c3a7b98..bb2f571834 100644 --- a/components/engine/daemon/graphdriver/vfs/quota_linux.go +++ b/components/engine/daemon/graphdriver/vfs/quota_linux.go @@ -1,6 +1,7 @@ package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" import ( + "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver/quota" "github.com/sirupsen/logrus" ) @@ -10,7 +11,17 @@ type driverQuota struct { } func setupDriverQuota(driver *Driver) { - if quotaCtl, err := quota.NewControl(driver.home); err == nil { + // Probe fs type before setting quota, now only supports xfs and extfs + fsMagic, err := graphdriver.GetFSMagic(driver.home) + if err != nil { + return + } + fsName, ok := graphdriver.FsNames[fsMagic] + if !ok { + return + } + + if quotaCtl, err := quota.NewControl(driver.home, fsName); err == nil { driver.quotaCtl = quotaCtl } else if err != quota.ErrQuotaNotSupported { logrus.Warnf("Unable to setup quota: %v\n", err) -- 2.17.1