diff options
Diffstat (limited to 'btrfs-corrupt-block.c')
-rw-r--r-- | btrfs-corrupt-block.c | 274 |
1 files changed, 226 insertions, 48 deletions
diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c index ace61c1..8176fad 100644 --- a/btrfs-corrupt-block.c +++ b/btrfs-corrupt-block.c @@ -32,11 +32,6 @@ #include "list.h" #include "version.h" -/* we write the mirror info to stdout unless they are dumping the data - * to stdout - * */ -static FILE *info_file; - struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, int copy) { @@ -55,20 +50,21 @@ struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr, length = blocksize; while (1) { ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, - eb->start, &length, &multi, mirror_num); + eb->start, &length, &multi, + mirror_num, NULL); BUG_ON(ret); device = multi->stripes[0].dev; eb->fd = device->fd; device->total_ios++; eb->dev_bytenr = multi->stripes[0].physical; - fprintf(info_file, "mirror %d logical %Lu physical %Lu " + fprintf(stdout, "mirror %d logical %Lu physical %Lu " "device %s\n", mirror_num, (unsigned long long)bytenr, (unsigned long long)eb->dev_bytenr, device->name); kfree(multi); if (!copy || mirror_num == copy) { - ret = read_extent_from_disk(eb); + ret = read_extent_from_disk(eb, 0, eb->len); printf("corrupting %llu copy %d\n", eb->start, mirror_num); memset(eb->data, 0, eb->len); @@ -90,40 +86,220 @@ struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr, static void print_usage(void) { - fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); - fprintf(stderr, "\t-l Logical extent to map\n"); - fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); - fprintf(stderr, "\t-o Output file to hold the extent\n"); - fprintf(stderr, "\t-b Number of bytes to read\n"); + fprintf(stderr, "usage: btrfs-corrupt-block [options] device\n"); + fprintf(stderr, "\t-l Logical extent to be corrupted\n"); + fprintf(stderr, "\t-c Copy of the extent to be corrupted" + " (usually 1 or 2, default: 0)\n"); + fprintf(stderr, "\t-b Number of bytes to be corrupted\n"); + fprintf(stderr, "\t-e Extent to be corrupted\n"); + fprintf(stderr, "\t-E The whole extent free to be corrupted\n"); exit(1); } +static void corrupt_keys(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct extent_buffer *eb) +{ + int slot; + int bad_slot; + int nr; + struct btrfs_disk_key bad_key;; + + nr = btrfs_header_nritems(eb); + if (nr == 0) + return; + + slot = rand() % nr; + bad_slot = rand() % nr; + + if (bad_slot == slot) + return; + + fprintf(stderr, "corrupting keys in block %llu slot %d swapping with %d\n", + (unsigned long long)eb->start, slot, bad_slot); + + if (btrfs_header_level(eb) == 0) { + btrfs_item_key(eb, &bad_key, bad_slot); + btrfs_set_item_key(eb, &bad_key, slot); + } else { + btrfs_node_key(eb, &bad_key, bad_slot); + btrfs_set_node_key(eb, &bad_key, slot); + } + btrfs_mark_buffer_dirty(eb); + if (!trans) { + csum_tree_block(root, eb, 0); + write_extent_to_disk(eb); + } +} + + +static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr) +{ + struct extent_buffer *eb; + + eb = read_tree_block(root, bytenr, root->leafsize, 0); + if (!eb) + return -EIO;; + + corrupt_keys(NULL, root, eb); + free_extent_buffer(eb); + return 0; +} + +static int corrupt_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 bytenr, int copy) +{ + struct btrfs_key key; + struct extent_buffer *leaf; + u32 item_size; + unsigned long ptr; + struct btrfs_path *path; + int ret; + int slot; + int should_del = rand() % 3; + + path = btrfs_alloc_path(); + + key.objectid = bytenr; + key.type = (u8)-1; + key.offset = (u64)-1; + + while(1) { + ret = btrfs_search_slot(trans, root->fs_info->extent_root, + &key, path, -1, 1); + if (ret < 0) + break; + + if (ret > 0) { + if (path->slots[0] == 0) + break; + path->slots[0]--; + ret = 0; + } + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid != bytenr) + break; + + if (key.type != BTRFS_EXTENT_ITEM_KEY && + key.type != BTRFS_TREE_BLOCK_REF_KEY && + key.type != BTRFS_EXTENT_DATA_REF_KEY && + key.type != BTRFS_EXTENT_REF_V0_KEY && + key.type != BTRFS_SHARED_BLOCK_REF_KEY && + key.type != BTRFS_SHARED_DATA_REF_KEY) + goto next; + + if (should_del) { + fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n", + key.objectid, key.type, key.offset); + + if (key.type == BTRFS_EXTENT_ITEM_KEY) { + /* make sure this extent doesn't get + * reused for other purposes */ + btrfs_pin_extent(root->fs_info, + key.objectid, key.offset); + } + + btrfs_del_item(trans, root, path); + } else { + fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n", + key.objectid, key.type, key.offset); + ptr = btrfs_item_ptr_offset(leaf, slot); + item_size = btrfs_item_size_nr(leaf, slot); + memset_extent_buffer(leaf, 0, ptr, item_size); + btrfs_mark_buffer_dirty(leaf); + } +next: + btrfs_release_path(NULL, path); + + if (key.offset > 0) + key.offset--; + if (key.offset == 0) + break; + } + + btrfs_free_path(path); + return 0; +} + +static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *eb) +{ + u32 nr = btrfs_header_nritems(eb); + u32 victim = rand() % nr; + u64 objectid; + struct btrfs_key key; + + btrfs_item_key_to_cpu(eb, &key, victim); + objectid = key.objectid; + corrupt_extent(trans, root, objectid, 1); +} + +static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct extent_buffer *eb) +{ + int i; + u32 nr; + + if (!eb) + return; + + nr = btrfs_header_nritems(eb); + if (btrfs_is_leaf(eb)) { + btrfs_corrupt_extent_leaf(trans, root, eb); + return; + } + + if (btrfs_header_level(eb) == 1 && eb != root->node) { + if (rand() % 5) + return; + } + + for (i = 0; i < nr; i++) { + struct extent_buffer *next; + + next = read_tree_block(root, btrfs_node_blockptr(eb, i), + root->leafsize, btrfs_node_ptr_generation(eb, i)); + if (!next) + continue; + btrfs_corrupt_extent_tree(trans, root, next); + free_extent_buffer(next); + } +} + static struct option long_options[] = { /* { "byte-count", 1, NULL, 'b' }, */ { "logical", 1, NULL, 'l' }, { "copy", 1, NULL, 'c' }, { "bytes", 1, NULL, 'b' }, + { "extent-record", 0, NULL, 'e' }, + { "extent-tree", 0, NULL, 'E' }, + { "keys", 0, NULL, 'k' }, { 0, 0, 0, 0} }; + int main(int ac, char **av) { struct cache_tree root_cache; struct btrfs_root *root; struct extent_buffer *eb; char *dev; - char *output_file = NULL; u64 logical = 0; int ret = 0; int option_index = 0; int copy = 0; u64 bytes = 4096; - int out_fd = 0; - int err; + int extent_rec = 0; + int extent_tree = 0; + int corrupt_block_keys = 0; + + srand(128); while(1) { int c; - c = getopt_long(ac, av, "l:c:", long_options, + c = getopt_long(ac, av, "l:c:b:eEk", long_options, &option_index); if (c < 0) break; @@ -152,6 +328,15 @@ int main(int ac, char **av) print_usage(); } break; + case 'e': + extent_rec = 1; + break; + case 'E': + extent_tree = 1; + break; + case 'k': + corrupt_block_keys = 1; + break; default: print_usage(); } @@ -159,7 +344,7 @@ int main(int ac, char **av) ac = ac - optind; if (ac == 0) print_usage(); - if (logical == 0) + if (logical == 0 && !extent_tree) print_usage(); if (copy < 0) print_usage(); @@ -174,23 +359,20 @@ int main(int ac, char **av) fprintf(stderr, "Open ctree failed\n"); exit(1); } - - info_file = stdout; - if (output_file) { - if (strcmp(output_file, "-") == 0) { - out_fd = 1; - info_file = stderr; - } else { - out_fd = open(output_file, O_RDWR | O_CREAT, 0600); - if (out_fd < 0) - goto close; - err = ftruncate(out_fd, 0); - if (err) { - close(out_fd); - goto close; - } - info_file = stdout; - } + if (extent_rec) { + struct btrfs_trans_handle *trans; + trans = btrfs_start_transaction(root, 1); + ret = corrupt_extent (trans, root, logical, 0); + btrfs_commit_transaction(trans, root); + goto out_close; + } + if (extent_tree) { + struct btrfs_trans_handle *trans; + trans = btrfs_start_transaction(root, 1); + btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root, + root->fs_info->extent_root->node); + btrfs_commit_transaction(trans, root); + goto out_close; } if (bytes == 0) @@ -200,22 +382,18 @@ int main(int ac, char **av) bytes *= root->sectorsize; while (bytes > 0) { - eb = debug_corrupt_block(root, logical, root->sectorsize, copy); - if (eb && output_file) { - err = write(out_fd, eb->data, eb->len); - if (err < 0 || err != eb->len) { - fprintf(stderr, "output file write failed\n"); - goto out_close_fd; - } + if (corrupt_block_keys) { + corrupt_keys_in_block(root, logical); + } else { + eb = debug_corrupt_block(root, logical, + root->sectorsize, copy); + free_extent_buffer(eb); } - free_extent_buffer(eb); logical += root->sectorsize; bytes -= root->sectorsize; } - -out_close_fd: - if (output_file && out_fd != 1) - close(out_fd); -close: + return ret; +out_close: + close_ctree(root); return ret; } |