#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
+#include <limits.h>
+
#include "kerncompat.h"
#include "ctree.h"
#include "volumes.h"
#define FIELD_BUF_LEN 80
-struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
- u32 blocksize, u64 copy)
+static struct extent_buffer *debug_corrupt_block(struct btrfs_root *root,
+ u64 bytenr, u32 blocksize, u64 copy)
{
int ret;
struct extent_buffer *eb;
int num_copies;
int mirror_num = 1;
- eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
+ eb = btrfs_find_create_tree_block(root->fs_info, bytenr, blocksize);
if (!eb)
return NULL;
return eb;
}
-static void print_usage(void)
+static void print_usage(int ret)
{
fprintf(stderr, "usage: btrfs-corrupt-block [options] device\n");
fprintf(stderr, "\t-l Logical extent to be corrupted\n");
"to corrupt and a root+key for the item)\n");
fprintf(stderr, "\t-D Corrupt a dir item, must specify key and field\n");
fprintf(stderr, "\t-d Delete this item (must specify -K)\n");
- exit(1);
+ fprintf(stderr, "\t-r Operate on this root (only works with -d)\n");
+ fprintf(stderr, "\t-C Delete a csum for the specified bytenr. When "
+ "used with -b it'll delete that many bytes, otherwise it's "
+ "just sectorsize\n");
+ exit(ret);
}
static void corrupt_keys(struct btrfs_trans_handle *trans,
{
struct extent_buffer *eb;
- eb = read_tree_block(root, bytenr, root->leafsize, 0);
- if (!eb)
+ eb = read_tree_block(root, bytenr, root->nodesize, 0);
+ if (!extent_buffer_uptodate(eb))
return -EIO;;
corrupt_keys(NULL, root, eb);
struct extent_buffer *next;
next = read_tree_block(root, btrfs_node_blockptr(eb, i),
- root->leafsize,
+ root->nodesize,
btrfs_node_ptr_generation(eb, i));
- if (!next)
+ if (!extent_buffer_uptodate(next))
continue;
btrfs_corrupt_extent_tree(trans, root, next);
free_extent_buffer(next);
enum btrfs_inode_field {
BTRFS_INODE_FIELD_ISIZE,
+ BTRFS_INODE_FIELD_NBYTES,
BTRFS_INODE_FIELD_BAD,
};
{
if (!strncmp(field, "isize", FIELD_BUF_LEN))
return BTRFS_INODE_FIELD_ISIZE;
+ if (!strncmp(field, "nbytes", FIELD_BUF_LEN))
+ return BTRFS_INODE_FIELD_NBYTES;
return BTRFS_INODE_FIELD_BAD;
}
struct btrfs_trans_handle *trans;
struct btrfs_dir_item *di;
struct btrfs_path *path;
- char *name;
+ char name[PATH_MAX];
struct btrfs_key location;
struct btrfs_disk_key disk_key;
unsigned long name_ptr;
switch (corrupt_field) {
case BTRFS_DIR_ITEM_NAME:
name_len = btrfs_dir_name_len(path->nodes[0], di);
- name = malloc(name_len);
- if (!name) {
- ret = -ENOMEM;
- goto out;
- }
name_ptr = (unsigned long)(di + 1);
read_extent_buffer(path->nodes[0], name, name_ptr, name_len);
name[0]++;
write_extent_buffer(path->nodes[0], name, name_ptr, name_len);
btrfs_mark_buffer_dirty(path->nodes[0]);
- free(name);
goto out;
case BTRFS_DIR_ITEM_LOCATION_OBJECTID:
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
bogus = generate_u64(orig);
btrfs_set_inode_size(path->nodes[0], ei, bogus);
break;
+ case BTRFS_INODE_FIELD_NBYTES:
+ orig = btrfs_inode_nbytes(path->nodes[0], ei);
+ bogus = generate_u64(orig);
+ btrfs_set_inode_nbytes(path->nodes[0], ei, bogus);
+ break;
default:
ret = -EINVAL;
break;
return -EINVAL;
}
- eb = read_tree_block(root, block, root->leafsize, 0);
- if (!eb) {
+ eb = read_tree_block(root, block, root->nodesize, 0);
+ if (!extent_buffer_uptodate(eb)) {
fprintf(stderr, "Couldn't read in tree block %s\n", field);
return -EINVAL;
}
root = btrfs_read_fs_root(root->fs_info, &root_key);
if (IS_ERR(root)) {
- fprintf(stderr, "Couldn't finde owner root %llu\n",
+ fprintf(stderr, "Couldn't find owner root %llu\n",
key.objectid);
return PTR_ERR(root);
}
return ret;
}
+static int delete_csum(struct btrfs_root *root, u64 bytenr, u64 bytes)
+{
+ struct btrfs_trans_handle *trans;
+ int ret;
+
+ root = root->fs_info->csum_root;
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ fprintf(stderr, "Couldn't start transaction %ld\n",
+ PTR_ERR(trans));
+ return PTR_ERR(trans);
+ }
+
+ ret = btrfs_del_csums(trans, root, bytenr, bytes);
+ if (ret)
+ fprintf(stderr, "Error deleting csums %d\n", ret);
+ btrfs_commit_transaction(trans, root);
+ return ret;
+}
+
/* corrupt item using NO cow.
- * Because chunk recover will recover based on whole partition scaning,
+ * Because chunk recover will recover based on whole partition scanning,
* If using COW, chunk recover will use the old item to recover,
* which is still OK but we want to check the ability to rebuild chunk
* not only restore the old ones */
-int corrupt_item_nocow(struct btrfs_trans_handle *trans,
+static int corrupt_item_nocow(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_path *path,
int del)
{
}
return ret;
}
-int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
+static int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
int ret;
btrfs_free_path(path);
return ret;
}
-int find_chunk_offset(struct btrfs_root *root,
+static int find_chunk_offset(struct btrfs_root *root,
struct btrfs_path *path, u64 offset)
{
struct btrfs_key key;
goto out;
}
if (ret < 0) {
- fprintf(stderr, "Error searching chunk");
+ fprintf(stderr, "Error searching chunk\n");
goto out;
}
out:
return ret;
}
-int main(int ac, char **av)
+int main(int argc, char **argv)
{
struct cache_tree root_cache;
struct btrfs_key key;
u64 metadata_block = 0;
u64 inode = 0;
u64 file_extent = (u64)-1;
+ u64 root_objectid = 0;
+ u64 csum_bytenr = 0;
char field[FIELD_BUF_LEN];
field[0] = '\0';
while(1) {
int c;
- int option_index = 0;
static const 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' },
- { "chunk-record", 0, NULL, 'u' },
- { "chunk-tree", 0, NULL, 'U' },
- { "inode", 1, NULL, 'i'},
- { "file-extent", 1, NULL, 'x'},
- { "metadata-block", 1, NULL, 'm'},
- { "field", 1, NULL, 'f'},
- { "key", 1, NULL, 'K'},
- { "item", 0, NULL, 'I'},
- { "dir-item", 0, NULL, 'D'},
- { "delete", 0, NULL, 'd'},
+ { "logical", required_argument, NULL, 'l' },
+ { "copy", required_argument, NULL, 'c' },
+ { "bytes", required_argument, NULL, 'b' },
+ { "extent-record", no_argument, NULL, 'e' },
+ { "extent-tree", no_argument, NULL, 'E' },
+ { "keys", no_argument, NULL, 'k' },
+ { "chunk-record", no_argument, NULL, 'u' },
+ { "chunk-tree", no_argument, NULL, 'U' },
+ { "inode", required_argument, NULL, 'i'},
+ { "file-extent", required_argument, NULL, 'x'},
+ { "metadata-block", required_argument, NULL, 'm'},
+ { "field", required_argument, NULL, 'f'},
+ { "key", required_argument, NULL, 'K'},
+ { "item", no_argument, NULL, 'I'},
+ { "dir-item", no_argument, NULL, 'D'},
+ { "delete", no_argument, NULL, 'd'},
+ { "root", no_argument, NULL, 'r'},
+ { "csum", required_argument, NULL, 'C'},
+ { "help", no_argument, NULL, GETOPT_VAL_HELP},
{ NULL, 0, NULL, 0 }
};
- c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:IDd", long_options,
- &option_index);
+ c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:IDdr:C:",
+ long_options, NULL);
if (c < 0)
break;
switch(c) {
if (ret != 3) {
fprintf(stderr, "error reading key "
"%d\n", errno);
- print_usage();
+ print_usage(1);
}
break;
case 'D':
case 'd':
delete = 1;
break;
+ case 'r':
+ root_objectid = arg_strtou64(optarg);
+ break;
+ case 'C':
+ csum_bytenr = arg_strtou64(optarg);
+ break;
+ case GETOPT_VAL_HELP:
default:
- print_usage();
+ print_usage(c != GETOPT_VAL_HELP);
}
}
- set_argv0(av);
- ac = ac - optind;
- if (check_argc_min(ac, 1))
- print_usage();
- dev = av[optind];
+ set_argv0(argv);
+ if (check_argc_min(argc - optind, 1))
+ print_usage(1);
+ dev = argv[optind];
radix_tree_init();
cache_tree_init(&root_cache);
struct btrfs_trans_handle *trans;
if (logical == (u64)-1)
- print_usage();
+ print_usage(1);
trans = btrfs_start_transaction(root, 1);
ret = corrupt_extent (trans, root, logical, 0);
btrfs_commit_transaction(trans, root);
int del;
if (logical == (u64)-1)
- print_usage();
+ print_usage(1);
del = rand() % 3;
path = btrfs_alloc_path();
if (!path) {
if (inode) {
struct btrfs_trans_handle *trans;
- if (!strlen(field))
- print_usage();
+ if (*field == 0)
+ print_usage(1);
trans = btrfs_start_transaction(root, 1);
if (file_extent == (u64)-1) {
goto out_close;
}
if (metadata_block) {
- if (!strlen(field))
- print_usage();
+ if (*field == 0)
+ print_usage(1);
ret = corrupt_metadata_block(root, metadata_block, field);
goto out_close;
}
if (corrupt_di) {
- if (!key.objectid || !strlen(field))
- print_usage();
+ if (!key.objectid || *field == 0)
+ print_usage(1);
ret = corrupt_dir_item(root, &key, field);
goto out_close;
}
+ if (csum_bytenr) {
+ ret = delete_csum(root, csum_bytenr, bytes);
+ goto out_close;
+ }
if (corrupt_item) {
if (!key.objectid)
- print_usage();
+ print_usage(1);
ret = corrupt_btrfs_item(root, &key, field);
}
if (delete) {
+ struct btrfs_root *target = root;
+
if (!key.objectid)
- print_usage();
- ret = delete_item(root, &key);
+ print_usage(1);
+ if (root_objectid) {
+ struct btrfs_key root_key;
+
+ root_key.objectid = root_objectid;
+ root_key.type = BTRFS_ROOT_ITEM_KEY;
+ root_key.offset = (u64)-1;
+
+ target = btrfs_read_fs_root(root->fs_info, &root_key);
+ if (IS_ERR(target)) {
+ fprintf(stderr, "Couldn't find root %llu\n",
+ (unsigned long long)root_objectid);
+ print_usage(1);
+ }
+ }
+ ret = delete_item(target, &key);
goto out_close;
}
if (key.objectid || key.offset || key.type) {
- if (!strlen(field))
- print_usage();
+ if (*field == 0)
+ print_usage(1);
ret = corrupt_key(root, &key, field);
goto out_close;
}
* inode and we're screwed.
*/
if (file_extent != (u64)-1)
- print_usage();
+ print_usage(1);
if (logical == (u64)-1)
- print_usage();
+ print_usage(1);
if (bytes == 0)
bytes = root->sectorsize;