btrfs-progs: Add chunk corrupt funtion to btrfs-corrupt-block
[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
167         key.objectid = bytenr;
168         key.type = (u8)-1;
169         key.offset = (u64)-1;
170
171         while(1) {
172                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
173                                         &key, path, -1, 1);
174                 if (ret < 0)
175                         break;
176
177                 if (ret > 0) {
178                         if (path->slots[0] == 0)
179                                 break;
180                         path->slots[0]--;
181                         ret = 0;
182                 }
183                 leaf = path->nodes[0];
184                 slot = path->slots[0];
185                 btrfs_item_key_to_cpu(leaf, &key, slot);
186                 if (key.objectid != bytenr)
187                         break;
188
189                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
190                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
191                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
192                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
193                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
194                     key.type != BTRFS_SHARED_DATA_REF_KEY)
195                         goto next;
196
197                 if (should_del) {
198                         fprintf(stderr,
199                                 "deleting extent record: key %llu %u %llu\n",
200                                 key.objectid, key.type, key.offset);
201
202                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
203                                 /* make sure this extent doesn't get
204                                  * reused for other purposes */
205                                 btrfs_pin_extent(root->fs_info,
206                                                  key.objectid, key.offset);
207                         }
208
209                         btrfs_del_item(trans, root, path);
210                 } else {
211                         fprintf(stderr,
212                                 "corrupting extent record: key %llu %u %llu\n",
213                                 key.objectid, key.type, key.offset);
214                         ptr = btrfs_item_ptr_offset(leaf, slot);
215                         item_size = btrfs_item_size_nr(leaf, slot);
216                         memset_extent_buffer(leaf, 0, ptr, item_size);
217                         btrfs_mark_buffer_dirty(leaf);
218                 }
219 next:
220                 btrfs_release_path(NULL, path);
221
222                 if (key.offset > 0)
223                         key.offset--;
224                 if (key.offset == 0)
225                         break;
226         }
227
228         btrfs_free_path(path);
229         return 0;
230 }
231
232 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
233                                       struct btrfs_root *root,
234                                       struct extent_buffer *eb)
235 {
236         u32 nr = btrfs_header_nritems(eb);
237         u32 victim = rand() % nr;
238         u64 objectid;
239         struct btrfs_key key;
240
241         btrfs_item_key_to_cpu(eb, &key, victim);
242         objectid = key.objectid;
243         corrupt_extent(trans, root, objectid, 1);
244 }
245
246 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
247                                       struct btrfs_root *root,
248                                       struct extent_buffer *eb)
249 {
250         int i;
251         u32 nr;
252
253         if (!eb)
254                 return;
255
256         nr = btrfs_header_nritems(eb);
257         if (btrfs_is_leaf(eb)) {
258                 btrfs_corrupt_extent_leaf(trans, root, eb);
259                 return;
260         }
261
262         if (btrfs_header_level(eb) == 1 && eb != root->node) {
263                 if (rand() % 5)
264                         return;
265         }
266
267         for (i = 0; i < nr; i++) {
268                 struct extent_buffer *next;
269
270                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
271                                        root->leafsize,
272                                        btrfs_node_ptr_generation(eb, i));
273                 if (!next)
274                         continue;
275                 btrfs_corrupt_extent_tree(trans, root, next);
276                 free_extent_buffer(next);
277         }
278 }
279
280 static struct option long_options[] = {
281         /* { "byte-count", 1, NULL, 'b' }, */
282         { "logical", 1, NULL, 'l' },
283         { "copy", 1, NULL, 'c' },
284         { "bytes", 1, NULL, 'b' },
285         { "extent-record", 0, NULL, 'e' },
286         { "extent-tree", 0, NULL, 'E' },
287         { "keys", 0, NULL, 'k' },
288         { "chunk-record", 0, NULL, 'u' },
289         { "chunk-tree", 0, NULL, 'U' },
290         { 0, 0, 0, 0}
291 };
292
293 /* corrupt item using NO cow.
294  * Because chunk recover will recover based on whole partition scaning,
295  * If using COW, chunk recover will use the old item to recover,
296  * which is still OK but we want to check the ability to rebuild chunk
297  * not only restore the old ones */
298 int corrupt_item_nocow(struct btrfs_trans_handle *trans,
299                        struct btrfs_root *root, struct btrfs_path *path,
300                        int del)
301 {
302         int ret = 0;
303         struct btrfs_key key;
304         struct extent_buffer *leaf;
305         unsigned long ptr;
306         int slot;
307         u32 item_size;
308
309         leaf = path->nodes[0];
310         slot = path->slots[0];
311         /* Not deleting the first item of a leaf to keep leaf structure */
312         if (slot == 0)
313                 del = 0;
314         /* Only accept valid eb */
315         BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf));
316         btrfs_item_key_to_cpu(leaf, &key, slot);
317         if (del) {
318                 fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n",
319                         key.objectid, key.type, key.offset);
320                 btrfs_del_item(trans, root, path);
321         } else {
322                 fprintf(stdout, "Corrupting key and data [%llu, %u, %llu].\n",
323                         key.objectid, key.type, key.offset);
324                 ptr = btrfs_item_ptr_offset(leaf, slot);
325                 item_size = btrfs_item_size_nr(leaf, slot);
326                 memset_extent_buffer(leaf, 0, ptr, item_size);
327                 btrfs_mark_buffer_dirty(leaf);
328         }
329         return ret;
330 }
331 int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
332                        struct btrfs_root *root)
333 {
334         int ret;
335         int del;
336         int slot;
337         struct btrfs_path *path;
338         struct btrfs_key key;
339         struct btrfs_key found_key;
340         struct extent_buffer *leaf;
341
342         path = btrfs_alloc_path();
343         key.objectid = (u64)-1;
344         key.offset = (u64)-1;
345         key.type = (u8)-1;
346
347         /* Here, cow and ins_len must equals 0 for the following reasons:
348          * 1) chunk recover is based on disk scanning, so COW should be
349          *    disabled in case the original chunk being scanned and
350          *    recovered using the old chunk.
351          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
352          *    triggered.
353          */
354         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
355         BUG_ON(ret == 0);
356         if (ret < 0) {
357                 fprintf(stderr, "Error searching tree\n");
358                 goto free_out;
359         }
360         /* corrupt/del dev_item first */
361         while (!btrfs_previous_item(root, path, 0, BTRFS_DEV_ITEM_KEY)) {
362                 slot = path->slots[0];
363                 leaf = path->nodes[0];
364                 del = rand() % 3;
365                 /* Never delete the first item to keep the leaf structure */
366                 if (path->slots[0] == 0)
367                         del = 0;
368                 ret = corrupt_item_nocow(trans, root, path, del);
369                 if (ret)
370                         goto free_out;
371         }
372         btrfs_free_path(path);
373
374         /* Here, cow and ins_len must equals 0 for the following reasons:
375          * 1) chunk recover is based on disk scanning, so COW should be
376          *    disabled in case the original chunk being scanned and
377          *    recovered using the old chunk.
378          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
379          *    triggered.
380          */
381         path = btrfs_alloc_path();
382         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
383         BUG_ON(ret == 0);
384         if (ret < 0) {
385                 fprintf(stderr, "Error searching tree\n");
386                 goto free_out;
387         }
388         /* corrupt/del chunk then*/
389         while (!btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY)) {
390                 slot = path->slots[0];
391                 leaf = path->nodes[0];
392                 del = rand() % 3;
393                 btrfs_item_key_to_cpu(leaf, &found_key, slot);
394                 ret = corrupt_item_nocow(trans, root, path, del);
395                 if (ret)
396                         goto free_out;
397         }
398 free_out:
399         btrfs_free_path(path);
400         return ret;
401 }
402 int find_chunk_offset(struct btrfs_root *root,
403                       struct btrfs_path *path, u64 offset)
404 {
405         struct btrfs_key key;
406         int ret;
407
408         key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
409         key.type = BTRFS_CHUNK_ITEM_KEY;
410         key.offset = offset;
411
412         /* Here, cow and ins_len must equals 0 for following reasons:
413          * 1) chunk recover is based on disk scanning, so COW should
414          *    be disabled in case the original chunk being scanned
415          *    and recovered using the old chunk.
416          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON
417          *    will be triggered.
418          */
419         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
420         if (ret > 0) {
421                 fprintf(stderr, "Can't find chunk with given offset %llu\n",
422                         offset);
423                 goto out;
424         }
425         if (ret < 0) {
426                 fprintf(stderr, "Error searching chunk");
427                 goto out;
428         }
429 out:
430         return ret;
431
432 }
433 int main(int ac, char **av)
434 {
435         struct cache_tree root_cache;
436         struct btrfs_root *root;
437         struct extent_buffer *eb;
438         char *dev;
439         /* chunk offset can be 0,so change to (u64)-1 */
440         u64 logical = (u64)-1;
441         int ret = 0;
442         int option_index = 0;
443         int copy = 0;
444         u64 bytes = 4096;
445         int extent_rec = 0;
446         int extent_tree = 0;
447         int corrupt_block_keys = 0;
448         int chunk_rec = 0;
449         int chunk_tree = 0;
450
451         srand(128);
452
453         while(1) {
454                 int c;
455                 c = getopt_long(ac, av, "l:c:b:eEkuU", long_options,
456                                 &option_index);
457                 if (c < 0)
458                         break;
459                 switch(c) {
460                         case 'l':
461                                 logical = atoll(optarg);
462                                 break;
463                         case 'c':
464                                 copy = atoi(optarg);
465                                 if (copy == 0) {
466                                         fprintf(stderr,
467                                                 "invalid copy number\n");
468                                         print_usage();
469                                 }
470                                 break;
471                         case 'b':
472                                 bytes = atoll(optarg);
473                                 if (bytes == 0) {
474                                         fprintf(stderr,
475                                                 "invalid byte count\n");
476                                         print_usage();
477                                 }
478                                 break;
479                         case 'e':
480                                 extent_rec = 1;
481                                 break;
482                         case 'E':
483                                 extent_tree = 1;
484                                 break;
485                         case 'k':
486                                 corrupt_block_keys = 1;
487                                 break;
488                         case 'u':
489                                 chunk_rec = 1;
490                                 break;
491                         case 'U':
492                                 chunk_tree = 1;
493                                 break;
494                         default:
495                                 print_usage();
496                 }
497         }
498         ac = ac - optind;
499         if (ac == 0)
500                 print_usage();
501         if (logical == (u64)-1 && !(extent_tree || chunk_tree))
502                 print_usage();
503         if (copy < 0)
504                 print_usage();
505
506         dev = av[optind];
507
508         radix_tree_init();
509         cache_tree_init(&root_cache);
510
511         root = open_ctree(dev, 0, 1);
512         if (!root) {
513                 fprintf(stderr, "Open ctree failed\n");
514                 exit(1);
515         }
516         if (extent_rec) {
517                 struct btrfs_trans_handle *trans;
518                 trans = btrfs_start_transaction(root, 1);
519                 ret = corrupt_extent (trans, root, logical, 0);
520                 btrfs_commit_transaction(trans, root);
521                 goto out_close;
522         }
523         if (extent_tree) {
524                 struct btrfs_trans_handle *trans;
525                 trans = btrfs_start_transaction(root, 1);
526                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
527                                           root->fs_info->extent_root->node);
528                 btrfs_commit_transaction(trans, root);
529                 goto out_close;
530         }
531         if (chunk_rec) {
532                 struct btrfs_trans_handle *trans;
533                 struct btrfs_path *path;
534                 int del;
535
536                 del = rand() % 3;
537                 path = btrfs_alloc_path();
538
539                 if (find_chunk_offset(root->fs_info->chunk_root, path,
540                                       logical) != 0) {
541                         btrfs_free_path(path);
542                         goto out_close;
543                 }
544                 trans = btrfs_start_transaction(root, 1);
545                 ret = corrupt_item_nocow(trans, root->fs_info->chunk_root,
546                                          path, del);
547                 if (ret < 0)
548                         fprintf(stderr, "Failed to corrupt chunk record\n");
549                 btrfs_commit_transaction(trans, root);
550                 goto out_close;
551         }
552         if (chunk_tree) {
553                 struct btrfs_trans_handle *trans;
554                 trans = btrfs_start_transaction(root, 1);
555                 ret = corrupt_chunk_tree(trans, root->fs_info->chunk_root);
556                 if (ret < 0)
557                         fprintf(stderr, "Failed to corrupt chunk tree\n");
558                 btrfs_commit_transaction(trans, root);
559                 goto out_close;
560         }
561
562         if (bytes == 0)
563                 bytes = root->sectorsize;
564
565         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
566         bytes *= root->sectorsize;
567
568         while (bytes > 0) {
569                 if (corrupt_block_keys) {
570                         corrupt_keys_in_block(root, logical);
571                 } else {
572                         eb = debug_corrupt_block(root, logical,
573                                                  root->sectorsize, copy);
574                         free_extent_buffer(eb);
575                 }
576                 logical += root->sectorsize;
577                 bytes -= root->sectorsize;
578         }
579         return ret;
580 out_close:
581         close_ctree(root);
582         return ret;
583 }