One user in mail list reported free space cache error where clear_cache
mount option failes to fix.

To reproduce such problem, add support to corrupt free space cache
file(old free space cache implement, or space_cache=v1).

With this patch, we can reproduce the problem quite easily:
Not suitable for a btrfs-progs or xfstest test case, as btrfsck won't
treat it as an error.

======
$ fallocate  test.img -l 2G
$ mkfs.btrfs test.img
$ sudo mount test.img  /mnt/btrfs/
$ sudo xfs_io  -f -c "pwrite 0 16M" /mnt/btrfs/tmp
$ sudo umount /mnt/btrfs
$ ./btrfs-corrupt-block -s content -l 136708096 ../test.img
  Here 136708096 is the second data chunk bytenr.
  (The first data chunk is the 8M one from mkfs, which doesn't have free
   space cache)

$ ./btrfsck test.img
Checking filesystem on ../test.img
UUID: 9542051b-8150-42e5-a9e6-95829656485c
checking extents
checking free space cache
btrfs: csum mismatch on free space cache  <<< Found space cache problem
failed to load free space cache for block group 136708096
checking fs roots
checking csums
checking root refs
found 16941058 bytes used err is 0
total csum bytes: 16384
total tree bytes: 163840
total fs tree bytes: 32768
total extent tree bytes: 16384
btree space waste bytes: 139567
file data blocks allocated: 16908288
 referenced 16908288

$ sudo mount -o clear_cache test.img /mnt/btrfs
$ sudo umount /mnt/btrfs
$ ./btrfsck test.img
Checking filesystem on ../test.img
UUID: 9542051b-8150-42e5-a9e6-95829656485c
checking extents
checking free space cache
btrfs: csum mismatch on free space cache <<< Problem not fixed by kernel
failed to load free space cache for block group 136708096
checking fs roots
checking csums
checking root refs
found 16941058 bytes used err is 0
total csum bytes: 16384
total tree bytes: 163840
total fs tree bytes: 32768
total extent tree bytes: 16384
btree space waste bytes: 139567
file data blocks allocated: 16908288
 referenced 16908288
======

Btrfs-progs fix will follow soon.

Reported-by: Ivan P <chrnosphe...@gmail.com>
Signed-off-by: Qu Wenruo <quwen...@cn.fujitsu.com>
---
 btrfs-corrupt-block.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 123 insertions(+), 1 deletion(-)

diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index d331f96..eb27265 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -115,6 +115,9 @@ static void print_usage(int ret)
        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");
+       fprintf(stderr, "\t-s <method>\n");
+       fprintf(stderr, "\t   Corrupt free space cache file(space_cache v1), 
must also specify -l for blockgroup bytenr\n");
+       fprintf(stderr, "\t   <method> can be 'zero_gen','rand_gen' or 
'content'\n");
        exit(ret);
 }
 
@@ -1012,6 +1015,100 @@ out:
        return ret;
 
 }
