btrfsck: add early code to handle corrupted block groups
[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, mirror_num);
54                 BUG_ON(ret);
55                 device = multi->stripes[0].dev;
56                 eb->fd = device->fd;
57                 device->total_ios++;
58                 eb->dev_bytenr = multi->stripes[0].physical;
59
60                 fprintf(stdout, "mirror %d logical %Lu physical %Lu "
61                         "device %s\n", mirror_num, (unsigned long long)bytenr,
62                         (unsigned long long)eb->dev_bytenr, device->name);
63                 kfree(multi);
64
65                 if (!copy || mirror_num == copy) {
66                         ret = read_extent_from_disk(eb);
67                         printf("corrupting %llu copy %d\n", eb->start,
68                                mirror_num);
69                         memset(eb->data, 0, eb->len);
70                         write_extent_to_disk(eb);
71                         fsync(eb->fd);
72                 }
73
74                 num_copies = btrfs_num_copies(&root->fs_info->mapping_tree,
75                                               eb->start, eb->len);
76                 if (num_copies == 1)
77                         break;
78
79                 mirror_num++;
80                 if (mirror_num > num_copies)
81                         break;
82         }
83         return eb;
84 }
85
86 static void print_usage(void)
87 {
88         fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n");
89         fprintf(stderr, "\t-l Logical extent to map\n");
90         fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n");
91         fprintf(stderr, "\t-o Output file to hold the extent\n");
92         fprintf(stderr, "\t-b Number of bytes to read\n");
93         exit(1);
94 }
95
96 static void corrupt_keys(struct btrfs_trans_handle *trans,
97                          struct btrfs_root *root,
98                          struct extent_buffer *eb)
99 {
100         int slot;
101         int bad_slot;
102         int nr;
103         struct btrfs_disk_key bad_key;;
104
105         nr = btrfs_header_nritems(eb);
106         if (nr == 0)
107                 return;
108
109         slot = rand() % nr;
110         bad_slot = rand() % nr;
111
112         if (bad_slot == slot)
113                 return;
114
115         fprintf(stderr, "corrupting keys in block %llu slot %d swapping with %d\n",
116                 (unsigned long long)eb->start, slot, bad_slot);
117
118         if (btrfs_header_level(eb) == 0) {
119                 btrfs_item_key(eb, &bad_key, bad_slot);
120                 btrfs_set_item_key(eb, &bad_key, slot);
121         } else {
122                 btrfs_node_key(eb, &bad_key, bad_slot);
123                 btrfs_set_node_key(eb, &bad_key, slot);
124         }
125         btrfs_mark_buffer_dirty(eb);
126         if (!trans) {
127                 csum_tree_block(root, eb, 0);
128                 write_extent_to_disk(eb);
129         }
130 }
131
132
133 static int corrupt_keys_in_block(struct btrfs_root *root, u64 bytenr)
134 {
135         struct extent_buffer *eb;
136
137         eb = read_tree_block(root, bytenr, root->leafsize, 0);
138         if (!eb)
139                 return -EIO;;
140
141         corrupt_keys(NULL, root, eb);
142         free_extent_buffer(eb);
143         return 0;
144 }
145
146 static int corrupt_extent(struct btrfs_trans_handle *trans,
147                           struct btrfs_root *root, u64 bytenr, int copy)
148 {
149         struct btrfs_key key;
150         struct extent_buffer *leaf;
151         u32 item_size;
152         unsigned long ptr;
153         struct btrfs_path *path;
154         int ret;
155         int slot;
156         int should_del = rand() % 3;
157
158         path = btrfs_alloc_path();
159
160         key.objectid = bytenr;
161         key.type = (u8)-1;
162         key.offset = (u64)-1;
163
164         while(1) {
165                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
166                                         &key, path, -1, 1);
167                 if (ret < 0)
168                         break;
169
170                 if (ret > 0) {
171                         if (path->slots[0] == 0)
172                                 break;
173                         path->slots[0]--;
174                         ret = 0;
175                 }
176                 leaf = path->nodes[0];
177                 slot = path->slots[0];
178                 btrfs_item_key_to_cpu(leaf, &key, slot);
179                 if (key.objectid != bytenr)
180                         break;
181
182                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
183                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
184                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
185                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
186                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
187                     key.type != BTRFS_SHARED_DATA_REF_KEY)
188                         goto next;
189
190                 if (should_del) {
191                         fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n",
192                                 key.objectid, key.type, key.offset);
193
194                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
195                                 /* make sure this extent doesn't get
196                                  * reused for other purposes */
197                                 btrfs_pin_extent(root->fs_info,
198                                                  key.objectid, key.offset);
199                         }
200
201                         btrfs_del_item(trans, root, path);
202                 } else {
203                         fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n",
204                                 key.objectid, key.type, key.offset);
205                         ptr = btrfs_item_ptr_offset(leaf, slot);
206                         item_size = btrfs_item_size_nr(leaf, slot);
207                         memset_extent_buffer(leaf, 0, ptr, item_size);
208                         btrfs_mark_buffer_dirty(leaf);
209                 }
210 next:
211                 btrfs_release_path(NULL, path);
212
213                 if (key.offset > 0)
214                         key.offset--;
215                 if (key.offset == 0)
216                         break;
217         }
218
219         btrfs_free_path(path);
220         return 0;
221 }
222
223 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
224                                       struct btrfs_root *root, struct extent_buffer *eb)
225 {
226         u32 nr = btrfs_header_nritems(eb);
227         u32 victim = rand() % nr;
228         u64 objectid;
229         struct btrfs_key key;
230
231         btrfs_item_key_to_cpu(eb, &key, victim);
232         objectid = key.objectid;
233         corrupt_extent(trans, root, objectid, 1);
234 }
235
236 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
237                                       struct btrfs_root *root, struct extent_buffer *eb)
238 {
239         int i;
240         u32 nr;
241
242         if (!eb)
243                 return;
244
245         nr = btrfs_header_nritems(eb);
246         if (btrfs_is_leaf(eb)) {
247                 btrfs_corrupt_extent_leaf(trans, root, eb);
248                 return;
249         }
250
251         if (btrfs_header_level(eb) == 1 && eb != root->node) {
252                 if (rand() % 5)
253                         return;
254         }
255
256         for (i = 0; i < nr; i++) {
257                 struct extent_buffer *next;
258
259                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
260                                        root->leafsize, btrfs_node_ptr_generation(eb, i));
261                 if (!next)
262                         continue;
263                 btrfs_corrupt_extent_tree(trans, root, next);
264                 free_extent_buffer(next);
265         }
266 }
267
268 static struct option long_options[] = {
269         /* { "byte-count", 1, NULL, 'b' }, */
270         { "logical", 1, NULL, 'l' },
271         { "copy", 1, NULL, 'c' },
272         { "bytes", 1, NULL, 'b' },
273         { "extent-record", 0, NULL, 'e' },
274         { "extent-tree", 0, NULL, 'E' },
275         { "keys", 0, NULL, 'k' },
276         { 0, 0, 0, 0}
277 };
278
279
280 int main(int ac, char **av)
281 {
282         struct cache_tree root_cache;
283         struct btrfs_root *root;
284         struct extent_buffer *eb;
285         char *dev;
286         u64 logical = 0;
287         int ret = 0;
288         int option_index = 0;
289         int copy = 0;
290         u64 bytes = 4096;
291         int extent_rec = 0;
292         int extent_tree = 0;
293         int corrupt_block_keys = 0;
294
295         srand(128);
296
297         while(1) {
298                 int c;
299                 c = getopt_long(ac, av, "l:c:eEk", long_options,
300                                 &option_index);
301                 if (c < 0)
302                         break;
303                 switch(c) {
304                         case 'l':
305                                 logical = atoll(optarg);
306                                 if (logical == 0) {
307                                         fprintf(stderr,
308                                                 "invalid extent number\n");
309                                         print_usage();
310                                 }
311                                 break;
312                         case 'c':
313                                 copy = atoi(optarg);
314                                 if (copy == 0) {
315                                         fprintf(stderr,
316                                                 "invalid copy number\n");
317                                         print_usage();
318                                 }
319                                 break;
320                         case 'b':
321                                 bytes = atoll(optarg);
322                                 if (bytes == 0) {
323                                         fprintf(stderr,
324                                                 "invalid byte count\n");
325                                         print_usage();
326                                 }
327                                 break;
328                         case 'e':
329                                 extent_rec = 1;
330                                 break;
331                         case 'E':
332                                 extent_tree = 1;
333                                 break;
334                         case 'k':
335                                 corrupt_block_keys = 1;
336                                 break;
337                         default:
338                                 print_usage();
339                 }
340         }
341         ac = ac - optind;
342         if (ac == 0)
343                 print_usage();
344         if (logical == 0 && !extent_tree)
345                 print_usage();
346         if (copy < 0)
347                 print_usage();
348
349         dev = av[optind];
350
351         radix_tree_init();
352         cache_tree_init(&root_cache);
353
354         root = open_ctree(dev, 0, 1);
355         if (!root) {
356                 fprintf(stderr, "Open ctree failed\n");
357                 exit(1);
358         }
359         if (extent_rec) {
360                 struct btrfs_trans_handle *trans;
361                 trans = btrfs_start_transaction(root, 1);
362                 ret = corrupt_extent (trans, root, logical, 0);
363                 btrfs_commit_transaction(trans, root);
364                 goto out_close;
365         }
366         if (extent_tree) {
367                 struct btrfs_trans_handle *trans;
368                 trans = btrfs_start_transaction(root, 1);
369                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
370                                           root->fs_info->extent_root->node);
371                 btrfs_commit_transaction(trans, root);
372                 goto out_close;
373         }
374
375         if (bytes == 0)
376                 bytes = root->sectorsize;
377
378         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
379         bytes *= root->sectorsize;
380
381         while (bytes > 0) {
382                 if (corrupt_block_keys) {
383                         corrupt_keys_in_block(root, logical);
384                 } else {
385                         eb = debug_corrupt_block(root, logical,
386                                                  root->sectorsize, copy);
387                         free_extent_buffer(eb);
388                 }
389                 logical += root->sectorsize;
390                 bytes -= root->sectorsize;
391         }
392         return ret;
393 out_close:
394         close_ctree(root);
395         return ret;
396 }