Merge branch 'for-chris' of git://github.com/idryomov/btrfs-progs
[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 int corrupt_extent(struct btrfs_trans_handle *trans,
97                           struct btrfs_root *root, u64 bytenr, int copy)
98 {
99         struct btrfs_key key;
100         struct extent_buffer *leaf;
101         u32 item_size;
102         unsigned long ptr;
103         struct btrfs_path *path;
104         int ret;
105         int slot;
106         int should_del = rand() % 3;
107
108         path = btrfs_alloc_path();
109
110         key.objectid = bytenr;
111         key.type = (u8)-1;
112         key.offset = (u64)-1;
113
114         while(1) {
115                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
116                                         &key, path, -1, 1);
117                 if (ret < 0)
118                         break;
119
120                 if (ret > 0) {
121                         if (path->slots[0] == 0)
122                                 break;
123                         path->slots[0]--;
124                         ret = 0;
125                 }
126                 leaf = path->nodes[0];
127                 slot = path->slots[0];
128                 btrfs_item_key_to_cpu(leaf, &key, slot);
129                 if (key.objectid != bytenr)
130                         break;
131
132                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
133                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
134                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
135                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
136                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
137                     key.type != BTRFS_SHARED_DATA_REF_KEY)
138                         goto next;
139
140                 if (should_del) {
141                         fprintf(stderr, "deleting extent record: key %Lu %u %Lu\n",
142                                 key.objectid, key.type, key.offset);
143
144                         if (key.type == BTRFS_EXTENT_ITEM_KEY) {
145                                 /* make sure this extent doesn't get
146                                  * reused for other purposes */
147                                 btrfs_pin_extent(root->fs_info,
148                                                  key.objectid, key.offset);
149                         }
150
151                         btrfs_del_item(trans, root, path);
152                 } else {
153                         fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n",
154                                 key.objectid, key.type, key.offset);
155                         ptr = btrfs_item_ptr_offset(leaf, slot);
156                         item_size = btrfs_item_size_nr(leaf, slot);
157                         memset_extent_buffer(leaf, 0, ptr, item_size);
158                         btrfs_mark_buffer_dirty(leaf);
159                 }
160 next:
161                 btrfs_release_path(NULL, path);
162
163                 if (key.offset > 0)
164                         key.offset--;
165                 if (key.offset == 0)
166                         break;
167         }
168
169         btrfs_free_path(path);
170         return 0;
171 }
172
173 static void btrfs_corrupt_extent_leaf(struct btrfs_trans_handle *trans,
174                                       struct btrfs_root *root, struct extent_buffer *eb)
175 {
176         u32 nr = btrfs_header_nritems(eb);
177         u32 victim = rand() % nr;
178         u64 objectid;
179         struct btrfs_key key;
180
181         btrfs_item_key_to_cpu(eb, &key, victim);
182         objectid = key.objectid;
183         corrupt_extent(trans, root, objectid, 1);
184 }
185
186 static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
187                                       struct btrfs_root *root, struct extent_buffer *eb)
188 {
189         int i;
190         u32 nr;
191
192         if (!eb)
193                 return;
194
195         nr = btrfs_header_nritems(eb);
196         if (btrfs_is_leaf(eb)) {
197                 btrfs_corrupt_extent_leaf(trans, root, eb);
198                 return;
199         }
200
201         if (btrfs_header_level(eb) == 1 && eb != root->node) {
202                 if (rand() % 5)
203                         return;
204         }
205
206         for (i = 0; i < nr; i++) {
207                 struct extent_buffer *next;
208
209                 next = read_tree_block(root, btrfs_node_blockptr(eb, i),
210                                        root->leafsize, btrfs_node_ptr_generation(eb, i));
211                 if (!next)
212                         continue;
213                 btrfs_corrupt_extent_tree(trans, root, next);
214                 free_extent_buffer(next);
215         }
216 }
217
218 static struct option long_options[] = {
219         /* { "byte-count", 1, NULL, 'b' }, */
220         { "logical", 1, NULL, 'l' },
221         { "copy", 1, NULL, 'c' },
222         { "bytes", 1, NULL, 'b' },
223         { "extent-record", 0, NULL, 'e' },
224         { "extent-tree", 0, NULL, 'E' },
225         { 0, 0, 0, 0}
226 };
227
228
229 int main(int ac, char **av)
230 {
231         struct cache_tree root_cache;
232         struct btrfs_root *root;
233         struct extent_buffer *eb;
234         char *dev;
235         u64 logical = 0;
236         int ret = 0;
237         int option_index = 0;
238         int copy = 0;
239         u64 bytes = 4096;
240         int extent_rec = 0;
241         int extent_tree = 0;
242
243         srand(128);
244
245         while(1) {
246                 int c;
247                 c = getopt_long(ac, av, "l:c:eE", long_options,
248                                 &option_index);
249                 if (c < 0)
250                         break;
251                 switch(c) {
252                         case 'l':
253                                 logical = atoll(optarg);
254                                 if (logical == 0) {
255                                         fprintf(stderr,
256                                                 "invalid extent number\n");
257                                         print_usage();
258                                 }
259                                 break;
260                         case 'c':
261                                 copy = atoi(optarg);
262                                 if (copy == 0) {
263                                         fprintf(stderr,
264                                                 "invalid copy number\n");
265                                         print_usage();
266                                 }
267                                 break;
268                         case 'b':
269                                 bytes = atoll(optarg);
270                                 if (bytes == 0) {
271                                         fprintf(stderr,
272                                                 "invalid byte count\n");
273                                         print_usage();
274                                 }
275                                 break;
276                         case 'e':
277                                 extent_rec = 1;
278                                 break;
279                         case 'E':
280                                 extent_tree = 1;
281                                 break;
282                         default:
283                                 print_usage();
284                 }
285         }
286         ac = ac - optind;
287         if (ac == 0)
288                 print_usage();
289         if (logical == 0 && !extent_tree)
290                 print_usage();
291         if (copy < 0)
292                 print_usage();
293
294         dev = av[optind];
295
296         radix_tree_init();
297         cache_tree_init(&root_cache);
298
299         root = open_ctree(dev, 0, 1);
300         if (!root) {
301                 fprintf(stderr, "Open ctree failed\n");
302                 exit(1);
303         }
304         if (extent_rec) {
305                 struct btrfs_trans_handle *trans;
306                 trans = btrfs_start_transaction(root, 1);
307                 ret = corrupt_extent (trans, root, logical, 0);
308                 btrfs_commit_transaction(trans, root);
309                 goto out_close;
310         }
311         if (extent_tree) {
312                 struct btrfs_trans_handle *trans;
313                 trans = btrfs_start_transaction(root, 1);
314                 btrfs_corrupt_extent_tree(trans, root->fs_info->extent_root,
315                                           root->fs_info->extent_root->node);
316                 btrfs_commit_transaction(trans, root);
317                 goto out_close;
318         }
319
320         if (bytes == 0)
321                 bytes = root->sectorsize;
322
323         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
324         bytes *= root->sectorsize;
325
326         while (bytes > 0) {
327                 eb = debug_corrupt_block(root, logical, root->sectorsize, copy);
328                 free_extent_buffer(eb);
329                 logical += root->sectorsize;
330                 bytes -= root->sectorsize;
331         }
332         return ret;
333 out_close:
334         close_ctree(root);
335         return ret;
336 }