btrfs-corrupt-block: add -e option to corrupt the extent record
[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 struct option long_options[] = {
97         /* { "byte-count", 1, NULL, 'b' }, */
98         { "logical", 1, NULL, 'l' },
99         { "copy", 1, NULL, 'c' },
100         { "bytes", 1, NULL, 'b' },
101         { 0, 0, 0, 0}
102 };
103
104 static int corrupt_extent(struct btrfs_root *root, u64 bytenr, int copy)
105 {
106         struct btrfs_trans_handle *trans;
107         struct btrfs_key key;
108         struct extent_buffer *leaf;
109         u32 item_size;
110         unsigned long ptr;
111         struct btrfs_path *path;
112         int ret;
113         int slot;
114
115         trans = btrfs_start_transaction(root, 1);
116         path = btrfs_alloc_path();
117
118         key.objectid = bytenr;
119         key.type = (u8)-1;
120         key.offset = (u64)-1;
121
122         while(1) {
123                 ret = btrfs_search_slot(trans, root->fs_info->extent_root,
124                                         &key, path, 0, 1);
125                 if (ret < 0)
126                         break;
127
128                 if (ret > 0) {
129                         if (path->slots[0] == 0)
130                                 break;
131                         path->slots[0]--;
132                 }
133                 leaf = path->nodes[0];
134                 slot = path->slots[0];
135                 btrfs_item_key_to_cpu(leaf, &key, slot);
136                 if (key.objectid != bytenr)
137                         break;
138
139                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
140                     key.type != BTRFS_TREE_BLOCK_REF_KEY &&
141                     key.type != BTRFS_EXTENT_DATA_REF_KEY &&
142                     key.type != BTRFS_EXTENT_REF_V0_KEY &&
143                     key.type != BTRFS_SHARED_BLOCK_REF_KEY &&
144                     key.type != BTRFS_SHARED_DATA_REF_KEY)
145                         goto next;
146
147                 fprintf(stderr, "corrupting extent record: key %Lu %u %Lu\n",
148                         key.objectid, key.type, key.offset);
149
150                 ptr = btrfs_item_ptr_offset(leaf, slot);
151                 item_size = btrfs_item_size_nr(leaf, slot);
152                 memset_extent_buffer(leaf, 0, ptr, item_size);
153                 btrfs_mark_buffer_dirty(leaf);
154 next:
155                 btrfs_release_path(NULL, path);
156
157                 if (key.offset > 0)
158                         key.offset--;
159                 if (key.offset == 0)
160                         break;
161         }
162
163         btrfs_free_path(path);
164         btrfs_commit_transaction(trans, root);
165         ret = close_ctree(root);
166         BUG_ON(ret);
167         return 0;
168 }
169
170 int main(int ac, char **av)
171 {
172         struct cache_tree root_cache;
173         struct btrfs_root *root;
174         struct extent_buffer *eb;
175         char *dev;
176         u64 logical = 0;
177         int ret = 0;
178         int option_index = 0;
179         int copy = 0;
180         u64 bytes = 4096;
181         int extent_rec;
182
183         while(1) {
184                 int c;
185                 c = getopt_long(ac, av, "l:c:e", long_options,
186                                 &option_index);
187                 if (c < 0)
188                         break;
189                 switch(c) {
190                         case 'l':
191                                 logical = atoll(optarg);
192                                 if (logical == 0) {
193                                         fprintf(stderr,
194                                                 "invalid extent number\n");
195                                         print_usage();
196                                 }
197                                 break;
198                         case 'c':
199                                 copy = atoi(optarg);
200                                 if (copy == 0) {
201                                         fprintf(stderr,
202                                                 "invalid copy number\n");
203                                         print_usage();
204                                 }
205                                 break;
206                         case 'b':
207                                 bytes = atoll(optarg);
208                                 if (bytes == 0) {
209                                         fprintf(stderr,
210                                                 "invalid byte count\n");
211                                         print_usage();
212                                 }
213                                 break;
214                         case 'e':
215                                 extent_rec = 1;
216                                 break;
217                         default:
218                                 print_usage();
219                 }
220         }
221         ac = ac - optind;
222         if (ac == 0)
223                 print_usage();
224         if (logical == 0)
225                 print_usage();
226         if (copy < 0)
227                 print_usage();
228
229         dev = av[optind];
230
231         radix_tree_init();
232         cache_tree_init(&root_cache);
233
234         root = open_ctree(dev, 0, 1);
235         if (!root) {
236                 fprintf(stderr, "Open ctree failed\n");
237                 exit(1);
238         }
239         if (extent_rec) {
240                 ret = corrupt_extent (root, logical, 0);
241                 goto out;
242         }
243
244         if (bytes == 0)
245                 bytes = root->sectorsize;
246
247         bytes = (bytes + root->sectorsize - 1) / root->sectorsize;
248         bytes *= root->sectorsize;
249
250         while (bytes > 0) {
251                 eb = debug_corrupt_block(root, logical, root->sectorsize, copy);
252                 free_extent_buffer(eb);
253                 logical += root->sectorsize;
254                 bytes -= root->sectorsize;
255         }
256 out:
257         return ret;
258 }