mkfs and dir-test fixes
[platform/upstream/btrfs-progs.git] / dir-test.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include "kerncompat.h"
8 #include "radix-tree.h"
9 #include "ctree.h"
10 #include "disk-io.h"
11 #include "print-tree.h"
12 #include "hash.h"
13 #include "transaction.h"
14
15 int keep_running = 1;
16 struct btrfs_super_block super;
17 static u64 dir_oid = 0;
18 static u64 file_oid = 33778;
19
20 static int find_num(struct radix_tree_root *root, unsigned long *num_ret,
21                      int exists)
22 {
23         unsigned long num = rand();
24         unsigned long res[2];
25         int ret;
26
27 again:
28         ret = radix_tree_gang_lookup(root, (void **)res, num, 2);
29         if (exists) {
30                 if (ret == 0)
31                         return -1;
32                 num = res[0];
33         } else if (ret != 0 && num == res[0]) {
34                 num++;
35                 if (ret > 1 && num == res[1]) {
36                         num++;
37                         goto again;
38                 }
39         }
40         *num_ret = num;
41         return 0;
42 }
43
44 static void initial_inode_init(struct btrfs_root *root,
45                                struct btrfs_inode_item *inode_item)
46 {
47         memset(inode_item, 0, sizeof(*inode_item));
48         btrfs_set_inode_generation(inode_item, root->fs_info->generation);
49         btrfs_set_inode_mode(inode_item, S_IFREG | 0700);
50 }
51
52 static int ins_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
53                    struct radix_tree_root *radix)
54 {
55         int ret;
56         char buf[128];
57         unsigned long oid;
58         u64 objectid;
59         struct btrfs_path path;
60         struct btrfs_key inode_map;
61         struct btrfs_inode_item inode_item;
62
63         find_num(radix, &oid, 0);
64         sprintf(buf, "str-%lu", oid);
65
66         ret = btrfs_find_free_objectid(trans, root, dir_oid + 1, &objectid);
67         if (ret)
68                 goto error;
69
70         inode_map.objectid = objectid;
71         inode_map.flags = 0;
72         inode_map.offset = 0;
73
74         ret = btrfs_insert_inode_map(trans, root, objectid, &inode_map);
75         if (ret)
76                 goto error;
77
78         initial_inode_init(root, &inode_item);
79         ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
80         if (ret)
81                 goto error;
82         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
83                                     objectid, 1);
84         if (ret)
85                 goto error;
86
87         radix_tree_preload(GFP_KERNEL);
88         ret = radix_tree_insert(radix, oid, (void *)oid);
89         radix_tree_preload_end();
90         if (ret)
91                 goto error;
92         return ret;
93 error:
94         if (ret != -EEXIST)
95                 goto fatal;
96
97         /*
98          * if we got an EEXIST, it may be due to hash collision, double
99          * check
100          */
101         btrfs_init_path(&path);
102         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
103                                     strlen(buf), 0);
104         if (ret)
105                 goto fatal_release;
106         if (!btrfs_match_dir_item_name(root, &path, buf, strlen(buf))) {
107                 struct btrfs_dir_item *di;
108                 char *found;
109                 u32 found_len;
110                 u64 myhash;
111                 u64 foundhash;
112
113                 di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
114                                     struct btrfs_dir_item);
115                 found = (char *)(di + 1);
116                 found_len = btrfs_dir_name_len(di);
117                 btrfs_name_hash(buf, strlen(buf), &myhash);
118                 btrfs_name_hash(found, found_len, &foundhash);
119                 if (myhash != foundhash)
120                         goto fatal_release;
121                 btrfs_release_path(root, &path);
122                 return 0;
123         }
124 fatal_release:
125         btrfs_release_path(root, &path);
126 fatal:
127         printf("failed to insert %lu ret %d\n", oid, ret);
128         return -1;
129 }
130
131 static int insert_dup(struct btrfs_trans_handle *trans, struct btrfs_root
132                       *root, struct radix_tree_root *radix)
133 {
134         int ret;
135         char buf[128];
136         unsigned long oid;
137
138         ret = find_num(radix, &oid, 1);
139         if (ret < 0)
140                 return 0;
141         sprintf(buf, "str-%lu", oid);
142
143         ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf), dir_oid,
144                                     file_oid, 1);
145         if (ret != -EEXIST) {
146                 printf("insert on %s gave us %d\n", buf, ret);
147                 return 1;
148         }
149         return 0;
150 }
151
152 static int del_dir_item(struct btrfs_trans_handle *trans,
153                         struct btrfs_root *root,
154                         struct radix_tree_root *radix,
155                         unsigned long radix_index,
156                         struct btrfs_path *path)
157 {
158         int ret;
159         unsigned long *ptr;
160         u64 file_objectid;
161         struct btrfs_dir_item *di;
162
163         /* find the inode number of the file */
164         di = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0],
165                             struct btrfs_dir_item);
166         file_objectid = btrfs_dir_objectid(di);
167
168         /* delete the directory item */
169         ret = btrfs_del_item(trans, root, path);
170         if (ret)
171                 goto out_release;
172         btrfs_release_path(root, path);
173
174         /* delete the inode */
175         btrfs_init_path(path);
176         ret = btrfs_lookup_inode(trans, root, path, file_objectid, -1);
177         if (ret)
178                 goto out_release;
179         ret = btrfs_del_item(trans, root, path);
180         if (ret)
181                 goto out_release;
182         btrfs_release_path(root, path);
183
184         /* delete the inode mapping */
185         btrfs_init_path(path);
186         ret = btrfs_lookup_inode_map(trans, root, path, file_objectid, -1);
187         if (ret)
188                 goto out_release;
189         ret = btrfs_del_item(trans, root->fs_info->inode_root, path);
190         if (ret)
191                 goto out_release;
192
193         if (root->fs_info->last_inode_alloc > file_objectid)
194                 root->fs_info->last_inode_alloc = file_objectid;
195         btrfs_release_path(root, path);
196         ptr = radix_tree_delete(radix, radix_index);
197         if (!ptr) {
198                 ret = -5555;
199                 goto out;
200         }
201         return 0;
202 out_release:
203         btrfs_release_path(root, path);
204 out:
205         printf("failed to delete %lu %d\n", radix_index, ret);
206         return -1;
207 }
208
209 static int del_one(struct btrfs_trans_handle *trans, struct btrfs_root *root,
210                    struct radix_tree_root *radix)
211 {
212         int ret;
213         char buf[128];
214         unsigned long oid;
215         struct btrfs_path path;
216
217         ret = find_num(radix, &oid, 1);
218         if (ret < 0)
219                 return 0;
220         sprintf(buf, "str-%lu", oid);
221         btrfs_init_path(&path);
222         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
223                                     strlen(buf), -1);
224         if (ret)
225                 goto out_release;
226
227         ret = del_dir_item(trans, root, radix, oid, &path);
228         if (ret)
229                 goto out_release;
230         return ret;
231 out_release:
232         btrfs_release_path(root, &path);
233         printf("failed to delete %lu %d\n", oid, ret);
234         return -1;
235 }
236
237 static int lookup_item(struct btrfs_trans_handle *trans, struct btrfs_root
238                        *root, struct radix_tree_root *radix)
239 {
240         struct btrfs_path path;
241         char buf[128];
242         int ret;
243         unsigned long oid;
244         u64 objectid;
245         struct btrfs_dir_item *di;
246
247         ret = find_num(radix, &oid, 1);
248         if (ret < 0)
249                 return 0;
250         sprintf(buf, "str-%lu", oid);
251         btrfs_init_path(&path);
252         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
253                                     strlen(buf), 0);
254         if (!ret) {
255                 di = btrfs_item_ptr(&path.nodes[0]->leaf, path.slots[0],
256                                     struct btrfs_dir_item);
257                 objectid = btrfs_dir_objectid(di);
258                 btrfs_release_path(root, &path);
259                 btrfs_init_path(&path);
260                 ret = btrfs_lookup_inode_map(trans, root, &path, objectid, 0);
261         }
262         btrfs_release_path(root, &path);
263         if (ret) {
264                 printf("unable to find key %lu\n", oid);
265                 return -1;
266         }
267         return 0;
268 }
269
270 static int lookup_enoent(struct btrfs_trans_handle *trans, struct btrfs_root
271                          *root, struct radix_tree_root *radix)
272 {
273         struct btrfs_path path;
274         char buf[128];
275         int ret;
276         unsigned long oid;
277
278         ret = find_num(radix, &oid, 0);
279         if (ret < 0)
280                 return 0;
281         sprintf(buf, "str-%lu", oid);
282         btrfs_init_path(&path);
283         ret = btrfs_lookup_dir_item(trans, root, &path, dir_oid, buf,
284                                     strlen(buf), 0);
285         btrfs_release_path(root, &path);
286         if (!ret) {
287                 printf("able to find key that should not exist %lu\n", oid);
288                 return -1;
289         }
290         return 0;
291 }
292
293 static int empty_tree(struct btrfs_trans_handle *trans, struct btrfs_root
294                       *root, struct radix_tree_root *radix, int nr)
295 {
296         struct btrfs_path path;
297         struct btrfs_key key;
298         unsigned long found = 0;
299         u32 found_len;
300         int ret;
301         int slot;
302         int count = 0;
303         char buf[128];
304         struct btrfs_dir_item *di;
305
306         key.offset = (u64)-1;
307         key.flags = 0;
308         btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
309         key.objectid = dir_oid;
310         while(nr-- >= 0) {
311                 btrfs_init_path(&path);
312                 ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
313                 if (ret < 0) {
314                         btrfs_release_path(root, &path);
315                         return ret;
316                 }
317                 if (ret != 0) {
318                         if (path.slots[0] == 0) {
319                                 btrfs_release_path(root, &path);
320                                 break;
321                         }
322                         path.slots[0] -= 1;
323                 }
324                 slot = path.slots[0];
325                 di = btrfs_item_ptr(&path.nodes[0]->leaf, slot,
326                                     struct btrfs_dir_item);
327                 found_len = btrfs_dir_name_len(di);
328                 memcpy(buf, (char *)(di + 1), found_len);
329                 BUG_ON(found_len > 128);
330                 buf[found_len] = '\0';
331                 found = atoi(buf + 4);
332                 ret = del_dir_item(trans, root, radix, found, &path);
333                 count++;
334                 if (ret) {
335                         fprintf(stderr,
336                                 "failed to remove %lu from tree\n",
337                                 found);
338                         return -1;
339                 }
340                 if (!keep_running)
341                         break;
342         }
343         return 0;
344         fprintf(stderr, "failed to delete from the radix %lu\n", found);
345         return -1;
346 }
347
348 static int fill_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root,
349                      struct radix_tree_root *radix, int count)
350 {
351         int i;
352         int ret = 0;
353         for (i = 0; i < count; i++) {
354                 ret = ins_one(trans, root, radix);
355                 if (ret) {
356                         fprintf(stderr, "fill failed\n");
357                         goto out;
358                 }
359                 if (i % 1000 == 0) {
360                         ret = btrfs_commit_transaction(trans, root, &super);
361                         if (ret) {
362                                 fprintf(stderr, "fill commit failed\n");
363                                 return ret;
364                         }
365                 }
366                 if (i && i % 10000 == 0) {
367                         printf("bigfill %d\n", i);
368                 }
369                 if (!keep_running)
370                         break;
371         }
372 out:
373         return ret;
374 }
375
376 static int bulk_op(struct btrfs_trans_handle *trans, struct btrfs_root *root,
377                    struct radix_tree_root *radix)
378 {
379         int ret;
380         int nr = rand() % 5000;
381         static int run_nr = 0;
382
383         /* do the bulk op much less frequently */
384         if (run_nr++ % 100)
385                 return 0;
386         ret = empty_tree(trans, root, radix, nr);
387         if (ret)
388                 return ret;
389         ret = fill_tree(trans, root, radix, nr);
390         if (ret)
391                 return ret;
392         return 0;
393 }
394
395
396 int (*ops[])(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct
397              radix_tree_root *radix) =
398         { ins_one, insert_dup, del_one, lookup_item,
399           lookup_enoent, bulk_op };
400
401 void sigstopper(int ignored)
402 {
403         keep_running = 0;
404         fprintf(stderr, "caught exit signal, stopping\n");
405 }
406
407 int print_usage(void)
408 {
409         printf("usage: tester [-ih] [-c count] [-f count]\n");
410         printf("\t -c count -- iteration count after filling\n");
411         printf("\t -f count -- run this many random inserts before starting\n");
412         printf("\t -i       -- only do initial fill\n");
413         printf("\t -h       -- this help text\n");
414         exit(1);
415 }
416 int main(int ac, char **av)
417 {
418         RADIX_TREE(radix, GFP_KERNEL);
419         struct btrfs_root *root;
420         int i;
421         int ret;
422         int count;
423         int op;
424         int iterations = 20000;
425         int init_fill_count = 800000;
426         int err = 0;
427         int initial_only = 0;
428         struct btrfs_trans_handle *trans;
429         radix_tree_init();
430
431         root = open_ctree(av[ac-1], &super);
432         trans = btrfs_start_transaction(root, 1);
433
434         dir_oid = btrfs_super_root_dir(&super);
435
436         signal(SIGTERM, sigstopper);
437         signal(SIGINT, sigstopper);
438
439         for (i = 1 ; i < ac - 1; i++) {
440                 if (strcmp(av[i], "-i") == 0) {
441                         initial_only = 1;
442                 } else if (strcmp(av[i], "-c") == 0) {
443                         iterations = atoi(av[i+1]);
444                         i++;
445                 } else if (strcmp(av[i], "-f") == 0) {
446                         init_fill_count = atoi(av[i+1]);
447                         i++;
448                 } else {
449                         print_usage();
450                 }
451         }
452         printf("initial fill\n");
453         ret = fill_tree(trans, root, &radix, init_fill_count);
454         printf("starting run\n");
455         if (ret) {
456                 err = ret;
457                 goto out;
458         }
459         if (initial_only == 1) {
460                 goto out;
461         }
462         for (i = 0; i < iterations; i++) {
463                 op = rand() % ARRAY_SIZE(ops);
464                 count = rand() % 128;
465                 if (i % 2000 == 0) {
466                         printf("%d\n", i);
467                         fflush(stdout);
468                 }
469                 if (i && i % 5000 == 0) {
470                         printf("open & close, root level %d nritems %d\n",
471                                 btrfs_header_level(&root->node->node.header),
472                                 btrfs_header_nritems(&root->node->node.header));
473                         close_ctree(root, &super);
474                         root = open_ctree("dbfile", &super);
475                 }
476                 while(count--) {
477                         ret = ops[op](trans, root, &radix);
478                         if (ret) {
479                                 fprintf(stderr, "op %d failed %d:%d\n",
480                                         op, i, iterations);
481                                 btrfs_print_tree(root, root->node);
482                                 fprintf(stderr, "op %d failed %d:%d\n",
483                                         op, i, iterations);
484                                 err = ret;
485                                 goto out;
486                         }
487                         if (ops[op] == bulk_op)
488                                 break;
489                         if (keep_running == 0) {
490                                 err = 0;
491                                 goto out;
492                         }
493                 }
494         }
495 out:
496         close_ctree(root, &super);
497         return err;
498 }
499