@@ -24,6 +24,7 @@ import (
2424 "github.com/docker/docker/pkg/parsers"
2525 "github.com/docker/docker/pkg/parsers/kernel"
2626 "github.com/docker/docker/pkg/system"
27+ "github.com/docker/go-units"
2728 "github.com/opencontainers/runc/libcontainer/label"
2829)
2930
@@ -75,15 +76,25 @@ const (
7576 idLength = 26
7677)
7778
79+ type overlayOptions struct {
80+ overrideKernelCheck bool
81+ quota graphdriver.Quota
82+ }
83+
7884// Driver contains information about the home directory and the list of active mounts that are created using this driver.
7985type Driver struct {
80- home string
81- uidMaps []idtools.IDMap
82- gidMaps []idtools.IDMap
83- ctr * graphdriver.RefCounter
86+ home string
87+ uidMaps []idtools.IDMap
88+ gidMaps []idtools.IDMap
89+ ctr * graphdriver.RefCounter
90+ quotaCtl * graphdriver.QuotaCtl
91+ options overlayOptions
8492}
8593
86- var backingFs = "<unknown>"
94+ var (
95+ backingFs = "<unknown>"
96+ projectQuotaSupported = false
97+ )
8798
8899func init () {
89100 graphdriver .Register (driverName , Init )
@@ -149,13 +160,24 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
149160 uidMaps : uidMaps ,
150161 gidMaps : gidMaps ,
151162 ctr : graphdriver .NewRefCounter (graphdriver .NewFsChecker (graphdriver .FsMagicOverlay )),
163+ options : * opts ,
152164 }
153165
154- return d , nil
155- }
166+ if backingFs == "xfs" {
167+ // Try to enable project quota support over xfs.
168+ if d .quotaCtl , err = graphdriver .NewQuotaCtl (home ); err == nil {
169+ projectQuotaSupported = true
170+ } else if opts .quota .Size > 0 {
171+ return nil , fmt .Errorf ("Storage option overlay2.size not supported. Filesystem does not support Project Quota: %v" , err )
172+ }
173+ } else if opts .quota .Size > 0 {
174+ // if xfs is not the backing fs then error out if the storage-opt overlay2.size is used.
175+ return nil , fmt .Errorf ("Storage Option overlay2.size only supported for backingFS XFS. Found %v" , backingFs )
176+ }
156177
157- type overlayOptions struct {
158- overrideKernelCheck bool
178+ logrus .Debugf ("backingFs=%s, projectQuotaSupported=%v" , backingFs , projectQuotaSupported )
179+
180+ return d , nil
159181}
160182
161183func parseOptions (options []string ) (* overlayOptions , error ) {
@@ -172,8 +194,14 @@ func parseOptions(options []string) (*overlayOptions, error) {
172194 if err != nil {
173195 return nil , err
174196 }
197+ case "overlay2.size" :
198+ size , err := units .RAMInBytes (val )
199+ if err != nil {
200+ return nil , err
201+ }
202+ o .quota .Size = uint64 (size )
175203 default :
176- return nil , fmt .Errorf ("overlay2: Unknown option %s\n " , key )
204+ return nil , fmt .Errorf ("overlay2: unknown option %s" , key )
177205 }
178206 }
179207 return o , nil
@@ -253,11 +281,22 @@ func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[s
253281// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
254282// The parent filesystem is used to configure these directories for the overlay.
255283func (d * Driver ) Create (id , parent , mountLabel string , storageOpt map [string ]string ) (retErr error ) {
284+ if len (storageOpt ) != 0 && ! projectQuotaSupported {
285+ return fmt .Errorf ("--storage-opt is supported only for overlay over xfs with 'pquota' mount option" )
286+ }
256287
257- if len ( storageOpt ) != 0 {
258- return fmt . Errorf ( "--storage-opt is not supported for overlay" )
288+ if storageOpt == nil {
289+ storageOpt = map [ string ] string {}
259290 }
260291
292+ if _ , ok := storageOpt ["size" ]; ! ok {
293+ storageOpt ["size" ] = strconv .FormatUint (d .options .quota .Size , 10 )
294+ }
295+ return d .create (id , parent , storageOpt )
296+ }
297+
298+ func (d * Driver ) create (id , parent string , storageOpt map [string ]string ) (retErr error ) {
299+
261300 dir := d .dir (id )
262301
263302 rootUID , rootGID , err := idtools .GetRootUIDGID (d .uidMaps , d .gidMaps )
@@ -278,6 +317,20 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
278317 }
279318 }()
280319
320+ if len (storageOpt ) > 0 {
321+ driver := & Driver {}
322+ if err := d .parseStorageOpt (storageOpt , driver ); err != nil {
323+ return err
324+ }
325+
326+ if driver .options .quota .Size > 0 {
327+ // Set container disk quota limit
328+ if err := d .quotaCtl .SetQuota (dir , driver .options .quota ); err != nil {
329+ return err
330+ }
331+ }
332+ }
333+
281334 if err := idtools .MkdirAs (path .Join (dir , "diff" ), 0755 , rootUID , rootGID ); err != nil {
282335 return err
283336 }
@@ -317,6 +370,26 @@ func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]str
317370 return nil
318371}
319372
373+ // Parse overlay storage options
374+ func (d * Driver ) parseStorageOpt (storageOpt map [string ]string , driver * Driver ) error {
375+ // Read size to set the disk project quota per container
376+ for key , val := range storageOpt {
377+ key := strings .ToLower (key )
378+ switch key {
379+ case "size" :
380+ size , err := units .RAMInBytes (val )
381+ if err != nil {
382+ return err
383+ }
384+ driver .options .quota .Size = uint64 (size )
385+ default :
386+ return fmt .Errorf ("Unknown option %s" , key )
387+ }
388+ }
389+
390+ return nil
391+ }
392+
320393func (d * Driver ) getLower (parent string ) (string , error ) {
321394 parentDir := d .dir (parent )
322395
@@ -440,7 +513,7 @@ func (d *Driver) Put(id string) error {
440513 if count := d .ctr .Decrement (mountpoint ); count > 0 {
441514 return nil
442515 }
443- if err := syscall .Unmount (mountpoint , 0 ); err != nil {
516+ if err := syscall .Unmount (mountpoint , syscall . MNT_DETACH ); err != nil {
444517 logrus .Debugf ("Failed to unmount %s overlay: %v" , id , err )
445518 }
446519 return nil
0 commit comments