| /* |
| * Copyright (C) 2009 Oracle. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public |
| * License v2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public |
| * License along with this program; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 021110-1307, USA. |
| */ |
| |
| #define _XOPEN_SOURCE 500 |
| #define _GNU_SOURCE 1 |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <getopt.h> |
| #include "kerncompat.h" |
| #include "ctree.h" |
| #include "volumes.h" |
| #include "disk-io.h" |
| #include "print-tree.h" |
| #include "transaction.h" |
| #include "list.h" |
| #include "version.h" |
| |
| /* we write the mirror info to stdout unless they are dumping the data |
| * to stdout |
| * */ |
| static FILE *info_file; |
| |
| struct extent_buffer *debug_read_block(struct btrfs_root *root, u64 bytenr, |
| u32 blocksize, int copy) |
| { |
| int ret; |
| struct extent_buffer *eb; |
| u64 length; |
| struct btrfs_multi_bio *multi = NULL; |
| struct btrfs_device *device; |
| int num_copies; |
| int mirror_num = 1; |
| |
| eb = btrfs_find_create_tree_block(root, bytenr, blocksize); |
| if (!eb) |
| return NULL; |
| |
| length = blocksize; |
| while (1) { |
| ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, |
| eb->start, &length, &multi, mirror_num); |
| BUG_ON(ret); |
| device = multi->stripes[0].dev; |
| eb->fd = device->fd; |
| device->total_ios++; |
| eb->dev_bytenr = multi->stripes[0].physical; |
| |
| fprintf(info_file, "mirror %d logical %Lu physical %Lu " |
| "device %s\n", mirror_num, (unsigned long long)bytenr, |
| (unsigned long long)eb->dev_bytenr, device->name); |
| kfree(multi); |
| |
| if (!copy || mirror_num == copy) |
| ret = read_extent_from_disk(eb); |
| |
| num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, |
| eb->start, eb->len); |
| if (num_copies == 1) |
| break; |
| |
| mirror_num++; |
| if (mirror_num > num_copies) |
| break; |
| } |
| return eb; |
| } |
| |
| static void print_usage(void) |
| { |
| fprintf(stderr, "usage: btrfs-map-logical [options] mount_point\n"); |
| fprintf(stderr, "\t-l Logical extent to map\n"); |
| fprintf(stderr, "\t-c Copy of the extent to read (usually 1 or 2)\n"); |
| fprintf(stderr, "\t-o Output file to hold the extent\n"); |
| fprintf(stderr, "\t-b Number of bytes to read\n"); |
| exit(1); |
| } |
| |
| static struct option long_options[] = { |
| /* { "byte-count", 1, NULL, 'b' }, */ |
| { "logical", 1, NULL, 'l' }, |
| { "copy", 1, NULL, 'c' }, |
| { "output", 1, NULL, 'c' }, |
| { "bytes", 1, NULL, 'b' }, |
| { 0, 0, 0, 0} |
| }; |
| |
| int main(int ac, char **av) |
| { |
| struct cache_tree root_cache; |
| struct btrfs_root *root; |
| struct extent_buffer *eb; |
| char *dev; |
| char *output_file = NULL; |
| u64 logical = 0; |
| int ret = 0; |
| int option_index = 0; |
| int copy = 0; |
| u64 bytes = 0; |
| int out_fd = 0; |
| int err; |
| |
| while(1) { |
| int c; |
| c = getopt_long(ac, av, "l:c:o:b:", long_options, |
| &option_index); |
| if (c < 0) |
| break; |
| switch(c) { |
| case 'l': |
| logical = atoll(optarg); |
| if (logical == 0) { |
| fprintf(stderr, |
| "invalid extent number\n"); |
| print_usage(); |
| } |
| break; |
| case 'c': |
| copy = atoi(optarg); |
| if (copy == 0) { |
| fprintf(stderr, |
| "invalid copy number\n"); |
| print_usage(); |
| } |
| break; |
| case 'b': |
| bytes = atoll(optarg); |
| if (bytes == 0) { |
| fprintf(stderr, |
| "invalid byte count\n"); |
| print_usage(); |
| } |
| break; |
| case 'o': |
| output_file = strdup(optarg); |
| break; |
| default: |
| print_usage(); |
| } |
| } |
| ac = ac - optind; |
| if (ac == 0) |
| print_usage(); |
| if (logical == 0) |
| print_usage(); |
| if (copy < 0) |
| print_usage(); |
| |
| dev = av[optind]; |
| |
| radix_tree_init(); |
| cache_tree_init(&root_cache); |
| |
| root = open_ctree(dev, 0, 0); |
| if (!root) { |
| fprintf(stderr, "Open ctree failed\n"); |
| exit(1); |
| } |
| |
| info_file = stdout; |
| if (output_file) { |
| if (strcmp(output_file, "-") == 0) { |
| out_fd = 1; |
| info_file = stderr; |
| } else { |
| out_fd = open(output_file, O_RDWR | O_CREAT, 0600); |
| if (out_fd < 0) |
| goto close; |
| err = ftruncate(out_fd, 0); |
| if (err) { |
| close(out_fd); |
| goto close; |
| } |
| info_file = stdout; |
| } |
| } |
| |
| if (bytes == 0) |
| bytes = root->sectorsize; |
| |
| bytes = (bytes + root->sectorsize - 1) / root->sectorsize; |
| bytes *= root->sectorsize; |
| |
| while (bytes > 0) { |
| eb = debug_read_block(root, logical, root->sectorsize, copy); |
| if (eb && output_file) { |
| err = write(out_fd, eb->data, eb->len); |
| if (err < 0 || err != eb->len) { |
| fprintf(stderr, "output file write failed\n"); |
| goto out_close_fd; |
| } |
| } |
| free_extent_buffer(eb); |
| logical += root->sectorsize; |
| bytes -= root->sectorsize; |
| } |
| |
| out_close_fd: |
| if (output_file && out_fd != 1) |
| close(out_fd); |
| close: |
| close_ctree(root); |
| return ret; |
| } |