mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-16 22:25:13 -05:00
btrfs: fix squota compressed stats leak
The following workload on a squota enabled fs:
btrfs subvol create mnt/subvol
# ensure subvol extents get accounted
sync
btrfs qgroup create 1/1 mnt
btrfs qgroup assign mnt/subvol 1/1 mnt
btrfs qgroup delete mnt/subvol
# make the cleaner thread run
btrfs filesystem sync mnt
sleep 1
btrfs filesystem sync mnt
btrfs qgroup destroy 1/1 mnt
will fail with EBUSY. The reason is that 1/1 does the quick accounting
when we assign subvol to it, gaining its exclusive usage as excl and
excl_cmpr. But then when we delete subvol, the decrement happens via
record_squota_delta() which does not update excl_cmpr, as squotas does
not make any distinction between compressed and normal extents. Thus,
we increment excl_cmpr but never decrement it, and are unable to delete
1/1. The two possible fixes are to make squota always mirror excl and
excl_cmpr or to make the fast accounting separately track the plain and
cmpr numbers. The latter felt cleaner to me so that is what I opted for.
Fixes: 1e0e9d5771 ("btrfs: add helper for recording simple quota deltas")
CC: stable@vger.kernel.org # 6.12+
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
committed by
David Sterba
parent
986bf6ed44
commit
de134cb54c
@@ -1455,6 +1455,7 @@ static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info, u64 ref_root,
|
||||
struct btrfs_qgroup *qgroup;
|
||||
LIST_HEAD(qgroup_list);
|
||||
u64 num_bytes = src->excl;
|
||||
u64 num_bytes_cmpr = src->excl_cmpr;
|
||||
int ret = 0;
|
||||
|
||||
qgroup = find_qgroup_rb(fs_info, ref_root);
|
||||
@@ -1466,11 +1467,12 @@ static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info, u64 ref_root,
|
||||
struct btrfs_qgroup_list *glist;
|
||||
|
||||
qgroup->rfer += sign * num_bytes;
|
||||
qgroup->rfer_cmpr += sign * num_bytes;
|
||||
qgroup->rfer_cmpr += sign * num_bytes_cmpr;
|
||||
|
||||
WARN_ON(sign < 0 && qgroup->excl < num_bytes);
|
||||
WARN_ON(sign < 0 && qgroup->excl_cmpr < num_bytes_cmpr);
|
||||
qgroup->excl += sign * num_bytes;
|
||||
qgroup->excl_cmpr += sign * num_bytes;
|
||||
qgroup->excl_cmpr += sign * num_bytes_cmpr;
|
||||
|
||||
if (sign > 0)
|
||||
qgroup_rsv_add_by_qgroup(fs_info, qgroup, src);
|
||||
|
||||
Reference in New Issue
Block a user