* Boston, MA 021110-1307, USA.
*/
-#define _XOPEN_SOURCE 500
-#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
+
#include "kerncompat.h"
#include "ctree.h"
#include "volumes.h"
#include "print-tree.h"
#include "transaction.h"
#include "list.h"
-#include "version.h"
#include "utils.h"
#define FIELD_BUF_LEN 80
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)
+ if (!extent_buffer_uptodate(eb))
return -EIO;;
corrupt_keys(NULL, root, eb);
next = read_tree_block(root, btrfs_node_blockptr(eb, i),
root->leafsize,
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);
}
eb = read_tree_block(root, block, root->leafsize, 0);
- if (!eb) {
+ if (!extent_buffer_uptodate(eb)) {
fprintf(stderr, "Couldn't read in tree block %s\n", field);
return -EINVAL;
}
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,
* If using COW, chunk recover will use the old item to recover,
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'},
- { 0, 0, 0, 0}
+ { "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(ac, av, "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();
+ print_usage(1);
dev = av[optind];
radix_tree_init();
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) {
struct btrfs_trans_handle *trans;
if (!strlen(field))
- print_usage();
+ print_usage(1);
trans = btrfs_start_transaction(root, 1);
if (file_extent == (u64)-1) {
}
if (metadata_block) {
if (!strlen(field))
- print_usage();
+ print_usage(1);
ret = corrupt_metadata_block(root, metadata_block, field);
goto out_close;
}
if (corrupt_di) {
if (!key.objectid || !strlen(field))
- print_usage();
+ 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();
+ 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;