diff options
author | Li Zefan <lizf@cn.fujitsu.com> | 2011-03-27 22:01:25 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2011-04-05 01:20:24 -0400 |
commit | 08fe4db170b4193603d9d31f40ebaf652d07ac9c (patch) | |
tree | 2d0d2abe40b77266600653e399d4973a679716a9 /fs/btrfs | |
parent | adae52b94e18afa1f84fab67df2a8a872c2f5533 (diff) |
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: Andreas Philipp <philipp.andreas@gmail.com>
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Tested-by: Andreas Philipp <philipp.andreas@gmail.com>
cc: stable@kernel.org
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.h | 4 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 4 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 4 | ||||
-rw-r--r-- | fs/btrfs/root-tree.c | 18 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 1 |
5 files changed, 30 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index d47ce8307854..3458b5725540 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1284,6 +1284,8 @@ struct btrfs_root { | |||
1284 | #define BTRFS_INODE_DIRSYNC (1 << 10) | 1284 | #define BTRFS_INODE_DIRSYNC (1 << 10) |
1285 | #define BTRFS_INODE_COMPRESS (1 << 11) | 1285 | #define BTRFS_INODE_COMPRESS (1 << 11) |
1286 | 1286 | ||
1287 | #define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31) | ||
1288 | |||
1287 | /* some macros to generate set/get funcs for the struct fields. This | 1289 | /* some macros to generate set/get funcs for the struct fields. This |
1288 | * assumes there is a lefoo_to_cpu for every type, so lets make a simple | 1290 | * assumes there is a lefoo_to_cpu for every type, so lets make a simple |
1289 | * one for u8: | 1291 | * one for u8: |
@@ -2359,6 +2361,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid); | |||
2359 | int btrfs_find_orphan_roots(struct btrfs_root *tree_root); | 2361 | int btrfs_find_orphan_roots(struct btrfs_root *tree_root); |
2360 | int btrfs_set_root_node(struct btrfs_root_item *item, | 2362 | int btrfs_set_root_node(struct btrfs_root_item *item, |
2361 | struct extent_buffer *node); | 2363 | struct extent_buffer *node); |
2364 | void btrfs_check_and_init_root_item(struct btrfs_root_item *item); | ||
2365 | |||
2362 | /* dir-item.c */ | 2366 | /* dir-item.c */ |
2363 | int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, | 2367 | int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, |
2364 | struct btrfs_root *root, const char *name, | 2368 | struct btrfs_root *root, const char *name, |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 5cf3aa7b125c..a272bfd74ea0 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -1276,8 +1276,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, | |||
1276 | root->commit_root = btrfs_root_node(root); | 1276 | root->commit_root = btrfs_root_node(root); |
1277 | BUG_ON(!root->node); | 1277 | BUG_ON(!root->node); |
1278 | out: | 1278 | out: |
1279 | if (location->objectid != BTRFS_TREE_LOG_OBJECTID) | 1279 | if (location->objectid != BTRFS_TREE_LOG_OBJECTID) { |
1280 | root->ref_cows = 1; | 1280 | root->ref_cows = 1; |
1281 | btrfs_check_and_init_root_item(&root->root_item); | ||
1282 | } | ||
1281 | 1283 | ||
1282 | return root; | 1284 | return root; |
1283 | } | 1285 | } |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 255c7c5279c4..f9c93a9ed4a7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -373,6 +373,10 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
373 | inode_item->nbytes = cpu_to_le64(root->leafsize); | 373 | inode_item->nbytes = cpu_to_le64(root->leafsize); |
374 | inode_item->mode = cpu_to_le32(S_IFDIR | 0755); | 374 | inode_item->mode = cpu_to_le32(S_IFDIR | 0755); |
375 | 375 | ||
376 | root_item.flags = 0; | ||
377 | root_item.byte_limit = 0; | ||
378 | inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); | ||
379 | |||
376 | btrfs_set_root_bytenr(&root_item, leaf->start); | 380 | btrfs_set_root_bytenr(&root_item, leaf->start); |
377 | btrfs_set_root_generation(&root_item, trans->transid); | 381 | btrfs_set_root_generation(&root_item, trans->transid); |
378 | btrfs_set_root_level(&root_item, 0); | 382 | btrfs_set_root_level(&root_item, 0); |
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 29b2d7c930eb..6928bff62daa 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c | |||
@@ -473,3 +473,21 @@ again: | |||
473 | btrfs_free_path(path); | 473 | btrfs_free_path(path); |
474 | return 0; | 474 | return 0; |
475 | } | 475 | } |
476 | |||
477 | /* | ||
478 | * Old btrfs forgets to init root_item->flags and root_item->byte_limit | ||
479 | * for subvolumes. To work around this problem, we steal a bit from | ||
480 | * root_item->inode_item->flags, and use it to indicate if those fields | ||
481 | * have been properly initialized. | ||
482 | */ | ||
483 | void btrfs_check_and_init_root_item(struct btrfs_root_item *root_item) | ||
484 | { | ||
485 | u64 inode_flags = le64_to_cpu(root_item->inode.flags); | ||
486 | |||
487 | if (!(inode_flags & BTRFS_INODE_ROOT_ITEM_INIT)) { | ||
488 | inode_flags |= BTRFS_INODE_ROOT_ITEM_INIT; | ||
489 | root_item->inode.flags = cpu_to_le64(inode_flags); | ||
490 | root_item->flags = 0; | ||
491 | root_item->byte_limit = 0; | ||
492 | } | ||
493 | } | ||
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index d01cc249a8d3..5b158da7e0bb 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -976,6 +976,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | |||
976 | record_root_in_trans(trans, root); | 976 | record_root_in_trans(trans, root); |
977 | btrfs_set_root_last_snapshot(&root->root_item, trans->transid); | 977 | btrfs_set_root_last_snapshot(&root->root_item, trans->transid); |
978 | memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); | 978 | memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); |
979 | btrfs_check_and_init_root_item(new_root_item); | ||
979 | 980 | ||
980 | root_flags = btrfs_root_flags(new_root_item); | 981 | root_flags = btrfs_root_flags(new_root_item); |
981 | if (pending->readonly) | 982 | if (pending->readonly) |