Btrfsck: add the ability to prune corrupt extent allocation tree blocks
[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         if ((rand() % 10) == 0) {
246                 corrupt_keys(trans, root, eb);
247                 return;
248         }
249
250         nr = btrfs_header_nritems(eb);
251         if (btrfs_is_leaf(eb)) {
252                 btrfs_corrupt_extent_leaf(trans, root, eb);
253                 return;
254         }
255
256         if (btrfs_header_level(eb) == 1 && eb != root->node) {
257                 if (rand() % 5)
258                         return;
259         }
260
261         for (i = 0; i < nr; i++) {
262                 struct extent_buffer *next;
263
264                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
265                                        root->leafsize, btrfs_node_ptr_generation(eb, i));
266                 if (!next)
267                         continue;
268                 btrfs_corrupt_extent_tree(trans, root, next);
269                 free_extent_buffer(next);
270         }
271 }
272
273 static struct option long_options[] = {
274         /* { "byte-count", 1, NULL, 'b' }, */
275         { "logical", 1, NULL, 'l' },
276         { "copy", 1, NULL, 'c' },
277         { "bytes", 1, NULL, 'b' },
278         { "extent-record", 0, NULL, 'e' },
279         { "extent-tree", 0, NULL, 'E' },
280         { "keys", 0, NULL, 'k' },
281         { 0, 0, 0, 0}
282 };
283
284
285 int main(int ac, char **av)
286 {
287         struct cache_tree root_cache;
288         struct btrfs_root *root;
289         struct extent_buffer *eb;
290         char *dev;
291         u64 logical = 0;
292         int ret = 0;
293         int option_index = 0;
294         int copy = 0;
295         u64 bytes = 4096;
296         int extent_rec = 0;
297         int extent_tree = 0;
298         int corrupt_block_keys = 0;
299
300         srand(128);
301
302         while(1) {
303                 int c;
304                 c = getopt_long(ac, av, "l:c:eEk", long_options,
305                                 &option_index);
306                 if (c < 0)
307                         break;
308                 switch(c) {
309                         case 'l':
310                                 logical = atoll(optarg);
311                                 if (logical == 0) {
312                                         fprintf(stderr,
313                                                 "invalid extent number\n");
314                                         print_usage();
315                                 }
316                                 break;
317                         case 'c':
318                                 copy = atoi(optarg);
319                                 if (copy == 0) {
320                                         fprintf(stderr,
321                                                 "invalid copy number\n");
322                                         print_usage();
323                                 }
324                                 break;
325                         case 'b':
326                                 bytes = atoll(optarg);
327                                 if (bytes == 0) {
328                                         fprintf(stderr,
329                                                 "invalid byte count\n");
330                                         print_usage();
331                                 }
332                                 break;
333                         case 'e':
334                                 extent_rec = 1;
335                                 break;
336                         case 'E':
337                                 extent_tree = 1;
338                                 break;
339                         case 'k':
340                                 corrupt_block_keys = 1;
341                                 break;
342                         default:
343                                 print_usage();
344                 }
345         }
346         ac = ac - optind;
347         if (ac == 0)
348                 print_usage();
349         if (logical == 0 && !extent_tree)
350                 print_usage();
351         if (copy < 0)
352                 print_usage();
353
354         dev = av[optind];
355
356         radix_tree_init();
357         cache_tree_init(&root_cache);
358
359         root = open_ctree(dev, 0, 1);
360         if (!root) {
361                 fprintf(stderr, "Open ctree failed\n");
362                 exit(1);
363         }
364         if (extent_rec) {
365                 struct btrfs_trans_handle *trans;
366                 trans = btrfs_start_transaction(root, 1);
367                 ret = corrupt_extent (trans, root, logical, 0);
368                 btrfs_commit_transaction(trans, root);
369                 goto out_close;
370         }
371         if (extent_tree) {
372                 struct btrfs_trans_handle *trans;
373                 trans = btrfs_start_transaction(root, 1);
374                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
375                                           root->fs_info->extent_root->node);
376                 btrfs_commit_transaction(trans, root);
377                 goto out_close;
378         }
379
380         if (bytes == 0)
381                 bytes = root->sectorsize;
382
383         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
384         bytes *= root->sectorsize;
385
386         while (bytes > 0) {
387                 if (corrupt_block_keys) {
388                         corrupt_keys_in_block(root, logical);
389                 } else {
390                         eb = debug_corrupt_block(root, logical,
391                                                  root->sectorsize, copy);
392                         free_extent_buffer(eb);
393                 }
394                 logical += root->sectorsize;
395                 bytes -= root->sectorsize;
396         }
397         return ret;
398 out_close:
399         close_ctree(root);
400         return ret;
401 }