From c6bce46c297dda63ad3bf153e07b5f075ed01301 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Tue, 25 Feb 2025 08:54:26 -0500 Subject: [PATCH] Do not allowing setting project ID on non-empty directories We have previously relied on the PROJINHERIT flag for XFS quotas, which causes the ID of the parent directory to be recursively applied to subdirectories under the volume's parent directory. However, PROJINHERIT only applies to directories created after the project ID was first set. Pre-existing directories do not get the project ID if we only set it on the parent. This means that quota enforcement is not complete if we allow quotas to be set on directories that are not empty. We could set recursively but that comes with its own problems; quotas on directories that contain pre-existing files behave strangely. Relevant to https://github.com/containers/podman/issues/25368 but is not a fix for that PR, more of a cleanup to make sure we don't make the same mistake elsewhere. Signed-off-by: Matt Heon --- drivers/quota/projectquota_supported.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/quota/projectquota_supported.go b/drivers/quota/projectquota_supported.go index 59ba5b0b2..c3b334f7c 100644 --- a/drivers/quota/projectquota_supported.go +++ b/drivers/quota/projectquota_supported.go @@ -190,7 +190,8 @@ func NewControl(basePath string) (*Control, error) { } // SetQuota - assign a unique project id to directory and set the quota limits -// for that project id +// for that project id. +// targetPath must exist, must be a directory, and must be empty. func (q *Control) SetQuota(targetPath string, quota Quota) error { var projectID uint32 value, ok := q.quotas.Load(targetPath) @@ -200,10 +201,20 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error { if !ok { projectID = q.nextProjectID + // The directory we are setting an ID on must be empty, as + // the ID will not be propagated to pre-existing subdirectories. + dents, err := os.ReadDir(targetPath) + if err != nil { + return fmt.Errorf("reading directory %s: %w", targetPath, err) + } + if len(dents) > 0 { + return fmt.Errorf("can only set project ID on empty directories, %s is not empty", targetPath) + } + // // assign project id to new container directory // - err := setProjectID(targetPath, projectID) + err = setProjectID(targetPath, projectID) if err != nil { return err }