Skip to content

Commit 4166726

Browse files
author
Darrick J. Wong
committed
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>
1 parent 871b931 commit 4166726

1 file changed

Lines changed: 32 additions & 1 deletion

File tree

fs/xfs/xfs_inode.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3097,7 +3097,8 @@ xfs_rename(
30973097
bool new_parent = (src_dp != target_dp);
30983098
bool src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
30993099
int spaceres;
3100-
int error;
3100+
bool retried = false;
3101+
int error, nospace_error = 0;
31013102

31023103
trace_xfs_rename(src_dp, target_dp, src_name, target_name);
31033104

@@ -3121,9 +3122,12 @@ xfs_rename(
31213122
xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
31223123
inodes, &num_inodes);
31233124

3125+
retry:
3126+
nospace_error = 0;
31243127
spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
31253128
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);
31263129
if (error == -ENOSPC) {
3130+
nospace_error = error;
31273131
spaceres = 0;
31283132
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, 0, 0, 0,
31293133
&tp);
@@ -3177,6 +3181,31 @@ xfs_rename(
31773181
target_dp, target_name, target_ip,
31783182
spaceres);
31793183

3184+
/*
3185+
* Try to reserve quota to handle an expansion of the target directory.
3186+
* We'll allow the rename to continue in reservationless mode if we hit
3187+
* a space usage constraint. If we trigger reservationless mode, save
3188+
* the errno if there isn't any free space in the target directory.
3189+
*/
3190+
if (spaceres != 0) {
3191+
error = xfs_trans_reserve_quota_nblks(tp, target_dp, spaceres,
3192+
0, false);
3193+
if (error == -EDQUOT || error == -ENOSPC) {
3194+
if (!retried) {
3195+
xfs_trans_cancel(tp);
3196+
xfs_blockgc_free_quota(target_dp, 0);
3197+
retried = true;
3198+
goto retry;
3199+
}
3200+
3201+
nospace_error = error;
3202+
spaceres = 0;
3203+
error = 0;
3204+
}
3205+
if (error)
3206+
goto out_trans_cancel;
3207+
}
3208+
31803209
/*
31813210
* Check for expected errors before we dirty the transaction
31823211
* so we can return an error without a transaction abort.
@@ -3423,6 +3452,8 @@ xfs_rename(
34233452
out_release_wip:
34243453
if (wip)
34253454
xfs_irele(wip);
3455+
if (error == -ENOSPC && nospace_error)
3456+
error = nospace_error;
34263457
return error;
34273458
}
34283459

0 commit comments

Comments
 (0)