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 <mheon@redhat.com>
This commit is contained in:
Matt Heon 2025-02-25 08:54:26 -05:00
parent 3a013da40e
commit c6bce46c29
1 changed files with 13 additions and 2 deletions

View File

@ -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
}