@@ -355,6 +355,24 @@ enum btrfs_block_group_field {
BTRFS_BLOCK_GROUP_ITEM_BAD,
};
+enum btrfs_super_field {
+ BTRFS_SUPER_FIELD_FLAGS,
+ BTRFS_SUPER_FIELD_TOTAL_BYTES,
+ BTRFS_SUPER_FIELD_BYTES_USED,
+ BTRFS_SUPER_FIELD_BAD,
+};
+
+static enum btrfs_super_field convert_super_field(char *field)
+{
+ if (!strncmp(field, "flags", FIELD_BUF_LEN))
+ return BTRFS_SUPER_FIELD_FLAGS;
+ if (!strncmp(field, "total_bytes", FIELD_BUF_LEN))
+ return BTRFS_SUPER_FIELD_TOTAL_BYTES;
+ if (!strncmp(field, "bytes_used", FIELD_BUF_LEN))
+ return BTRFS_SUPER_FIELD_BYTES_USED;
+ return BTRFS_SUPER_FIELD_BAD;
+}
+
static enum btrfs_block_group_field convert_block_group_field(char *field)
{
if (!strncmp(field, "used", FIELD_BUF_LEN))
@@ -460,6 +478,41 @@ static u8 generate_u8(u8 orig)
return ret;
}
+static int corrupt_super_block(struct btrfs_root *root, char *field)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ enum btrfs_super_field corrupt_field;
+ u64 orig, bogus;
+
+ corrupt_field = convert_super_field(field);
+ if (corrupt_field == BTRFS_SUPER_FIELD_BAD) {
+ fprintf(stderr, "Invalid field %s\n", field);
+ return -EINVAL;
+ }
+
+ switch (corrupt_field) {
+ case BTRFS_SUPER_FIELD_FLAGS:
+ orig = btrfs_super_flags(fs_info->super_copy);
+ bogus = generate_u64(orig);
+ btrfs_set_super_flags(fs_info->super_copy, bogus);
+ break;
+ case BTRFS_SUPER_FIELD_TOTAL_BYTES:
+ orig = btrfs_super_total_bytes(fs_info->super_copy);
+ bogus = generate_u64(orig);
+ btrfs_set_super_total_bytes(fs_info->super_copy, bogus);
+ break;
+ case BTRFS_SUPER_FIELD_BYTES_USED:
+ orig = btrfs_super_bytes_used(fs_info->super_copy);
+ bogus = generate_u64(orig);
+ btrfs_set_super_bytes_used(fs_info->super_copy, bogus);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return write_all_supers(fs_info);
+}
+
static int corrupt_block_group(struct btrfs_root *root, u64 bg, char *field)
{
struct btrfs_trans_handle *trans;
@@ -1240,6 +1293,7 @@ int main(int argc, char **argv)
int corrupt_di = 0;
int delete = 0;
int should_corrupt_key = 0;
+ int corrupt_super = 0;
u64 metadata_block = 0;
u64 inode = 0;
u64 file_extent = (u64)-1;
@@ -1274,11 +1328,12 @@ int main(int argc, char **argv)
{ "root", no_argument, NULL, 'r'},
{ "csum", required_argument, NULL, 'C'},
{ "block-group", required_argument, NULL, 'B'},
+ { "super", no_argument, NULL, 's'},
{ "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:B:",
+ c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:I:D:d:r:C:B:s",
long_options, NULL);
if (c < 0)
break;
@@ -1344,6 +1399,9 @@ int main(int argc, char **argv)
case 'B':
block_group = arg_strtou64(optarg);
break;
+ case 's':
+ corrupt_super = 1;
+ break;
case GETOPT_VAL_HELP:
default:
print_usage(c != GETOPT_VAL_HELP);
@@ -1491,6 +1549,12 @@ int main(int argc, char **argv)
ret = corrupt_block_group(root, block_group, field);
goto out_close;
}
+ if (corrupt_super) {
+ if (*field == 0)
+ print_usage(1);
+ ret = corrupt_super_block(root, field);
+ goto out_close;
+ }
/*
* If we made it here and we have extent set then we didn't specify
* inode and we're screwed.
Doing the extent tree v2 work I generated an invalid super block with the wrong bytes_used set, and only noticed because it affected the block groups as well. Neither modes of fsck check for a valid bytes_used, so add the ability to corrupt this field so I can generate a testcase for fsck. Signed-off-by: Josef Bacik <josef@toxicpanda.com> --- btrfs-corrupt-block.c | 66 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-)