mirror of
https://github.com/kdave/btrfs-progs
synced 2025-01-30 09:21:45 +00:00
btrfs-progs: corrupt-block: corrupt generic item data
btrfs-corrupt-block already has a mix of generic and specific corruption options, but currently lacks the capacity for totally arbitrary corruption in item data. There is already a flag for corruption size (bytes/-b), so add a flag for an offset and a value to memset the item with. Exercise the new flags with a new variant for -I (item) corruption. Look up the item as before, but instead of corrupting a field in the item struct, corrupt an offset/size in the item data. The motivating example for this is that in testing fsverity with btrfs, we need to corrupt the generated Merkle tree--metadata item data which is an opaque blob to btrfs. Reviewed-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Boris Burkov <boris@bur.io> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
cef73a5e98
commit
b2ada05941
@ -98,12 +98,14 @@ static void print_usage(int ret)
|
||||
printf("\t-m The metadata block to corrupt (must also specify -f for the field to corrupt)\n");
|
||||
printf("\t-K <u64,u8,u64> Corrupt the given key (must also specify -f for the field and optionally -r for the root)\n");
|
||||
printf("\t-f The field in the item to corrupt\n");
|
||||
printf("\t-I <u64,u8,u64> Corrupt an item corresponding to the passed key triplet (must also specify the field to corrupt and root for the item)\n");
|
||||
printf("\t-I <u64,u8,u64> Corrupt an item corresponding to the passed key triplet (must also specify the field, or a (bytes, offset, value) tuple to corrupt and root for the item)\n");
|
||||
printf("\t-D <u64,u8,u64> Corrupt a dir item corresponding to the passed key triplet, must also specify a field\n");
|
||||
printf("\t-d <u64,u8,u64> Delete item corresponding to passed key triplet\n");
|
||||
printf("\t-r Operate on this root\n");
|
||||
printf("\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");
|
||||
printf("\t--block-group OFFSET corrupt the given block group\n");
|
||||
printf("\t-v Value to use for corrupting item data\n");
|
||||
printf("\t-o Offset to use for corrupting item data\n");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
@ -974,6 +976,56 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int corrupt_btrfs_item_data(struct btrfs_root *root,
|
||||
struct btrfs_key *key,
|
||||
u64 bogus_offset, u64 bogus_size,
|
||||
char bogus_value)
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct btrfs_path *path;
|
||||
int ret;
|
||||
void *data;
|
||||
struct extent_buffer *leaf;
|
||||
int slot;
|
||||
u32 item_size;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
if (IS_ERR(trans)) {
|
||||
fprintf(stderr, "Couldn't start transaction %ld\n",
|
||||
PTR_ERR(trans));
|
||||
ret = PTR_ERR(trans);
|
||||
goto free_path;
|
||||
}
|
||||
|
||||
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error searching to node %d\n", ret);
|
||||
goto commit_txn;
|
||||
}
|
||||
leaf = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
data = btrfs_item_ptr(leaf, slot, void);
|
||||
item_size = btrfs_item_size(leaf, slot);
|
||||
if (bogus_offset + bogus_size > item_size) {
|
||||
fprintf(stderr, "Item corruption past end of item: %llu > %u\n", bogus_offset + bogus_size, item_size);
|
||||
ret = -EINVAL;
|
||||
goto commit_txn;
|
||||
}
|
||||
data += bogus_offset;
|
||||
memset_extent_buffer(leaf, bogus_value, (unsigned long)data, bogus_size);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
||||
commit_txn:
|
||||
btrfs_commit_transaction(trans, root);
|
||||
free_path:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int delete_item(struct btrfs_root *root, struct btrfs_key *key)
|
||||
{
|
||||
struct btrfs_trans_handle *trans;
|
||||
@ -1230,6 +1282,8 @@ int main(int argc, char **argv)
|
||||
u64 csum_bytenr = 0;
|
||||
u64 block_group = 0;
|
||||
char field[FIELD_BUF_LEN];
|
||||
u64 bogus_value = (u64)-1;
|
||||
u64 bogus_offset = (u64)-1;
|
||||
|
||||
field[0] = '\0';
|
||||
memset(&key, 0, sizeof(key));
|
||||
@ -1258,11 +1312,13 @@ int main(int argc, char **argv)
|
||||
{ "root", no_argument, NULL, 'r'},
|
||||
{ "csum", required_argument, NULL, 'C'},
|
||||
{ "block-group", required_argument, NULL, GETOPT_VAL_BLOCK_GROUP},
|
||||
{ "value", required_argument, NULL, 'v'},
|
||||
{ "offset", required_argument, NULL, 'o'},
|
||||
{ "help", no_argument, NULL, GETOPT_VAL_HELP},
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:I:D:d:r:C:",
|
||||
c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:I:D:d:r:C:v:o:",
|
||||
long_options, NULL);
|
||||
if (c < 0)
|
||||
break;
|
||||
@ -1328,6 +1384,12 @@ int main(int argc, char **argv)
|
||||
case GETOPT_VAL_BLOCK_GROUP:
|
||||
block_group = arg_strtou64(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
bogus_value = arg_strtou64(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
bogus_offset = arg_strtou64(optarg);
|
||||
break;
|
||||
case GETOPT_VAL_HELP:
|
||||
default:
|
||||
print_usage(c != GETOPT_VAL_HELP);
|
||||
@ -1454,7 +1516,16 @@ int main(int argc, char **argv)
|
||||
if (!root_objectid)
|
||||
print_usage(1);
|
||||
|
||||
ret = corrupt_btrfs_item(target_root, &key, field);
|
||||
if (*field != 0)
|
||||
ret = corrupt_btrfs_item(target_root, &key, field);
|
||||
else if (bogus_offset != (u64)-1 &&
|
||||
bytes != (u64)-1 &&
|
||||
bogus_value != (u64)-1)
|
||||
ret = corrupt_btrfs_item_data(target_root, &key,
|
||||
bogus_offset, bytes,
|
||||
bogus_value);
|
||||
else
|
||||
print_usage(1);
|
||||
goto out_close;
|
||||
}
|
||||
if (delete) {
|
||||
|
Loading…
Reference in New Issue
Block a user