diff options
author | Mark Fasheh <mfasheh@suse.de> | 2016-03-30 20:57:48 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2016-04-04 10:29:22 -0400 |
commit | 918c2ee103cf9956f1c61d3f848dbb49fd2d104a (patch) | |
tree | be9e58da9801a1210acc6f4fec901eb1797acc17 | |
parent | 0305bc279317a6ba261a663cc32721d78e6544cf (diff) |
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>
-rw-r--r-- | fs/btrfs/qgroup.c | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 5279fdae7142..7173360eea7a 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c | |||
@@ -1842,8 +1842,10 @@ out: | |||
1842 | } | 1842 | } |
1843 | 1843 | ||
1844 | /* | 1844 | /* |
1845 | * copy the acounting information between qgroups. This is necessary when a | 1845 | * Copy the acounting information between qgroups. This is necessary |
1846 | * snapshot or a subvolume is created | 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. | ||
1847 | */ | 1849 | */ |
1848 | int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | 1850 | int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, |
1849 | struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid, | 1851 | struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid, |
@@ -1873,15 +1875,15 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
1873 | 2 * inherit->num_excl_copies; | 1875 | 2 * inherit->num_excl_copies; |
1874 | for (i = 0; i < nums; ++i) { | 1876 | for (i = 0; i < nums; ++i) { |
1875 | srcgroup = find_qgroup_rb(fs_info, *i_qgroups); | 1877 | srcgroup = find_qgroup_rb(fs_info, *i_qgroups); |
1876 | if (!srcgroup) { | ||
1877 | ret = -EINVAL; | ||
1878 | goto out; | ||
1879 | } | ||
1880 | 1878 | ||
1881 | if ((srcgroup->qgroupid >> 48) <= (objectid >> 48)) { | 1879 | /* |
1882 | ret = -EINVAL; | 1880 | * Zero out invalid groups so we can ignore |
1883 | goto out; | 1881 | * them later. |
1884 | } | 1882 | */ |
1883 | if (!srcgroup || | ||
1884 | ((srcgroup->qgroupid >> 48) <= (objectid >> 48))) | ||
1885 | *i_qgroups = 0ULL; | ||
1886 | |||
1885 | ++i_qgroups; | 1887 | ++i_qgroups; |
1886 | } | 1888 | } |
1887 | } | 1889 | } |
@@ -1916,17 +1918,19 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
1916 | */ | 1918 | */ |
1917 | if (inherit) { | 1919 | if (inherit) { |
1918 | i_qgroups = (u64 *)(inherit + 1); | 1920 | 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; | ||
1920 | ret = add_qgroup_relation_item(trans, quota_root, | 1924 | ret = add_qgroup_relation_item(trans, quota_root, |
1921 | objectid, *i_qgroups); | 1925 | objectid, *i_qgroups); |
1922 | if (ret) | 1926 | if (ret && ret != -EEXIST) |
1923 | goto out; | 1927 | goto out; |
1924 | ret = add_qgroup_relation_item(trans, quota_root, | 1928 | ret = add_qgroup_relation_item(trans, quota_root, |
1925 | *i_qgroups, objectid); | 1929 | *i_qgroups, objectid); |
1926 | if (ret) | 1930 | if (ret && ret != -EEXIST) |
1927 | goto out; | 1931 | goto out; |
1928 | ++i_qgroups; | ||
1929 | } | 1932 | } |
1933 | ret = 0; | ||
1930 | } | 1934 | } |
1931 | 1935 | ||
1932 | 1936 | ||
@@ -1987,17 +1991,22 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
1987 | 1991 | ||
1988 | i_qgroups = (u64 *)(inherit + 1); | 1992 | i_qgroups = (u64 *)(inherit + 1); |
1989 | for (i = 0; i < inherit->num_qgroups; ++i) { | 1993 | for (i = 0; i < inherit->num_qgroups; ++i) { |
1990 | ret = add_relation_rb(quota_root->fs_info, objectid, | 1994 | if (*i_qgroups) { |
1991 | *i_qgroups); | 1995 | ret = add_relation_rb(quota_root->fs_info, objectid, |
1992 | if (ret) | 1996 | *i_qgroups); |
1993 | goto unlock; | 1997 | if (ret) |
1998 | goto unlock; | ||
1999 | } | ||
1994 | ++i_qgroups; | 2000 | ++i_qgroups; |
1995 | } | 2001 | } |
1996 | 2002 | ||
1997 | for (i = 0; i < inherit->num_ref_copies; ++i) { | 2003 | for (i = 0; i < inherit->num_ref_copies; ++i, i_qgroups += 2) { |
1998 | struct btrfs_qgroup *src; | 2004 | struct btrfs_qgroup *src; |
1999 | struct btrfs_qgroup *dst; | 2005 | struct btrfs_qgroup *dst; |
2000 | 2006 | ||
2007 | if (!i_qgroups[0] || !i_qgroups[1]) | ||
2008 | continue; | ||
2009 | |||
2001 | src = find_qgroup_rb(fs_info, i_qgroups[0]); | 2010 | src = find_qgroup_rb(fs_info, i_qgroups[0]); |
2002 | dst = find_qgroup_rb(fs_info, i_qgroups[1]); | 2011 | dst = find_qgroup_rb(fs_info, i_qgroups[1]); |
2003 | 2012 | ||
@@ -2008,12 +2017,14 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
2008 | 2017 | ||
2009 | dst->rfer = src->rfer - level_size; | 2018 | dst->rfer = src->rfer - level_size; |
2010 | dst->rfer_cmpr = src->rfer_cmpr - level_size; | 2019 | dst->rfer_cmpr = src->rfer_cmpr - level_size; |
2011 | i_qgroups += 2; | ||
2012 | } | 2020 | } |
2013 | for (i = 0; i < inherit->num_excl_copies; ++i) { | 2021 | for (i = 0; i < inherit->num_excl_copies; ++i, i_qgroups += 2) { |
2014 | struct btrfs_qgroup *src; | 2022 | struct btrfs_qgroup *src; |
2015 | struct btrfs_qgroup *dst; | 2023 | struct btrfs_qgroup *dst; |
2016 | 2024 | ||
2025 | if (!i_qgroups[0] || !i_qgroups[1]) | ||
2026 | continue; | ||
2027 | |||
2017 | src = find_qgroup_rb(fs_info, i_qgroups[0]); | 2028 | src = find_qgroup_rb(fs_info, i_qgroups[0]); |
2018 | dst = find_qgroup_rb(fs_info, i_qgroups[1]); | 2029 | dst = find_qgroup_rb(fs_info, i_qgroups[1]); |
2019 | 2030 | ||
@@ -2024,7 +2035,6 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, | |||
2024 | 2035 | ||
2025 | dst->excl = src->excl + level_size; | 2036 | dst->excl = src->excl + level_size; |
2026 | dst->excl_cmpr = src->excl_cmpr + level_size; | 2037 | dst->excl_cmpr = src->excl_cmpr + level_size; |
2027 | i_qgroups += 2; | ||
2028 | } | 2038 | } |
2029 | 2039 | ||
2030 | unlock: | 2040 | unlock: |