Skip to content

Commit 08fe4db

Browse files
Li Zefanchrismason-xx
authored andcommitted
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>
1 parent adae52b commit 08fe4db

File tree

5 files changed

+30
-1
lines changed

5 files changed

+30
-1
lines changed

fs/btrfs/ctree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,8 @@ struct btrfs_root {
12841284
#define BTRFS_INODE_DIRSYNC (1 << 10)
12851285
#define BTRFS_INODE_COMPRESS (1 << 11)
12861286

1287+
#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31)
1288+
12871289
/* some macros to generate set/get funcs for the struct fields. This
12881290
* assumes there is a lefoo_to_cpu for every type, so lets make a simple
12891291
* one for u8:
@@ -2359,6 +2361,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
23592361
int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
23602362
int btrfs_set_root_node(struct btrfs_root_item *item,
23612363
struct extent_buffer *node);
2364+
void btrfs_check_and_init_root_item(struct btrfs_root_item *item);
2365+
23622366
/* dir-item.c */
23632367
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
23642368
struct btrfs_root *root, const char *name,

fs/btrfs/disk-io.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,8 +1276,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
12761276
root->commit_root = btrfs_root_node(root);
12771277
BUG_ON(!root->node);
12781278
out:
1279-
if (location->objectid != BTRFS_TREE_LOG_OBJECTID)
1279+
if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
12801280
root->ref_cows = 1;
1281+
btrfs_check_and_init_root_item(&root->root_item);
1282+
}
12811283

12821284
return root;
12831285
}

fs/btrfs/ioctl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ static noinline int create_subvol(struct btrfs_root *root,
373373
inode_item->nbytes = cpu_to_le64(root->leafsize);
374374
inode_item->mode = cpu_to_le32(S_IFDIR | 0755);
375375

376+
root_item.flags = 0;
377+
root_item.byte_limit = 0;
378+
inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT);
379+
376380
btrfs_set_root_bytenr(&root_item, leaf->start);
377381
btrfs_set_root_generation(&root_item, trans->transid);
378382
btrfs_set_root_level(&root_item, 0);

fs/btrfs/root-tree.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,21 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans,
473473
btrfs_free_path(path);
474474
return 0;
475475
}
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+
}

fs/btrfs/transaction.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
976976
record_root_in_trans(trans, root);
977977
btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
978978
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
979+
btrfs_check_and_init_root_item(new_root_item);
979980

980981
root_flags = btrfs_root_flags(new_root_item);
981982
if (pending->readonly)

0 commit comments

Comments
 (0)