mirror of
https://github.com/kdave/btrfs-progs
synced 2024-12-24 23:22:27 +00:00
Merge branch 'liubo-image-restore'
Signed-off-by: Chris Mason <chris.mason@fusionio.com> Conflicts: disk-io.c volumes.h
This commit is contained in:
commit
0bae08fdab
315
btrfs-image.c
315
btrfs-image.c
@ -35,6 +35,7 @@
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include "volumes.h"
|
||||
#include "extent_io.h"
|
||||
|
||||
#define HEADER_MAGIC 0xbd5c25e27295668bULL
|
||||
#define MAX_PENDING_SIZE (256 * 1024)
|
||||
@ -136,6 +137,9 @@ struct mdrestore_struct {
|
||||
int done;
|
||||
int error;
|
||||
int old_restore;
|
||||
int fixup_offset;
|
||||
int multi_devices;
|
||||
struct btrfs_fs_info *info;
|
||||
};
|
||||
|
||||
static int search_for_chunk_blocks(struct mdrestore_struct *mdres,
|
||||
@ -1169,9 +1173,9 @@ static int copy_from_extent_tree(struct metadump_struct *metadump,
|
||||
|
||||
bytenr = key.objectid;
|
||||
if (key.type == BTRFS_METADATA_ITEM_KEY)
|
||||
num_bytes = key.offset;
|
||||
else
|
||||
num_bytes = extent_root->leafsize;
|
||||
else
|
||||
num_bytes = key.offset;
|
||||
|
||||
if (btrfs_item_size_nr(leaf, path->slots[0]) > sizeof(*ei)) {
|
||||
ei = btrfs_item_ptr(leaf, path->slots[0],
|
||||
@ -1589,9 +1593,10 @@ static void *restore_worker(void *data)
|
||||
u8 *outbuf;
|
||||
int outfd;
|
||||
int ret;
|
||||
int compress_size = MAX_PENDING_SIZE * 4;
|
||||
|
||||
outfd = fileno(mdres->out);
|
||||
buffer = malloc(MAX_PENDING_SIZE * 2);
|
||||
buffer = malloc(compress_size);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Error allocing buffer\n");
|
||||
pthread_mutex_lock(&mdres->mutex);
|
||||
@ -1619,7 +1624,7 @@ static void *restore_worker(void *data)
|
||||
pthread_mutex_unlock(&mdres->mutex);
|
||||
|
||||
if (mdres->compress_method == COMPRESS_ZLIB) {
|
||||
size = MAX_PENDING_SIZE * 2;
|
||||
size = compress_size;
|
||||
ret = uncompress(buffer, (unsigned long *)&size,
|
||||
async->buffer, async->bufsize);
|
||||
if (ret != Z_OK) {
|
||||
@ -1633,44 +1638,60 @@ static void *restore_worker(void *data)
|
||||
size = async->bufsize;
|
||||
}
|
||||
|
||||
if (async->start == BTRFS_SUPER_INFO_OFFSET) {
|
||||
if (mdres->old_restore) {
|
||||
update_super_old(outbuf);
|
||||
} else {
|
||||
ret = update_super(outbuf);
|
||||
if (!mdres->multi_devices) {
|
||||
if (async->start == BTRFS_SUPER_INFO_OFFSET) {
|
||||
if (mdres->old_restore) {
|
||||
update_super_old(outbuf);
|
||||
} else {
|
||||
ret = update_super(outbuf);
|
||||
if (ret)
|
||||
err = ret;
|
||||
}
|
||||
} else if (!mdres->old_restore) {
|
||||
ret = fixup_chunk_tree_block(mdres, async, outbuf, size);
|
||||
if (ret)
|
||||
err = ret;
|
||||
}
|
||||
} else if (!mdres->old_restore) {
|
||||
ret = fixup_chunk_tree_block(mdres, async, outbuf, size);
|
||||
if (ret)
|
||||
err = ret;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
u64 chunk_size = size;
|
||||
bytenr = logical_to_physical(mdres,
|
||||
async->start + offset,
|
||||
&chunk_size);
|
||||
ret = pwrite64(outfd, outbuf+offset, chunk_size,
|
||||
bytenr);
|
||||
if (ret < chunk_size) {
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to "
|
||||
"device %d\n", errno);
|
||||
err = errno;
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Short write\n");
|
||||
err = -EIO;
|
||||
break;
|
||||
if (!mdres->fixup_offset) {
|
||||
while (size) {
|
||||
u64 chunk_size = size;
|
||||
if (!mdres->multi_devices)
|
||||
bytenr = logical_to_physical(mdres,
|
||||
async->start + offset,
|
||||
&chunk_size);
|
||||
else
|
||||
bytenr = async->start + offset;
|
||||
|
||||
ret = pwrite64(outfd, outbuf+offset, chunk_size,
|
||||
bytenr);
|
||||
if (ret != chunk_size) {
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to "
|
||||
"device %d\n", errno);
|
||||
err = errno;
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Short write\n");
|
||||
err = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
size -= chunk_size;
|
||||
offset += chunk_size;
|
||||
}
|
||||
} else if (async->start != BTRFS_SUPER_INFO_OFFSET) {
|
||||
ret = write_data_to_disk(mdres->info, outbuf, async->start, size, 0);
|
||||
if (ret) {
|
||||
printk("Error write data\n");
|
||||
exit(1);
|
||||
}
|
||||
size -= chunk_size;
|
||||
offset += chunk_size;
|
||||
}
|
||||
|
||||
if (async->start == BTRFS_SUPER_INFO_OFFSET)
|
||||
|
||||
/* backup super blocks are already there at fixup_offset stage */
|
||||
if (!mdres->multi_devices && async->start == BTRFS_SUPER_INFO_OFFSET)
|
||||
write_backup_supers(outfd, outbuf);
|
||||
|
||||
pthread_mutex_lock(&mdres->mutex);
|
||||
@ -1714,7 +1735,8 @@ static void mdrestore_destroy(struct mdrestore_struct *mdres)
|
||||
|
||||
static int mdrestore_init(struct mdrestore_struct *mdres,
|
||||
FILE *in, FILE *out, int old_restore,
|
||||
int num_threads)
|
||||
int num_threads, int fixup_offset,
|
||||
struct btrfs_fs_info *info, int multi_devices)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
@ -1726,6 +1748,9 @@ static int mdrestore_init(struct mdrestore_struct *mdres,
|
||||
mdres->out = out;
|
||||
mdres->old_restore = old_restore;
|
||||
mdres->chunk_tree.rb_node = NULL;
|
||||
mdres->fixup_offset = fixup_offset;
|
||||
mdres->info = info;
|
||||
mdres->multi_devices = multi_devices;
|
||||
|
||||
if (!num_threads)
|
||||
return 0;
|
||||
@ -2186,12 +2211,14 @@ static int build_chunk_tree(struct mdrestore_struct *mdres,
|
||||
return search_for_chunk_blocks(mdres, chunk_root_bytenr, 0);
|
||||
}
|
||||
|
||||
static int restore_metadump(const char *input, FILE *out, int old_restore,
|
||||
int num_threads)
|
||||
static int __restore_metadump(const char *input, FILE *out, int old_restore,
|
||||
int num_threads, int fixup_offset,
|
||||
const char *target, int multi_devices)
|
||||
{
|
||||
struct meta_cluster *cluster = NULL;
|
||||
struct meta_cluster_header *header;
|
||||
struct mdrestore_struct mdrestore;
|
||||
struct btrfs_fs_info *info = NULL;
|
||||
u64 bytenr = 0;
|
||||
FILE *in = NULL;
|
||||
int ret = 0;
|
||||
@ -2206,26 +2233,36 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: open with write mode */
|
||||
if (fixup_offset) {
|
||||
BUG_ON(!target);
|
||||
info = open_ctree_fs_info_restore(target, 0, 0, 1, 1);
|
||||
if (!info) {
|
||||
fprintf(stderr, "%s: open ctree failed\n", __func__);
|
||||
ret = -EIO;
|
||||
goto failed_open;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = malloc(BLOCK_SIZE);
|
||||
if (!cluster) {
|
||||
fprintf(stderr, "Error allocating cluster\n");
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto failed_info;
|
||||
}
|
||||
|
||||
ret = mdrestore_init(&mdrestore, in, out, old_restore, num_threads);
|
||||
ret = mdrestore_init(&mdrestore, in, out, old_restore, num_threads,
|
||||
fixup_offset, info, multi_devices);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Error initing mdrestore %d\n", ret);
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
free(cluster);
|
||||
return ret;
|
||||
goto failed_cluster;
|
||||
}
|
||||
|
||||
ret = build_chunk_tree(&mdrestore, cluster);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!multi_devices) {
|
||||
ret = build_chunk_tree(&mdrestore, cluster);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (in != stdin && fseek(in, 0, SEEK_SET)) {
|
||||
fprintf(stderr, "Error seeking %d\n", errno);
|
||||
@ -2259,12 +2296,123 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
|
||||
}
|
||||
out:
|
||||
mdrestore_destroy(&mdrestore);
|
||||
failed_cluster:
|
||||
free(cluster);
|
||||
failed_info:
|
||||
if (fixup_offset && info)
|
||||
close_ctree(info->chunk_root);
|
||||
failed_open:
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int restore_metadump(const char *input, FILE *out, int old_restore,
|
||||
int num_threads, int multi_devices)
|
||||
{
|
||||
return __restore_metadump(input, out, old_restore, num_threads, 0, NULL,
|
||||
multi_devices);
|
||||
}
|
||||
|
||||
static int fixup_metadump(const char *input, FILE *out, int num_threads,
|
||||
const char *target)
|
||||
{
|
||||
return __restore_metadump(input, out, 0, num_threads, 1, target, 1);
|
||||
}
|
||||
|
||||
static int update_disk_super_on_device(struct btrfs_fs_info *info,
|
||||
const char *other_dev, u64 cur_devid)
|
||||
{
|
||||
struct btrfs_key key;
|
||||
struct extent_buffer *leaf;
|
||||
struct btrfs_path path;
|
||||
struct btrfs_dev_item *dev_item;
|
||||
struct btrfs_super_block *disk_super;
|
||||
char dev_uuid[BTRFS_UUID_SIZE];
|
||||
char fs_uuid[BTRFS_UUID_SIZE];
|
||||
u64 devid, type, io_align, io_width;
|
||||
u64 sector_size, total_bytes, bytes_used;
|
||||
char *buf;
|
||||
int fp;
|
||||
int ret;
|
||||
|
||||
key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
|
||||
key.type = BTRFS_DEV_ITEM_KEY;
|
||||
key.offset = cur_devid;
|
||||
|
||||
btrfs_init_path(&path);
|
||||
ret = btrfs_search_slot(NULL, info->chunk_root, &key, &path, 0, 0);
|
||||
if (ret) {
|
||||
fprintf(stderr, "search key fails\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
leaf = path.nodes[0];
|
||||
dev_item = btrfs_item_ptr(leaf, path.slots[0],
|
||||
struct btrfs_dev_item);
|
||||
|
||||
devid = btrfs_device_id(leaf, dev_item);
|
||||
if (devid != cur_devid) {
|
||||
printk("devid %llu mismatch with %llu\n", devid, cur_devid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
type = btrfs_device_type(leaf, dev_item);
|
||||
io_align = btrfs_device_io_align(leaf, dev_item);
|
||||
io_width = btrfs_device_io_width(leaf, dev_item);
|
||||
sector_size = btrfs_device_sector_size(leaf, dev_item);
|
||||
total_bytes = btrfs_device_total_bytes(leaf, dev_item);
|
||||
bytes_used = btrfs_device_bytes_used(leaf, dev_item);
|
||||
read_extent_buffer(leaf, dev_uuid, (unsigned long)btrfs_device_uuid(dev_item), BTRFS_UUID_SIZE);
|
||||
read_extent_buffer(leaf, fs_uuid, (unsigned long)btrfs_device_fsid(dev_item), BTRFS_UUID_SIZE);
|
||||
|
||||
btrfs_release_path(info->chunk_root, &path);
|
||||
|
||||
printk("update disk super on %s devid=%llu\n", other_dev, devid);
|
||||
|
||||
/* update other devices' super block */
|
||||
fp = open(other_dev, O_CREAT | O_RDWR, 0600);
|
||||
if (fp < 0) {
|
||||
fprintf(stderr, "could not open %s\n", other_dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
buf = malloc(BTRFS_SUPER_INFO_SIZE);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(buf, info->super_copy, BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
disk_super = (struct btrfs_super_block *)buf;
|
||||
dev_item = &disk_super->dev_item;
|
||||
|
||||
btrfs_set_stack_device_type(dev_item, type);
|
||||
btrfs_set_stack_device_id(dev_item, devid);
|
||||
btrfs_set_stack_device_total_bytes(dev_item, total_bytes);
|
||||
btrfs_set_stack_device_bytes_used(dev_item, bytes_used);
|
||||
btrfs_set_stack_device_io_align(dev_item, io_align);
|
||||
btrfs_set_stack_device_io_width(dev_item, io_width);
|
||||
btrfs_set_stack_device_sector_size(dev_item, sector_size);
|
||||
memcpy(dev_item->uuid, dev_uuid, BTRFS_UUID_SIZE);
|
||||
memcpy(dev_item->fsid, fs_uuid, BTRFS_UUID_SIZE);
|
||||
csum_block((u8 *)buf, BTRFS_SUPER_INFO_SIZE);
|
||||
|
||||
ret = pwrite64(fp, buf, BTRFS_SUPER_INFO_SIZE, BTRFS_SUPER_INFO_OFFSET);
|
||||
if (ret != BTRFS_SUPER_INFO_SIZE) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_backup_supers(fp, (u8 *)buf);
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
close(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: btrfs-image [options] source target\n");
|
||||
@ -2272,7 +2420,7 @@ static void print_usage(void)
|
||||
fprintf(stderr, "\t-c value\tcompression level (0 ~ 9)\n");
|
||||
fprintf(stderr, "\t-t value\tnumber of threads (1 ~ 32)\n");
|
||||
fprintf(stderr, "\t-o \tdon't mess with the chunk tree when restoring\n");
|
||||
fprintf(stderr, "\t-s \tsanitize file names, use once to just use garbage, use twice if you want crc collisions");
|
||||
fprintf(stderr, "\t-s \tsanitize file names, use once to just use garbage, use twice if you want crc collisions\n");
|
||||
fprintf(stderr, "\t-w \twalk all trees instead of using extent tree, do this if your extent tree is broken\n");
|
||||
exit(1);
|
||||
}
|
||||
@ -2286,12 +2434,14 @@ int main(int argc, char *argv[])
|
||||
int create = 1;
|
||||
int old_restore = 0;
|
||||
int walk_trees = 0;
|
||||
int multi_devices = 0;
|
||||
int ret;
|
||||
int sanitize = 0;
|
||||
int dev_cnt = 0;
|
||||
FILE *out;
|
||||
|
||||
while (1) {
|
||||
int c = getopt(argc, argv, "rc:t:osw");
|
||||
int c = getopt(argc, argv, "rc:t:oswm");
|
||||
if (c < 0)
|
||||
break;
|
||||
switch (c) {
|
||||
@ -2317,17 +2467,26 @@ int main(int argc, char *argv[])
|
||||
case 'w':
|
||||
walk_trees = 1;
|
||||
break;
|
||||
case 'm':
|
||||
create = 0;
|
||||
multi_devices = 1;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (old_restore && create)
|
||||
if ((old_restore) && create)
|
||||
print_usage();
|
||||
|
||||
argc = argc - optind;
|
||||
if (argc != 2)
|
||||
dev_cnt = argc - 1;
|
||||
|
||||
if (multi_devices && dev_cnt < 2)
|
||||
print_usage();
|
||||
if (!multi_devices && dev_cnt != 1)
|
||||
print_usage();
|
||||
|
||||
source = argv[optind];
|
||||
target = argv[optind + 1];
|
||||
|
||||
@ -2351,8 +2510,60 @@ int main(int argc, char *argv[])
|
||||
ret = create_metadump(source, out, num_threads,
|
||||
compress_level, sanitize, walk_trees);
|
||||
else
|
||||
ret = restore_metadump(source, out, old_restore, 1);
|
||||
ret = restore_metadump(source, out, old_restore, 1,
|
||||
multi_devices);
|
||||
if (ret) {
|
||||
printk("%s failed (%s)\n", (create) ? "create" : "restore",
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* extended support for multiple devices */
|
||||
if (!create && multi_devices) {
|
||||
struct btrfs_fs_info *info;
|
||||
u64 total_devs;
|
||||
int i;
|
||||
|
||||
info = open_ctree_fs_info_restore(target, 0, 0, 0, 1);
|
||||
if (!info) {
|
||||
int e = errno;
|
||||
fprintf(stderr, "unable to open %s error = %s\n",
|
||||
target, strerror(e));
|
||||
return 1;
|
||||
}
|
||||
|
||||
total_devs = btrfs_super_num_devices(info->super_copy);
|
||||
if (total_devs != dev_cnt) {
|
||||
printk("it needs %llu devices but has only %d\n",
|
||||
total_devs, dev_cnt);
|
||||
close_ctree(info->chunk_root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* update super block on other disks */
|
||||
for (i = 2; i <= dev_cnt; i++) {
|
||||
ret = update_disk_super_on_device(info,
|
||||
argv[optind + i], (u64)i);
|
||||
if (ret) {
|
||||
printk("update disk super failed devid=%d (error=%d)\n",
|
||||
i, ret);
|
||||
close_ctree(info->chunk_root);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
close_ctree(info->chunk_root);
|
||||
|
||||
/* fix metadata block to map correct chunk */
|
||||
ret = fixup_metadump(source, out, 1, target);
|
||||
if (ret) {
|
||||
fprintf(stderr, "fix metadump failed (error=%d)\n",
|
||||
ret);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (out == stdout)
|
||||
fflush(out);
|
||||
else
|
||||
|
1
ctree.h
1
ctree.h
@ -949,6 +949,7 @@ struct btrfs_fs_info {
|
||||
struct list_head space_info;
|
||||
int system_allocs;
|
||||
int readonly;
|
||||
int on_restoring;
|
||||
int (*free_extent_hook)(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
u64 bytenr, u64 num_bytes, u64 parent,
|
||||
|
227
disk-io.c
227
disk-io.c
@ -192,7 +192,7 @@ out:
|
||||
}
|
||||
|
||||
|
||||
static int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
|
||||
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror)
|
||||
{
|
||||
unsigned long offset = 0;
|
||||
struct btrfs_multi_bio *multi = NULL;
|
||||
@ -203,26 +203,40 @@ static int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, i
|
||||
|
||||
while (bytes_left) {
|
||||
read_len = bytes_left;
|
||||
ret = btrfs_map_block(&info->mapping_tree, READ,
|
||||
eb->start + offset, &read_len, &multi,
|
||||
mirror, NULL);
|
||||
if (ret) {
|
||||
printk("Couldn't map the block %Lu\n", eb->start + offset);
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
device = multi->stripes[0].dev;
|
||||
device = NULL;
|
||||
|
||||
if (device->fd == 0) {
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
if (!info->on_restoring) {
|
||||
ret = btrfs_map_block(&info->mapping_tree, READ,
|
||||
eb->start + offset, &read_len, &multi,
|
||||
mirror, NULL);
|
||||
if (ret) {
|
||||
printk("Couldn't map the block %Lu\n", eb->start + offset);
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
device = multi->stripes[0].dev;
|
||||
|
||||
eb->fd = device->fd;
|
||||
device->total_ios++;
|
||||
eb->dev_bytenr = multi->stripes[0].physical;
|
||||
kfree(multi);
|
||||
multi = NULL;
|
||||
if (device->fd == 0) {
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
eb->fd = device->fd;
|
||||
device->total_ios++;
|
||||
eb->dev_bytenr = multi->stripes[0].physical;
|
||||
kfree(multi);
|
||||
multi = NULL;
|
||||
} else {
|
||||
/* special case for restore metadump */
|
||||
list_for_each_entry(device, &info->fs_devices->devices, dev_list) {
|
||||
if (device->devid == 1)
|
||||
break;
|
||||
}
|
||||
|
||||
eb->fd = device->fd;
|
||||
eb->dev_bytenr = eb->start;
|
||||
device->total_ios++;
|
||||
}
|
||||
|
||||
if (read_len > bytes_left)
|
||||
read_len = bytes_left;
|
||||
@ -291,149 +305,6 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int rmw_eb(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *eb, struct extent_buffer *orig_eb)
|
||||
{
|
||||
int ret;
|
||||
unsigned long orig_off = 0;
|
||||
unsigned long dest_off = 0;
|
||||
unsigned long copy_len = eb->len;
|
||||
|
||||
ret = read_whole_eb(info, eb, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (eb->start + eb->len <= orig_eb->start ||
|
||||
eb->start >= orig_eb->start + orig_eb->len)
|
||||
return 0;
|
||||
/*
|
||||
* | ----- orig_eb ------- |
|
||||
* | ----- stripe ------- |
|
||||
* | ----- orig_eb ------- |
|
||||
* | ----- orig_eb ------- |
|
||||
*/
|
||||
if (eb->start > orig_eb->start)
|
||||
orig_off = eb->start - orig_eb->start;
|
||||
if (orig_eb->start > eb->start)
|
||||
dest_off = orig_eb->start - eb->start;
|
||||
|
||||
if (copy_len > orig_eb->len - orig_off)
|
||||
copy_len = orig_eb->len - orig_off;
|
||||
if (copy_len > eb->len - dest_off)
|
||||
copy_len = eb->len - dest_off;
|
||||
|
||||
memcpy(eb->data + dest_off, orig_eb->data + orig_off, copy_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void split_eb_for_raid56(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *orig_eb,
|
||||
struct extent_buffer **ebs,
|
||||
u64 stripe_len, u64 *raid_map,
|
||||
int num_stripes)
|
||||
{
|
||||
struct extent_buffer *eb;
|
||||
u64 start = orig_eb->start;
|
||||
u64 this_eb_start;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < num_stripes; i++) {
|
||||
if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
|
||||
break;
|
||||
|
||||
eb = malloc(sizeof(struct extent_buffer) + stripe_len);
|
||||
if (!eb)
|
||||
BUG();
|
||||
memset(eb, 0, sizeof(struct extent_buffer) + stripe_len);
|
||||
|
||||
eb->start = raid_map[i];
|
||||
eb->len = stripe_len;
|
||||
eb->refs = 1;
|
||||
eb->flags = 0;
|
||||
eb->fd = -1;
|
||||
eb->dev_bytenr = (u64)-1;
|
||||
|
||||
this_eb_start = raid_map[i];
|
||||
|
||||
if (start > this_eb_start ||
|
||||
start + orig_eb->len < this_eb_start + stripe_len) {
|
||||
ret = rmw_eb(info, eb, orig_eb);
|
||||
BUG_ON(ret);
|
||||
} else {
|
||||
memcpy(eb->data, orig_eb->data + eb->start - start, stripe_len);
|
||||
}
|
||||
ebs[i] = eb;
|
||||
}
|
||||
}
|
||||
|
||||
static int write_raid56_with_parity(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *eb,
|
||||
struct btrfs_multi_bio *multi,
|
||||
u64 stripe_len, u64 *raid_map)
|
||||
{
|
||||
struct extent_buffer *ebs[multi->num_stripes], *p_eb = NULL, *q_eb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
int ret;
|
||||
int alloc_size = eb->len;
|
||||
|
||||
if (stripe_len > alloc_size)
|
||||
alloc_size = stripe_len;
|
||||
|
||||
split_eb_for_raid56(info, eb, ebs, stripe_len, raid_map,
|
||||
multi->num_stripes);
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++) {
|
||||
struct extent_buffer *new_eb;
|
||||
if (raid_map[i] < BTRFS_RAID5_P_STRIPE) {
|
||||
ebs[i]->dev_bytenr = multi->stripes[i].physical;
|
||||
ebs[i]->fd = multi->stripes[i].dev->fd;
|
||||
multi->stripes[i].dev->total_ios++;
|
||||
BUG_ON(ebs[i]->start != raid_map[i]);
|
||||
continue;
|
||||
}
|
||||
new_eb = kmalloc(sizeof(*eb) + alloc_size, GFP_NOFS);
|
||||
BUG_ON(!new_eb);
|
||||
new_eb->dev_bytenr = multi->stripes[i].physical;
|
||||
new_eb->fd = multi->stripes[i].dev->fd;
|
||||
multi->stripes[i].dev->total_ios++;
|
||||
new_eb->len = stripe_len;
|
||||
|
||||
if (raid_map[i] == BTRFS_RAID5_P_STRIPE)
|
||||
p_eb = new_eb;
|
||||
else if (raid_map[i] == BTRFS_RAID6_Q_STRIPE)
|
||||
q_eb = new_eb;
|
||||
}
|
||||
if (q_eb) {
|
||||
void *pointers[multi->num_stripes];
|
||||
ebs[multi->num_stripes - 2] = p_eb;
|
||||
ebs[multi->num_stripes - 1] = q_eb;
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++)
|
||||
pointers[i] = ebs[i]->data;
|
||||
|
||||
raid6_gen_syndrome(multi->num_stripes, stripe_len, pointers);
|
||||
} else {
|
||||
ebs[multi->num_stripes - 1] = p_eb;
|
||||
memcpy(p_eb->data, ebs[0]->data, stripe_len);
|
||||
for (j = 1; j < multi->num_stripes - 1; j++) {
|
||||
for (i = 0; i < stripe_len; i += sizeof(unsigned long)) {
|
||||
*(unsigned long *)(p_eb->data + i) ^=
|
||||
*(unsigned long *)(ebs[j]->data + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++) {
|
||||
ret = write_extent_to_disk(ebs[i]);
|
||||
BUG_ON(ret);
|
||||
if (ebs[i] != eb)
|
||||
kfree(ebs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *eb)
|
||||
{
|
||||
@ -445,6 +316,7 @@ int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
|
||||
if (check_tree_block(root, eb))
|
||||
BUG();
|
||||
|
||||
if (!btrfs_buffer_uptodate(eb, trans->transid))
|
||||
BUG();
|
||||
|
||||
@ -1106,7 +978,7 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info)
|
||||
static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
|
||||
u64 sb_bytenr,
|
||||
u64 root_tree_bytenr, int writes,
|
||||
int partial)
|
||||
int partial, int restore)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info;
|
||||
struct btrfs_super_block *disk_super;
|
||||
@ -1126,6 +998,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
|
||||
fprintf(stderr, "Failed to allocate memory for fs_info\n");
|
||||
return NULL;
|
||||
}
|
||||
if (restore)
|
||||
fs_info->on_restoring = 1;
|
||||
|
||||
ret = btrfs_scan_fs_devices(fp, path, &fs_devices);
|
||||
if (ret)
|
||||
@ -1182,6 +1056,29 @@ out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct btrfs_fs_info *open_ctree_fs_info_restore(const char *filename,
|
||||
u64 sb_bytenr, u64 root_tree_bytenr,
|
||||
int writes, int partial)
|
||||
{
|
||||
int fp;
|
||||
struct btrfs_fs_info *info;
|
||||
int flags = O_CREAT | O_RDWR;
|
||||
int restore = 1;
|
||||
|
||||
if (!writes)
|
||||
flags = O_RDONLY;
|
||||
|
||||
fp = open(filename, flags, 0600);
|
||||
if (fp < 0) {
|
||||
fprintf (stderr, "Could not open %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
|
||||
writes, partial, restore);
|
||||
close(fp);
|
||||
return info;
|
||||
}
|
||||
|
||||
struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
|
||||
u64 sb_bytenr, u64 root_tree_bytenr,
|
||||
int writes, int partial)
|
||||
@ -1199,7 +1096,7 @@ struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
|
||||
return NULL;
|
||||
}
|
||||
info = __open_ctree_fd(fp, filename, sb_bytenr, root_tree_bytenr,
|
||||
writes, partial);
|
||||
writes, partial, 0);
|
||||
close(fp);
|
||||
return info;
|
||||
}
|
||||
@ -1218,7 +1115,7 @@ struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
|
||||
int writes)
|
||||
{
|
||||
struct btrfs_fs_info *info;
|
||||
info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0);
|
||||
info = __open_ctree_fd(fp, path, sb_bytenr, 0, writes, 0, 0);
|
||||
if (!info)
|
||||
return NULL;
|
||||
return info->fs_root;
|
||||
|
@ -35,10 +35,13 @@ static inline u64 btrfs_sb_offset(int mirror)
|
||||
|
||||
struct btrfs_device;
|
||||
|
||||
int read_whole_eb(struct btrfs_fs_info *info, struct extent_buffer *eb, int mirror);
|
||||
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
||||
u32 blocksize, u64 parent_transid);
|
||||
int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
|
||||
u64 parent_transid);
|
||||
int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct extent_buffer *eb);
|
||||
struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize);
|
||||
|
||||
@ -62,6 +65,9 @@ int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info);
|
||||
struct btrfs_root *open_ctree(const char *filename, u64 sb_bytenr, int writes);
|
||||
struct btrfs_root *open_ctree_fd(int fp, const char *path, u64 sb_bytenr,
|
||||
int writes);
|
||||
struct btrfs_fs_info *open_ctree_fs_info_restore(const char *filename,
|
||||
u64 sb_bytenr, u64 root_tree_bytenr,
|
||||
int writes, int partial);
|
||||
struct btrfs_fs_info *open_ctree_fs_info(const char *filename,
|
||||
u64 sb_bytenr, u64 root_tree_bytenr,
|
||||
int writes, int partial);
|
||||
|
87
extent_io.c
87
extent_io.c
@ -749,6 +749,93 @@ int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
|
||||
u64 bytes, int mirror)
|
||||
{
|
||||
struct btrfs_multi_bio *multi = NULL;
|
||||
struct btrfs_device *device;
|
||||
u64 bytes_left = bytes;
|
||||
u64 this_len;
|
||||
u64 total_write = 0;
|
||||
u64 *raid_map = NULL;
|
||||
u64 dev_bytenr;
|
||||
int dev_nr;
|
||||
int ret = 0;
|
||||
|
||||
while (bytes_left > 0) {
|
||||
this_len = bytes_left;
|
||||
dev_nr = 0;
|
||||
|
||||
ret = btrfs_map_block(&info->mapping_tree, WRITE, offset,
|
||||
&this_len, &multi, mirror, &raid_map);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Couldn't map the block %Lu\n",
|
||||
offset);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (raid_map) {
|
||||
struct extent_buffer *eb;
|
||||
u64 stripe_len = this_len;
|
||||
|
||||
this_len = min(this_len, bytes_left);
|
||||
this_len = min(this_len, (u64)info->tree_root->leafsize);
|
||||
|
||||
eb = malloc(sizeof(struct extent_buffer) + this_len);
|
||||
BUG_ON(!eb);
|
||||
|
||||
memset(eb, 0, sizeof(struct extent_buffer) + this_len);
|
||||
eb->start = offset;
|
||||
eb->len = this_len;
|
||||
|
||||
memcpy(eb->data, buf + total_write, this_len);
|
||||
ret = write_raid56_with_parity(info, eb, multi,
|
||||
stripe_len, raid_map);
|
||||
BUG_ON(ret);
|
||||
|
||||
free(eb);
|
||||
kfree(raid_map);
|
||||
raid_map = NULL;
|
||||
} else while (dev_nr < multi->num_stripes) {
|
||||
device = multi->stripes[dev_nr].dev;
|
||||
if (device->fd == 0) {
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_bytenr = multi->stripes[dev_nr].physical;
|
||||
this_len = min(this_len, bytes_left);
|
||||
dev_nr++;
|
||||
|
||||
ret = pwrite(device->fd, buf + total_write, this_len, dev_bytenr);
|
||||
if (ret != this_len) {
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error writing to "
|
||||
"device %d\n", errno);
|
||||
ret = errno;
|
||||
kfree(multi);
|
||||
return ret;
|
||||
} else {
|
||||
fprintf(stderr, "Short write\n");
|
||||
kfree(multi);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BUG_ON(bytes_left < this_len);
|
||||
|
||||
bytes_left -= this_len;
|
||||
offset += this_len;
|
||||
total_write += this_len;
|
||||
|
||||
kfree(multi);
|
||||
multi = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int set_extent_buffer_uptodate(struct extent_buffer *eb)
|
||||
{
|
||||
eb->flags |= EXTENT_UPTODATE;
|
||||
|
@ -132,4 +132,6 @@ int set_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
int clear_extent_buffer_dirty(struct extent_buffer *eb);
|
||||
int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
|
||||
u64 bytes, int mirror);
|
||||
int write_data_to_disk(struct btrfs_fs_info *info, void *buf, u64 offset,
|
||||
u64 bytes, int mirror);
|
||||
#endif
|
||||
|
147
volumes.c
147
volumes.c
@ -189,6 +189,10 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, int flags)
|
||||
|
||||
list_for_each(cur, head) {
|
||||
device = list_entry(cur, struct btrfs_device, dev_list);
|
||||
if (!device->name) {
|
||||
printk("no name for device %llu, skip it now\n", device->devid);
|
||||
continue;
|
||||
}
|
||||
|
||||
fd = open(device->name, flags);
|
||||
if (fd < 0) {
|
||||
@ -1769,3 +1773,146 @@ struct list_head *btrfs_scanned_uuids(void)
|
||||
{
|
||||
return &fs_uuids;
|
||||
}
|
||||
|
||||
static int rmw_eb(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *eb, struct extent_buffer *orig_eb)
|
||||
{
|
||||
int ret;
|
||||
unsigned long orig_off = 0;
|
||||
unsigned long dest_off = 0;
|
||||
unsigned long copy_len = eb->len;
|
||||
|
||||
ret = read_whole_eb(info, eb, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (eb->start + eb->len <= orig_eb->start ||
|
||||
eb->start >= orig_eb->start + orig_eb->len)
|
||||
return 0;
|
||||
/*
|
||||
* | ----- orig_eb ------- |
|
||||
* | ----- stripe ------- |
|
||||
* | ----- orig_eb ------- |
|
||||
* | ----- orig_eb ------- |
|
||||
*/
|
||||
if (eb->start > orig_eb->start)
|
||||
orig_off = eb->start - orig_eb->start;
|
||||
if (orig_eb->start > eb->start)
|
||||
dest_off = orig_eb->start - eb->start;
|
||||
|
||||
if (copy_len > orig_eb->len - orig_off)
|
||||
copy_len = orig_eb->len - orig_off;
|
||||
if (copy_len > eb->len - dest_off)
|
||||
copy_len = eb->len - dest_off;
|
||||
|
||||
memcpy(eb->data + dest_off, orig_eb->data + orig_off, copy_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void split_eb_for_raid56(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *orig_eb,
|
||||
struct extent_buffer **ebs,
|
||||
u64 stripe_len, u64 *raid_map,
|
||||
int num_stripes)
|
||||
{
|
||||
struct extent_buffer *eb;
|
||||
u64 start = orig_eb->start;
|
||||
u64 this_eb_start;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < num_stripes; i++) {
|
||||
if (raid_map[i] >= BTRFS_RAID5_P_STRIPE)
|
||||
break;
|
||||
|
||||
eb = malloc(sizeof(struct extent_buffer) + stripe_len);
|
||||
if (!eb)
|
||||
BUG();
|
||||
memset(eb, 0, sizeof(struct extent_buffer) + stripe_len);
|
||||
|
||||
eb->start = raid_map[i];
|
||||
eb->len = stripe_len;
|
||||
eb->refs = 1;
|
||||
eb->flags = 0;
|
||||
eb->fd = -1;
|
||||
eb->dev_bytenr = (u64)-1;
|
||||
|
||||
this_eb_start = raid_map[i];
|
||||
|
||||
if (start > this_eb_start ||
|
||||
start + orig_eb->len < this_eb_start + stripe_len) {
|
||||
ret = rmw_eb(info, eb, orig_eb);
|
||||
BUG_ON(ret);
|
||||
} else {
|
||||
memcpy(eb->data, orig_eb->data + eb->start - start, stripe_len);
|
||||
}
|
||||
ebs[i] = eb;
|
||||
}
|
||||
}
|
||||
|
||||
int write_raid56_with_parity(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *eb,
|
||||
struct btrfs_multi_bio *multi,
|
||||
u64 stripe_len, u64 *raid_map)
|
||||
{
|
||||
struct extent_buffer *ebs[multi->num_stripes], *p_eb = NULL, *q_eb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
int ret;
|
||||
int alloc_size = eb->len;
|
||||
|
||||
if (stripe_len > alloc_size)
|
||||
alloc_size = stripe_len;
|
||||
|
||||
split_eb_for_raid56(info, eb, ebs, stripe_len, raid_map,
|
||||
multi->num_stripes);
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++) {
|
||||
struct extent_buffer *new_eb;
|
||||
if (raid_map[i] < BTRFS_RAID5_P_STRIPE) {
|
||||
ebs[i]->dev_bytenr = multi->stripes[i].physical;
|
||||
ebs[i]->fd = multi->stripes[i].dev->fd;
|
||||
multi->stripes[i].dev->total_ios++;
|
||||
BUG_ON(ebs[i]->start != raid_map[i]);
|
||||
continue;
|
||||
}
|
||||
new_eb = kmalloc(sizeof(*eb) + alloc_size, GFP_NOFS);
|
||||
BUG_ON(!new_eb);
|
||||
new_eb->dev_bytenr = multi->stripes[i].physical;
|
||||
new_eb->fd = multi->stripes[i].dev->fd;
|
||||
multi->stripes[i].dev->total_ios++;
|
||||
new_eb->len = stripe_len;
|
||||
|
||||
if (raid_map[i] == BTRFS_RAID5_P_STRIPE)
|
||||
p_eb = new_eb;
|
||||
else if (raid_map[i] == BTRFS_RAID6_Q_STRIPE)
|
||||
q_eb = new_eb;
|
||||
}
|
||||
if (q_eb) {
|
||||
void *pointers[multi->num_stripes];
|
||||
ebs[multi->num_stripes - 2] = p_eb;
|
||||
ebs[multi->num_stripes - 1] = q_eb;
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++)
|
||||
pointers[i] = ebs[i]->data;
|
||||
|
||||
raid6_gen_syndrome(multi->num_stripes, stripe_len, pointers);
|
||||
} else {
|
||||
ebs[multi->num_stripes - 1] = p_eb;
|
||||
memcpy(p_eb->data, ebs[0]->data, stripe_len);
|
||||
for (j = 1; j < multi->num_stripes - 1; j++) {
|
||||
for (i = 0; i < stripe_len; i += sizeof(unsigned long)) {
|
||||
*(unsigned long *)(p_eb->data + i) ^=
|
||||
*(unsigned long *)(ebs[j]->data + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < multi->num_stripes; i++) {
|
||||
ret = write_extent_to_disk(ebs[i]);
|
||||
BUG_ON(ret);
|
||||
if (ebs[i] != eb)
|
||||
kfree(ebs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -195,4 +195,8 @@ btrfs_find_device_by_devid(struct btrfs_fs_devices *fs_devices,
|
||||
u64 devid, int instance);
|
||||
struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
|
||||
u8 *uuid, u8 *fsid);
|
||||
int write_raid56_with_parity(struct btrfs_fs_info *info,
|
||||
struct extent_buffer *eb,
|
||||
struct btrfs_multi_bio *multi,
|
||||
u64 stripe_len, u64 *raid_map);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user