Patch "btrfs: handle non-fatal errors in btrfs_qgroup_inherit()" has been added to the 4.5-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
btrfs: handle non-fatal errors in btrfs_qgroup_inherit()
to the 4.5-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
btrfs-handle-non-fatal-errors-in-btrfs_qgroup_inherit.patch
and it can be found in the queue-4.5 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
918c2ee103cf9956f1c61d3f848dbb49fd2d104aMon Sep 17 00:00:00 2001
From: Mark Fasheh <[email protected]>
Date: Wed, 30 Mar 2016 17:57:48 -0700
Subject: btrfs: handle non-fatal errors in btrfs_qgroup_inherit()
From: Mark Fasheh <[email protected]>
commit 918c2ee103cf9956f1c61d3f848dbb49fd2d104a upstream.
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
--- fs/btrfs/qgroup.c | 54 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 22 deletions(-)
fs/btrfs/qgroup.c +32 -22
@@ -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_tr2 * 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_tr*/ 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_tr 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_tr 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_tr dst->excl = src->excl + level_size; dst->excl_cmpr = src->excl_cmpr + level_size; - i_qgroups +=2 ; } unlock:
Patches currently in stable-queue which might be from [email protected] are
queue-4.5/ocfs2-fix-posix_acl_create-deadlock.patch queue-4.5/btrfs-handle-non-fatal-errors-in-btrfs_qgroup_inherit.patch queue-4.5/ocfs2-revert-using-ocfs2_acl_chmod-to-avoid-inode-cluster-lock-hang.patch