Btrfs-progs: add ability to corrupt metadata block fields
[platform/upstream/btrfs-progs.git] / btrfs-corrupt-block.c
1 /*
2  * Copyright (C) 2009 Oracle.  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 #define _XOPEN_SOURCE 500
20 #define _GNU_SOURCE 1
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include "kerncompat.h"
27 #include "ctree.h"
28 #include "volumes.h"
29 #include "disk-io.h"
30 #include "print-tree.h"
31 #include "transaction.h"
32 #include "list.h"
33 #include "version.h"
34 #include "utils.h"
35
36 #define FIELD_BUF_LEN 80
37
38 struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
39                                      u32 blocksize, int copy)
40 {
41         int ret;
42         struct extent_buffer *eb;
43         u64 length;
44         struct btrfs_multi_bio *multi = NULL;
45         struct btrfs_device *device;
46         int num_copies;
47         int mirror_num = 1;
48
49         eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
50         if (!eb)
51                 return NULL;
52
53         length = blocksize;
54         while (1) {
55                 ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
56                                       eb->start, &length, &multi,
57                                       mirror_num, NULL);
58                 BUG_ON(ret);
59                 device = multi->stripes[0].dev;
60                 eb->fd = device->fd;
61                 device->total_ios++;
62                 eb->dev_bytenr = multi->stripes[0].physical;
63
64                 fprintf(stdout,
65                         "mirror %d logical %llu physical %llu device %s\n",
66                         mirror_num, (unsigned long long)bytenr,
67                         (unsigned long long)eb->dev_bytenr, device->name);
68                 kfree(multi);
69
70                 if (!copy || mirror_num == copy) {
71                         ret = read_extent_from_disk(eb, 0, eb->len);
72                         printf("corrupting %llu copy %d\n", eb->start,
73                                mirror_num);
74                         memset(eb->data, 0, eb->len);
75                         write_extent_to_disk(eb);
76                         fsync(eb->fd);
77                 }
78
79                 num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
80                                               eb->start, eb->len);
81                 if (num_copies == 1)
82                         break;
83
84                 mirror_num++;
85                 if (mirror_num > num_copies)
86                         break;
87         }
88         return eb;
89 }
90
91 static void print_usage(void)
92 {
93         fprintf(stderr, "usage: btrfs-corrupt-block [options] device\n");
94         fprintf(stderr, "\t-l Logical extent to be corrupted\n");
95         fprintf(stderr, "\t-c Copy of the extent to be corrupted"
96                 " (usually 1 or 2, default: 0)\n");
97         fprintf(stderr, "\t-b Number of bytes to be corrupted\n");
98         fprintf(stderr, "\t-e Extent to be corrupted\n");
99         fprintf(stderr, "\t-E The whole extent tree to be corrupted\n");
100         fprintf(stderr, "\t-u Given chunk item to be corrupted\n");
101         fprintf(stderr, "\t-U The whole chunk tree to be corrupted\n");
102         fprintf(stderr, "\t-i The inode item to corrupt (must also specify "
103                 "the field to corrupt)\n");
104         fprintf(stderr, "\t-x The file extent item to corrupt (must also "
105                 "specify -i for the inode and -f for the field to corrupt)\n");
106         fprintf(stderr, "\t-m The metadata block to corrupt (must also "
107                 "specify -f for the field to corrupt)\n");
108         fprintf(stderr, "\t-f The field in the item to corrupt\n");
109         exit(1);
110 }
111
112 static void corrupt_keys(struct btrfs_trans_handle *trans,
113                          struct btrfs_root *root,
114                          struct extent_buffer *eb)
115 {
116         int slot;
117         int bad_slot;
118         int nr;
119         struct btrfs_disk_key bad_key;;
120
121         nr = btrfs_header_nritems(eb);
122         if (nr == 0)
123                 return;
124
125         slot = rand() % nr;
126         bad_slot = rand() % nr;
127
128         if (bad_slot == slot)
129                 return;
130
131         fprintf(stderr,
132                 "corrupting keys in block %llu slot %d swapping with %d\n",
133                 (unsigned long long)eb->start, slot, bad_slot);
134
135         if (btrfs_header_level(eb) == 0) {
136                 btrfs_item_key(eb, &bad_key, bad_slot);
137                 btrfs_set_item_key(eb, &bad_key, slot);
138         } else {
139                 btrfs_node_key(eb, &bad_key, bad_slot);
140                 btrfs_set_node_key(eb, &bad_key, slot);
141         }
142         btrfs_mark_buffer_dirty(eb);
143         if (!trans) {
144                 u16 csum_size =
145                         btrfs_super_csum_size(root->fs_info->super_copy);
146                 csum_tree_block_size(eb, csum_size, 0);
147                 write_extent_to_disk(eb);
148         }
149 }
150
151
152 static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
153 {
154         struct extent_buffer *eb;
155
156         eb = read_tree_block(root, bytenr, root->leafsize, 0);
157         if (!eb)
158                 return -EIO;;
159
160         corrupt_keys(NULL, root, eb);
161         free_extent_buffer(eb);
162         return 0;
163 }
164
165 static int corrupt_extent(struct btrfs_trans_handle *trans,
166                           struct btrfs_root *root, u64 bytenr, int copy)
167 {
168         struct btrfs_key key;
169         struct extent_buffer *leaf;
170         u32 item_size;
171         unsigned long ptr;
172         struct btrfs_path *path;
173         int ret;
174         int slot;
175         int should_del = rand() % 3;
176
177         path = btrfs_alloc_path();
178         if (!path)
179                 return -ENOMEM;
180
181         key.objectid = bytenr;
182         key.type = (u8)-1;
183         key.offset = (u64)-1;
184
185         while(1) {
186                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
187                                         &key, path, -1, 1);
188                 if (ret < 0)
189                         break;
190
191                 if (ret > 0) {
192                         if (path->slots[0] == 0)
193                                 break;
194                         path->slots[0]--;
195                         ret = 0;
196                 }
197                 leaf = path->nodes[0];
198                 slot = path->slots[0];
199                 btrfs_item_key_to_cpu(leaf, &key, slot);
200                 if (key.objectid != bytenr)
201                         break;
202
203                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
204                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
205                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
206                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
207                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
208                     key.type != BTRFS_SHARED_DATA_REF_KEY)
209                         goto next;
210
211                 if (should_del) {
212                         fprintf(stderr,
213                                 "deleting extent record: key %llu %u %llu\n",
214                                 key.objectid, key.type, key.offset);
215
216                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
217                                 /* make sure this extent doesn't get
218                                  * reused for other purposes */
219                                 btrfs_pin_extent(root->fs_info,
220                                                  key.objectid, key.offset);
221                         }
222
223                         btrfs_del_item(trans, root, path);
224                 } else {
225                         fprintf(stderr,
226                                 "corrupting extent record: key %llu %u %llu\n",
227                                 key.objectid, key.type, key.offset);
228                         ptr = btrfs_item_ptr_offset(leaf, slot);
229                         item_size = btrfs_item_size_nr(leaf, slot);
230                         memset_extent_buffer(leaf, 0, ptr, item_size);
231                         btrfs_mark_buffer_dirty(leaf);
232                 }
233 next:
234                 btrfs_release_path(path);
235
236                 if (key.offset > 0)
237                         key.offset--;
238                 if (key.offset == 0)
239                         break;
240         }
241
242         btrfs_free_path(path);
243         return 0;
244 }
245
246 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
247                                       struct btrfs_root *root,
248                                       struct extent_buffer *eb)
249 {
250         u32 nr = btrfs_header_nritems(eb);
251         u32 victim = rand() % nr;
252         u64 objectid;
253         struct btrfs_key key;
254
255         btrfs_item_key_to_cpu(eb, &key, victim);
256         objectid = key.objectid;
257         corrupt_extent(trans, root, objectid, 1);
258 }
259
260 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
261                                       struct btrfs_root *root,
262                                       struct extent_buffer *eb)
263 {
264         int i;
265         u32 nr;
266
267         if (!eb)
268                 return;
269
270         nr = btrfs_header_nritems(eb);
271         if (btrfs_is_leaf(eb)) {
272                 btrfs_corrupt_extent_leaf(trans, root, eb);
273                 return;
274         }
275
276         if (btrfs_header_level(eb) == 1 && eb != root->node) {
277                 if (rand() % 5)
278                         return;
279         }
280
281         for (i = 0; i < nr; i++) {
282                 struct extent_buffer *next;
283
284                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
285                                        root->leafsize,
286                                        btrfs_node_ptr_generation(eb, i));
287                 if (!next)
288                         continue;
289                 btrfs_corrupt_extent_tree(trans, root, next);
290                 free_extent_buffer(next);
291         }
292 }
293
294 enum btrfs_inode_field {
295         BTRFS_INODE_FIELD_ISIZE,
296         BTRFS_INODE_FIELD_BAD,
297 };
298
299 enum btrfs_file_extent_field {
300         BTRFS_FILE_EXTENT_DISK_BYTENR,
301         BTRFS_FILE_EXTENT_BAD,
302 };
303
304 enum btrfs_metadata_block_field {
305         BTRFS_METADATA_BLOCK_GENERATION,
306         BTRFS_METADATA_BLOCK_BAD,
307 };
308
309 static enum btrfs_inode_field convert_inode_field(char *field)
310 {
311         if (!strncmp(field, "isize", FIELD_BUF_LEN))
312                 return BTRFS_INODE_FIELD_ISIZE;
313         return BTRFS_INODE_FIELD_BAD;
314 }
315
316 static enum btrfs_file_extent_field convert_file_extent_field(char *field)
317 {
318         if (!strncmp(field, "disk_bytenr", FIELD_BUF_LEN))
319                 return BTRFS_FILE_EXTENT_DISK_BYTENR;
320         return BTRFS_FILE_EXTENT_BAD;
321 }
322
323 static enum btrfs_metadata_block_field
324 convert_metadata_block_field(char *field)
325 {
326         if (!strncmp(field, "generation", FIELD_BUF_LEN))
327                 return BTRFS_METADATA_BLOCK_GENERATION;
328         return BTRFS_METADATA_BLOCK_BAD;
329 }
330
331 static u64 generate_u64(u64 orig)
332 {
333         u64 ret;
334         do {
335                 ret = rand();
336         } while (ret == orig);
337         return ret;
338 }
339
340 static int corrupt_inode(struct btrfs_trans_handle *trans,
341                          struct btrfs_root *root, u64 inode, char *field)
342 {
343         struct btrfs_inode_item *ei;
344         struct btrfs_path *path;
345         struct btrfs_key key;
346         enum btrfs_inode_field corrupt_field = convert_inode_field(field);
347         u64 bogus;
348         u64 orig;
349         int ret;
350
351         if (corrupt_field == BTRFS_INODE_FIELD_BAD) {
352                 fprintf(stderr, "Invalid field %s\n", field);
353                 return -EINVAL;
354         }
355
356         key.objectid = inode;
357         key.type = BTRFS_INODE_ITEM_KEY;
358         key.offset = (u64)-1;
359
360         path = btrfs_alloc_path();
361         if (!path)
362                 return -ENOMEM;
363
364         ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
365         if (ret < 0)
366                 goto out;
367         if (ret) {
368                 if (!path->slots[0]) {
369                         fprintf(stderr, "Couldn't find inode %Lu\n", inode);
370                         ret = -ENOENT;
371                         goto out;
372                 }
373                 path->slots[0]--;
374                 ret = 0;
375         }
376
377         btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
378         if (key.objectid != inode) {
379                 fprintf(stderr, "Couldn't find inode %Lu\n", inode);
380                 ret = -ENOENT;
381                 goto out;
382         }
383
384         ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
385                             struct btrfs_inode_item);
386         switch (corrupt_field) {
387         case BTRFS_INODE_FIELD_ISIZE:
388                 orig = btrfs_inode_size(path->nodes[0], ei);
389                 bogus = generate_u64(orig);
390                 btrfs_set_inode_size(path->nodes[0], ei, bogus);
391                 break;
392         default:
393                 ret = -EINVAL;
394                 break;
395         }
396         btrfs_mark_buffer_dirty(path->nodes[0]);
397 out:
398         btrfs_free_path(path);
399         return ret;
400 }
401
402 static int corrupt_file_extent(struct btrfs_trans_handle *trans,
403                                struct btrfs_root *root, u64 inode, u64 extent,
404                                char *field)
405 {
406         struct btrfs_file_extent_item *fi;
407         struct btrfs_path *path;
408         struct btrfs_key key;
409         enum btrfs_file_extent_field corrupt_field;
410         u64 bogus;
411         u64 orig;
412         int ret = 0;
413
414         corrupt_field = convert_file_extent_field(field);
415         if (corrupt_field == BTRFS_FILE_EXTENT_BAD) {
416                 fprintf(stderr, "Invalid field %s\n", field);
417                 return -EINVAL;
418         }
419
420         key.objectid = inode;
421         key.type = BTRFS_EXTENT_DATA_KEY;
422         key.offset = extent;
423
424         path = btrfs_alloc_path();
425         if (!path)
426                 return -ENOMEM;
427
428         ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
429         if (ret < 0)
430                 goto out;
431         if (ret) {
432                 fprintf(stderr, "Couldn't find extent %llu for inode %llu\n",
433                         extent, inode);
434                 ret = -ENOENT;
435                 goto out;
436         }
437
438         fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
439                             struct btrfs_file_extent_item);
440         switch (corrupt_field) {
441         case BTRFS_FILE_EXTENT_DISK_BYTENR:
442                 orig = btrfs_file_extent_disk_bytenr(path->nodes[0], fi);
443                 bogus = generate_u64(orig);
444                 btrfs_set_file_extent_disk_bytenr(path->nodes[0], fi, bogus);
445                 break;
446         default:
447                 ret = -EINVAL;
448                 break;
449         }
450         btrfs_mark_buffer_dirty(path->nodes[0]);
451 out:
452         btrfs_free_path(path);
453         return ret;
454 }
455
456 static int corrupt_metadata_block(struct btrfs_root *root, u64 block,
457                                   char *field)
458 {
459         struct btrfs_trans_handle *trans;
460         struct btrfs_path *path;
461         struct extent_buffer *eb;
462         struct btrfs_key key, root_key;
463         enum btrfs_metadata_block_field corrupt_field;
464         u64 root_objectid;
465         u64 orig, bogus;
466         u8 level;
467         int ret;
468
469         corrupt_field = convert_metadata_block_field(field);
470         if (corrupt_field == BTRFS_METADATA_BLOCK_BAD) {
471                 fprintf(stderr, "Invalid field %s\n", field);
472                 return -EINVAL;
473         }
474
475         eb = read_tree_block(root, block, root->leafsize, 0);
476         if (!eb) {
477                 fprintf(stderr, "Couldn't read in tree block %s\n", field);
478                 return -EINVAL;
479         }
480         root_objectid = btrfs_header_owner(eb);
481         level = btrfs_header_level(eb);
482         if (level)
483                 btrfs_node_key_to_cpu(eb, &key, 0);
484         else
485                 btrfs_item_key_to_cpu(eb, &key, 0);
486         free_extent_buffer(eb);
487
488         root_key.objectid = root_objectid;
489         root_key.type = BTRFS_ROOT_ITEM_KEY;
490         root_key.offset = (u64)-1;
491
492         root = btrfs_read_fs_root(root->fs_info, &root_key);
493         if (IS_ERR(root)) {
494                 fprintf(stderr, "Couldn't finde owner root %llu\n",
495                         key.objectid);
496                 return PTR_ERR(root);
497         }
498
499         path = btrfs_alloc_path();
500         if (!path)
501                 return -ENOMEM;
502
503         trans = btrfs_start_transaction(root, 1);
504         if (IS_ERR(trans)) {
505                 btrfs_free_path(path);
506                 fprintf(stderr, "Couldn't start transaction %ld\n",
507                         PTR_ERR(trans));
508                 return PTR_ERR(trans);
509         }
510
511         path->lowest_level = level;
512         ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
513         if (ret < 0) {
514                 fprintf(stderr, "Error searching to node %d\n", ret);
515                 goto out;
516         }
517         eb = path->nodes[level];
518
519         ret = 0;
520         switch (corrupt_field) {
521         case BTRFS_METADATA_BLOCK_GENERATION:
522                 orig = btrfs_header_generation(eb);
523                 bogus = generate_u64(orig);
524                 btrfs_set_header_generation(eb, bogus);
525                 break;
526         default:
527                 ret = -EINVAL;
528                 break;
529         }
530         btrfs_mark_buffer_dirty(path->nodes[level]);
531 out:
532         btrfs_commit_transaction(trans, root);
533         btrfs_free_path(path);
534         return ret;
535 }
536
537 static struct option long_options[] = {
538         /* { "byte-count", 1, NULL, 'b' }, */
539         { "logical", 1, NULL, 'l' },
540         { "copy", 1, NULL, 'c' },
541         { "bytes", 1, NULL, 'b' },
542         { "extent-record", 0, NULL, 'e' },
543         { "extent-tree", 0, NULL, 'E' },
544         { "keys", 0, NULL, 'k' },
545         { "chunk-record", 0, NULL, 'u' },
546         { "chunk-tree", 0, NULL, 'U' },
547         { "inode", 1, NULL, 'i'},
548         { "file-extent", 1, NULL, 'x'},
549         { "metadata-block", 1, NULL, 'm'},
550         { "field", 1, NULL, 'f'},
551         { 0, 0, 0, 0}
552 };
553
554 /* corrupt item using NO cow.
555  * Because chunk recover will recover based on whole partition scaning,
556  * If using COW, chunk recover will use the old item to recover,
557  * which is still OK but we want to check the ability to rebuild chunk
558  * not only restore the old ones */
559 int corrupt_item_nocow(struct btrfs_trans_handle *trans,
560                        struct btrfs_root *root, struct btrfs_path *path,
561                        int del)
562 {
563         int ret = 0;
564         struct btrfs_key key;
565         struct extent_buffer *leaf;
566         unsigned long ptr;
567         int slot;
568         u32 item_size;
569
570         leaf = path->nodes[0];
571         slot = path->slots[0];
572         /* Not deleting the first item of a leaf to keep leaf structure */
573         if (slot == 0)
574                 del = 0;
575         /* Only accept valid eb */
576         BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf));
577         btrfs_item_key_to_cpu(leaf, &key, slot);
578         if (del) {
579                 fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n",
580                         key.objectid, key.type, key.offset);
581                 btrfs_del_item(trans, root, path);
582         } else {
583                 fprintf(stdout, "Corrupting key and data [%llu, %u, %llu].\n",
584                         key.objectid, key.type, key.offset);
585                 ptr = btrfs_item_ptr_offset(leaf, slot);
586                 item_size = btrfs_item_size_nr(leaf, slot);
587                 memset_extent_buffer(leaf, 0, ptr, item_size);
588                 btrfs_mark_buffer_dirty(leaf);
589         }
590         return ret;
591 }
592 int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
593                        struct btrfs_root *root)
594 {
595         int ret;
596         int del;
597         int slot;
598         struct btrfs_path *path;
599         struct btrfs_key key;
600         struct btrfs_key found_key;
601         struct extent_buffer *leaf;
602
603         path = btrfs_alloc_path();
604         key.objectid = (u64)-1;
605         key.offset = (u64)-1;
606         key.type = (u8)-1;
607
608         /* Here, cow and ins_len must equals 0 for the following reasons:
609          * 1) chunk recover is based on disk scanning, so COW should be
610          *    disabled in case the original chunk being scanned and
611          *    recovered using the old chunk.
612          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
613          *    triggered.
614          */
615         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
616         BUG_ON(ret == 0);
617         if (ret < 0) {
618                 fprintf(stderr, "Error searching tree\n");
619                 goto free_out;
620         }
621         /* corrupt/del dev_item first */
622         while (!btrfs_previous_item(root, path, 0, BTRFS_DEV_ITEM_KEY)) {
623                 slot = path->slots[0];
624                 leaf = path->nodes[0];
625                 del = rand() % 3;
626                 /* Never delete the first item to keep the leaf structure */
627                 if (path->slots[0] == 0)
628                         del = 0;
629                 ret = corrupt_item_nocow(trans, root, path, del);
630                 if (ret)
631                         goto free_out;
632         }
633         btrfs_free_path(path);
634
635         /* Here, cow and ins_len must equals 0 for the following reasons:
636          * 1) chunk recover is based on disk scanning, so COW should be
637          *    disabled in case the original chunk being scanned and
638          *    recovered using the old chunk.
639          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
640          *    triggered.
641          */
642         path = btrfs_alloc_path();
643         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
644         BUG_ON(ret == 0);
645         if (ret < 0) {
646                 fprintf(stderr, "Error searching tree\n");
647                 goto free_out;
648         }
649         /* corrupt/del chunk then*/
650         while (!btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY)) {
651                 slot = path->slots[0];
652                 leaf = path->nodes[0];
653                 del = rand() % 3;
654                 btrfs_item_key_to_cpu(leaf, &found_key, slot);
655                 ret = corrupt_item_nocow(trans, root, path, del);
656                 if (ret)
657                         goto free_out;
658         }
659 free_out:
660         btrfs_free_path(path);
661         return ret;
662 }
663 int find_chunk_offset(struct btrfs_root *root,
664                       struct btrfs_path *path, u64 offset)
665 {
666         struct btrfs_key key;
667         int ret;
668
669         key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
670         key.type = BTRFS_CHUNK_ITEM_KEY;
671         key.offset = offset;
672
673         /* Here, cow and ins_len must equals 0 for following reasons:
674          * 1) chunk recover is based on disk scanning, so COW should
675          *    be disabled in case the original chunk being scanned
676          *    and recovered using the old chunk.
677          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON
678          *    will be triggered.
679          */
680         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
681         if (ret > 0) {
682                 fprintf(stderr, "Can't find chunk with given offset %llu\n",
683                         offset);
684                 goto out;
685         }
686         if (ret < 0) {
687                 fprintf(stderr, "Error searching chunk");
688                 goto out;
689         }
690 out:
691         return ret;
692
693 }
694 int main(int ac, char **av)
695 {
696         struct cache_tree root_cache;
697         struct btrfs_root *root;
698         struct extent_buffer *eb;
699         char *dev;
700         /* chunk offset can be 0,so change to (u64)-1 */
701         u64 logical = (u64)-1;
702         int ret = 0;
703         int option_index = 0;
704         int copy = 0;
705         u64 bytes = 4096;
706         int extent_rec = 0;
707         int extent_tree = 0;
708         int corrupt_block_keys = 0;
709         int chunk_rec = 0;
710         int chunk_tree = 0;
711         u64 metadata_block = 0;
712         u64 inode = 0;
713         u64 file_extent = (u64)-1;
714         char field[FIELD_BUF_LEN];
715
716         field[0] = '\0';
717         srand(128);
718
719         while(1) {
720                 int c;
721                 c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:", long_options,
722                                 &option_index);
723                 if (c < 0)
724                         break;
725                 switch(c) {
726                         case 'l':
727                                 logical = atoll(optarg);
728                                 break;
729                         case 'c':
730                                 copy = atoi(optarg);
731                                 if (copy <= 0) {
732                                         fprintf(stderr,
733                                                 "invalid copy number\n");
734                                         print_usage();
735                                 }
736                                 break;
737                         case 'b':
738                                 bytes = atoll(optarg);
739                                 if (bytes == 0) {
740                                         fprintf(stderr,
741                                                 "invalid byte count\n");
742                                         print_usage();
743                                 }
744                                 break;
745                         case 'e':
746                                 extent_rec = 1;
747                                 break;
748                         case 'E':
749                                 extent_tree = 1;
750                                 break;
751                         case 'k':
752                                 corrupt_block_keys = 1;
753                                 break;
754                         case 'u':
755                                 chunk_rec = 1;
756                                 break;
757                         case 'U':
758                                 chunk_tree = 1;
759                         case 'i':
760                                 inode = atoll(optarg);
761                                 if (inode == 0) {
762                                         fprintf(stderr,
763                                                 "invalid inode number\n");
764                                         print_usage();
765                                 }
766                                 break;
767                         case 'f':
768                                 strncpy(field, optarg, FIELD_BUF_LEN);
769                                 break;
770                         case 'x':
771                                 errno = 0;
772                                 file_extent = atoll(optarg);
773                                 if (errno) {
774                                         fprintf(stderr, "error converting "
775                                                 "%d\n", errno);
776                                         print_usage();
777                                 }
778                                 break;
779                         case 'm':
780                                 errno = 0;
781                                 metadata_block = atoll(optarg);
782                                 if (errno) {
783                                         fprintf(stderr, "error converting "
784                                                 "%d\n", errno);
785                                         print_usage();
786                                 }
787                                 break;
788                         default:
789                                 print_usage();
790                 }
791         }
792         ac = ac - optind;
793         if (ac == 0)
794                 print_usage();
795         dev = av[optind];
796
797         radix_tree_init();
798         cache_tree_init(&root_cache);
799
800         root = open_ctree(dev, 0, 1);
801         if (!root) {
802                 fprintf(stderr, "Open ctree failed\n");
803                 exit(1);
804         }
805         if (extent_rec) {
806                 struct btrfs_trans_handle *trans;
807
808                 if (logical == (u64)-1)
809                         print_usage();
810                 trans = btrfs_start_transaction(root, 1);
811                 ret = corrupt_extent (trans, root, logical, 0);
812                 btrfs_commit_transaction(trans, root);
813                 goto out_close;
814         }
815         if (extent_tree) {
816                 struct btrfs_trans_handle *trans;
817                 trans = btrfs_start_transaction(root, 1);
818                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
819                                           root->fs_info->extent_root->node);
820                 btrfs_commit_transaction(trans, root);
821                 goto out_close;
822         }
823         if (chunk_rec) {
824                 struct btrfs_trans_handle *trans;
825                 struct btrfs_path *path;
826                 int del;
827
828                 if (logical == (u64)-1)
829                         print_usage();
830                 del = rand() % 3;
831                 path = btrfs_alloc_path();
832
833                 if (find_chunk_offset(root->fs_info->chunk_root, path,
834                                       logical) != 0) {
835                         btrfs_free_path(path);
836                         goto out_close;
837                 }
838                 trans = btrfs_start_transaction(root, 1);
839                 ret = corrupt_item_nocow(trans, root->fs_info->chunk_root,
840                                          path, del);
841                 if (ret < 0)
842                         fprintf(stderr, "Failed to corrupt chunk record\n");
843                 btrfs_commit_transaction(trans, root);
844                 goto out_close;
845         }
846         if (chunk_tree) {
847                 struct btrfs_trans_handle *trans;
848                 trans = btrfs_start_transaction(root, 1);
849                 ret = corrupt_chunk_tree(trans, root->fs_info->chunk_root);
850                 if (ret < 0)
851                         fprintf(stderr, "Failed to corrupt chunk tree\n");
852                 btrfs_commit_transaction(trans, root);
853                 goto out_close;
854         }
855         if (inode) {
856                 struct btrfs_trans_handle *trans;
857
858                 if (!strlen(field))
859                         print_usage();
860
861                 trans = btrfs_start_transaction(root, 1);
862                 if (file_extent == (u64)-1) {
863                         printf("corrupting inode\n");
864                         ret = corrupt_inode(trans, root, inode, field);
865                 } else {
866                         printf("corrupting file extent\n");
867                         ret = corrupt_file_extent(trans, root, inode,
868                                                   file_extent, field);
869                 }
870                 btrfs_commit_transaction(trans, root);
871                 goto out_close;
872         }
873         if (metadata_block) {
874                 if (!strlen(field))
875                         print_usage();
876                 ret = corrupt_metadata_block(root, metadata_block, field);
877                 goto out_close;
878         }
879         /*
880          * If we made it here and we have extent set then we didn't specify
881          * inode and we're screwed.
882          */
883         if (file_extent != (u64)-1)
884                 print_usage();
885
886         if (logical == (u64)-1)
887                 print_usage();
888
889         if (bytes == 0)
890                 bytes = root->sectorsize;
891
892         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
893         bytes *= root->sectorsize;
894
895         while (bytes > 0) {
896                 if (corrupt_block_keys) {
897                         corrupt_keys_in_block(root, logical);
898                 } else {
899                         eb = debug_corrupt_block(root, logical,
900                                                  root->sectorsize, copy);
901                         free_extent_buffer(eb);
902                 }
903                 logical += root->sectorsize;
904                 bytes -= root->sectorsize;
905         }
906         return ret;
907 out_close:
908         close_ctree(root);
909         return ret;
910 }