diff --git a/Makefile b/Makefile index a5024f1b..96e20020 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ LIBS=-luuid RESTORE_LIBS=-lz progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs btrfs-map-logical restore find-root calc-size + btrfs btrfs-map-logical restore find-root calc-size btrfs-corrupt-block # make C=1 to enable sparse ifdef C @@ -79,6 +79,9 @@ btrfstune: $(objects) btrfstune.o btrfs-map-logical: $(objects) btrfs-map-logical.o $(CC) $(CFLAGS) -o btrfs-map-logical $(objects) btrfs-map-logical.o $(LDFLAGS) $(LIBS) +btrfs-corrupt-block: $(objects) btrfs-corrupt-block.o + $(CC) $(CFLAGS) -o btrfs-corrupt-block $(objects) btrfs-corrupt-block.o $(LDFLAGS) $(LIBS) + btrfs-image: $(objects) btrfs-image.o $(CC) $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS) diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c new file mode 100644 index 00000000..ace61c13 --- /dev/null +++ b/btrfs-corrupt-block.c @@ -0,0 +1,221 @@ +/* + * 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 +#include +#include +#include +#include +#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_corrupt_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); + printf("corrupting %llu copy %d\n", eb->start, + mirror_num); + memset(eb->data, 0, eb->len); + write_extent_to_disk(eb); + fsync(eb->fd); + } + + 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' }, + { "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 = 4096; + int out_fd = 0; + int err; + + while(1) { + int c; + c = getopt_long(ac, av, "l:c:", 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; + 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, 1); + 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_corrupt_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: + return ret; +}