Btrfs-progs: setup framework to corrupt specific fields of an inode
[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-f The field in the item to corrupt\n");
105         exit(1);
106 }
107
108 static void corrupt_keys(struct btrfs_trans_handle *trans,
109                          struct btrfs_root *root,
110                          struct extent_buffer *eb)
111 {
112         int slot;
113         int bad_slot;
114         int nr;
115         struct btrfs_disk_key bad_key;;
116
117         nr = btrfs_header_nritems(eb);
118         if (nr == 0)
119                 return;
120
121         slot = rand() % nr;
122         bad_slot = rand() % nr;
123
124         if (bad_slot == slot)
125                 return;
126
127         fprintf(stderr,
128                 "corrupting keys in block %llu slot %d swapping with %d\n",
129                 (unsigned long long)eb->start, slot, bad_slot);
130
131         if (btrfs_header_level(eb) == 0) {
132                 btrfs_item_key(eb, &bad_key, bad_slot);
133                 btrfs_set_item_key(eb, &bad_key, slot);
134         } else {
135                 btrfs_node_key(eb, &bad_key, bad_slot);
136                 btrfs_set_node_key(eb, &bad_key, slot);
137         }
138         btrfs_mark_buffer_dirty(eb);
139         if (!trans) {
140                 csum_tree_block(root, eb, 0);
141                 write_extent_to_disk(eb);
142         }
143 }
144
145
146 static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
147 {
148         struct extent_buffer *eb;
149
150         eb = read_tree_block(root, bytenr, root->leafsize, 0);
151         if (!eb)
152                 return -EIO;;
153
154         corrupt_keys(NULL, root, eb);
155         free_extent_buffer(eb);
156         return 0;
157 }
158
159 static int corrupt_extent(struct btrfs_trans_handle *trans,
160                           struct btrfs_root *root, u64 bytenr, int copy)
161 {
162         struct btrfs_key key;
163         struct extent_buffer *leaf;
164         u32 item_size;
165         unsigned long ptr;
166         struct btrfs_path *path;
167         int ret;
168         int slot;
169         int should_del = rand() % 3;
170
171         path = btrfs_alloc_path();
172         if (!path)
173                 return -ENOMEM;
174
175         key.objectid = bytenr;
176         key.type = (u8)-1;
177         key.offset = (u64)-1;
178
179         while(1) {
180                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
181                                         &key, path, -1, 1);
182                 if (ret < 0)
183                         break;
184
185                 if (ret > 0) {
186                         if (path->slots[0] == 0)
187                                 break;
188                         path->slots[0]--;
189                         ret = 0;
190                 }
191                 leaf = path->nodes[0];
192                 slot = path->slots[0];
193                 btrfs_item_key_to_cpu(leaf, &key, slot);
194                 if (key.objectid != bytenr)
195                         break;
196
197                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
198                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
199                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
200                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
201                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
202                     key.type != BTRFS_SHARED_DATA_REF_KEY)
203                         goto next;
204
205                 if (should_del) {
206                         fprintf(stderr,
207                                 "deleting extent record: key %llu %u %llu\n",
208                                 key.objectid, key.type, key.offset);
209
210                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
211                                 /* make sure this extent doesn't get
212                                  * reused for other purposes */
213                                 btrfs_pin_extent(root->fs_info,
214                                                  key.objectid, key.offset);
215                         }
216
217                         btrfs_del_item(trans, root, path);
218                 } else {
219                         fprintf(stderr,
220                                 "corrupting extent record: key %llu %u %llu\n",
221                                 key.objectid, key.type, key.offset);
222                         ptr = btrfs_item_ptr_offset(leaf, slot);
223                         item_size = btrfs_item_size_nr(leaf, slot);
224                         memset_extent_buffer(leaf, 0, ptr, item_size);
225                         btrfs_mark_buffer_dirty(leaf);
226                 }
227 next:
228                 btrfs_release_path(path);
229
230                 if (key.offset > 0)
231                         key.offset--;
232                 if (key.offset == 0)
233                         break;
234         }
235
236         btrfs_free_path(path);
237         return 0;
238 }
239
240 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
241                                       struct btrfs_root *root,
242                                       struct extent_buffer *eb)
243 {
244         u32 nr = btrfs_header_nritems(eb);
245         u32 victim = rand() % nr;
246         u64 objectid;
247         struct btrfs_key key;
248
249         btrfs_item_key_to_cpu(eb, &key, victim);
250         objectid = key.objectid;
251         corrupt_extent(trans, root, objectid, 1);
252 }
253
254 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
255                                       struct btrfs_root *root,
256                                       struct extent_buffer *eb)
257 {
258         int i;
259         u32 nr;
260
261         if (!eb)
262                 return;
263
264         nr = btrfs_header_nritems(eb);
265         if (btrfs_is_leaf(eb)) {
266                 btrfs_corrupt_extent_leaf(trans, root, eb);
267                 return;
268         }
269
270         if (btrfs_header_level(eb) == 1 && eb != root->node) {
271                 if (rand() % 5)
272                         return;
273         }
274
275         for (i = 0; i < nr; i++) {
276                 struct extent_buffer *next;
277
278                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
279                                        root->leafsize,
280                                        btrfs_node_ptr_generation(eb, i));
281                 if (!next)
282                         continue;
283                 btrfs_corrupt_extent_tree(trans, root, next);
284                 free_extent_buffer(next);
285         }
286 }
287
288 enum btrfs_inode_field {
289         BTRFS_INODE_FIELD_ISIZE,
290         BTRFS_INODE_FIELD_BAD,
291 };
292
293 static enum btrfs_inode_field convert_field(char *field)
294 {
295         if (!strncmp(field, "isize", FIELD_BUF_LEN))
296                 return BTRFS_INODE_FIELD_ISIZE;
297         return BTRFS_INODE_FIELD_BAD;
298 }
299
300 static int corrupt_inode(struct btrfs_trans_handle *trans,
301                          struct btrfs_root *root, u64 inode, char *field)
302 {
303         struct btrfs_inode_item *ei;
304         struct btrfs_path *path;
305         struct btrfs_key key;
306         enum btrfs_inode_field corrupt_field = convert_field(field);
307         u64 bogus;
308         u64 orig;
309         int ret;
310
311         if (corrupt_field == BTRFS_INODE_FIELD_BAD) {
312                 fprintf(stderr, "Invalid field %s\n", field);
313                 return -EINVAL;
314         }
315
316         key.objectid = inode;
317         key.type = BTRFS_INODE_ITEM_KEY;
318         key.offset = (u64)-1;
319
320         path = btrfs_alloc_path();
321         if (!path)
322                 return -ENOMEM;
323
324         ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
325         if (ret < 0)
326                 goto out;
327         if (ret) {
328                 if (!path->slots[0]) {
329                         fprintf(stderr, "Couldn't find inode %Lu\n", inode);
330                         ret = -ENOENT;
331                         goto out;
332                 }
333                 path->slots[0]--;
334                 ret = 0;
335         }
336
337         btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
338         if (key.objectid != inode) {
339                 fprintf(stderr, "Couldn't find inode %Lu\n", inode);
340                 ret = -ENOENT;
341                 goto out;
342         }
343
344         ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
345                             struct btrfs_inode_item);
346         switch (corrupt_field) {
347         case BTRFS_INODE_FIELD_ISIZE:
348                 orig = btrfs_inode_size(path->nodes[0], ei);
349                 do {
350                         bogus = rand();
351                 } while (bogus == orig);
352
353                 btrfs_set_inode_size(path->nodes[0], ei, bogus);
354                 break;
355         default:
356                 ret = -EINVAL;
357                 break;
358         }
359         btrfs_mark_buffer_dirty(path->nodes[0]);
360 out:
361         btrfs_free_path(path);
362         return ret;
363 }
364
365 static struct option long_options[] = {
366         /* { "byte-count", 1, NULL, 'b' }, */
367         { "logical", 1, NULL, 'l' },
368         { "copy", 1, NULL, 'c' },
369         { "bytes", 1, NULL, 'b' },
370         { "extent-record", 0, NULL, 'e' },
371         { "extent-tree", 0, NULL, 'E' },
372         { "keys", 0, NULL, 'k' },
373         { "chunk-record", 0, NULL, 'u' },
374         { "chunk-tree", 0, NULL, 'U' },
375         { "inode", 1, NULL, 'i'},
376         { "field", 1, NULL, 'f'},
377         { 0, 0, 0, 0}
378 };
379
380 /* corrupt item using NO cow.
381  * Because chunk recover will recover based on whole partition scaning,
382  * If using COW, chunk recover will use the old item to recover,
383  * which is still OK but we want to check the ability to rebuild chunk
384  * not only restore the old ones */
385 int corrupt_item_nocow(struct btrfs_trans_handle *trans,
386                        struct btrfs_root *root, struct btrfs_path *path,
387                        int del)
388 {
389         int ret = 0;
390         struct btrfs_key key;
391         struct extent_buffer *leaf;
392         unsigned long ptr;
393         int slot;
394         u32 item_size;
395
396         leaf = path->nodes[0];
397         slot = path->slots[0];
398         /* Not deleting the first item of a leaf to keep leaf structure */
399         if (slot == 0)
400                 del = 0;
401         /* Only accept valid eb */
402         BUG_ON(!leaf->data || slot >= btrfs_header_nritems(leaf));
403         btrfs_item_key_to_cpu(leaf, &key, slot);
404         if (del) {
405                 fprintf(stdout, "Deleting key and data [%llu, %u, %llu].\n",
406                         key.objectid, key.type, key.offset);
407                 btrfs_del_item(trans, root, path);
408         } else {
409                 fprintf(stdout, "Corrupting key and data [%llu, %u, %llu].\n",
410                         key.objectid, key.type, key.offset);
411                 ptr = btrfs_item_ptr_offset(leaf, slot);
412                 item_size = btrfs_item_size_nr(leaf, slot);
413                 memset_extent_buffer(leaf, 0, ptr, item_size);
414                 btrfs_mark_buffer_dirty(leaf);
415         }
416         return ret;
417 }
418 int corrupt_chunk_tree(struct btrfs_trans_handle *trans,
419                        struct btrfs_root *root)
420 {
421         int ret;
422         int del;
423         int slot;
424         struct btrfs_path *path;
425         struct btrfs_key key;
426         struct btrfs_key found_key;
427         struct extent_buffer *leaf;
428
429         path = btrfs_alloc_path();
430         key.objectid = (u64)-1;
431         key.offset = (u64)-1;
432         key.type = (u8)-1;
433
434         /* Here, cow and ins_len must equals 0 for the following reasons:
435          * 1) chunk recover is based on disk scanning, so COW should be
436          *    disabled in case the original chunk being scanned and
437          *    recovered using the old chunk.
438          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
439          *    triggered.
440          */
441         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
442         BUG_ON(ret == 0);
443         if (ret < 0) {
444                 fprintf(stderr, "Error searching tree\n");
445                 goto free_out;
446         }
447         /* corrupt/del dev_item first */
448         while (!btrfs_previous_item(root, path, 0, BTRFS_DEV_ITEM_KEY)) {
449                 slot = path->slots[0];
450                 leaf = path->nodes[0];
451                 del = rand() % 3;
452                 /* Never delete the first item to keep the leaf structure */
453                 if (path->slots[0] == 0)
454                         del = 0;
455                 ret = corrupt_item_nocow(trans, root, path, del);
456                 if (ret)
457                         goto free_out;
458         }
459         btrfs_free_path(path);
460
461         /* Here, cow and ins_len must equals 0 for the following reasons:
462          * 1) chunk recover is based on disk scanning, so COW should be
463          *    disabled in case the original chunk being scanned and
464          *    recovered using the old chunk.
465          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON will be
466          *    triggered.
467          */
468         path = btrfs_alloc_path();
469         ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
470         BUG_ON(ret == 0);
471         if (ret < 0) {
472                 fprintf(stderr, "Error searching tree\n");
473                 goto free_out;
474         }
475         /* corrupt/del chunk then*/
476         while (!btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY)) {
477                 slot = path->slots[0];
478                 leaf = path->nodes[0];
479                 del = rand() % 3;
480                 btrfs_item_key_to_cpu(leaf, &found_key, slot);
481                 ret = corrupt_item_nocow(trans, root, path, del);
482                 if (ret)
483                         goto free_out;
484         }
485 free_out:
486         btrfs_free_path(path);
487         return ret;
488 }
489 int find_chunk_offset(struct btrfs_root *root,
490                       struct btrfs_path *path, u64 offset)
491 {
492         struct btrfs_key key;
493         int ret;
494
495         key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
496         key.type = BTRFS_CHUNK_ITEM_KEY;
497         key.offset = offset;
498
499         /* Here, cow and ins_len must equals 0 for following reasons:
500          * 1) chunk recover is based on disk scanning, so COW should
501          *    be disabled in case the original chunk being scanned
502          *    and recovered using the old chunk.
503          * 2) if cow = 0, ins_len must also be set to 0, or BUG_ON
504          *    will be triggered.
505          */
506         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
507         if (ret > 0) {
508                 fprintf(stderr, "Can't find chunk with given offset %llu\n",
509                         offset);
510                 goto out;
511         }
512         if (ret < 0) {
513                 fprintf(stderr, "Error searching chunk");
514                 goto out;
515         }
516 out:
517         return ret;
518
519 }
520 int main(int ac, char **av)
521 {
522         struct cache_tree root_cache;
523         struct btrfs_root *root;
524         struct extent_buffer *eb;
525         char *dev;
526         /* chunk offset can be 0,so change to (u64)-1 */
527         u64 logical = (u64)-1;
528         int ret = 0;
529         int option_index = 0;
530         int copy = 0;
531         u64 bytes = 4096;
532         int extent_rec = 0;
533         int extent_tree = 0;
534         int corrupt_block_keys = 0;
535         int chunk_rec = 0;
536         int chunk_tree = 0;
537         u64 inode = 0;
538         char field[FIELD_BUF_LEN];
539
540         field[0] = '\0';
541         srand(128);
542
543         while(1) {
544                 int c;
545                 c = getopt_long(ac, av, "l:c:b:eEkuUi:f:", long_options,
546                                 &option_index);
547                 if (c < 0)
548                         break;
549                 switch(c) {
550                         case 'l':
551                                 logical = atoll(optarg);
552                                 break;
553                         case 'c':
554                                 copy = atoi(optarg);
555                                 if (copy <= 0) {
556                                         fprintf(stderr,
557                                                 "invalid copy number\n");
558                                         print_usage();
559                                 }
560                                 break;
561                         case 'b':
562                                 bytes = atoll(optarg);
563                                 if (bytes == 0) {
564                                         fprintf(stderr,
565                                                 "invalid byte count\n");
566                                         print_usage();
567                                 }
568                                 break;
569                         case 'e':
570                                 extent_rec = 1;
571                                 break;
572                         case 'E':
573                                 extent_tree = 1;
574                                 break;
575                         case 'k':
576                                 corrupt_block_keys = 1;
577                                 break;
578                         case 'u':
579                                 chunk_rec = 1;
580                                 break;
581                         case 'U':
582                                 chunk_tree = 1;
583                         case 'i':
584                                 inode = atoll(optarg);
585                                 if (inode == 0) {
586                                         fprintf(stderr,
587                                                 "invalid inode number\n");
588                                         print_usage();
589                                 }
590                                 break;
591                         case 'f':
592                                 strncpy(field, optarg, FIELD_BUF_LEN);
593                                 break;
594                         default:
595                                 print_usage();
596                 }
597         }
598         ac = ac - optind;
599         if (ac == 0)
600                 print_usage();
601         dev = av[optind];
602
603         radix_tree_init();
604         cache_tree_init(&root_cache);
605
606         root = open_ctree(dev, 0, 1);
607         if (!root) {
608                 fprintf(stderr, "Open ctree failed\n");
609                 exit(1);
610         }
611         if (extent_rec) {
612                 struct btrfs_trans_handle *trans;
613
614                 if (logical == (u64)-1)
615                         print_usage();
616                 trans = btrfs_start_transaction(root, 1);
617                 ret = corrupt_extent (trans, root, logical, 0);
618                 btrfs_commit_transaction(trans, root);
619                 goto out_close;
620         }
621         if (extent_tree) {
622                 struct btrfs_trans_handle *trans;
623                 trans = btrfs_start_transaction(root, 1);
624                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
625                                           root->fs_info->extent_root->node);
626                 btrfs_commit_transaction(trans, root);
627                 goto out_close;
628         }
629         if (chunk_rec) {
630                 struct btrfs_trans_handle *trans;
631                 struct btrfs_path *path;
632                 int del;
633
634                 if (logical == (u64)-1)
635                         print_usage();
636                 del = rand() % 3;
637                 path = btrfs_alloc_path();
638
639                 if (find_chunk_offset(root->fs_info->chunk_root, path,
640                                       logical) != 0) {
641                         btrfs_free_path(path);
642                         goto out_close;
643                 }
644                 trans = btrfs_start_transaction(root, 1);
645                 ret = corrupt_item_nocow(trans, root->fs_info->chunk_root,
646                                          path, del);
647                 if (ret < 0)
648                         fprintf(stderr, "Failed to corrupt chunk record\n");
649                 btrfs_commit_transaction(trans, root);
650                 goto out_close;
651         }
652         if (chunk_tree) {
653                 struct btrfs_trans_handle *trans;
654                 trans = btrfs_start_transaction(root, 1);
655                 ret = corrupt_chunk_tree(trans, root->fs_info->chunk_root);
656                 if (ret < 0)
657                         fprintf(stderr, "Failed to corrupt chunk tree\n");
658                 btrfs_commit_transaction(trans, root);
659                 goto out_close;
660         }
661         if (inode) {
662                 struct btrfs_trans_handle *trans;
663
664                 if (!strlen(field))
665                         print_usage();
666                 printf("corrupting inode\n");
667                 trans = btrfs_start_transaction(root, 1);
668                 ret = corrupt_inode(trans, root, inode, field);
669                 btrfs_commit_transaction(trans, root);
670                 goto out_close;
671         }
672
673         if (logical == (u64)-1)
674                 print_usage();
675
676         if (bytes == 0)
677                 bytes = root->sectorsize;
678
679         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
680         bytes *= root->sectorsize;
681
682         while (bytes > 0) {
683                 if (corrupt_block_keys) {
684                         corrupt_keys_in_block(root, logical);
685                 } else {
686                         eb = debug_corrupt_block(root, logical,
687                                                  root->sectorsize, copy);
688                         free_extent_buffer(eb);
689                 }
690                 logical += root->sectorsize;
691                 bytes -= root->sectorsize;
692         }
693         return ret;
694 out_close:
695         close_ctree(root);
696         return ret;
697 }