+
+u64 rand_u64(void)
+{
+       u64 ret = 0;
+       int n;
+
+       for (n = 0; n < sizeof(ret) / sizeof(RAND_MAX); n++)
+               ret += rand() << (n * sizeof(RAND_MAX) * 8);
+       return ret;
+}
+
+#define CORRUPT_SC_FILE_ZERO_GEN       1
+#define CORRUPT_SC_FILE_RAND_GEN       2
+#define CORRUPT_SC_FILE_CONTENT                3
+int corrupt_space_cache_file(struct btrfs_fs_info *fs_info, u64 block_group,
+                            int method)
+{
+       struct btrfs_root *tree_root = fs_info->tree_root;
+       struct btrfs_path path;
+       struct btrfs_key key;
+       struct btrfs_disk_key location;
+       struct btrfs_free_space_header *sc_header;
+       struct btrfs_file_extent_item *fi;
+       struct extent_buffer *node;
+       struct extent_buffer *corrupted_node;
+       u64 disk_bytenr;
+       int slot;
+       int ret;
+
+       key.objectid = BTRFS_FREE_SPACE_OBJECTID;
+       key.type = 0;
+       key.offset = block_group;
+
+       btrfs_init_path(&path);
+
+       /* Don't start trans, as this will cause generation different */
+       ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
+       if (ret) {
+               error("failed to find free space cahce file for block group 
%llu, ret: %d\n",
+                     block_group, ret);
+               goto out;
+       }
+       slot = path.slots[0];
+       node = path.nodes[0];
+       sc_header = btrfs_item_ptr(node, slot, struct btrfs_free_space_header);
+       if (method == CORRUPT_SC_FILE_ZERO_GEN ||
+           method == CORRUPT_SC_FILE_RAND_GEN) {
+               u64 dst_gen;
+
+               if (method == CORRUPT_SC_FILE_ZERO_GEN)
+                       dst_gen = 0;
+               else
+                       dst_gen = rand_u64();
+
+               btrfs_set_free_space_generation(node, sc_header, dst_gen);
+               /* Manually re-calc csum and write to disk */
+               ret = write_tree_block(NULL, tree_root, node);
+               goto out;
+       }
+
+       btrfs_free_space_key(node, sc_header, &location);
+       btrfs_disk_key_to_cpu(&key, &location);
+       btrfs_release_path(&path);
+
+       /* Change to type and offset to search file extent */
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = 0;
+
+       ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
+       if (ret) {
+               error("failed to find free space cache extent data for 
blockgroup %llu, ret: %d\n",
+                     block_group, ret);
+               goto out;
+       }
+
+       slot = path.slots[0];
+       node = path.nodes[0];
+       fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
+       disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
+
+       /*
+        * Directly write random data into
+        * [disk_bytenr, disk_bytenr + sectorsize) as free space cache inode
+        * doesn't have csum
+        */
+       corrupted_node = debug_corrupt_block(tree_root, disk_bytenr,
+                                            tree_root->sectorsize, 0);
+       free_extent_buffer(corrupted_node);
+
+out:
+       btrfs_release_path(&path);
+       return ret;
+}
+
 int main(int argc, char **argv)
 {
        struct cache_tree root_cache;
@@ -1032,6 +1129,7 @@ int main(int argc, char **argv)
        int corrupt_item = 0;
        int corrupt_di = 0;
        int delete = 0;
+       int corrupt_sc_file = 0;
        u64 metadata_block = 0;
        u64 inode = 0;
        u64 file_extent = (u64)-1;
@@ -1065,11 +1163,12 @@ int main(int argc, char **argv)
                        { "delete", no_argument, NULL, 'd'},
                        { "root", no_argument, NULL, 'r'},
                        { "csum", required_argument, NULL, 'C'},
+                       { "space-cache-file", required_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:IDdr:C:",
+               c = getopt_long(argc, argv, "l:c:b:eEkuUi:f:x:m:K:IDdr:C:s:",
                                long_options, NULL);
                if (c < 0)
                        break;
@@ -1136,6 +1235,22 @@ int main(int argc, char **argv)
                        case 'C':
                                csum_bytenr = arg_strtou64(optarg);
                                break;
+                       case 's':
+                               if (!strncmp(optarg, "zero_gen",
+                                            sizeof("zero_gen")))
+                                       corrupt_sc_file =
+                                               CORRUPT_SC_FILE_ZERO_GEN;
+                               else if (!strncmp(optarg, "rand_gen",
+                                                 sizeof("rand_gen")))
+                                       corrupt_sc_file =
+                                               CORRUPT_SC_FILE_RAND_GEN;
+                               else if (!strncmp(optarg, "content",
+                                                 sizeof("content")))
+                                       corrupt_sc_file =
+                                               CORRUPT_SC_FILE_CONTENT;
+                               else
+                                       print_usage(1);
+                               break;
                        case GETOPT_VAL_HELP:
                        default:
                                print_usage(c != GETOPT_VAL_HELP);
@@ -1154,6 +1269,13 @@ int main(int argc, char **argv)
                fprintf(stderr, "Open ctree failed\n");
                exit(1);
        }
+       if (corrupt_sc_file) {
+               if (logical == (u64)-1)
+                       print_usage(1);
+               ret = corrupt_space_cache_file(root->fs_info, logical,
+                                              corrupt_sc_file);
+               goto out_close;
+       }
        if (extent_rec) {
                struct btrfs_trans_handle *trans;
 
-- 
2.8.2



--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to