Commit 08fe4db1 authored by Li Zefan's avatar Li Zefan Committed by Chris Mason

Btrfs: Fix uninitialized root flags for subvolumes

root_item->flags and root_item->byte_limit are not initialized when
a subvolume is created. This bug is not revealed until we added
readonly snapshot support - now you mount a btrfs filesystem and you
may find the subvolumes in it are readonly.

To work around this problem, we steal a bit from root_item->inode_item->flags,
and use it to indicate if those fields have been properly initialized.
When we read a tree root from disk, we check if the bit is set, and if
not we'll set the flag and initialize the two fields of the root item.
Reported-by: default avatarAndreas Philipp <philipp.andreas@gmail.com>
Signed-off-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Tested-by: default avatarAndreas Philipp <philipp.andreas@gmail.com>
cc: stable@kernel.org
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent adae52b9
...@@ -1284,6 +1284,8 @@ struct btrfs_root { ...@@ -1284,6 +1284,8 @@ struct btrfs_root {
#define BTRFS_INODE_DIRSYNC (1 << 10) #define BTRFS_INODE_DIRSYNC (1 << 10)
#define BTRFS_INODE_COMPRESS (1 << 11) #define BTRFS_INODE_COMPRESS (1 << 11)
#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
/* some macros to generate set/get funcs for the struct fields. This /* some macros to generate set/get funcs for the struct fields. This
* assumes there is a lefoo_to_cpu for every type, so lets make a simple * assumes there is a lefoo_to_cpu for every type, so lets make a simple
* one for u8: * one for u8:
...@@ -2359,6 +2361,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid); ...@@ -2359,6 +2361,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
int btrfs_find_orphan_roots(struct btrfs_root *tree_root); int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
int btrfs_set_root_node(struct btrfs_root_item *item, int btrfs_set_root_node(struct btrfs_root_item *item,
struct extent_buffer *node); struct extent_buffer *node);
void btrfs_check_and_init_root_item(struct btrfs_root_item *item);
/* dir-item.c */ /* dir-item.c */
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, const char *name, struct btrfs_root *root, const char *name,
......
...@@ -1276,8 +1276,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, ...@@ -1276,8 +1276,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
root->commit_root = btrfs_root_node(root); root->commit_root = btrfs_root_node(root);
BUG_ON(!root->node); BUG_ON(!root->node);
out: out:
if (location->objectid != BTRFS_TREE_LOG_OBJECTID) if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
root->ref_cows = 1; root->ref_cows = 1;
btrfs_check_and_init_root_item(&root->root_item);
}
return root; return root;
} }
......
...@@ -373,6 +373,10 @@ static noinline int create_subvol(struct btrfs_root *root, ...@@ -373,6 +373,10 @@ static noinline int create_subvol(struct btrfs_root *root,
inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->nbytes = cpu_to_le64(root->leafsize);
inode_item->mode = cpu_to_le32(S_IFDIR | 0755); inode_item->mode = cpu_to_le32(S_IFDIR | 0755);
root_item.flags = 0;
root_item.byte_limit = 0;
inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT);
btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_bytenr(&root_item, leaf->start);
btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_generation(&root_item, trans->transid);
btrfs_set_root_level(&root_item, 0); btrfs_set_root_level(&root_item, 0);
......
...@@ -473,3 +473,21 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, ...@@ -473,3 +473,21 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
btrfs_free_path(path); btrfs_free_path(path);
return 0; return 0;
} }
/*
* Old btrfs forgets to init root_item->flags and root_item->byte_limit
* for subvolumes. To work around this problem, we steal a bit from
* root_item->inode_item->flags, and use it to indicate if those fields
* have been properly initialized.
*/
void btrfs_check_and_init_root_item(struct btrfs_root_item *root_item)
{
u64 inode_flags = le64_to_cpu(root_item->inode.flags);
if (!(inode_flags & BTRFS_INODE_ROOT_ITEM_INIT)) {
inode_flags |= BTRFS_INODE_ROOT_ITEM_INIT;
root_item->inode.flags = cpu_to_le64(inode_flags);
root_item->flags = 0;
root_item->byte_limit = 0;
}
}
...@@ -976,6 +976,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -976,6 +976,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
record_root_in_trans(trans, root); record_root_in_trans(trans, root);
btrfs_set_root_last_snapshot(&root->root_item, trans->transid); btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
btrfs_check_and_init_root_item(new_root_item);
root_flags = btrfs_root_flags(new_root_item); root_flags = btrfs_root_flags(new_root_item);
if (pending->readonly) if (pending->readonly)
......
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