Skip to content

Commit 918c2ee

Browse files
Mark Fashehkdave
authored andcommitted
btrfs: handle non-fatal errors in btrfs_qgroup_inherit()
create_pending_snapshot() will go readonly on _any_ error return from btrfs_qgroup_inherit(). If qgroups are enabled, a user can crash their fs by just making a snapshot and asking it to inherit from an invalid qgroup. For example: $ btrfs sub snap -i 1/10 /btrfs/ /btrfs/foo Will cause a transaction abort. Fix this by only throwing errors in btrfs_qgroup_inherit() when we know going readonly is acceptable. The following xfstests test case reproduces this bug: seq=`basename $0` seqres=$RESULT_DIR/$seq echo "QA output created by $seq" here=`pwd` tmp=/tmp/$$ status=1 # failure is the default! trap "_cleanup; exit \$status" 0 1 2 3 15 _cleanup() { cd / rm -f $tmp.* } # get standard environment, filters and checks . ./common/rc . ./common/filter # remove previous $seqres.full before test rm -f $seqres.full # real QA test starts here _supported_fs btrfs _supported_os Linux _require_scratch rm -f $seqres.full _scratch_mkfs _scratch_mount _run_btrfs_util_prog quota enable $SCRATCH_MNT # The qgroup '1/10' does not exist and should be silently ignored _run_btrfs_util_prog subvolume snapshot -i 1/10 $SCRATCH_MNT $SCRATCH_MNT/snap1 _scratch_unmount echo "Silence is golden" status=0 exit Signed-off-by: Mark Fasheh <[email protected]> Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 0305bc2 commit 918c2ee

File tree

1 file changed

+32
-22
lines changed

1 file changed

+32
-22
lines changed

fs/btrfs/qgroup.c

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,8 +1842,10 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
18421842
}
18431843

18441844
/*
1845-
* copy the acounting information between qgroups. This is necessary when a
1846-
* snapshot or a subvolume is created
1845+
* Copy the acounting information between qgroups. This is necessary
1846+
* when a snapshot or a subvolume is created. Throwing an error will
1847+
* cause a transaction abort so we take extra care here to only error
1848+
* when a readonly fs is a reasonable outcome.
18471849
*/
18481850
int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
18491851
struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid,
@@ -1873,15 +1875,15 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
18731875
2 * inherit->num_excl_copies;
18741876
for (i = 0; i < nums; ++i) {
18751877
srcgroup = find_qgroup_rb(fs_info, *i_qgroups);
1876-
if (!srcgroup) {
1877-
ret = -EINVAL;
1878-
goto out;
1879-
}
18801878

1881-
if ((srcgroup->qgroupid >> 48) <= (objectid >> 48)) {
1882-
ret = -EINVAL;
1883-
goto out;
1884-
}
1879+
/*
1880+
* Zero out invalid groups so we can ignore
1881+
* them later.
1882+
*/
1883+
if (!srcgroup ||
1884+
((srcgroup->qgroupid >> 48) <= (objectid >> 48)))
1885+
*i_qgroups = 0ULL;
1886+
18851887
++i_qgroups;
18861888
}
18871889
}
@@ -1916,17 +1918,19 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
19161918
*/
19171919
if (inherit) {
19181920
i_qgroups = (u64 *)(inherit + 1);
1919-
for (i = 0; i < inherit->num_qgroups; ++i) {
1921+
for (i = 0; i < inherit->num_qgroups; ++i, ++i_qgroups) {
1922+
if (*i_qgroups == 0)
1923+
continue;
19201924
ret = add_qgroup_relation_item(trans, quota_root,
19211925
objectid, *i_qgroups);
1922-
if (ret)
1926+
if (ret && ret != -EEXIST)
19231927
goto out;
19241928
ret = add_qgroup_relation_item(trans, quota_root,
19251929
*i_qgroups, objectid);
1926-
if (ret)
1930+
if (ret && ret != -EEXIST)
19271931
goto out;
1928-
++i_qgroups;
19291932
}
1933+
ret = 0;
19301934
}
19311935

19321936

@@ -1987,17 +1991,22 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
19871991

19881992
i_qgroups = (u64 *)(inherit + 1);
19891993
for (i = 0; i < inherit->num_qgroups; ++i) {
1990-
ret = add_relation_rb(quota_root->fs_info, objectid,
1991-
*i_qgroups);
1992-
if (ret)
1993-
goto unlock;
1994+
if (*i_qgroups) {
1995+
ret = add_relation_rb(quota_root->fs_info, objectid,
1996+
*i_qgroups);
1997+
if (ret)
1998+
goto unlock;
1999+
}
19942000
++i_qgroups;
19952001
}
19962002

1997-
for (i = 0; i < inherit->num_ref_copies; ++i) {
2003+
for (i = 0; i < inherit->num_ref_copies; ++i, i_qgroups += 2) {
19982004
struct btrfs_qgroup *src;
19992005
struct btrfs_qgroup *dst;
20002006

2007+
if (!i_qgroups[0] || !i_qgroups[1])
2008+
continue;
2009+
20012010
src = find_qgroup_rb(fs_info, i_qgroups[0]);
20022011
dst = find_qgroup_rb(fs_info, i_qgroups[1]);
20032012

@@ -2008,12 +2017,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
20082017

20092018
dst->rfer = src->rfer - level_size;
20102019
dst->rfer_cmpr = src->rfer_cmpr - level_size;
2011-
i_qgroups += 2;
20122020
}
2013-
for (i = 0; i < inherit->num_excl_copies; ++i) {
2021+
for (i = 0; i < inherit->num_excl_copies; ++i, i_qgroups += 2) {
20142022
struct btrfs_qgroup *src;
20152023
struct btrfs_qgroup *dst;
20162024

2025+
if (!i_qgroups[0] || !i_qgroups[1])
2026+
continue;
2027+
20172028
src = find_qgroup_rb(fs_info, i_qgroups[0]);
20182029
dst = find_qgroup_rb(fs_info, i_qgroups[1]);
20192030

@@ -2024,7 +2035,6 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
20242035

20252036
dst->excl = src->excl + level_size;
20262037
dst->excl_cmpr = src->excl_cmpr + level_size;
2027-
i_qgroups += 2;
20282038
}
20292039

20302040
unlock:

0 commit comments

Comments
 (0)