From 91ead15283200feb9db8054a26d322fbfdc3fa59 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 28 Apr 2022 11:39:12 +0200 Subject: volume: add new option -o o=noquota add a new option to completely disable xfs quota usage for a volume. xfs quota set on a volume, even just for tracking disk usage, can cause weird errors if the volume is later re-used by a container with a different quota projid. More specifically, link(2) and rename(2) might fail with EXDEV if the source file has a projid that is different from the parent directory. To prevent such kind of issues, the volume should be created beforehand with `podman volume create -o o=noquota $ID` Closes: https://github.com/containers/podman/issues/14049 Signed-off-by: Giuseppe Scrivano --- libpod/options.go | 13 +++++++++++++ libpod/runtime_volume_linux.go | 39 ++++++++++++++++++++++----------------- libpod/volume.go | 3 +++ libpod/volume_internal.go | 3 +++ 4 files changed, 41 insertions(+), 17 deletions(-) (limited to 'libpod') diff --git a/libpod/options.go b/libpod/options.go index 57e2d7cf6..98eb45e76 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1634,6 +1634,19 @@ func WithVolumeNoChown() VolumeCreateOption { } } +// WithVolumeDisableQuota prevents the volume from being assigned a quota. +func WithVolumeDisableQuota() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.config.DisableQuota = true + + return nil + } +} + // withSetAnon sets a bool notifying libpod that this volume is anonymous and // should be removed when containers using it are removed and volumes are // specified for removal. diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index 241f6e2f2..f8788e183 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -73,7 +73,7 @@ func (r *Runtime) newVolume(options ...VolumeCreateOption) (_ *Volume, deferredE return nil, errors.Wrapf(err, "invalid volume option %s for driver 'local'", key) } } - case "o", "type", "uid", "gid", "size", "inodes": + case "o", "type", "uid", "gid", "size", "inodes", "noquota": // Do nothing, valid keys default: return nil, errors.Wrapf(define.ErrInvalidArg, "invalid mount option %s for driver 'local'", key) @@ -111,23 +111,28 @@ func (r *Runtime) newVolume(options ...VolumeCreateOption) (_ *Volume, deferredE if err := LabelVolumePath(fullVolPath); err != nil { return nil, err } - projectQuotaSupported := false - - q, err := quota.NewControl(r.config.Engine.VolumePath) - if err == nil { - projectQuotaSupported = true - } - quota := quota.Quota{} - if volume.config.Size > 0 || volume.config.Inodes > 0 { - if !projectQuotaSupported { - return nil, errors.New("Volume options size and inodes not supported. Filesystem does not support Project Quota") + if volume.config.DisableQuota { + if volume.config.Size > 0 || volume.config.Inodes > 0 { + return nil, errors.New("volume options size and inodes cannot be used without quota") } - quota.Size = volume.config.Size - quota.Inodes = volume.config.Inodes - } - if projectQuotaSupported { - if err := q.SetQuota(fullVolPath, quota); err != nil { - return nil, errors.Wrapf(err, "failed to set size quota size=%d inodes=%d for volume directory %q", volume.config.Size, volume.config.Inodes, fullVolPath) + } else { + projectQuotaSupported := false + q, err := quota.NewControl(r.config.Engine.VolumePath) + if err == nil { + projectQuotaSupported = true + } + quota := quota.Quota{} + if volume.config.Size > 0 || volume.config.Inodes > 0 { + if !projectQuotaSupported { + return nil, errors.New("volume options size and inodes not supported. Filesystem does not support Project Quota") + } + quota.Size = volume.config.Size + quota.Inodes = volume.config.Inodes + } + if projectQuotaSupported { + if err := q.SetQuota(fullVolPath, quota); err != nil { + return nil, errors.Wrapf(err, "failed to set size quota size=%d inodes=%d for volume directory %q", volume.config.Size, volume.config.Inodes, fullVolPath) + } } } diff --git a/libpod/volume.go b/libpod/volume.go index bffafdc15..ab461a37f 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -52,6 +52,9 @@ type VolumeConfig struct { Size uint64 `json:"size"` // Inodes maximum of the volume. Inodes uint64 `json:"inodes"` + // DisableQuota indicates that the volume should completely disable using any + // quota tracking. + DisableQuota bool `json:"disableQuota,omitempty"` } // VolumeState holds the volume's mutable state. diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 9850c2ea1..e0ebb729d 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -52,6 +52,9 @@ func (v *Volume) needsMount() bool { if _, ok := v.config.Options["SIZE"]; ok { index++ } + if _, ok := v.config.Options["NOQUOTA"]; ok { + index++ + } // when uid or gid is set there is also the "o" option // set so we have to ignore this one as well if index > 0 { -- cgit v1.2.3-54-g00ecf