btrfs-progs: make btrfs-corrupt-block compile again
[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 struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
37                                      u32 blocksize, int copy)
38 {
39         int ret;
40         struct extent_buffer *eb;
41         u64 length;
42         struct btrfs_multi_bio *multi = NULL;
43         struct btrfs_device *device;
44         int num_copies;
45         int mirror_num = 1;
46
47         eb = btrfs_find_create_tree_block(root, bytenr, blocksize);
48         if (!eb)
49                 return NULL;
50
51         length = blocksize;
52         while (1) {
53                 ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
54                                       eb->start, &length, &multi,
55                                       mirror_num, NULL);
56                 BUG_ON(ret);
57                 device = multi->stripes[0].dev;
58                 eb->fd = device->fd;
59                 device->total_ios++;
60                 eb->dev_bytenr = multi->stripes[0].physical;
61
62                 fprintf(stdout,
63                         "mirror %d logical %llu physical %llu device %s\n",
64                         mirror_num, (unsigned long long)bytenr,
65                         (unsigned long long)eb->dev_bytenr, device->name);
66                 kfree(multi);
67
68                 if (!copy || mirror_num == copy) {
69                         ret = read_extent_from_disk(eb, 0, eb->len);
70                         printf("corrupting %llu copy %d\n", eb->start,
71                                mirror_num);
72                         memset(eb->data, 0, eb->len);
73                         write_extent_to_disk(eb);
74                         fsync(eb->fd);
75                 }
76
77                 num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
78                                               eb->start, eb->len);
79                 if (num_copies == 1)
80                         break;
81
82                 mirror_num++;
83                 if (mirror_num > num_copies)
84                         break;
85         }
86         return eb;
87 }
88
89 static void print_usage(void)
90 {
91         fprintf(stderr, "usage: btrfs-corrupt-block [options] device\n");
92         fprintf(stderr, "\t-l Logical extent to be corrupted\n");
93         fprintf(stderr, "\t-c Copy of the extent to be corrupted"
94                 " (usually 1 or 2, default: 0)\n");
95         fprintf(stderr, "\t-b Number of bytes to be corrupted\n");
96         fprintf(stderr, "\t-e Extent to be corrupted\n");
97         fprintf(stderr, "\t-E The whole extent tree to be corrupted\n");
98         fprintf(stderr, "\t-u Given chunk item to be corrupted\n");
99         fprintf(stderr, "\t-U The whole chunk tree to be corrupted\n");
100         exit(1);
101 }
102
103 static void corrupt_keys(struct btrfs_trans_handle *trans,
104                          struct btrfs_root *root,
105                          struct extent_buffer *eb)
106 {
107         int slot;
108         int bad_slot;
109         int nr;
110         struct btrfs_disk_key bad_key;;
111
112         nr = btrfs_header_nritems(eb);
113         if (nr == 0)
114                 return;
115
116         slot = rand() % nr;
117         bad_slot = rand() % nr;
118
119         if (bad_slot == slot)
120                 return;
121
122         fprintf(stderr,
123                 "corrupting keys in block %llu slot %d swapping with %d\n",
124                 (unsigned long long)eb->start, slot, bad_slot);
125
126         if (btrfs_header_level(eb) == 0) {
127                 btrfs_item_key(eb, &bad_key, bad_slot);
128                 btrfs_set_item_key(eb, &bad_key, slot);
129         } else {
130                 btrfs_node_key(eb, &bad_key, bad_slot);
131                 btrfs_set_node_key(eb, &bad_key, slot);
132         }
133         btrfs_mark_buffer_dirty(eb);
134         if (!trans) {
135                 csum_tree_block(root, eb, 0);
136                 write_extent_to_disk(eb);
137         }
138 }
139
140
141 static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
142 {
143         struct extent_buffer *eb;
144
145         eb = read_tree_block(root, bytenr, root->leafsize, 0);
146         if (!eb)
147                 return -EIO;;
148
149         corrupt_keys(NULL, root, eb);
150         free_extent_buffer(eb);
151         return 0;
152 }
153
154 static int corrupt_extent(struct btrfs_trans_handle *trans,
155                           struct btrfs_root *root, u64 bytenr, int copy)
156 {
157         struct btrfs_key key;
158         struct extent_buffer *leaf;
159         u32 item_size;
160         unsigned long ptr;
161         struct btrfs_path *path;
162         int ret;
163         int slot;
164         int should_del = rand() % 3;
165
166         path = btrfs_alloc_path();
167         if (!path)
168                 return -ENOMEM;
169
170         key.objectid = bytenr;
171         key.type = (u8)-1;
172         key.offset = (u64)-1;
173
174         while(1) {
175                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
176                                         &key, path, -1, 1);
177                 if (ret < 0)
178                         break;
179
180                 if (ret > 0) {
181                         if (path->slots[0] == 0)
182                                 break;
183                         path->slots[0]--;
184                         ret = 0;
185                 }
186                 leaf = path->nodes[0];
187                 slot = path->slots[0];
188                 btrfs_item_key_to_cpu(leaf, &key, slot);
189                 if (key.objectid != bytenr)
190                         break;
191
192                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
193                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
194                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
195                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
196                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
197                     key.type != BTRFS_SHARED_DATA_REF_KEY)
198                         goto next;
199
200                 if (should_del) {
201                         fprintf(stderr,
202                                 "deleting extent record: key %llu %u %llu\n",
203                                 key.objectid, key.type, key.offset);
204
205                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
206                                 /* make sure this extent doesn't get
207                                  * reused for other purposes */
208                                 btrfs_pin_extent(root->fs_info,
209                                                  key.objectid, key.offset);
210                         }
211
212                         btrfs_del_item(trans, root, path);
213                 } else {
214                         fprintf(stderr,
215                                 "corrupting extent record: key %llu %u %llu\n",
216                                 key.objectid, key.type, key.offset);
217                         ptr = btrfs_item_ptr_offset(leaf, slot);
218                         item_size = btrfs_item_size_nr(leaf, slot);
219                         memset_extent_buffer(leaf, 0, ptr, item_size);
220                         btrfs_mark_buffer_dirty(leaf);
221                 }
222 next:
223                 btrfs_release_path(path);
224
225                 if (key.offset > 0)
226                         key.offset--;
227                 if (key.offset == 0)
228                         break;
229         }
230
231         btrfs_free_path(path);
232         return 0;
233 }
234
235 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
236                                       struct btrfs_root *root,
237                                       struct extent_buffer *eb)
238 {
239         u32 nr = btrfs_header_nritems(eb);
240         u32 victim = rand() % nr;
241         u64 objectid;
242         struct btrfs_key key;
243
244         btrfs_item_key_to_cpu(eb, &key, victim);
245         objectid = key.objectid;
246         corrupt_extent(trans, root, objectid, 1);
247 }
248
249 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
250                                       struct btrfs_root *root,
251                                       struct extent_buffer *eb)
252 {
253         int i;
254         u32 nr;
255
256         if (!eb)
257                 return;
258
259         nr = btrfs_header_nritems(eb);
260         if (btrfs_is_leaf(eb)) {
261                 btrfs_corrupt_extent_leaf(trans, root, eb);
262                 return;
263         }
264
265         if (btrfs_header_level(eb) == 1 && eb != root->node) {
266                 if (rand() % 5)
267                         return;
268         }
269
270         for (i = 0; i < nr; i++) {
271                 struct extent_buffer *next;
272
273                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
274                                        root->leafsize,
275                                        btrfs_node_ptr_generation(eb, i));
276                 if (!next)
277                         continue;
278                 btrfs_corrupt_extent_tree(trans, root, next);
279                 free_extent_buffer(next);
280         }
281 }
282
283 static struct option long_options[] = {
284         /* { "byte-count", 1, NULL, 'b' }, */
285         { "logical", 1, NULL, 'l' },
286         { "copy", 1, NULL, 'c' },
287         { "bytes", 1, NULL, 'b' },
288         { "extent-record", 0, NULL, 'e' },
289         { "extent-tree", 0, NULL, 'E' },
290         { "keys", 0, NULL, 'k' },
291         { "chunk-record", 0, NULL, 'u' },
292         { "chunk-tree", 0, NULL, 'U' },
293         { 0, 0, 0, 0}
294 };
295
296 /* corrupt item using NO cow.
297  * Because chunk recover will recover based on whole partition scaning,
298  * If using COW, chunk recover will use the old item to recover,
299  * which is still OK but we want to check the ability to rebuild chunk
300  * not only restore the old ones */
301 int corrupt_item_nocow(struct btrfs_trans_handle *trans,
302                        struct btrfs_root *root, struct btrfs_path *path,
303                        int del)
304 {
305         int ret = 0;
306         struct btrfs_key key;
307         struct extent_buffer *leaf;
308         unsigned long ptr;
309         int slot;
310         u32 item_size;
311
312         leaf = path->nodes[0];
313         slot = path->slots[0];
314         /* Not deleting the first item of a leaf to keep leaf structure */
315         if (slot == 0)
316                 del = 0;
317         /* Only accept valid eb */
318         BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf));
319         btrfs_item_key_to_cpu(leaf, &key, slot);
320         if (del) {
321                 fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n",
322                         key.objectid, key.type, key.offset);
323                 btrfs_del_item(trans, root, path);
324         } else {
325                 fprintf(stdout, "Corrupting key and data [%llu, %u, %llu].\n",
326                         key.objectid, key.type, key.offset);
327                 ptr = btrfs_item_ptr_offset(leaf, slot);
328                 item_size = btrfs_item_size_nr(leaf, slot);
329                 memset_extent_buffer(leaf, 0, ptr, item_size);
330                 btrfs_mark_buffer_dirty(leaf);
331         }
332         return ret;
333 }
334 int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
335                        struct btrfs_root *root)
336 {
337         int ret;
338         int del;
339         int slot;
340         struct btrfs_path *path;
341         struct btrfs_key key;
342         struct btrfs_key found_key;
343         struct extent_buffer *leaf;
344
345         path = btrfs_alloc_path();
346         key.objectid = (u64)-1;
347         key.offset = (u64)-1;
348         key.type = (u8)-1;
349
350         /* Here, cow and ins_len must equals 0 for the following reasons:
351          * 1) chunk recover is based on disk scanning, so COW should be
352          *    disabled in case the original chunk being scanned and
353          *    recovered using the old chunk.
354          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
355          *    triggered.
356          */
357         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
358         BUG_ON(ret == 0);
359         if (ret < 0) {
360                 fprintf(stderr, "Error searching tree\n");
361                 goto free_out;
362         }
363         /* corrupt/del dev_item first */
364         while (!btrfs_previous_item(root, path, 0, BTRFS_DEV_ITEM_KEY)) {
365                 slot = path->slots[0];
366                 leaf = path->nodes[0];
367                 del = rand() % 3;
368                 /* Never delete the first item to keep the leaf structure */
369                 if (path->slots[0] == 0)
370                         del = 0;
371                 ret = corrupt_item_nocow(trans, root, path, del);
372                 if (ret)
373                         goto free_out;
374         }
375         btrfs_free_path(path);
376
377         /* Here, cow and ins_len must equals 0 for the following reasons:
378          * 1) chunk recover is based on disk scanning, so COW should be
379          *    disabled in case the original chunk being scanned and
380          *    recovered using the old chunk.
381          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
382          *    triggered.
383          */
384         path = btrfs_alloc_path();
385         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
386         BUG_ON(ret == 0);
387         if (ret < 0) {
388                 fprintf(stderr, "Error searching tree\n");
389                 goto free_out;
390         }
391         /* corrupt/del chunk then*/
392         while (!btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY)) {
393                 slot = path->slots[0];
394                 leaf = path->nodes[0];
395                 del = rand() % 3;
396                 btrfs_item_key_to_cpu(leaf, &found_key, slot);
397                 ret = corrupt_item_nocow(trans, root, path, del);
398                 if (ret)
399                         goto free_out;
400         }
401 free_out:
402         btrfs_free_path(path);
403         return ret;
404 }
405 int find_chunk_offset(struct btrfs_root *root,
406                       struct btrfs_path *path, u64 offset)
407 {
408         struct btrfs_key key;
409         int ret;
410
411         key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
412         key.type = BTRFS_CHUNK_ITEM_KEY;
413         key.offset = offset;
414
415         /* Here, cow and ins_len must equals 0 for following reasons:
416          * 1) chunk recover is based on disk scanning, so COW should
417          *    be disabled in case the original chunk being scanned
418          *    and recovered using the old chunk.
419          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON
420          *    will be triggered.
421          */
422         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
423         if (ret > 0) {
424                 fprintf(stderr, "Can't find chunk with given offset %llu\n",
425                         offset);
426                 goto out;
427         }
428         if (ret < 0) {
429                 fprintf(stderr, "Error searching chunk");
430                 goto out;
431         }
432 out:
433         return ret;
434
435 }
436 int main(int ac, char **av)
437 {
438         struct cache_tree root_cache;
439         struct btrfs_root *root;
440         struct extent_buffer *eb;
441         char *dev;
442         /* chunk offset can be 0,so change to (u64)-1 */
443         u64 logical = (u64)-1;
444         int ret = 0;
445         int option_index = 0;
446         int copy = 0;
447         u64 bytes = 4096;
448         int extent_rec = 0;
449         int extent_tree = 0;
450         int corrupt_block_keys = 0;
451         int chunk_rec = 0;
452         int chunk_tree = 0;
453
454         srand(128);
455
456         while(1) {
457                 int c;
458                 c = getopt_long(ac, av, "l:c:b:eEkuU", long_options,
459                                 &option_index);
460                 if (c < 0)
461                         break;
462                 switch(c) {
463                         case 'l':
464                                 logical = atoll(optarg);
465                                 break;
466                         case 'c':
467                                 copy = atoi(optarg);
468                                 if (copy == 0) {
469                                         fprintf(stderr,
470                                                 "invalid copy number\n");
471                                         print_usage();
472                                 }
473                                 break;
474                         case 'b':
475                                 bytes = atoll(optarg);
476                                 if (bytes == 0) {
477                                         fprintf(stderr,
478                                                 "invalid byte count\n");
479                                         print_usage();
480                                 }
481                                 break;
482                         case 'e':
483                                 extent_rec = 1;
484                                 break;
485                         case 'E':
486                                 extent_tree = 1;
487                                 break;
488                         case 'k':
489                                 corrupt_block_keys = 1;
490                                 break;
491                         case 'u':
492                                 chunk_rec = 1;
493                                 break;
494                         case 'U':
495                                 chunk_tree = 1;
496                                 break;
497                         default:
498                                 print_usage();
499                 }
500         }
501         ac = ac - optind;
502         if (ac == 0)
503                 print_usage();
504         if (logical == (u64)-1 && !(extent_tree || chunk_tree))
505                 print_usage();
506         if (copy < 0)
507                 print_usage();
508
509         dev = av[optind];
510
511         radix_tree_init();
512         cache_tree_init(&root_cache);
513
514         root = open_ctree(dev, 0, 1);
515         if (!root) {
516                 fprintf(stderr, "Open ctree failed\n");
517                 exit(1);
518         }
519         if (extent_rec) {
520                 struct btrfs_trans_handle *trans;
521                 trans = btrfs_start_transaction(root, 1);
522                 ret = corrupt_extent (trans, root, logical, 0);
523                 btrfs_commit_transaction(trans, root);
524                 goto out_close;
525         }
526         if (extent_tree) {
527                 struct btrfs_trans_handle *trans;
528                 trans = btrfs_start_transaction(root, 1);
529                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
530                                           root->fs_info->extent_root->node);
531                 btrfs_commit_transaction(trans, root);
532                 goto out_close;
533         }
534         if (chunk_rec) {
535                 struct btrfs_trans_handle *trans;
536                 struct btrfs_path *path;
537                 int del;
538
539                 del = rand() % 3;
540                 path = btrfs_alloc_path();
541
542                 if (find_chunk_offset(root->fs_info->chunk_root, path,
543                                       logical) != 0) {
544                         btrfs_free_path(path);
545                         goto out_close;
546                 }
547                 trans = btrfs_start_transaction(root, 1);
548                 ret = corrupt_item_nocow(trans, root->fs_info->chunk_root,
549                                          path, del);
550                 if (ret < 0)
551                         fprintf(stderr, "Failed to corrupt chunk record\n");
552                 btrfs_commit_transaction(trans, root);
553                 goto out_close;
554         }
555         if (chunk_tree) {
556                 struct btrfs_trans_handle *trans;
557                 trans = btrfs_start_transaction(root, 1);
558                 ret = corrupt_chunk_tree(trans, root->fs_info->chunk_root);
559                 if (ret < 0)
560                         fprintf(stderr, "Failed to corrupt chunk tree\n");
561                 btrfs_commit_transaction(trans, root);
562                 goto out_close;
563         }
564
565         if (bytes == 0)
566                 bytes = root->sectorsize;
567
568         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
569         bytes *= root->sectorsize;
570
571         while (bytes > 0) {
572                 if (corrupt_block_keys) {
573                         corrupt_keys_in_block(root, logical);
574                 } else {
575                         eb = debug_corrupt_block(root, logical,
576                                                  root->sectorsize, copy);
577                         free_extent_buffer(eb);
578                 }
579                 logical += root->sectorsize;
580                 bytes -= root->sectorsize;
581         }
582         return ret;
583 out_close:
584         close_ctree(root);
585         return ret;
586 }