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