From 3e944ce89462b7b8623ca0019ea6c5beb5df3c47 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 2 Oct 2014 15:12:20 -0400 Subject: btrfs-progs: corrupt btrfs items in btrfs-corrup-block For testing fsck against completely broken btrfs_items. Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- btrfs-corrupt-block.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) (limited to 'btrfs-corrupt-block.c') diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c index 75dca6e..15f1082 100644 --- a/btrfs-corrupt-block.c +++ b/btrfs-corrupt-block.c @@ -108,6 +108,8 @@ static void print_usage(void) fprintf(stderr, "\t-K The key to corrupt in the format " ",, (must also specify -f for the field)\n"); fprintf(stderr, "\t-f The field in the item to corrupt\n"); + fprintf(stderr, "\t-I An item to corrupt (must also specify the field " + "to corrupt and a root+key for the item)\n"); fprintf(stderr, "\t-D Corrupt a dir item, must specify key and field\n"); exit(1); } @@ -313,6 +315,11 @@ enum btrfs_metadata_block_field { BTRFS_METADATA_BLOCK_BAD, }; +enum btrfs_item_field { + BTRFS_ITEM_OFFSET, + BTRFS_ITEM_BAD, +}; + enum btrfs_key_field { BTRFS_KEY_OBJECTID, BTRFS_KEY_TYPE, @@ -353,6 +360,13 @@ static enum btrfs_key_field convert_key_field(char *field) return BTRFS_KEY_BAD; } +static enum btrfs_item_field convert_item_field(char *field) +{ + if (!strncmp(field, "offset", FIELD_BUF_LEN)) + return BTRFS_ITEM_OFFSET; + return BTRFS_ITEM_BAD; +} + static enum btrfs_dir_item_field convert_dir_item_field(char *field) { if (!strncmp(field, "name", FIELD_BUF_LEN)) @@ -371,6 +385,15 @@ static u64 generate_u64(u64 orig) return ret; } +static u32 generate_u32(u32 orig) +{ + u32 ret; + do { + ret = rand(); + } while (ret == orig); + return ret; +} + static u8 generate_u8(u8 orig) { u8 ret; @@ -709,6 +732,58 @@ out: return ret; } +static int corrupt_btrfs_item(struct btrfs_root *root, struct btrfs_key *key, + char *field) +{ + struct btrfs_trans_handle *trans; + struct btrfs_path *path; + enum btrfs_item_field corrupt_field; + u32 orig, bogus; + int ret; + + corrupt_field = convert_item_field(field); + if (corrupt_field == BTRFS_ITEM_BAD) { + fprintf(stderr, "Invalid field %s\n", field); + return -EINVAL; + } + + 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); + } + + ret = btrfs_search_slot(trans, root, key, path, 0, 1); + if (ret != 0) { + fprintf(stderr, "Error searching to node %d\n", ret); + goto out; + } + + ret = 0; + switch (corrupt_field) { + case BTRFS_ITEM_OFFSET: + orig = btrfs_item_offset_nr(path->nodes[0], path->slots[0]); + bogus = generate_u32(orig); + btrfs_set_item_offset(path->nodes[0], + btrfs_item_nr(path->slots[0]), bogus); + break; + default: + ret = -EINVAL; + break; + } + btrfs_mark_buffer_dirty(path->nodes[0]); +out: + btrfs_commit_transaction(trans, root); + btrfs_free_path(path); + return ret; +} + static struct option long_options[] = { /* { "byte-count", 1, NULL, 'b' }, */ { "logical", 1, NULL, 'l' }, @@ -724,6 +799,7 @@ static struct option long_options[] = { { "metadata-block", 1, NULL, 'm'}, { "field", 1, NULL, 'f'}, { "key", 1, NULL, 'K'}, + { "item", 0, NULL, 'I'}, { "dir-item", 0, NULL, 'D'}, { 0, 0, 0, 0} }; @@ -888,6 +964,7 @@ int main(int ac, char **av) int corrupt_block_keys = 0; int chunk_rec = 0; int chunk_tree = 0; + int corrupt_item = 0; int corrupt_di = 0; u64 metadata_block = 0; u64 inode = 0; @@ -900,7 +977,7 @@ int main(int ac, char **av) while(1) { int c; - c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:D", long_options, + c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:ID", long_options, &option_index); if (c < 0) break; @@ -954,6 +1031,9 @@ int main(int ac, char **av) case 'D': corrupt_di = 1; break; + case 'I': + corrupt_item = 1; + break; default: print_usage(); } @@ -1056,6 +1136,12 @@ int main(int ac, char **av) ret = corrupt_dir_item(root, &key, field); goto out_close; } + if (corrupt_item) { + if (!key.objectid) + print_usage(); + ret = corrupt_btrfs_item(root, &key, field); + goto out_close; + } if (key.objectid || key.offset || key.type) { if (!strlen(field)) print_usage(); -- cgit v1.2.1