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