mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
btrfs: skip global block reserve accounting for rescue mounts
[BUG] Mounting with rescue=ibadroots after corrupting the block group tree root triggers a NULL pointer dereference: BUG: kernel NULL pointer dereference, address: 0000000000000100 RIP: 0010:btrfs_update_global_block_rsv+0x9d/0x1c0 [btrfs] Call Trace: fill_dummy_bgs+0xd4/0x120 [btrfs] open_ctree+0xc6e/0x1ca0 [btrfs] btrfs_get_tree+0x50d/0xa40 [btrfs] The same crash occurs with a corrupted raid stripe tree root, via btrfs_read_block_groups() instead of fill_dummy_bgs(). [CAUSE] With rescue=ibadroots, btrfs_read_roots() allows the mount to continue when either root cannot be read, leaving the corresponding root pointer NULL while its on-disk feature bit remains set. btrfs_update_global_block_rsv() then dereferences the missing root based on the feature bit alone. [FIX] Rescue mounts are fully read-only and cannot start transactions, so the global reserve is never consumed. Under btrfs_is_full_ro(), mark the reserve as full and return before performing the accounting. And since we need to check if the fs is mount fully RO, export fs_is_full_ro() as btrfs_is_full_ro(), and move it to fs.h. Fixes:8dbfc14fc7("btrfs: account block group tree when calculating global reserve size") Fixes:515020900d("btrfs: read raid stripe tree from disk") Suggested-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Dongjiang Zhu <zhudongjiang@fnnas.com> [ Squash the fs_is_full_ro() export commit into this one. ] Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
committed by
David Sterba
parent
5fabb1cf25
commit
51a0e83998
+17
-2
@@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv,
|
||||
void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
|
||||
struct btrfs_space_info *sinfo = block_rsv->space_info;
|
||||
struct btrfs_space_info *sinfo;
|
||||
struct btrfs_root *root, *tmp;
|
||||
u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
|
||||
unsigned int min_items = 1;
|
||||
u64 num_bytes;
|
||||
|
||||
/*
|
||||
* A full read-only mount (rescue options) cannot start transactions,
|
||||
* so the global reserve is never consumed. Mark it as full and skip
|
||||
* the accounting.
|
||||
*/
|
||||
if (btrfs_is_full_ro(fs_info)) {
|
||||
spin_lock(&block_rsv->lock);
|
||||
block_rsv->full = true;
|
||||
spin_unlock(&block_rsv->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
sinfo = block_rsv->space_info;
|
||||
num_bytes = btrfs_root_used(&fs_info->tree_root->root_item);
|
||||
|
||||
/*
|
||||
* The global block rsv is based on the size of the extent tree, the
|
||||
|
||||
+1
-10
@@ -3288,15 +3288,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
if (!sb_rdonly(fs_info->sb))
|
||||
return false;
|
||||
if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to wait for any metadata readahead, and invalidate all btree folios.
|
||||
*
|
||||
@@ -3462,7 +3453,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
|
||||
WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
|
||||
|
||||
/* If the fs has any rescue options, no transaction is allowed. */
|
||||
if (fs_is_full_ro(fs_info))
|
||||
if (btrfs_is_full_ro(fs_info))
|
||||
WRITE_ONCE(fs_info->fs_error, -EROFS);
|
||||
|
||||
/* Set up fs_info before parsing mount options */
|
||||
|
||||
@@ -1159,6 +1159,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag,
|
||||
#define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \
|
||||
BTRFS_MOUNT_##opt)
|
||||
|
||||
static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
if (!sb_rdonly(fs_info->sb))
|
||||
return false;
|
||||
if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags));
|
||||
|
||||
Reference in New Issue
Block a user