summaryrefslogtreecommitdiff
path: root/convert.c
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-04-22 14:06:56 -0400
committerDavid Woodhouse <dwmw2@hera.kernel.org>2008-04-22 14:06:56 -0400
commit8bfbb6b6f83975d85cf2f2018d9c08d0e6cfb500 (patch)
treefc2d28d6a05cba2f92e303f98e9fca122b26cf3b /convert.c
parent358564890ac230ccc8af2661268a6bcb0d6ea18f (diff)
Update the Ext3 converter
The main changes in this patch are adding chunk handing and data relocation ability. In the last step of conversion, the converter relocates data in system chunk and move chunk tree into system chunk. In the rollback process, the converter remove chunk tree from system chunk and copy data back. Regards YZ ---
Diffstat (limited to 'convert.c')
-rw-r--r--convert.c1128
1 files changed, 939 insertions, 189 deletions
diff --git a/convert.c b/convert.c
index 83928f4b..01464549 100644
--- a/convert.c
+++ b/convert.c
@@ -33,6 +33,7 @@
#include "kerncompat.h"
#include "ctree.h"
#include "disk-io.h"
+#include "volumes.h"
#include "transaction.h"
#include "crc32c.h"
#include "utils.h"
@@ -76,6 +77,7 @@ static int close_ext2fs(ext2_filsys fs)
ext2fs_close(fs);
return 0;
}
+
static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
{
blk_t block;
@@ -98,32 +100,39 @@ static int ext2_free_block(ext2_filsys fs, u64 block)
static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
u64 hint_byte, struct btrfs_key *ins)
{
- ext2_filsys fs = (ext2_filsys)root->fs_info->priv_data;
- u32 blocksize = fs->blocksize;
- u64 first = 0;
- u64 block;
+ struct btrfs_block_group_cache *cache;
+ ext2_filsys ext2_fs = (ext2_filsys)root->fs_info->priv_data;
+ u32 blocksize = ext2_fs->blocksize;
u64 bytenr;
+ blk_t block;
+ int wrapped = 0;
int ret;
- block = hint_byte / blocksize;
- BUG_ON(block != (blk_t)block);
- BUG_ON(num_bytes != blocksize);
while (1) {
- ret = ext2_alloc_block(fs, block, &block);
+ block = hint_byte / blocksize;
+ ret = ext2fs_new_block(ext2_fs, block, NULL, &block);
if (ret)
goto fail;
- /* all free blocks are pinned */
- if (first == block)
+
+ bytenr = (u64)block * blocksize;
+ if (bytenr < hint_byte && ++wrapped > 1)
goto fail;
- if (first == 0)
- first = block;
- bytenr = block * blocksize;
- if (!test_range_bit(&root->fs_info->pinned_extents, bytenr,
- bytenr + blocksize - 1, EXTENT_DIRTY, 0))
- break;
- ext2_free_block(fs, block);
- block++;
+ cache = btrfs_lookup_block_group(root->fs_info, bytenr);
+ BUG_ON(!cache);
+ if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
+ hint_byte = cache->key.objectid + cache->key.offset;
+ continue;
+ }
+
+ if (test_range_bit(&root->fs_info->pinned_extents, bytenr,
+ bytenr + blocksize - 1, EXTENT_DIRTY, 0)) {
+ hint_byte = bytenr + blocksize;
+ continue;
+ }
+
+ ext2fs_fast_mark_block_bitmap(ext2_fs->block_map, block);
+ break;
}
ins->objectid = bytenr;
ins->offset = blocksize;
@@ -133,6 +142,7 @@ fail:
fprintf(stderr, "not enough free space\n");
return -ENOSPC;
}
+
static int custom_free_extent(struct btrfs_root *root, u64 bytenr,
u64 num_bytes)
{
@@ -162,6 +172,7 @@ struct dir_iterate_data {
u64 parent;
int errcode;
};
+
static u8 filetype_conversion_table[EXT2_FT_MAX] = {
[EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN,
[EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE,
@@ -259,9 +270,9 @@ static int read_disk_extent(struct btrfs_root *root, u64 bytenr,
u32 num_bytes, char *buffer)
{
int ret;
- struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
- ret = pread(fs_info->fp, buffer, num_bytes, bytenr);
+ ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr);
if (ret != num_bytes)
goto fail;
ret = 0;
@@ -376,6 +387,7 @@ struct blk_iterate_data {
u64 first_block;
u64 disk_block;
u64 num_blocks;
+ u64 boundary;
int checksum;
int errcode;
};
@@ -385,13 +397,15 @@ static int block_iterate_proc(ext2_filsys ext2_fs,
struct blk_iterate_data *idata)
{
int ret;
- u32 blocksize = ext2_fs->blocksize;
struct btrfs_root *root = idata->root;
struct btrfs_trans_handle *trans = idata->trans;
+ struct btrfs_block_group_cache *cache;
+ u64 bytenr;
- if ((file_block > idata->first_block + idata->num_blocks) ||
- (disk_block != idata->disk_block + idata->num_blocks) ||
- (idata->num_blocks >= BTRFS_BLOCK_GROUP_SIZE / blocksize)) {
+ BUG_ON(disk_block == 0);
+ if ((idata->num_blocks > 0 && disk_block >= idata->boundary) ||
+ (file_block > idata->first_block + idata->num_blocks) ||
+ (disk_block != idata->disk_block + idata->num_blocks)) {
if (idata->num_blocks > 0) {
ret = record_file_blocks(trans, root, idata->objectid,
idata->inode, idata->first_block,
@@ -410,8 +424,15 @@ static int block_iterate_proc(ext2_filsys ext2_fs,
if (ret)
goto fail;
}
+
+ bytenr = disk_block * root->sectorsize;
+ cache = btrfs_lookup_block_group(root->fs_info, bytenr);
+ BUG_ON(!cache);
+ bytenr = cache->key.objectid + cache->key.offset;
+
idata->first_block = file_block;
idata->disk_block = disk_block;
+ idata->boundary = bytenr / root->sectorsize;
}
idata->num_blocks++;
return 0;
@@ -452,6 +473,7 @@ static int create_file_extents(struct btrfs_trans_handle *trans,
.first_block = 0,
.disk_block = 0,
.num_blocks = 0,
+ .boundary = (u64)-1,
.checksum = datacsum,
.errcode = 0,
};
@@ -972,15 +994,15 @@ static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr,
{
int ret;
char *buffer;
- struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices;
buffer = malloc(num_bytes);
if (!buffer)
return -ENOMEM;
- ret = pread(fs_info->fp, buffer, num_bytes, src_bytenr);
+ ret = pread(fs_devs->latest_bdev, buffer, num_bytes, src_bytenr);
if (ret != num_bytes)
goto fail;
- ret = pwrite(fs_info->fp, buffer, num_bytes, dst_bytenr);
+ ret = pwrite(fs_devs->latest_bdev, buffer, num_bytes, dst_bytenr);
if (ret != num_bytes)
goto fail;
ret = 0;
@@ -1019,7 +1041,7 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
if (ret)
return ret;
}
- if (trans->blocks_used >= 8192) {
+ if (trans->blocks_used >= 4096) {
ret = btrfs_commit_transaction(trans, root);
BUG_ON(ret);
trans = btrfs_start_transaction(root, 1);
@@ -1031,6 +1053,7 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
return ret;
}
+
static int lookup_extent_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes)
@@ -1038,12 +1061,25 @@ static int lookup_extent_item(struct btrfs_trans_handle *trans,
int ret;
struct btrfs_key key;
struct btrfs_path path;
+
btrfs_init_path(&path);
key.objectid = bytenr;
- key.offset = num_bytes;
+ key.offset = (u64)-1;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ BUG_ON(ret == 0);
+
+ ret = btrfs_previous_item(root, &path, 0, BTRFS_EXTENT_ITEM_KEY);
+ if (ret != 0)
+ goto out;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid + key.offset < bytenr + num_bytes)
+ ret = 1;
+out:
btrfs_release_path(root, &path);
return ret;
}
@@ -1072,8 +1108,9 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
.inode = inode,
.objectid = objectid,
.first_block = block,
- .disk_block = block,
+ .disk_block = 0,
.num_blocks = 0,
+ .boundary = (u64)-1,
.checksum = 0,
.errcode = 0,
};
@@ -1088,19 +1125,18 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
if (ret == 0)
continue;
- ret = block_iterate_proc(ext2_fs, block, block, &data);
- if (ret & BLOCK_ABORT)
- break;
+ ret = block_iterate_proc(NULL, block, block, &data);
+ if (ret & BLOCK_ABORT) {
+ ret = data.errcode;
+ goto fail;
+ }
}
- ret = data.errcode;
- if (ret)
- return ret;
if (data.num_blocks > 0) {
ret = record_file_blocks(trans, root, objectid, inode,
data.first_block, data.disk_block,
data.num_blocks, 0);
if (ret)
- return ret;
+ goto fail;
data.first_block += data.num_blocks;
}
if (last_block > data.first_block) {
@@ -1108,10 +1144,10 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
data.first_block, 0, last_block -
data.first_block, 0);
if (ret)
- return ret;
+ goto fail;
}
fail:
- return 0;
+ return ret;
}
/*
* Create the ext2fs image file.
@@ -1135,12 +1171,10 @@ static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
u64 ref_root;
u64 ref_owner;
u64 objectid;
- u64 new_block;
u64 last_byte;
u64 first_free;
u64 total_bytes;
u32 sectorsize = root->sectorsize;
- int slot;
int file_extent;
total_bytes = btrfs_super_total_bytes(&fs_info->super_copy);
@@ -1165,56 +1199,47 @@ static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
goto fail;
/*
- * copy the first a few blocks to new positions. the relocation is
- * reuqired for block 0 and default btrfs super block.
+ * copy blocks covered by extent #0 to new positions. extent #0 is
+ * special, we can't rely on relocate_extents_range to relocate it.
*/
for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) {
- ret = ext2_alloc_block(ext2_fs, 0, &new_block);
+ ret = custom_alloc_extent(root, sectorsize, 0, &key);
if (ret)
goto fail;
- new_block *= sectorsize;
- ret = copy_disk_extent(root, new_block, last_byte, sectorsize);
+ ret = copy_disk_extent(root, key.objectid, last_byte,
+ sectorsize);
if (ret)
goto fail;
ret = record_file_extent(trans, root, objectid,
&btrfs_inode, last_byte,
- new_block, sectorsize, 0);
+ key.objectid, sectorsize, 0);
if (ret)
goto fail;
}
-again:
- if (trans->blocks_used >= 8192) {
- ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
- }
-
- key.objectid = last_byte;
- key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
- ret = btrfs_search_slot(trans, fs_info->extent_root,
- &key, &path, 0, 0);
- if (ret < 0)
- goto fail;
- leaf = path.nodes[0];
- slot = path.slots[0];
while(1) {
- if (slot >= btrfs_header_nritems(leaf)) {
+ key.objectid = last_byte;
+ key.offset = 0;
+ btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ ret = btrfs_search_slot(trans, fs_info->extent_root,
+ &key, &path, 0, 0);
+ if (ret < 0)
+ goto fail;
+next:
+ leaf = path.nodes[0];
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(extent_root, &path);
if (ret < 0)
goto fail;
if (ret > 0)
break;
leaf = path.nodes[0];
- slot = path.slots[0];
}
- btrfs_item_key_to_cpu(leaf, &key, slot);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (last_byte > key.objectid ||
key.type != BTRFS_EXTENT_ITEM_KEY) {
- slot++;
- continue;
+ path.slots[0]++;
+ goto next;
}
/*
* Check backref to distinguish extent items for normal
@@ -1225,23 +1250,22 @@ again:
num_bytes = key.offset;
file_extent = 0;
while (1) {
- if (slot >= btrfs_header_nritems(leaf)) {
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(extent_root, &path);
if (ret > 0)
break;
if (ret < 0)
goto fail;
leaf = path.nodes[0];
- slot = path.slots[0];
}
- btrfs_item_key_to_cpu(leaf, &key, slot);
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.objectid != bytenr)
break;
if (key.type != BTRFS_EXTENT_REF_KEY) {
- slot++;
+ path.slots[0]++;
continue;
}
- ref_item = btrfs_item_ptr(leaf, slot,
+ ref_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_extent_ref);
ref_root = btrfs_ref_root(leaf, ref_item);
ref_owner = btrfs_ref_objectid(leaf, ref_item);
@@ -1250,10 +1274,10 @@ again:
file_extent = 1;
break;
}
- slot++;
+ path.slots[0]++;
}
if (!file_extent)
- continue;
+ goto next;
if (bytenr > last_byte) {
ret = create_image_file_range(trans, root, objectid,
@@ -1267,8 +1291,14 @@ again:
if (ret)
goto fail;
last_byte = bytenr + num_bytes;
- btrfs_release_path(root, &path);
- goto again;
+ btrfs_release_path(extent_root, &path);
+
+ if (trans->blocks_used >= 4096) {
+ ret = btrfs_commit_transaction(trans, root);
+ BUG_ON(ret);
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(!trans);
+ }
}
btrfs_release_path(root, &path);
if (total_bytes > last_byte) {
@@ -1278,14 +1308,7 @@ again:
if (ret)
goto fail;
}
- /*
- * otime isn't used currently, so we can store some data in it.
- * These data are used by do_rollback to check whether the image
- * file has been modified.
- */
- btrfs_set_stack_timespec_sec(&btrfs_inode.otime, trans->transid);
- btrfs_set_stack_timespec_nsec(&btrfs_inode.otime,
- total_bytes / sectorsize);
+
ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
if (ret)
goto fail;
@@ -1322,6 +1345,7 @@ fail:
btrfs_release_path(root, &path);
return ret;
}
+
struct btrfs_root *create_subvol(struct btrfs_root *root, const char *name)
{
int ret;
@@ -1406,6 +1430,7 @@ static int fixup_block_accounting(struct btrfs_trans_handle *trans,
break;
start = cache->key.objectid + cache->key.offset;
btrfs_set_block_group_used(&cache->item, 0);
+ cache->space_info->bytes_used = 0;
}
btrfs_init_path(&path);
@@ -1442,6 +1467,129 @@ static int fixup_block_accounting(struct btrfs_trans_handle *trans,
return 0;
}
+static int create_chunk_mapping(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root)
+{
+ struct btrfs_fs_info *info = root->fs_info;
+ struct btrfs_root *chunk_root = info->chunk_root;
+ struct btrfs_root *extent_root = info->extent_root;
+ struct btrfs_device *device;
+ struct btrfs_block_group_cache *cache;
+ struct btrfs_dev_extent *extent;
+ struct extent_buffer *leaf;
+ struct btrfs_chunk chunk;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ u64 cur_start;
+ u64 total_bytes;
+ u64 chunk_objectid;
+ int ret;
+
+ btrfs_init_path(&path);
+
+ total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
+ chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+
+ BUG_ON(list_empty(&info->fs_devices->devices));
+ device = list_entry(info->fs_devices->devices.next,
+ struct btrfs_device, dev_list);
+ BUG_ON(device->devid != info->fs_devices->latest_devid);
+
+ /* delete device extent created by make_btrfs */
+ key.objectid = device->devid;
+ key.offset = 0;
+ key.type = BTRFS_DEV_EXTENT_KEY;
+ ret = btrfs_search_slot(trans, device->dev_root, &key, &path, -1, 1);
+ if (ret < 0)
+ goto err;
+
+ BUG_ON(ret > 0);
+ ret = btrfs_del_item(trans, device->dev_root, &path);
+ if (ret)
+ goto err;
+ btrfs_release_path(device->dev_root, &path);
+
+ /* delete chunk item created by make_btrfs */
+ key.objectid = chunk_objectid;
+ key.offset = 0;
+ key.type = BTRFS_CHUNK_ITEM_KEY;
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, -1, 1);
+ if (ret < 0)
+ goto err;
+
+ BUG_ON(ret > 0);
+ ret = btrfs_del_item(trans, chunk_root, &path);
+ if (ret)
+ goto err;
+ btrfs_release_path(chunk_root, &path);
+
+ /* for each block group, create device extent and chunk item */
+ cur_start = 0;
+ while (cur_start < total_bytes) {
+ cache = btrfs_lookup_block_group(root->fs_info, cur_start);
+ BUG_ON(!cache);
+
+ /* insert device extent */
+ key.objectid = device->devid;
+ key.offset = cache->key.objectid;
+ key.type = BTRFS_DEV_EXTENT_KEY;
+ ret = btrfs_insert_empty_item(trans, device->dev_root, &path,
+ &key, sizeof(*extent));
+ if (ret)
+ goto err;
+
+ leaf = path.nodes[0];
+ extent = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_dev_extent);
+
+ btrfs_set_dev_extent_chunk_tree(leaf, extent,
+ chunk_root->root_key.objectid);
+ btrfs_set_dev_extent_chunk_objectid(leaf, extent,
+ chunk_objectid);
+ btrfs_set_dev_extent_chunk_offset(leaf, extent,
+ cache->key.objectid);
+ btrfs_set_dev_extent_length(leaf, extent, cache->key.offset);
+ write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid,
+ (unsigned long)btrfs_dev_extent_chunk_tree_uuid(extent),
+ BTRFS_UUID_SIZE);
+ btrfs_mark_buffer_dirty(leaf);
+ btrfs_release_path(device->dev_root, &path);
+
+ /* insert chunk item */
+ btrfs_set_stack_chunk_length(&chunk, cache->key.offset);
+ btrfs_set_stack_chunk_owner(&chunk,
+ extent_root->root_key.objectid);
+ btrfs_set_stack_chunk_stripe_len(&chunk, 64 * 1024);
+ btrfs_set_stack_chunk_type(&chunk, cache->flags);
+ btrfs_set_stack_chunk_io_align(&chunk, device->io_align);
+ btrfs_set_stack_chunk_io_width(&chunk, device->io_width);
+ btrfs_set_stack_chunk_sector_size(&chunk, device->sector_size);
+ btrfs_set_stack_chunk_num_stripes(&chunk, 1);
+ btrfs_set_stack_chunk_sub_stripes(&chunk, 0);
+ btrfs_set_stack_stripe_devid(&chunk.stripe, device->devid);
+ btrfs_set_stack_stripe_offset(&chunk.stripe,
+ cache->key.objectid);
+ memcpy(&chunk.stripe.dev_uuid, device->uuid, BTRFS_UUID_SIZE);
+
+ key.objectid = chunk_objectid;
+ key.offset = cache->key.objectid;
+ key.type = BTRFS_CHUNK_ITEM_KEY;
+
+ ret = btrfs_insert_item(trans, chunk_root, &key, &chunk,
+ btrfs_chunk_item_size(1));
+ if (ret)
+ goto err;
+
+ cur_start = cache->key.objectid + cache->key.offset;
+ }
+
+ device->bytes_used = total_bytes;
+ ret = btrfs_update_device(trans, device);
+err:
+ btrfs_release_path(device->dev_root, &path);
+ return ret;
+}
+
static int init_btrfs(struct btrfs_root *root)
{
int ret;
@@ -1457,6 +1605,9 @@ static int init_btrfs(struct btrfs_root *root)
ret = fixup_block_accounting(trans, root);
if (ret)
goto err;
+ ret = create_chunk_mapping(trans, root);
+ if (ret)
+ goto err;
ret = btrfs_make_root_dir(trans, fs_info->tree_root,
BTRFS_ROOT_TREE_DIR_OBJECTID);
if (ret)
@@ -1480,54 +1631,47 @@ static int init_btrfs(struct btrfs_root *root)
err:
return ret;
}
+
/*
* Migrate super block to it's default position and zero 0 ~ 16k
*/
static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
{
int ret;
- char *buf;
- u64 bytenr;
- u32 crc = ~(u32)0;
- u32 len = 512 - BTRFS_CSUM_SIZE;
+ struct extent_buffer *buf;
struct btrfs_super_block *super;
+ u32 len;
+ u32 bytenr;
- ret = fsync(fd);
- if (ret)
- goto fail;
-
- BUG_ON(sectorsize < sizeof(super));
- buf = malloc(sectorsize);
+ BUG_ON(sectorsize < sizeof(*super));
+ buf = malloc(sizeof(*buf) + sectorsize);
if (!buf)
return -ENOMEM;
- ret = pread(fd, buf, sectorsize, old_bytenr);
+
+ buf->len = sectorsize;
+ ret = pread(fd, buf->data, sectorsize, old_bytenr);
if (ret != sectorsize)
goto fail;
- super = (struct btrfs_super_block *)buf;
+ super = (struct btrfs_super_block *)buf->data;
BUG_ON(btrfs_super_bytenr(super) != old_bytenr);
btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET);
- crc = crc32c(crc, buf + BTRFS_CSUM_SIZE, len);
- crc = ~cpu_to_le32(crc);
- memcpy(super->csum, &crc, BTRFS_CRC32_SIZE);
-
- ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
- if (ret < 0)
+ csum_tree_block(NULL, buf, 0);
+ ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET);
+ if (ret != sectorsize)
goto fail;
- /* How to handle this case? */
- BUG_ON(ret != sectorsize);
ret = fsync(fd);
if (ret)
goto fail;
- memset(buf, 0, sectorsize);
+ memset(buf->data, 0, sectorsize);
for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) {
len = BTRFS_SUPER_INFO_OFFSET - bytenr;
if (len > sectorsize)
len = sectorsize;
- ret = pwrite(fd, buf, len, bytenr);
+ ret = pwrite(fd, buf->data, len, bytenr);
if (ret != len) {
fprintf(stderr, "unable to zero fill device\n");
break;
@@ -1543,6 +1687,460 @@ fail:
return ret;
}
+static int prepare_system_chunk_sb(struct btrfs_super_block *super)
+{
+ struct btrfs_chunk *chunk;
+ struct btrfs_disk_key *key;
+ u32 sectorsize = btrfs_super_sectorsize(super);
+
+ key = (struct btrfs_disk_key *)(super->sys_chunk_array);
+ chunk = (struct btrfs_chunk *)(super->sys_chunk_array +
+ sizeof(struct btrfs_disk_key));
+
+ btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY);
+ btrfs_set_disk_key_offset(key, 0);
+
+ btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super));
+ btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID);
+ btrfs_set_stack_chunk_stripe_len(chunk, 64 * 1024);
+ btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM);
+ btrfs_set_stack_chunk_io_align(chunk, sectorsize);
+ btrfs_set_stack_chunk_io_width(chunk, sectorsize);
+ btrfs_set_stack_chunk_sector_size(chunk, sectorsize);
+ btrfs_set_stack_chunk_num_stripes(chunk, 1);
+ btrfs_set_stack_chunk_sub_stripes(chunk, 0);
+ chunk->stripe.devid = super->dev_item.devid;
+ chunk->stripe.offset = cpu_to_le64(0);
+ memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE);
+ btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk));
+ return 0;
+}
+
+static int prepare_system_chunk(int fd, u64 sb_bytenr, u32 sectorsize)
+{
+ int ret;
+ struct extent_buffer *buf;
+ struct btrfs_super_block *super;
+
+ BUG_ON(sectorsize < sizeof(*super));
+ buf = malloc(sizeof(*buf) + sectorsize);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->len = sectorsize;
+ ret = pread(fd, buf->data, sectorsize, sb_bytenr);
+ if (ret != sectorsize)
+ goto fail;
+
+ super = (struct btrfs_super_block *)buf->data;
+ BUG_ON(btrfs_super_bytenr(super) != sb_bytenr);
+ BUG_ON(btrfs_super_num_devices(super) != 1);
+
+ ret = prepare_system_chunk_sb(super);
+ if (ret)
+ goto fail;
+
+ csum_tree_block(NULL, buf, 0);
+ ret = pwrite(fd, buf->data, sectorsize, sb_bytenr);
+ if (ret != sectorsize)
+ goto fail;
+
+ ret = 0;
+fail:
+ free(buf);
+ if (ret > 0)
+ ret = -1;
+ return ret;
+}
+
+static int relocate_one_reference(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 objectid, u64 offset,
+ struct extent_io_tree *reloc_tree)
+{
+ struct extent_buffer *leaf;
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_inode_item inode;
+ struct blk_iterate_data data;
+ u64 bytenr;
+ u64 num_bytes;
+ u64 cur_offset;
+ u64 extent_start;
+ u64 extent_size;
+ u64 new_pos;
+ u64 nblocks;
+ u64 root_gen;
+ u64 root_owner;
+ u64 sector_end;
+ u32 sectorsize = root->sectorsize;
+ unsigned long ptr;
+ int ret;
+
+ btrfs_init_path(&path);
+
+ key.objectid = objectid;
+ key.offset = 0;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ ret = btrfs_lookup_inode(trans, root, &path, &key, 0);
+ if (ret)
+ goto fail;
+
+ leaf = path.nodes[0];
+ ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
+ read_extent_buffer(leaf, &inode, ptr, sizeof(inode));
+ btrfs_release_path(root, &path);
+
+ ret = btrfs_lookup_file_extent(trans, root, &path,
+ objectid, offset, -1);
+ if (ret)
+ goto fail;
+
+ leaf = path.nodes[0];
+ fi = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_file_extent_item);
+
+ root_gen = btrfs_header_generation(leaf);
+ root_owner = btrfs_header_owner(leaf);
+
+ extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
+ extent_size = btrfs_file_extent_disk_num_bytes(leaf, fi);
+ bytenr = extent_start + btrfs_file_extent_offset(leaf, fi);
+ num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
+
+ ret = btrfs_del_item(trans, root, &path);
+ if (ret)
+ goto fail;
+ btrfs_release_path(root, &path);
+
+ BUG_ON(num_bytes & (sectorsize - 1));
+ nblocks = btrfs_stack_inode_nblocks(&inode) - num_bytes / 512;
+ btrfs_set_stack_inode_nblocks(&inode, nblocks);
+
+ data = (struct blk_iterate_data) {
+ .trans = trans,
+ .root = root,
+ .inode = &inode,
+ .objectid = objectid,
+ .first_block = offset / sectorsize,
+ .disk_block = 0,
+ .num_blocks = 0,
+ .boundary = (u64)-1,
+ .checksum = 0,
+ .errcode = 0,
+ };
+
+ cur_offset = offset;
+ while (num_bytes > 0) {
+ sector_end = bytenr + sectorsize - 1;
+ if (test_range_bit(reloc_tree, bytenr, sector_end,
+ EXTENT_LOCKED, 1)) {
+ ret = get_state_private(reloc_tree, bytenr, &new_pos);
+ BUG_ON(ret);
+ } else {
+ ret = custom_alloc_extent(root, sectorsize, 0, &key);
+ if (ret)
+ goto fail;
+ new_pos = key.objectid;
+ ret = copy_disk_extent(root, new_pos, bytenr,
+ sectorsize);
+ if (ret)
+ goto fail;
+ ret = set_extent_bits(reloc_tree, bytenr, sector_end,
+ EXTENT_LOCKED, GFP_NOFS);
+ BUG_ON(ret);
+ ret = set_state_private(reloc_tree, bytenr, new_pos);
+ BUG_ON(ret);
+ }
+
+ ret = block_iterate_proc(NULL, new_pos / sectorsize,
+ cur_offset / sectorsize, &data);
+ if (ret & BLOCK_ABORT) {
+ ret = data.errcode;
+ goto fail;
+ }
+
+ cur_offset += sectorsize;
+ bytenr += sectorsize;
+ num_bytes -= sectorsize;
+ }
+
+ if (data.num_blocks > 0) {
+ ret = record_file_blocks(trans, root, objectid, &inode,
+ data.first_block, data.disk_block,
+ data.num_blocks, 0);
+ if (ret)
+ goto fail;
+ }
+
+ key.objectid = objectid;
+ key.offset = 0;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
+ if (ret)
+ goto fail;
+
+ leaf = path.nodes[0];
+ ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
+ write_extent_buffer(leaf, &inode, ptr, sizeof(inode));
+ btrfs_mark_buffer_dirty(leaf);
+ btrfs_release_path(root, &path);
+
+ ret = btrfs_free_extent(trans, root, extent_start, extent_size,
+ root_owner, root_gen, objectid, offset, 0);
+fail:
+ btrfs_release_path(root, &path);
+ return ret;
+}
+
+static int relocate_extents_range(struct btrfs_root *fs_root,
+ struct btrfs_root *ext2_root,
+ u64 start_byte, u64 end_byte)
+{
+ struct btrfs_fs_info *info = fs_root->fs_info;
+ struct btrfs_root *extent_root = info->extent_root;
+ struct btrfs_root *cur_root;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_extent_ref *ref_item;
+ struct extent_buffer *leaf;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct extent_io_tree reloc_tree;
+ u64 cur_byte;
+ u64 num_bytes;
+ u64 ref_root;
+ u64 ref_owner;
+ u64 ref_offset;
+ u64 num_refs;
+ int pass = 0;
+ int ret;
+ int found;
+
+ btrfs_init_path(&path);
+ extent_io_tree_init(&reloc_tree);
+again:
+ cur_root = (pass % 2 == 0) ? ext2_root : fs_root;
+ num_refs = 0;
+
+ trans = btrfs_start_transaction(cur_root, 1);
+ BUG_ON(!trans);
+
+ cur_byte = start_byte;
+ while (1) {
+ key.objectid = cur_byte;
+ key.offset = 0;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ ret = btrfs_search_slot(trans, extent_root,
+ &key, &path, 0, 0);
+ if (ret < 0)
+ goto fail;
+next:
+ leaf = path.nodes[0];
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(extent_root, &path);
+ if (ret < 0)
+ goto fail;
+ if (ret > 0)
+ break;
+ leaf = path.nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
+ if (key.objectid < cur_byte ||
+ key.type != BTRFS_EXTENT_ITEM_KEY) {
+ path.slots[0]++;
+ goto next;
+ }
+ if (key.objectid >= end_byte)
+ break;
+
+ cur_byte = key.objectid;
+ num_bytes = key.offset;
+ found = 0;
+ while (1) {
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(extent_root, &path);
+ if (ret > 0)
+ break;
+ if (ret < 0)
+ goto fail;
+ leaf = path.nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
+ if (key.objectid != cur_byte)
+ break;
+ if (key.type != BTRFS_EXTENT_REF_KEY) {
+ path.slots[0]++;
+ continue;
+ }
+ ref_item = btrfs_item_ptr(leaf, path.slots[0],
+ struct btrfs_extent_ref);
+ ref_root = btrfs_ref_root(leaf, ref_item);
+ ref_owner = btrfs_ref_objectid(leaf, ref_item);
+ ref_offset = btrfs_ref_offset(leaf, ref_item);
+ num_refs++;
+
+ BUG_ON(ref_owner < BTRFS_FIRST_FREE_OBJECTID);
+ if (ref_root == cur_root->root_key.objectid)
+ found = 1;
+
+ path.slots[0]++;
+ }
+ if (!found)
+ goto next;
+
+ cur_byte += num_bytes;
+ btrfs_release_path(extent_root, &path);
+
+ ret = relocate_one_reference(trans, cur_root, ref_owner,
+ ref_offset, &reloc_tree);
+ if (ret)
+ goto fail;
+
+ if (trans->blocks_used >= 4096) {
+ ret = btrfs_commit_transaction(trans, cur_root);
+ BUG_ON(ret);
+ trans = btrfs_start_transaction(cur_root, 1);
+ BUG_ON(!trans);
+ }
+ }
+ btrfs_release_path(cur_root, &path);
+
+ ret = btrfs_commit_transaction(trans, cur_root);
+ BUG_ON(ret);
+
+ if (num_refs > 0 && pass++ < 4)
+ goto again;
+
+ ret = (num_refs > 0) ? -1 : 0;
+fail:
+ btrfs_release_path(cur_root, &path);
+ extent_io_tree_cleanup(&reloc_tree);
+ return ret;
+}
+
+/*
+ * relocate data in system chunk
+ */
+static int cleanup_sys_chunk(struct btrfs_root *fs_root,
+ struct btrfs_root *ext2_root)
+{
+ struct btrfs_block_group_cache *cache;
+ int ret = 0;
+ u64 offset = 0;
+ u64 end_byte;
+
+ while(1) {
+ cache = btrfs_lookup_block_group(fs_root->fs_info, offset);
+ if (!cache)
+ break;
+
+ end_byte = cache->key.objectid + cache->key.offset;
+ if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
+ ret = relocate_extents_range(fs_root, ext2_root,
+ cache->key.objectid,
+ end_byte);
+ if (ret)
+ goto fail;
+ }
+ offset = end_byte;
+ }
+fail:
+ return ret;
+}
+
+static int fixup_chunk_mapping(struct btrfs_root *root)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_fs_info *info = root->fs_info;
+ struct btrfs_root *chunk_root = info->chunk_root;
+ struct extent_buffer *leaf;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_chunk chunk;
+ unsigned long ptr;
+ u32 size;
+ u64 type;
+ int ret;
+
+ btrfs_init_path(&path);
+
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(!trans);
+
+ /*
+ * recow the whole chunk tree. this will move all chunk tree blocks
+ * into system block group.
+ */
+ memset(&key, 0, sizeof(key));
+ while (1) {
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = btrfs_next_leaf(chunk_root, &path);
+ if (ret < 0)
+ goto err;
+ if (ret > 0)
+ break;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ btrfs_release_path(chunk_root, &path);
+ }
+ btrfs_release_path(chunk_root, &path);
+
+ /* fixup the system chunk array in super block */
+ btrfs_set_super_sys_array_size(&info->super_copy, 0);
+
+ key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+ key.offset = 0;
+ key.type = BTRFS_CHUNK_ITEM_KEY;
+
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto err;
+ BUG_ON(ret != 0);
+ while(1) {
+ leaf = path.nodes[0];
+ if (path.slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(chunk_root, &path);
+ if (ret < 0)
+ goto err;
+ if (ret > 0)
+ break;
+ leaf = path.nodes[0];
+ }
+ btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
+ if (key.type != BTRFS_CHUNK_ITEM_KEY)
+ goto next;
+
+ ptr = btrfs_item_ptr_offset(leaf, path.slots[0]);
+ size = btrfs_item_size_nr(leaf, path.slots[0]);
+ BUG_ON(size != sizeof(chunk));
+ read_extent_buffer(leaf, &chunk, ptr, size);
+ type = btrfs_stack_chunk_type(&chunk);
+
+ if (!(type & BTRFS_BLOCK_GROUP_SYSTEM))
+ goto next;
+
+ ret = btrfs_add_system_chunk(trans, chunk_root, &key,
+ &chunk, size);
+ if (ret)
+ goto err;
+next:
+ path.slots[0]++;
+ }
+
+ ret = btrfs_commit_transaction(trans, root);
+ BUG_ON(ret);
+err:
+ btrfs_release_path(chunk_root, &path);
+ return ret;
+}
+
int do_convert(const char *devname, int datacsum, int packing, int noxattr)
{
int i, fd, ret;
@@ -1570,7 +2168,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
fprintf(stderr, "filetype feature is missing\n");
goto fail;
}
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < 6; i++) {
ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
if (ret) {
fprintf(stderr, "not enough free space\n");
@@ -1584,22 +2182,25 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
fprintf(stderr, "unable to open %s\n", devname);
goto fail;
}
- ret = make_btrfs(fd, blocks, total_bytes, blocksize,
- blocksize, blocksize, blocksize);
+ ret = make_btrfs(fd, devname, ext2_fs->super->s_volume_name,
+ blocks, total_bytes, blocksize, blocksize,
+ blocksize, blocksize);
if (ret) {
fprintf(stderr, "unable to create initial ctree\n");
goto fail;
}
- root = open_ctree_fd(fd, super_bytenr);
- if (!root) {
- fprintf(stderr, "unable to open ctree\n");
+ /* create a system chunk that maps the whole device */
+ ret = prepare_system_chunk(fd, super_bytenr, blocksize);
+ if (ret) {
+ fprintf(stderr, "unable to update system chunk\n");
goto fail;
}
- fd = dup(fd);
- if (fd < 0) {
- fprintf(stderr, "unable to duplicate file descriptor\n");
+ root = open_ctree_fd(fd, devname, super_bytenr);
+ if (!root) {
+ fprintf(stderr, "unable to open ctree\n");
goto fail;
}
+
root->fs_info->priv_data = ext2_fs;
root->fs_info->extent_ops = &extent_ops;
ret = init_btrfs(root);
@@ -1624,6 +2225,12 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
fprintf(stderr, "error during create_ext2_image %d\n", ret);
goto fail;
}
+ printf("cleaning up system chunk.\n");
+ ret = cleanup_sys_chunk(root, ext2_root);
+ if (ret) {
+ fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret);
+ goto fail;
+ }
btrfs_free_fs_root(ext2_root->fs_info, ext2_root);
ret = close_ctree(root);
if (ret) {
@@ -1632,13 +2239,30 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
}
close_ext2fs(ext2_fs);
- /* finally migrate super block to its default postion */
+ /*
+ * If this step succeed, we get a mountable btrfs. Otherwise
+ * the ext2fs is left unchanged.
+ */
ret = migrate_super_block(fd, super_bytenr, blocksize);
if (ret) {
fprintf(stderr, "unable to migrate super block\n");
goto fail;
}
+
+ root = open_ctree_fd(fd, devname, 0);
+ if (!root) {
+ fprintf(stderr, "unable to open ctree\n");
+ goto fail;
+ }
+ /* move chunk tree into system chunk. */
+ ret = fixup_chunk_mapping(root);
+ if (ret) {
+ fprintf(stderr, "error during fixup_chunk_tree\n");
+ goto fail;
+ }
+ ret = close_ctree(root);
close(fd);
+
printf("conversion complete.\n");
return 0;
fail:
@@ -1646,20 +2270,62 @@ fail:
return -1;
}
+static int may_rollback(struct btrfs_root *root)
+{
+ struct btrfs_fs_info *info = root->fs_info;
+ struct btrfs_multi_bio *multi = NULL;
+ u64 bytenr;
+ u64 length;
+ u64 physical;
+ u64 total_bytes;
+ int num_stripes;
+ int ret;
+
+ if (btrfs_super_num_devices(&info->super_copy) != 1)
+ goto fail;
+
+ bytenr = BTRFS_SUPER_INFO_OFFSET;
+ total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
+
+ while (1) {
+ ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr,
+ &length, &multi, 0);
+ if (ret)
+ goto fail;
+
+ num_stripes = multi->num_stripes;
+ physical = multi->stripes[0].physical;
+ kfree(multi);
+
+ if (num_stripes != 1 || physical != bytenr)
+ goto fail;
+
+ bytenr += length;
+ if (bytenr >= total_bytes)
+ break;
+ }
+ return 0;
+fail:
+ return -1;
+}
+
int do_rollback(const char *devname, int force)
{
int fd;
int ret;
- int modified = 0;
struct btrfs_root *root;
struct btrfs_root *ext2_root;
+ struct btrfs_root *chunk_root;
struct btrfs_dir_item *dir;
struct btrfs_inode_item *inode;
struct btrfs_file_extent_item *fi;
- struct btrfs_timespec *tspec;
+ struct btrfs_trans_handle *trans;
struct extent_buffer *leaf;
+ struct btrfs_block_group_cache *cache1;
+ struct btrfs_block_group_cache *cache2;
struct btrfs_key key;
struct btrfs_path path;
+ struct extent_io_tree io_tree;
char *buf;
char *name;
u64 bytenr;
@@ -1667,29 +2333,33 @@ int do_rollback(const char *devname, int force)
u64 root_dir;
u64 objectid;
u64 offset;
+ u64 start;
+ u64 end;
+ u64 sb_bytenr;
u64 first_free;
- u64 last_trans;
u64 total_bytes;
+ u32 sectorsize;
+
+ extent_io_tree_init(&io_tree);
fd = open(devname, O_RDWR);
if (fd < 0) {
fprintf(stderr, "unable to open %s\n", devname);
goto fail;
}
- root = open_ctree_fd(fd, 0);
+ root = open_ctree_fd(fd, devname, 0);
if (!root) {
fprintf(stderr, "unable to open ctree\n");
goto fail;
}
- fd = dup(fd);
- if (fd < 0) {
- fprintf(stderr, "unable to duplicate file descriptor\n");
+ ret = may_rollback(root);
+ if (ret < 0) {
+ fprintf(stderr, "unable to do rollback\n");
goto fail;
}
- first_free = BTRFS_SUPER_INFO_OFFSET + root->sectorsize * 2 - 1;
- first_free &= ~((u64)root->sectorsize - 1);
- buf = malloc(first_free);
+ sectorsize = root->sectorsize;
+ buf = malloc(sectorsize);
if (!buf) {
fprintf(stderr, "unable to allocate memory\n");
goto fail;
@@ -1735,20 +2405,8 @@ int do_rollback(const char *devname, int force)
}
leaf = path.nodes[0];
inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item);
- tspec = btrfs_inode_otime(inode);
- /*
- * get image file size and transaction id stored in 'otime' field.
- * see comments in create_ext2_image.
- */
- last_trans = btrfs_timespec_sec(leaf, tspec);
- total_bytes = btrfs_timespec_nsec(leaf, tspec);
- total_bytes *= root->sectorsize;
+ total_bytes = btrfs_inode_size(leaf, inode);
btrfs_release_path(ext2_root, &path);
- if (total_bytes < first_free ||
- total_bytes != btrfs_inode_size(leaf, inode)) {
- fprintf(stderr, "image file size mismatch\n");
- goto fail;
- }
key.objectid = objectid;
key.offset = 0;
@@ -1759,6 +2417,8 @@ int do_rollback(const char *devname, int force)
btrfs_release_path(ext2_root, &path);
goto fail;
}
+
+ /* build mapping tree for the relocated blocks */
for (offset = 0; offset < total_bytes; ) {
leaf = path.nodes[0];
if (path.slots[0] >= btrfs_header_nritems(leaf)) {
@@ -1775,66 +2435,169 @@ int do_rollback(const char *devname, int force)
fi = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_file_extent_item);
- if (btrfs_file_extent_generation(leaf, fi) > last_trans) {
- modified = 1;
- break;
- }
if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG)
break;
- if (offset >= first_free)
- goto next;
-
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
- if (bytenr == 0)
- break;
+ /* skip holes and direct mapped extents */
+ if (bytenr == 0 || bytenr == offset)
+ goto next_extent;
+
bytenr += btrfs_file_extent_offset(leaf, fi);
num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
- if (num_bytes > first_free - offset)
- num_bytes = first_free - offset;
- ret = pread(fd, buf + offset, num_bytes, bytenr);
- if (ret != num_bytes) {
- fprintf(stderr, "unable to read required data\n");
- btrfs_release_path(ext2_root, &path);
- goto fail;
- }
-next:
+ cache1 = btrfs_lookup_block_group(root->fs_info, offset);
+ cache2 = btrfs_lookup_block_group(root->fs_info,
+ offset + num_bytes - 1);
+ if (!cache1 || cache1 != cache2 ||
+ !(cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM))
+ break;
+
+ set_extent_bits(&io_tree, offset, offset + num_bytes - 1,
+ EXTENT_LOCKED, GFP_NOFS);
+ set_state_private(&io_tree, offset, bytenr);
+next_extent:
offset += btrfs_file_extent_num_bytes(leaf, fi);
path.slots[0]++;
}
btrfs_release_path(ext2_root, &path);
+ btrfs_free_fs_root(ext2_root->fs_info, ext2_root);
- if (modified) {
- fprintf(stderr, "image file has been modified\n");
+ if (offset < total_bytes) {
+ fprintf(stderr, "unable to build extent mapping\n");
goto fail;
}
- if (offset < total_bytes) {
- fprintf(stderr, "unable to check all file extents\n");
+
+ first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1;
+ first_free &= ~((u64)sectorsize - 1);
+ /* backup for extent #0 should exist */
+ if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) {
+ fprintf(stderr, "no backup for the first extent\n");
goto fail;
}
+ /* force no allocation from system block group */
+ root->fs_info->system_allocs = -1;
+ trans = btrfs_start_transaction(root, 1);
+ BUG_ON(!trans);
+ /*
+ * recow the whole chunk tree, this will remove all chunk tree blocks
+ * from system block group
+ */
+ chunk_root = root->fs_info->chunk_root;
+ memset(&key, 0, sizeof(key));
+ while (1) {
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
+ if (ret < 0)
+ break;
+
+ ret = btrfs_next_leaf(chunk_root, &path);
+ if (ret)
+ break;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ btrfs_release_path(chunk_root, &path);
+ }
+ btrfs_release_path(chunk_root, &path);
+
+ offset = 0;
+ num_bytes = 0;
+ while(1) {
+ cache1 = btrfs_lookup_block_group(root->fs_info, offset);
+ if (!cache1)
+ break;
+
+ if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM)
+ num_bytes += btrfs_block_group_used(&cache1->item);
+
+ offset = cache1->key.objectid + cache1->key.offset;
+ }
+ /* only extent #0 left in system block group? */
+ if (num_bytes > first_free) {
+ fprintf(stderr, "unable to empty system block group\n");
+ goto fail;
+ }
+ /* create a system chunk that maps the whole device */
+ ret = prepare_system_chunk_sb(&root->fs_info->super_copy);
+ if (ret) {
+ fprintf(stderr, "unable to update system chunk\n");
+ goto fail;
+ }
+
+ ret = btrfs_commit_transaction(trans, root);
+ BUG_ON(ret);
- btrfs_free_fs_root(ext2_root->fs_info, ext2_root);
ret = close_ctree(root);
if (ret) {
fprintf(stderr, "error during close_ctree %d\n", ret);
goto fail;
}
- ret = pwrite(fd, buf, first_free, 0);
+ sb_bytenr = (u64)-1;
+ /* copy all relocated blocks back */
+ while(1) {
+ ret = find_first_extent_bit(&io_tree, 0, &start, &end,
+ EXTENT_LOCKED);
+ if (ret)
+ break;
+
+ ret = get_state_private(&io_tree, start, &bytenr);
+ BUG_ON(ret);
+
+ clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED,
+ GFP_NOFS);
+
+ while (start <= end) {
+ if (start == BTRFS_SUPER_INFO_OFFSET) {
+ sb_bytenr = bytenr;
+ goto next_sector;
+ }
+ ret = pread(fd, buf, sectorsize, bytenr);
+ if (ret < 0) {
+ fprintf(stderr, "error during pread %d\n", ret);
+ goto fail;
+ }
+ BUG_ON(ret != sectorsize);
+ ret = pwrite(fd, buf, sectorsize, start);
+ if (ret < 0) {
+ fprintf(stderr, "error during pwrite %d\n", ret);
+ goto fail;
+ }
+ BUG_ON(ret != sectorsize);
+next_sector:
+ start += sectorsize;
+ bytenr += sectorsize;
+ }
+ }
+
+ ret = fsync(fd);
+ if (ret) {
+ fprintf(stderr, "error during fsync %d\n", ret);
+ goto fail;
+ }
+ /*
+ * finally, overwrite btrfs super block.
+ */
+ ret = pread(fd, buf, sectorsize, sb_bytenr);
+ if (ret < 0) {
+ fprintf(stderr, "error during pread %d\n", ret);
+ goto fail;
+ }
+ BUG_ON(ret != sectorsize);
+ ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
if (ret < 0) {
fprintf(stderr, "error during pwrite %d\n", ret);
goto fail;
}
- /* How to handle this case? */
- BUG_ON(ret != first_free);
+ BUG_ON(ret != sectorsize);
ret = fsync(fd);
if (ret) {
fprintf(stderr, "error during fsync %d\n", ret);
goto fail;
}
+
close(fd);
free(buf);
+ extent_io_tree_cleanup(&io_tree);
printf("rollback complete.\n");
return 0;
fail:
@@ -1842,23 +2605,6 @@ fail:
return -1;
}
-static void check_mounted(const char *name)
-{
- int mnt_flags;
- errcode_t ret;
-
- ret = ext2fs_check_if_mounted(name, &mnt_flags);
- if (ret) {
- fprintf(stderr, "ext2fs_check_if_mounted: %s\n",
- error_message(ret));
- exit(1);
- }
- if (mnt_flags & EXT2_MF_MOUNTED) {
- fprintf(stderr, "%s is mounted\n", name);
- exit(1);
- }
-}
-
static void print_usage(void)
{
printf("usage: btrfs-convert [-d] [-i] [-n] [-r] device\n");
@@ -1866,7 +2612,6 @@ static void print_usage(void)
printf("\t-i ignore xattrs and ACLs\n");
printf("\t-n disable packing of small files\n");
printf("\t-r roll back to ext2fs\n");
- exit(1);
}
int main(int argc, char *argv[])
@@ -1899,12 +2644,17 @@ int main(int argc, char *argv[])
}
}
argc = argc - optind;
- if (argc == 1) {
- file = argv[optind];
- check_mounted(file);
- } else {
+ if (argc != 1) {
print_usage();
+ return 1;
}
+
+ file = argv[optind];
+ if (check_mounted(file)) {
+ fprintf(stderr, "%s is mounted\n", file);
+ return 1;
+ }
+
if (rollback) {
ret = do_rollback(file, 0);
} else {