Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Btrfs: add a sanity test for btrfs_split_item

While looking at somebodys corruption I became completely convinced that
btrfs_split_item was broken, so I wrote this test to verify that it was working
as it was supposed to. Thankfully it appears to be working as intended, so just
add this test to make sure nobody breaks it in the future. Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>

authored by

Josef Bacik and committed by
Chris Mason
06ea65a3 dd3cc16b

+283 -7
+2 -1
fs/btrfs/Makefile
··· 14 14 btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o 15 15 btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o 16 16 17 - btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o 17 + btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ 18 + tests/extent-buffer-tests.o
+3 -1
fs/btrfs/ctree.h
··· 1724 1724 int ref_cows; 1725 1725 int track_dirty; 1726 1726 int in_radix; 1727 - 1727 + #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS 1728 + int dummy_root; 1729 + #endif 1728 1730 u64 defrag_trans_start; 1729 1731 struct btrfs_key defrag_progress; 1730 1732 struct btrfs_key defrag_max;
+34 -4
fs/btrfs/disk-io.c
··· 1229 1229 atomic_set(&root->refs, 1); 1230 1230 root->log_transid = 0; 1231 1231 root->last_log_commit = 0; 1232 - extent_io_tree_init(&root->dirty_log_pages, 1233 - fs_info->btree_inode->i_mapping); 1232 + if (fs_info) 1233 + extent_io_tree_init(&root->dirty_log_pages, 1234 + fs_info->btree_inode->i_mapping); 1234 1235 1235 1236 memset(&root->root_key, 0, sizeof(root->root_key)); 1236 1237 memset(&root->root_item, 0, sizeof(root->root_item)); 1237 1238 memset(&root->defrag_progress, 0, sizeof(root->defrag_progress)); 1238 1239 memset(&root->root_kobj, 0, sizeof(root->root_kobj)); 1239 - root->defrag_trans_start = fs_info->generation; 1240 + if (fs_info) 1241 + root->defrag_trans_start = fs_info->generation; 1242 + else 1243 + root->defrag_trans_start = 0; 1240 1244 init_completion(&root->kobj_unregister); 1241 1245 root->defrag_running = 0; 1242 1246 root->root_key.objectid = objectid; ··· 1256 1252 root->fs_info = fs_info; 1257 1253 return root; 1258 1254 } 1255 + 1256 + #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS 1257 + /* Should only be used by the testing infrastructure */ 1258 + struct btrfs_root *btrfs_alloc_dummy_root(void) 1259 + { 1260 + struct btrfs_root *root; 1261 + 1262 + root = btrfs_alloc_root(NULL); 1263 + if (!root) 1264 + return ERR_PTR(-ENOMEM); 1265 + __setup_root(4096, 4096, 4096, 4096, root, NULL, 1); 1266 + root->dummy_root = 1; 1267 + 1268 + return root; 1269 + } 1270 + #endif 1259 1271 1260 1272 struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, 1261 1273 struct btrfs_fs_info *fs_info, ··· 3690 3670 3691 3671 void btrfs_mark_buffer_dirty(struct extent_buffer *buf) 3692 3672 { 3693 - struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root; 3673 + struct btrfs_root *root; 3694 3674 u64 transid = btrfs_header_generation(buf); 3695 3675 int was_dirty; 3696 3676 3677 + #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS 3678 + /* 3679 + * This is a fast path so only do this check if we have sanity tests 3680 + * enabled. Normal people shouldn't be marking dummy buffers as dirty 3681 + * outside of the sanity tests. 3682 + */ 3683 + if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &buf->bflags))) 3684 + return; 3685 + #endif 3686 + root = BTRFS_I(buf->pages[0]->mapping->host)->root; 3697 3687 btrfs_assert_tree_locked(buf); 3698 3688 if (transid != root->fs_info->generation) 3699 3689 WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, "
+4
fs/btrfs/disk-io.h
··· 86 86 struct btrfs_root *root); 87 87 void btrfs_free_fs_root(struct btrfs_root *root); 88 88 89 + #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS 90 + struct btrfs_root *btrfs_alloc_dummy_root(void); 91 + #endif 92 + 89 93 /* 90 94 * This function is used to grab the root, and avoid it is freed when we 91 95 * access it. But it doesn't ensure that the tree is not dropped.
+6 -1
fs/btrfs/super.c
··· 1789 1789 1790 1790 static int btrfs_run_sanity_tests(void) 1791 1791 { 1792 - return btrfs_test_free_space_cache(); 1792 + int ret; 1793 + 1794 + ret = btrfs_test_free_space_cache(); 1795 + if (ret) 1796 + return ret; 1797 + return btrfs_test_extent_buffer_operations(); 1793 1798 } 1794 1799 1795 1800 static int __init init_btrfs_fs(void)
+5
fs/btrfs/tests/btrfs-tests.h
··· 24 24 #define test_msg(fmt, ...) pr_info("btrfs: selftest: " fmt, ##__VA_ARGS__) 25 25 26 26 int btrfs_test_free_space_cache(void); 27 + int btrfs_test_extent_buffer_operations(void); 27 28 #else 28 29 static inline int btrfs_test_free_space_cache(void) 30 + { 31 + return 0; 32 + } 33 + static inline int btrfs_test_extent_buffer_operations(void) 29 34 { 30 35 return 0; 31 36 }
+229
fs/btrfs/tests/extent-buffer-tests.c
··· 1 + /* 2 + * Copyright (C) 2013 Fusion IO. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public 6 + * License v2 as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + * General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public 14 + * License along with this program; if not, write to the 15 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 16 + * Boston, MA 021110-1307, USA. 17 + */ 18 + 19 + #include <linux/slab.h> 20 + #include "btrfs-tests.h" 21 + #include "../ctree.h" 22 + #include "../extent_io.h" 23 + #include "../disk-io.h" 24 + 25 + static int test_btrfs_split_item(void) 26 + { 27 + struct btrfs_path *path; 28 + struct btrfs_root *root; 29 + struct extent_buffer *eb; 30 + struct btrfs_item *item; 31 + char *value = "mary had a little lamb"; 32 + char *split1 = "mary had a little"; 33 + char *split2 = " lamb"; 34 + char *split3 = "mary"; 35 + char *split4 = " had a little"; 36 + char buf[32]; 37 + struct btrfs_key key; 38 + u32 value_len = strlen(value); 39 + int ret = 0; 40 + 41 + test_msg("Running btrfs_split_item tests\n"); 42 + 43 + root = btrfs_alloc_dummy_root(); 44 + if (IS_ERR(root)) { 45 + test_msg("Could not allocate root\n"); 46 + return PTR_ERR(root); 47 + } 48 + 49 + path = btrfs_alloc_path(); 50 + if (!path) { 51 + test_msg("Could not allocate path\n"); 52 + kfree(root); 53 + return -ENOMEM; 54 + } 55 + 56 + path->nodes[0] = eb = alloc_dummy_extent_buffer(0, 4096); 57 + if (!eb) { 58 + test_msg("Could not allocate dummy buffer\n"); 59 + ret = -ENOMEM; 60 + goto out; 61 + } 62 + path->slots[0] = 0; 63 + 64 + key.objectid = 0; 65 + key.type = BTRFS_EXTENT_CSUM_KEY; 66 + key.offset = 0; 67 + 68 + setup_items_for_insert(root, path, &key, &value_len, value_len, 69 + value_len + sizeof(struct btrfs_item), 1); 70 + item = btrfs_item_nr(0); 71 + write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0), 72 + value_len); 73 + 74 + key.offset = 3; 75 + 76 + /* 77 + * Passing NULL trans here should be safe because we have plenty of 78 + * space in this leaf to split the item without having to split the 79 + * leaf. 80 + */ 81 + ret = btrfs_split_item(NULL, root, path, &key, 17); 82 + if (ret) { 83 + test_msg("Split item failed %d\n", ret); 84 + goto out; 85 + } 86 + 87 + /* 88 + * Read the first slot, it should have the original key and contain only 89 + * 'mary had a little' 90 + */ 91 + btrfs_item_key_to_cpu(eb, &key, 0); 92 + if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || 93 + key.offset != 0) { 94 + test_msg("Invalid key at slot 0\n"); 95 + ret = -EINVAL; 96 + goto out; 97 + } 98 + 99 + item = btrfs_item_nr(0); 100 + if (btrfs_item_size(eb, item) != strlen(split1)) { 101 + test_msg("Invalid len in the first split\n"); 102 + ret = -EINVAL; 103 + goto out; 104 + } 105 + 106 + read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), 107 + strlen(split1)); 108 + if (memcmp(buf, split1, strlen(split1))) { 109 + test_msg("Data in the buffer doesn't match what it should " 110 + "in the first split have='%.*s' want '%s'\n", 111 + (int)strlen(split1), buf, split1); 112 + ret = -EINVAL; 113 + goto out; 114 + } 115 + 116 + btrfs_item_key_to_cpu(eb, &key, 1); 117 + if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || 118 + key.offset != 3) { 119 + test_msg("Invalid key at slot 1\n"); 120 + ret = -EINVAL; 121 + goto out; 122 + } 123 + 124 + item = btrfs_item_nr(1); 125 + if (btrfs_item_size(eb, item) != strlen(split2)) { 126 + test_msg("Invalid len in the second split\n"); 127 + ret = -EINVAL; 128 + goto out; 129 + } 130 + 131 + read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), 132 + strlen(split2)); 133 + if (memcmp(buf, split2, strlen(split2))) { 134 + test_msg("Data in the buffer doesn't match what it should " 135 + "in the second split\n"); 136 + ret = -EINVAL; 137 + goto out; 138 + } 139 + 140 + key.offset = 1; 141 + /* Do it again so we test memmoving the other items in the leaf */ 142 + ret = btrfs_split_item(NULL, root, path, &key, 4); 143 + if (ret) { 144 + test_msg("Second split item failed %d\n", ret); 145 + goto out; 146 + } 147 + 148 + btrfs_item_key_to_cpu(eb, &key, 0); 149 + if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || 150 + key.offset != 0) { 151 + test_msg("Invalid key at slot 0\n"); 152 + ret = -EINVAL; 153 + goto out; 154 + } 155 + 156 + item = btrfs_item_nr(0); 157 + if (btrfs_item_size(eb, item) != strlen(split3)) { 158 + test_msg("Invalid len in the first split\n"); 159 + ret = -EINVAL; 160 + goto out; 161 + } 162 + 163 + read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), 164 + strlen(split3)); 165 + if (memcmp(buf, split3, strlen(split3))) { 166 + test_msg("Data in the buffer doesn't match what it should " 167 + "in the third split"); 168 + ret = -EINVAL; 169 + goto out; 170 + } 171 + 172 + btrfs_item_key_to_cpu(eb, &key, 1); 173 + if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || 174 + key.offset != 1) { 175 + test_msg("Invalid key at slot 1\n"); 176 + ret = -EINVAL; 177 + goto out; 178 + } 179 + 180 + item = btrfs_item_nr(1); 181 + if (btrfs_item_size(eb, item) != strlen(split4)) { 182 + test_msg("Invalid len in the second split\n"); 183 + ret = -EINVAL; 184 + goto out; 185 + } 186 + 187 + read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), 188 + strlen(split4)); 189 + if (memcmp(buf, split4, strlen(split4))) { 190 + test_msg("Data in the buffer doesn't match what it should " 191 + "in the fourth split\n"); 192 + ret = -EINVAL; 193 + goto out; 194 + } 195 + 196 + btrfs_item_key_to_cpu(eb, &key, 2); 197 + if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || 198 + key.offset != 3) { 199 + test_msg("Invalid key at slot 2\n"); 200 + ret = -EINVAL; 201 + goto out; 202 + } 203 + 204 + item = btrfs_item_nr(2); 205 + if (btrfs_item_size(eb, item) != strlen(split2)) { 206 + test_msg("Invalid len in the second split\n"); 207 + ret = -EINVAL; 208 + goto out; 209 + } 210 + 211 + read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2), 212 + strlen(split2)); 213 + if (memcmp(buf, split2, strlen(split2))) { 214 + test_msg("Data in the buffer doesn't match what it should " 215 + "in the last chunk\n"); 216 + ret = -EINVAL; 217 + goto out; 218 + } 219 + out: 220 + btrfs_free_path(path); 221 + kfree(root); 222 + return ret; 223 + } 224 + 225 + int btrfs_test_extent_buffer_operations(void) 226 + { 227 + test_msg("Running extent buffer operation tests"); 228 + return test_btrfs_split_item(); 229 + }