Commit 6675df31 authored by Omar Sandoval's avatar Omar Sandoval Committed by David Sterba

Btrfs: catch invalid free space trees

There are two separate issues that can lead to corrupted free space
trees.

1. The free space tree bitmaps had an endianness issue on big-endian
   systems which is fixed by an earlier patch in this series.
2. btrfs-progs before v4.7.3 modified filesystems without updating the
   free space tree.

To catch both of these issues at once, we need to force the free space
tree to be rebuilt. To do so, add a FREE_SPACE_TREE_VALID compat_ro bit.
If the bit isn't set, we know that it was either produced by a broken
big-endian kernel or may have been corrupted by btrfs-progs.

This also provides us with a way to add rudimentary read-write support
for the free space tree to btrfs-progs: it can just clear this bit and
have the kernel rebuild the free space tree.

Cc: stable@vger.kernel.org # 4.5+
Tested-by: default avatarHolger Hoffstätte <holger@applied-asynchrony.com>
Tested-by: default avatarChandan Rajendra <chandan@linux.vnet.ibm.com>
Signed-off-by: default avatarOmar Sandoval <osandov@fb.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent f8d468a1
...@@ -251,7 +251,8 @@ struct btrfs_super_block { ...@@ -251,7 +251,8 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL #define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SUPP \ #define BTRFS_FEATURE_COMPAT_RO_SUPP \
(BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE) (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | \
BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID)
#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL
......
...@@ -2566,6 +2566,7 @@ int open_ctree(struct super_block *sb, ...@@ -2566,6 +2566,7 @@ int open_ctree(struct super_block *sb,
int num_backups_tried = 0; int num_backups_tried = 0;
int backup_index = 0; int backup_index = 0;
int max_active; int max_active;
int clear_free_space_tree = 0;
tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL); tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL); chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
...@@ -3131,6 +3132,14 @@ int open_ctree(struct super_block *sb, ...@@ -3131,6 +3132,14 @@ int open_ctree(struct super_block *sb,
if (btrfs_test_opt(fs_info, CLEAR_CACHE) && if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) { btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
clear_free_space_tree = 1;
} else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
btrfs_warn(fs_info, "free space tree is invalid");
clear_free_space_tree = 1;
}
if (clear_free_space_tree) {
btrfs_info(fs_info, "clearing free space tree"); btrfs_info(fs_info, "clearing free space tree");
ret = btrfs_clear_free_space_tree(fs_info); ret = btrfs_clear_free_space_tree(fs_info);
if (ret) { if (ret) {
......
...@@ -1182,6 +1182,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info) ...@@ -1182,6 +1182,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info)
} }
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE); btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
fs_info->creating_free_space_tree = 0; fs_info->creating_free_space_tree = 0;
ret = btrfs_commit_transaction(trans, tree_root); ret = btrfs_commit_transaction(trans, tree_root);
...@@ -1250,6 +1251,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) ...@@ -1250,6 +1251,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
return PTR_ERR(trans); return PTR_ERR(trans);
btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE); btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE);
btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
fs_info->free_space_root = NULL; fs_info->free_space_root = NULL;
ret = clear_free_space_tree(trans, free_space_root); ret = clear_free_space_tree(trans, free_space_root);
......
...@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args { ...@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args {
* Used by: * Used by:
* struct btrfs_ioctl_feature_flags * struct btrfs_ioctl_feature_flags
*/ */
#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0) #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
/*
* Older kernels (< 4.9) on big-endian systems produced broken free space tree
* bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
* < 4.7.3). If this bit is clear, then the free space tree cannot be trusted.
* btrfs-progs can also intentionally clear this bit to ask the kernel to
* rebuild the free space tree, however this might not work on older kernels
* that do not know about this bit. If not sure, clear the cache manually on
* first mount when booting older kernel versions.
*/
#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1)
#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment