Skip to content

Commit ec26642

Browse files
author
Herton R. Krzesinski
committed
Merge: xfs: reserve quota for dir expansion when linking/unlinking files
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/1886 xfs: reserve quota for dir expansion when linking/unlinking files Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2086881 commit 4166726 Author: Darrick J. Wong <djwong@kernel.org> Date: Wed Mar 9 10:10:50 2022 -0800 xfs: reserve quota for target dir expansion when renaming files XFS does not reserve quota for directory expansion when renaming children into a directory. This means that we don't reject the expansion with EDQUOT when we're at or near a hard limit, which means that unprivileged userspace can use rename() to exceed quota. Rename operations don't always expand the target directory, and we allow a rename to proceed with no space reservation if we don't need to add a block to the target directory to handle the addition. Moreover, the unlink operation on the source directory generally does not expand the directory (you'd have to free a block and then cause a btree split) and it's probably of little consequence to leave the corner case that renaming a file out of a directory can increase its size. As with link and unlink, there is a further bug in that we do not trigger the blockgc workers to try to clear space when we're out of quota. Because rename is its own special tricky animal, we'll patch xfs_rename directly to reserve quota to the rename transaction. We'll leave cleaning up the rest of xfs_rename for the metadata directory tree patchset. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Bill O'Donnell <bodonnel@redhat.com> Approved-by: Brian Foster <bfoster@redhat.com> Approved-by: Carlos Maiolino <cmaiolino@redhat.com> Signed-off-by: Herton R. Krzesinski <herton@redhat.com>
2 parents f3851d6 + d22a9e1 commit ec26642

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

fs/xfs/xfs_inode.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3111,7 +3111,8 @@ xfs_rename(
31113111
bool new_parent = (src_dp != target_dp);
31123112
bool src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
31133113
int spaceres;
3114-
int error;
3114+
bool retried = false;
3115+
int error, nospace_error = 0;
31153116

31163117
trace_xfs_rename(src_dp, target_dp, src_name, target_name);
31173118

@@ -3136,9 +3137,12 @@ xfs_rename(
31363137
xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
31373138
inodes, &num_inodes);
31383139

3140+
retry:
3141+
nospace_error = 0;
31393142
spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
31403143
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);
31413144
if (error == -ENOSPC) {
3145+
nospace_error = error;
31423146
spaceres = 0;
31433147
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, 0, 0, 0,
31443148
&tp);
@@ -3192,6 +3196,31 @@ xfs_rename(
31923196
target_dp, target_name, target_ip,
31933197
spaceres);
31943198

3199+
/*
3200+
* Try to reserve quota to handle an expansion of the target directory.
3201+
* We'll allow the rename to continue in reservationless mode if we hit
3202+
* a space usage constraint. If we trigger reservationless mode, save
3203+
* the errno if there isn't any free space in the target directory.
3204+
*/
3205+
if (spaceres != 0) {
3206+
error = xfs_trans_reserve_quota_nblks(tp, target_dp, spaceres,
3207+
0, false);
3208+
if (error == -EDQUOT || error == -ENOSPC) {
3209+
if (!retried) {
3210+
xfs_trans_cancel(tp);
3211+
xfs_blockgc_free_quota(target_dp, 0);
3212+
retried = true;
3213+
goto retry;
3214+
}
3215+
3216+
nospace_error = error;
3217+
spaceres = 0;
3218+
error = 0;
3219+
}
3220+
if (error)
3221+
goto out_trans_cancel;
3222+
}
3223+
31953224
/*
31963225
* Check for expected errors before we dirty the transaction
31973226
* so we can return an error without a transaction abort.
@@ -3438,6 +3467,8 @@ xfs_rename(
34383467
out_release_wip:
34393468
if (wip)
34403469
xfs_irele(wip);
3470+
if (error == -ENOSPC && nospace_error)
3471+
error = nospace_error;
34413472
return error;
34423473
}
34433474

0 commit comments

Comments
 (0)