Skip to content

Commit d22a9e1

Browse files
author
Bill O'Donnell
committed
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>
1 parent 020c7e3 commit d22a9e1

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
@@ -3100,7 +3100,8 @@ xfs_rename(
31003100
bool new_parent = (src_dp != target_dp);
31013101
bool src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
31023102
int spaceres;
3103-
int error;
3103+
bool retried = false;
3104+
int error, nospace_error = 0;
31043105

31053106
trace_xfs_rename(src_dp, target_dp, src_name, target_name);
31063107

@@ -3124,9 +3125,12 @@ xfs_rename(
31243125
xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
31253126
inodes, &num_inodes);
31263127

3128+
retry:
3129+
nospace_error = 0;
31273130
spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
31283131
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);
31293132
if (error == -ENOSPC) {
3133+
nospace_error = error;
31303134
spaceres = 0;
31313135
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, 0, 0, 0,
31323136
&tp);
@@ -3180,6 +3184,31 @@ xfs_rename(
31803184
target_dp, target_name, target_ip,
31813185
spaceres);
31823186

3187+
/*
3188+
* Try to reserve quota to handle an expansion of the target directory.
3189+
* We'll allow the rename to continue in reservationless mode if we hit
3190+
* a space usage constraint. If we trigger reservationless mode, save
3191+
* the errno if there isn't any free space in the target directory.
3192+
*/
3193+
if (spaceres != 0) {
3194+
error = xfs_trans_reserve_quota_nblks(tp, target_dp, spaceres,
3195+
0, false);
3196+
if (error == -EDQUOT || error == -ENOSPC) {
3197+
if (!retried) {
3198+
xfs_trans_cancel(tp);
3199+
xfs_blockgc_free_quota(target_dp, 0);
3200+
retried = true;
3201+
goto retry;
3202+
}
3203+
3204+
nospace_error = error;
3205+
spaceres = 0;
3206+
error = 0;
3207+
}
3208+
if (error)
3209+
goto out_trans_cancel;
3210+
}
3211+
31833212
/*
31843213
* Check for expected errors before we dirty the transaction
31853214
* so we can return an error without a transaction abort.
@@ -3426,6 +3455,8 @@ xfs_rename(
34263455
out_release_wip:
34273456
if (wip)
34283457
xfs_irele(wip);
3458+
if (error == -ENOSPC && nospace_error)
3459+
error = nospace_error;
34293460
return error;
34303461
}
34313462

0 commit comments

Comments
 (0)