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(NULL, 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 }
230