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 <mfasheh@suse.de>
Reviewed-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
committed by
David Sterba
parent
0305bc2793
commit
918c2ee103
@@ -1842,8 +1842,10 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* copy the acounting information between qgroups. This is necessary when a
|
||||
* snapshot or a subvolume is created
|
||||
* Copy the acounting information between qgroups. This is necessary
|
||||
* when a snapshot or a subvolume is created. Throwing an error will
|
||||
* cause a transaction abort so we take extra care here to only error
|
||||
* when a readonly fs is a reasonable outcome.
|
||||
*/
|
||||
int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid,
|
||||
@@ -1873,15 +1875,15 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
2 * inherit->num_excl_copies;
|
||||
for (i = 0; i < nums; ++i) {
|
||||
srcgroup = find_qgroup_rb(fs_info, *i_qgroups);
|
||||
if (!srcgroup) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((srcgroup->qgroupid >> 48) <= (objectid >> 48)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Zero out invalid groups so we can ignore
|
||||
* them later.
|
||||
*/
|
||||
if (!srcgroup ||
|
||||
((srcgroup->qgroupid >> 48) <= (objectid >> 48)))
|
||||
*i_qgroups = 0ULL;
|
||||
|
||||
++i_qgroups;
|
||||
}
|
||||
}
|
||||
@@ -1916,17 +1918,19 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
if (inherit) {
|
||||
i_qgroups = (u64 *)(inherit + 1);
|
||||
for (i = 0; i < inherit->num_qgroups; ++i) {
|
||||
for (i = 0; i < inherit->num_qgroups; ++i, ++i_qgroups) {
|
||||
if (*i_qgroups == 0)
|
||||
continue;
|
||||
ret = add_qgroup_relation_item(trans, quota_root,
|
||||
objectid, *i_qgroups);
|
||||
if (ret)
|
||||
if (ret && ret != -EEXIST)
|
||||
goto out;
|
||||
ret = add_qgroup_relation_item(trans, quota_root,
|
||||
*i_qgroups, objectid);
|
||||
if (ret)
|
||||
if (ret && ret != -EEXIST)
|
||||
goto out;
|
||||
++i_qgroups;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1987,17 +1991,22 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
|
||||
i_qgroups = (u64 *)(inherit + 1);
|
||||
for (i = 0; i < inherit->num_qgroups; ++i) {
|
||||
ret = add_relation_rb(quota_root->fs_info, objectid,
|
||||
*i_qgroups);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
if (*i_qgroups) {
|
||||
ret = add_relation_rb(quota_root->fs_info, objectid,
|
||||
*i_qgroups);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
++i_qgroups;
|
||||
}
|
||||
|
||||
for (i = 0; i < inherit->num_ref_copies; ++i) {
|
||||
for (i = 0; i < inherit->num_ref_copies; ++i, i_qgroups += 2) {
|
||||
struct btrfs_qgroup *src;
|
||||
struct btrfs_qgroup *dst;
|
||||
|
||||
if (!i_qgroups[0] || !i_qgroups[1])
|
||||
continue;
|
||||
|
||||
src = find_qgroup_rb(fs_info, i_qgroups[0]);
|
||||
dst = find_qgroup_rb(fs_info, i_qgroups[1]);
|
||||
|
||||
@@ -2008,12 +2017,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
|
||||
dst->rfer = src->rfer - level_size;
|
||||
dst->rfer_cmpr = src->rfer_cmpr - level_size;
|
||||
i_qgroups += 2;
|
||||
}
|
||||
for (i = 0; i < inherit->num_excl_copies; ++i) {
|
||||
for (i = 0; i < inherit->num_excl_copies; ++i, i_qgroups += 2) {
|
||||
struct btrfs_qgroup *src;
|
||||
struct btrfs_qgroup *dst;
|
||||
|
||||
if (!i_qgroups[0] || !i_qgroups[1])
|
||||
continue;
|
||||
|
||||
src = find_qgroup_rb(fs_info, i_qgroups[0]);
|
||||
dst = find_qgroup_rb(fs_info, i_qgroups[1]);
|
||||
|
||||
@@ -2024,7 +2035,6 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
|
||||
|
||||
dst->excl = src->excl + level_size;
|
||||
dst->excl_cmpr = src->excl_cmpr + level_size;
|
||||
i_qgroups += 2;
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
||||
Reference in New Issue
Block a user