@@ -99,12 +99,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);
}
@@ -975,6 +977,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;
@@ -1231,6 +1283,8 @@ int main(int argc, char **argv)
u64 csum_bytenr = 0;
u64 block_group = 0;
char field[FIELD_BUF_LEN];
+ u64 bogus_value = UNSET_U64;
+ u64 bogus_offset = UNSET_U64;
field[0] = '\0';
memset(&key, 0, sizeof(key));
@@ -1259,11 +1313,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;
@@ -1329,6 +1385,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);
@@ -1455,7 +1517,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 != UNSET_U64 &&
+ bytes != UNSET_U64 &&
+ bogus_value != UNSET_U64)
+ ret = corrupt_btrfs_item_data(target_root, &key,
+ bogus_offset, bytes,
+ bogus_value);
+ else
+ print_usage(1);
goto out_close;
}
if (delete) {