mirror of
https://github.com/kdave/btrfs-progs
synced 2024-12-24 15:12:47 +00:00
btrfs-progs: corrupt-block: Refactor tree block corruption code
As progs' transaction/CoW logic evolved over the years the metadata block corruption code failed to do so. It's currently impossible to corrupt the generation because the CoW logic will not only set it to the value of the currently running transaction (__btrfs_cow_block) but the current code will ASSERT due to the following check in __btrfs_cow_block: WARN_ON(!(buf->flags & EXTENT_BAD_TRANSID) && btrfs_header_generation(buf) > trans->transid); Fix this by making the generation corruption code directly write the modified block, outside of the transaction mechanism. At the same time move the old code into BTRFS_METADATA_BLOCK_SHIFT_ITEMS handling case, essentially leaving it unchanged. Signed-off-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
989a99b5f8
commit
8ebc7219ee
@ -747,15 +747,8 @@ static void shift_items(struct btrfs_root *root, struct extent_buffer *eb)
|
||||
static int corrupt_metadata_block(struct btrfs_fs_info *fs_info, u64 block,
|
||||
char *field)
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_path *path;
|
||||
struct extent_buffer *eb;
|
||||
struct btrfs_key key, root_key;
|
||||
enum btrfs_metadata_block_field corrupt_field;
|
||||
u64 root_objectid;
|
||||
u64 orig, bogus;
|
||||
u8 level;
|
||||
int ret;
|
||||
|
||||
corrupt_field = convert_metadata_block_field(field);
|
||||
@ -769,63 +762,78 @@ static int corrupt_metadata_block(struct btrfs_fs_info *fs_info, u64 block,
|
||||
fprintf(stderr, "Couldn't read in tree block %s\n", field);
|
||||
return -EINVAL;
|
||||
}
|
||||
root_objectid = btrfs_header_owner(eb);
|
||||
level = btrfs_header_level(eb);
|
||||
if (level)
|
||||
btrfs_node_key_to_cpu(eb, &key, 0);
|
||||
else
|
||||
btrfs_item_key_to_cpu(eb, &key, 0);
|
||||
free_extent_buffer(eb);
|
||||
|
||||
root_key.objectid = root_objectid;
|
||||
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
root_key.offset = (u64)-1;
|
||||
|
||||
root = btrfs_read_fs_root(fs_info, &root_key);
|
||||
if (IS_ERR(root)) {
|
||||
fprintf(stderr, "Couldn't find owner root %llu\n",
|
||||
key.objectid);
|
||||
return PTR_ERR(root);
|
||||
}
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans)) {
|
||||
btrfs_free_path(path);
|
||||
fprintf(stderr, "Couldn't start transaction %ld\n",
|
||||
PTR_ERR(trans));
|
||||
return PTR_ERR(trans);
|
||||
}
|
||||
|
||||
path->lowest_level = level;
|
||||
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error searching to node %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
eb = path->nodes[level];
|
||||
|
||||
ret = 0;
|
||||
switch (corrupt_field) {
|
||||
case BTRFS_METADATA_BLOCK_GENERATION:
|
||||
orig = btrfs_header_generation(eb);
|
||||
bogus = generate_u64(orig);
|
||||
{
|
||||
u64 orig = btrfs_header_generation(eb);
|
||||
u64 bogus = generate_u64(orig);
|
||||
|
||||
btrfs_set_header_generation(eb, bogus);
|
||||
write_and_map_eb(fs_info, eb);
|
||||
free_extent_buffer(eb);
|
||||
break;
|
||||
}
|
||||
case BTRFS_METADATA_BLOCK_SHIFT_ITEMS:
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_key key, root_key;
|
||||
u64 root_objectid;
|
||||
u8 level;
|
||||
|
||||
root_objectid = btrfs_header_owner(eb);
|
||||
level = btrfs_header_level(eb);
|
||||
if (level)
|
||||
btrfs_node_key_to_cpu(eb, &key, 0);
|
||||
else
|
||||
btrfs_item_key_to_cpu(eb, &key, 0);
|
||||
free_extent_buffer(eb);
|
||||
|
||||
root_key.objectid = root_objectid;
|
||||
root_key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
root_key.offset = (u64)-1;
|
||||
|
||||
root = btrfs_read_fs_root(fs_info, &root_key);
|
||||
if (IS_ERR(root)) {
|
||||
fprintf(stderr, "Couldn't find owner root %llu\n",
|
||||
key.objectid);
|
||||
return PTR_ERR(root);
|
||||
}
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans)) {
|
||||
btrfs_free_path(path);
|
||||
fprintf(stderr, "Couldn't start transaction %ld\n",
|
||||
PTR_ERR(trans));
|
||||
return PTR_ERR(trans);
|
||||
}
|
||||
|
||||
path->lowest_level = level;
|
||||
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error searching to node %d\n", ret);
|
||||
btrfs_free_path(path);
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
return ret;
|
||||
}
|
||||
eb = path->nodes[level];
|
||||
shift_items(root, path->nodes[level]);
|
||||
btrfs_mark_buffer_dirty(path->nodes[level]);
|
||||
btrfs_commit_transaction(trans, root);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
btrfs_mark_buffer_dirty(path->nodes[level]);
|
||||
out:
|
||||
btrfs_commit_transaction(trans, root);
|
||||
btrfs_free_path(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user