2 * Copyright (C) 2009 Oracle. All rights reserved.
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.
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.
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.
24 #include "kerncompat.h"
28 #include "print-tree.h"
29 #include "transaction.h"
33 #define BUFFER_SIZE (64 * 1024)
35 /* we write the mirror info to stdout unless they are dumping the data
38 static FILE *info_file
;
40 static int map_one_extent(struct btrfs_fs_info
*fs_info
,
41 u64
*logical_ret
, u64
*len_ret
, int search_foward
)
43 struct btrfs_path
*path
;
50 logical
= *logical_ret
;
52 path
= btrfs_alloc_path();
56 key
.objectid
= logical
;
60 ret
= btrfs_search_slot(NULL
, fs_info
->extent_root
, &key
, path
,
68 btrfs_item_key_to_cpu(path
->nodes
[0], &key
, path
->slots
[0]);
69 if ((search_foward
&& key
.objectid
< logical
) ||
70 (!search_foward
&& key
.objectid
> logical
) ||
71 (key
.type
!= BTRFS_EXTENT_ITEM_KEY
&&
72 key
.type
!= BTRFS_METADATA_ITEM_KEY
)) {
74 ret
= btrfs_previous_extent_item(fs_info
->extent_root
,
77 ret
= btrfs_next_item(fs_info
->extent_root
, path
);
82 logical
= key
.objectid
;
83 if (key
.type
== BTRFS_METADATA_ITEM_KEY
)
84 len
= fs_info
->tree_root
->leafsize
;
89 btrfs_free_path(path
);
91 *logical_ret
= logical
;
98 static int __print_mapping_info(struct btrfs_fs_info
*fs_info
, u64 logical
,
99 u64 len
, int mirror_num
)
101 struct btrfs_multi_bio
*multi
= NULL
;
106 while (cur_offset
< len
) {
107 struct btrfs_device
*device
;
110 cur_len
= len
- cur_offset
;
111 ret
= btrfs_map_block(&fs_info
->mapping_tree
, READ
,
112 logical
+ cur_offset
, &cur_len
,
113 &multi
, mirror_num
, NULL
);
116 "Error: fails to map mirror%d logical %llu: %s\n",
117 mirror_num
, logical
, strerror(-ret
));
120 for (i
= 0; i
< multi
->num_stripes
; i
++) {
121 device
= multi
->stripes
[i
].dev
;
123 "mirror %d logical %Lu physical %Lu device %s\n",
124 mirror_num
, logical
+ cur_offset
,
125 multi
->stripes
[0].physical
,
130 cur_offset
+= cur_len
;
136 * Logical and len is the exact value of a extent.
137 * And offset is the offset inside the extent. It's only used for case
138 * where user only want to print part of the extent.
140 * Caller *MUST* ensure the range [logical,logical+len) are in one extent.
141 * Or we can encounter the following case, causing a -ENOENT error:
142 * |<-----given parameter------>|
143 * |<------ Extent A ----->|
145 static int print_mapping_info(struct btrfs_fs_info
*fs_info
, u64 logical
,
152 num_copies
= btrfs_num_copies(&fs_info
->mapping_tree
, logical
, len
);
153 for (mirror_num
= 1; mirror_num
<= num_copies
; mirror_num
++) {
154 ret
= __print_mapping_info(fs_info
, logical
, len
, mirror_num
);
161 /* Same requisition as print_mapping_info function */
162 static int write_extent_content(struct btrfs_fs_info
*fs_info
, int out_fd
,
163 u64 logical
, u64 length
, int mirror
)
165 char buffer
[BUFFER_SIZE
];
170 while (cur_offset
< length
) {
171 cur_len
= min_t(u64
, length
- cur_offset
, BUFFER_SIZE
);
172 ret
= read_extent_data(fs_info
->tree_root
, buffer
,
173 logical
+ cur_offset
, &cur_len
, mirror
);
176 "Failed to read extent at [%llu, %llu]: %s\n",
177 logical
, logical
+ length
, strerror(-ret
));
180 ret
= write(out_fd
, buffer
, cur_len
);
181 if (ret
< 0 || ret
!= cur_len
) {
184 fprintf(stderr
, "output file write failed: %s\n",
188 cur_offset
+= cur_len
;
193 static void print_usage(void) __attribute__((noreturn
));
194 static void print_usage(void)
196 fprintf(stderr
, "usage: btrfs-map-logical [options] device\n");
197 fprintf(stderr
, "\t-l Logical extent to map\n");
198 fprintf(stderr
, "\t-c Copy of the extent to read (usually 1 or 2)\n");
199 fprintf(stderr
, "\t-o Output file to hold the extent\n");
200 fprintf(stderr
, "\t-b Number of bytes to read\n");
204 int main(int argc
, char **argv
)
206 struct cache_tree root_cache
;
207 struct btrfs_root
*root
;
209 char *output_file
= NULL
;
221 static const struct option long_options
[] = {
222 /* { "byte-count", 1, NULL, 'b' }, */
223 { "logical", required_argument
, NULL
, 'l' },
224 { "copy", required_argument
, NULL
, 'c' },
225 { "output", required_argument
, NULL
, 'o' },
226 { "bytes", required_argument
, NULL
, 'b' },
230 c
= getopt_long(argc
, argv
, "l:c:o:b:", long_options
, NULL
);
235 logical
= arg_strtou64(optarg
);
238 copy
= arg_strtou64(optarg
);
241 bytes
= arg_strtou64(optarg
);
244 output_file
= strdup(optarg
);
251 if (check_argc_min(argc
- optind
, 1))
259 cache_tree_init(&root_cache
);
261 root
= open_ctree(dev
, 0, 0);
263 fprintf(stderr
, "Open ctree failed\n");
270 if (strcmp(output_file
, "-") == 0) {
274 out_fd
= open(output_file
, O_RDWR
| O_CREAT
, 0600);
277 ret
= ftruncate(out_fd
, 0);
288 bytes
= root
->nodesize
;
289 cur_logical
= logical
;
292 /* First find the nearest extent */
293 ret
= map_one_extent(root
->fs_info
, &cur_logical
, &cur_len
, 0);
295 fprintf(stderr
, "Failed to find extent at [%llu,%llu): %s\n",
296 cur_logical
, cur_logical
+ cur_len
, strerror(-ret
));
300 * Normally, search backward should be OK, but for special case like
301 * given logical is quite small where no extents are before it,
302 * we need to search forward.
305 ret
= map_one_extent(root
->fs_info
, &cur_logical
, &cur_len
, 1);
308 "Failed to find extent at [%llu,%llu): %s\n",
309 cur_logical
, cur_logical
+ cur_len
,
315 "Failed to find any extent at [%llu,%llu)\n",
316 cur_logical
, cur_logical
+ cur_len
);
321 while (cur_logical
+ cur_len
>= logical
&& cur_logical
< logical
+
327 ret
= map_one_extent(root
->fs_info
, &cur_logical
, &cur_len
, 1);
332 real_logical
= max(logical
, cur_logical
);
333 real_len
= min(logical
+ bytes
, cur_logical
+ cur_len
) -
336 ret
= print_mapping_info(root
->fs_info
, real_logical
, real_len
);
339 if (output_file
&& out_fd
!= -1) {
340 ret
= write_extent_content(root
->fs_info
, out_fd
,
341 real_logical
, real_len
, copy
);
346 cur_logical
+= cur_len
;
350 fprintf(stderr
, "No extent found at range [%llu,%llu)\n",
351 logical
, logical
+ bytes
);
354 if (output_file
&& out_fd
!= 1)
361 btrfs_close_all_devices();