#include "utils.h"
#include "task-utils.h"
+#if BTRFSCONVERT_EXT2
#include <ext2fs/ext2_fs.h>
#include <ext2fs/ext2fs.h>
#include <ext2fs/ext2_ext_attr.h>
#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
-#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
/*
* Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag
#define EXT2FS_B2C(fs, blk) (blk)
#endif
+#endif
+
+#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+
struct task_ctx {
uint32_t max_copy_inodes;
uint32_t cur_copy_inodes;
struct btrfs_root *root, int datacsum,
int packing, int noxattr, struct task_ctx *p);
void (*close_fs)(struct btrfs_convert_context *cctx);
+ int (*check_state)(struct btrfs_convert_context *cctx);
};
static void init_convert_context(struct btrfs_convert_context *cctx)
cctx->convert_ops->close_fs(cctx);
}
+static inline int convert_check_state(struct btrfs_convert_context *cctx)
+{
+ return cctx->convert_ops->check_state(cctx);
+}
+
static int intersect_with_sb(u64 bytenr, u64 num_bytes)
{
int i;
int ret = 0;
struct btrfs_root *root = data->root;
struct btrfs_root *convert_root = data->convert_root;
- struct btrfs_path *path;
+ struct btrfs_path path;
u64 file_pos = file_block * root->sectorsize;
u64 old_disk_bytenr = disk_block * root->sectorsize;
u64 num_bytes = num_blocks * root->sectorsize;
data->objectid, data->inode, file_pos, 0,
num_bytes);
- path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ btrfs_init_path(&path);
/*
* Search real disk bytenr from convert root
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = cur_off;
- ret = btrfs_search_slot(NULL, convert_root, &key, path, 0, 0);
+ ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0);
if (ret < 0)
break;
if (ret > 0) {
- ret = btrfs_previous_item(convert_root, path,
+ ret = btrfs_previous_item(convert_root, &path,
data->convert_ino,
BTRFS_EXTENT_DATA_KEY);
if (ret < 0)
break;
}
}
- node = path->nodes[0];
- slot = path->slots[0];
+ node = path.nodes[0];
+ slot = path.slots[0];
btrfs_item_key_to_cpu(node, &key, slot);
BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY ||
key.objectid != data->convert_ino ||
extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
BUG_ON(cur_off - key.offset >= extent_num_bytes);
- btrfs_release_path(path);
+ btrfs_release_path(&path);
if (extent_disk_bytenr)
real_disk_bytenr = cur_off - key.offset +
* need to waste CPU cycles now.
*/
}
- btrfs_free_path(path);
+ btrfs_release_path(&path);
return ret;
}
int i;
int ret;
- BUG_ON(bytenr != round_down(bytenr, root->sectorsize));
- BUG_ON(len != round_down(len, root->sectorsize));
+ if (bytenr != round_down(bytenr, root->sectorsize)) {
+ error("bytenr not sectorsize aligned: %llu",
+ (unsigned long long)bytenr);
+ return -EINVAL;
+ }
+ if (len != round_down(len, root->sectorsize)) {
+ error("length not sectorsize aligned: %llu",
+ (unsigned long long)len);
+ return -EINVAL;
+ }
len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE);
/*
bg_cache->key.offset - bytenr);
}
- BUG_ON(len != round_down(len, root->sectorsize));
+ if (len != round_down(len, root->sectorsize)) {
+ error("remaining length not sectorsize aligned: %llu",
+ (unsigned long long)len);
+ return -EINVAL;
+ }
ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr,
disk_bytenr, len);
if (ret < 0)
{
struct btrfs_inode_item buf;
struct btrfs_trans_handle *trans;
- struct btrfs_path *path = NULL;
+ struct btrfs_path path;
struct btrfs_key key;
struct cache_extent *cache;
struct cache_tree used_tmp;
return -ENOMEM;
cache_tree_init(&used_tmp);
+ btrfs_init_path(&path);
ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID,
&ino);
if (ret < 0)
goto out;
- path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
ret = (ret > 0 ? -ENOENT : ret);
goto out;
}
- read_extent_buffer(path->nodes[0], &buf,
- btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ read_extent_buffer(path.nodes[0], &buf,
+ btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
sizeof(buf));
- btrfs_release_path(path);
+ btrfs_release_path(&path);
/*
* Create a new used space cache, which doesn't contain the reserved
key.objectid = ino;
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+ ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
if (ret) {
ret = (ret > 0 ? -ENOENT : ret);
goto out;
}
btrfs_set_stack_inode_size(&buf, cfg->num_bytes);
- write_extent_buffer(path->nodes[0], &buf,
- btrfs_item_ptr_offset(path->nodes[0], path->slots[0]),
+ write_extent_buffer(path.nodes[0], &buf,
+ btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
sizeof(buf));
out:
free_extent_cache_tree(&used_tmp);
- btrfs_free_path(path);
+ btrfs_release_path(&path);
btrfs_commit_transaction(trans, root);
return ret;
}
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *new_root = NULL;
- struct btrfs_path *path;
+ struct btrfs_path path;
struct btrfs_inode_item *inode_item;
struct extent_buffer *leaf;
struct btrfs_key key;
if (len == 0 || len > BTRFS_NAME_LEN)
return NULL;
- path = btrfs_alloc_path();
- BUG_ON(!path);
-
+ btrfs_init_path(&path);
key.objectid = dirid;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = (u64)-1;
- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
- BUG_ON(ret <= 0);
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret <= 0) {
+ error("search for DIR_INDEX dirid %llu failed: %d",
+ (unsigned long long)dirid, ret);
+ goto fail;
+ }
- if (path->slots[0] > 0) {
- path->slots[0]--;
- btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ if (path.slots[0] > 0) {
+ path.slots[0]--;
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY)
index = key.offset + 1;
}
- btrfs_release_path(path);
+ btrfs_release_path(&path);
trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
+ if (!trans) {
+ error("unable to start transaction");
+ goto fail;
+ }
key.objectid = dirid;
key.offset = 0;
key.type = BTRFS_INODE_ITEM_KEY;
- ret = btrfs_lookup_inode(trans, root, path, &key, 1);
- BUG_ON(ret);
- leaf = path->nodes[0];
- inode_item = btrfs_item_ptr(leaf, path->slots[0],
+ ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
+ if (ret) {
+ error("search for INODE_ITEM %llu failed: %d",
+ (unsigned long long)dirid, ret);
+ goto fail;
+ }
+ leaf = path.nodes[0];
+ inode_item = btrfs_item_ptr(leaf, path.slots[0],
struct btrfs_inode_item);
key.objectid = root_objectid;
btrfs_set_inode_size(leaf, inode_item, len * 2 +
btrfs_inode_size(leaf, inode_item));
btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(path);
+ btrfs_release_path(&path);
/* add the backref first */
ret = btrfs_add_root_ref(trans, tree_root, root_objectid,
BTRFS_ROOT_BACKREF_KEY,
root->root_key.objectid,
dirid, index, buf, len);
- BUG_ON(ret);
+ if (ret) {
+ error("unable to add root backref for %llu: %d",
+ root->root_key.objectid, ret);
+ goto fail;
+ }
/* now add the forward ref */
ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid,
BTRFS_ROOT_REF_KEY, root_objectid,
dirid, index, buf, len);
+ if (ret) {
+ error("unable to add root ref for %llu: %d",
+ root->root_key.objectid, ret);
+ goto fail;
+ }
ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
+ if (ret) {
+ error("transaction commit failed: %d", ret);
+ goto fail;
+ }
new_root = btrfs_read_fs_root(fs_info, &key);
- if (IS_ERR(new_root))
+ if (IS_ERR(new_root)) {
+ error("unable to fs read root: %lu", PTR_ERR(new_root));
new_root = NULL;
+ }
fail:
- btrfs_free_path(path);
+ btrfs_init_path(&path);
return new_root;
}
ret = btrfs_copy_root(trans, root, root->node, &tmp,
root_objectid);
- BUG_ON(ret);
+ if (ret)
+ return ret;
memcpy(&root_item, &root->root_item, sizeof(root_item));
btrfs_set_root_bytenr(&root_item, tmp->start);
key.offset = (u64)-1;
new_root = btrfs_read_fs_root(root->fs_info, &key);
- BUG_ON(!new_root || IS_ERR(new_root));
+ if (!new_root || IS_ERR(new_root)) {
+ error("unable to fs read root: %lu", PTR_ERR(new_root));
+ return PTR_ERR(new_root);
+ }
ret = btrfs_make_root_dir(trans, new_root, BTRFS_FIRST_FREE_OBJECTID);
- BUG_ON(ret);
+ return ret;
+}
+
+/*
+ * New make_btrfs() has handle system and meta chunks quite well.
+ * So only need to add remaining data chunks.
+ */
+static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct btrfs_root *extent_root = fs_info->extent_root;
+ struct cache_tree *data_chunks = &cctx->data_chunks;
+ struct cache_extent *cache;
+ u64 max_chunk_size;
+ int ret = 0;
+
+ /*
+ * Don't create data chunk over 10% of the convert device
+ * And for single chunk, don't create chunk larger than 1G.
+ */
+ max_chunk_size = cfg->num_bytes / 10;
+ max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size);
+ max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize);
+
+ for (cache = first_cache_extent(data_chunks); cache;
+ cache = next_cache_extent(cache)) {
+ u64 cur = cache->start;
+
+ while (cur < cache->start + cache->size) {
+ u64 len;
+ u64 cur_backup = cur;
+
+ len = min(max_chunk_size,
+ cache->start + cache->size - cur);
+ ret = btrfs_alloc_data_chunk(trans, extent_root,
+ &cur_backup, len,
+ BTRFS_BLOCK_GROUP_DATA, 1);
+ if (ret < 0)
+ break;
+ ret = btrfs_make_block_group(trans, extent_root, 0,
+ BTRFS_BLOCK_GROUP_DATA,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+ cur, len);
+ if (ret < 0)
+ break;
+ cur += len;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Init the temp btrfs to a operational status.
+ *
+ * It will fix the extent usage accounting(XXX: Do we really need?) and
+ * insert needed data chunks, to ensure all old fs data extents are covered
+ * by DATA chunks, preventing wrong chunks are allocated.
+ *
+ * And also create convert image subvolume and relocation tree.
+ * (XXX: Not need again?)
+ * But the convert image subvolume is *NOT* linked to fs tree yet.
+ */
+static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root,
+ struct btrfs_convert_context *cctx, int datacsum,
+ int packing, int noxattr)
+{
+ struct btrfs_key location;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ int ret;
+
+ /*
+ * Don't alloc any metadata/system chunk, as we don't want
+ * any meta/sys chunk allcated before all data chunks are inserted.
+ * Or we screw up the chunk layout just like the old implement.
+ */
+ fs_info->avoid_sys_chunk_alloc = 1;
+ fs_info->avoid_meta_chunk_alloc = 1;
+ trans = btrfs_start_transaction(root, 1);
+ if (!trans) {
+ error("unable to start transaction");
+ ret = -EINVAL;
+ goto err;
+ }
+ ret = btrfs_fix_block_accounting(trans, root);
+ if (ret)
+ goto err;
+ ret = make_convert_data_block_groups(trans, fs_info, cfg, cctx);
+ if (ret)
+ goto err;
+ ret = btrfs_make_root_dir(trans, fs_info->tree_root,
+ BTRFS_ROOT_TREE_DIR_OBJECTID);
+ if (ret)
+ goto err;
+ memcpy(&location, &root->root_key, sizeof(location));
+ location.offset = (u64)-1;
+ ret = btrfs_insert_dir_item(trans, fs_info->tree_root, "default", 7,
+ btrfs_super_root_dir(fs_info->super_copy),
+ &location, BTRFS_FT_DIR, 0);
+ if (ret)
+ goto err;
+ ret = btrfs_insert_inode_ref(trans, fs_info->tree_root, "default", 7,
+ location.objectid,
+ btrfs_super_root_dir(fs_info->super_copy), 0);
+ if (ret)
+ goto err;
+ btrfs_set_root_dirid(&fs_info->fs_root->root_item,
+ BTRFS_FIRST_FREE_OBJECTID);
+
+ /* subvol for fs image file */
+ ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
+ if (ret < 0) {
+ error("failed to create subvolume image root: %d", ret);
+ goto err;
+ }
+ /* subvol for data relocation tree */
+ ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
+ if (ret < 0) {
+ error("failed to create DATA_RELOC root: %d", ret);
+ goto err;
+ }
+
+ ret = btrfs_commit_transaction(trans, root);
+ fs_info->avoid_sys_chunk_alloc = 0;
+ fs_info->avoid_meta_chunk_alloc = 0;
+err:
+ return ret;
+}
+
+/*
+ * Migrate super block to its default position and zero 0 ~ 16k
+ */
+static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
+{
+ int ret;
+ struct extent_buffer *buf;
+ struct btrfs_super_block *super;
+ u32 len;
+ u32 bytenr;
+
+ buf = malloc(sizeof(*buf) + sectorsize);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->len = sectorsize;
+ ret = pread(fd, buf->data, sectorsize, old_bytenr);
+ if (ret != sectorsize)
+ goto fail;
+
+ super = (struct btrfs_super_block *)buf->data;
+ BUG_ON(btrfs_super_bytenr(super) != old_bytenr);
+ btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET);
+
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+ ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET);
+ if (ret != sectorsize)
+ goto fail;
+
+ ret = fsync(fd);
+ if (ret)
+ goto fail;
+
+ 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->data, len, bytenr);
+ if (ret != len) {
+ fprintf(stderr, "unable to zero fill device\n");
+ break;
+ }
+ bytenr += len;
+ }
+ ret = 0;
+ fsync(fd);
+fail:
+ free(buf);
+ if (ret > 0)
+ ret = -1;
+ 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, BTRFS_STRIPE_LEN);
+ 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;
+ btrfs_set_stack_stripe_offset(&chunk->stripe, 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;
}
+#if BTRFSCONVERT_EXT2
+
/*
* Open Ext2fs in readonly mode, read block allocation bitmap and
* inode bitmap into memory.
if (!(ext2_fs->super->s_feature_incompat &
EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- fprintf(stderr, "filetype feature is missing\n");
+ error("filetype feature is missing");
goto fail;
}
}
memset(&dst->reserved, 0, sizeof(dst->reserved));
}
+static int ext2_check_state(struct btrfs_convert_context *cctx)
+{
+ ext2_filsys fs = cctx->fs_data;
+
+ if (!(fs->super->s_state & EXT2_VALID_FS))
+ return 1;
+ else if (fs->super->s_state & EXT2_ERROR_FS)
+ return 1;
+ else
+ return 0;
+}
+
+/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */
+#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \
+ if (ext2_inode->i_flags & EXT2_##name##_FL) \
+ flags |= BTRFS_INODE_##name; \
+})
+
+/*
+ * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags
+ *
+ * Only a subset of EXT_*_FL is supported in btrfs.
+ */
+static void ext2_convert_inode_flags(struct btrfs_inode_item *dst,
+ struct ext2_inode *src)
+{
+ u64 flags = 0;
+
+ COPY_ONE_EXT2_FLAG(flags, src, APPEND);
+ COPY_ONE_EXT2_FLAG(flags, src, SYNC);
+ COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE);
+ COPY_ONE_EXT2_FLAG(flags, src, NODUMP);
+ COPY_ONE_EXT2_FLAG(flags, src, NOATIME);
+ COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC);
+ btrfs_set_stack_inode_flags(dst, flags);
+}
/*
* copy a single inode. do all the required works, such as cloning
BTRFS_INODE_NODATASUM;
btrfs_set_stack_inode_flags(&btrfs_inode, flags);
}
+ ext2_convert_inode_flags(&btrfs_inode, ext2_inode);
switch (ext2_inode->i_mode & S_IFMT) {
case S_IFREG:
return ret;
}
-/*
- * New make_btrfs() has handle system and meta chunks quite well.
- * So only need to add remaining data chunks.
- */
-static int make_convert_data_block_groups(struct btrfs_trans_handle *trans,
- struct btrfs_fs_info *fs_info,
- struct btrfs_mkfs_config *cfg,
- struct btrfs_convert_context *cctx)
-{
- struct btrfs_root *extent_root = fs_info->extent_root;
- struct cache_tree *data_chunks = &cctx->data_chunks;
- struct cache_extent *cache;
- u64 max_chunk_size;
- int ret = 0;
-
- /*
- * Don't create data chunk over 10% of the convert device
- * And for single chunk, don't create chunk larger than 1G.
- */
- max_chunk_size = cfg->num_bytes / 10;
- max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size);
- max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize);
-
- for (cache = first_cache_extent(data_chunks); cache;
- cache = next_cache_extent(cache)) {
- u64 cur = cache->start;
-
- while (cur < cache->start + cache->size) {
- u64 len;
- u64 cur_backup = cur;
-
- len = min(max_chunk_size,
- cache->start + cache->size - cur);
- ret = btrfs_alloc_data_chunk(trans, extent_root,
- &cur_backup, len,
- BTRFS_BLOCK_GROUP_DATA, 1);
- if (ret < 0)
- break;
- ret = btrfs_make_block_group(trans, extent_root, 0,
- BTRFS_BLOCK_GROUP_DATA,
- BTRFS_FIRST_CHUNK_TREE_OBJECTID,
- cur, len);
- if (ret < 0)
- break;
- cur += len;
- }
- }
- return ret;
-}
-
-/*
- * Init the temp btrfs to a operational status.
- *
- * It will fix the extent usage accounting(XXX: Do we really need?) and
- * insert needed data chunks, to ensure all old fs data extents are covered
- * by DATA chunks, preventing wrong chunks are allocated.
- *
- * And also create convert image subvolume and relocation tree.
- * (XXX: Not need again?)
- * But the convert image subvolume is *NOT* linked to fs tree yet.
- */
-static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root,
- struct btrfs_convert_context *cctx, int datacsum,
- int packing, int noxattr)
-{
- struct btrfs_key location;
- struct btrfs_trans_handle *trans;
- struct btrfs_fs_info *fs_info = root->fs_info;
- int ret;
-
- /*
- * Don't alloc any metadata/system chunk, as we don't want
- * any meta/sys chunk allcated before all data chunks are inserted.
- * Or we screw up the chunk layout just like the old implement.
- */
- fs_info->avoid_sys_chunk_alloc = 1;
- fs_info->avoid_meta_chunk_alloc = 1;
- trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
- ret = btrfs_fix_block_accounting(trans, root);
- if (ret)
- goto err;
- ret = make_convert_data_block_groups(trans, fs_info, cfg, cctx);
- if (ret)
- goto err;
- ret = btrfs_make_root_dir(trans, fs_info->tree_root,
- BTRFS_ROOT_TREE_DIR_OBJECTID);
- if (ret)
- goto err;
- memcpy(&location, &root->root_key, sizeof(location));
- location.offset = (u64)-1;
- ret = btrfs_insert_dir_item(trans, fs_info->tree_root, "default", 7,
- btrfs_super_root_dir(fs_info->super_copy),
- &location, BTRFS_FT_DIR, 0);
- if (ret)
- goto err;
- ret = btrfs_insert_inode_ref(trans, fs_info->tree_root, "default", 7,
- location.objectid,
- btrfs_super_root_dir(fs_info->super_copy), 0);
- if (ret)
- goto err;
- btrfs_set_root_dirid(&fs_info->fs_root->root_item,
- BTRFS_FIRST_FREE_OBJECTID);
-
- /* subvol for fs image file */
- ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
- if (ret < 0)
- goto err;
- /* subvol for data relocation tree */
- ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
- if (ret < 0)
- goto err;
-
- ret = btrfs_commit_transaction(trans, root);
- fs_info->avoid_sys_chunk_alloc = 0;
- fs_info->avoid_meta_chunk_alloc = 0;
-err:
- return ret;
-}
-
-/*
- * Migrate super block to its default position and zero 0 ~ 16k
- */
-static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
-{
- int ret;
- struct extent_buffer *buf;
- struct btrfs_super_block *super;
- u32 len;
- u32 bytenr;
-
- BUG_ON(sectorsize < sizeof(*super));
- buf = malloc(sizeof(*buf) + sectorsize);
- if (!buf)
- return -ENOMEM;
-
- buf->len = sectorsize;
- ret = pread(fd, buf->data, sectorsize, old_bytenr);
- if (ret != sectorsize)
- goto fail;
-
- super = (struct btrfs_super_block *)buf->data;
- BUG_ON(btrfs_super_bytenr(super) != old_bytenr);
- btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET);
-
- csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET);
- if (ret != sectorsize)
- goto fail;
-
- ret = fsync(fd);
- if (ret)
- goto fail;
-
- 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->data, len, bytenr);
- if (ret != len) {
- fprintf(stderr, "unable to zero fill device\n");
- break;
- }
- bytenr += len;
- }
- ret = 0;
- fsync(fd);
-fail:
- free(buf);
- if (ret > 0)
- ret = -1;
- 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, BTRFS_STRIPE_LEN);
- 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;
- btrfs_set_stack_stripe_offset(&chunk->stripe, 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 const struct btrfs_convert_operations ext2_convert_ops = {
.name = "ext2",
.open_fs = ext2_open_fs,
.read_used_space = ext2_read_used_space,
.copy_inodes = ext2_copy_inodes,
.close_fs = ext2_close_fs,
+ .check_state = ext2_check_state,
};
+#endif
+
static const struct btrfs_convert_operations *convert_operations[] = {
+#if BTRFSCONVERT_EXT2
&ext2_convert_ops,
+#endif
};
static int convert_open_fs(const char *devname,
}
}
- fprintf(stderr, "No file system found to convert.\n");
+ error("no file system found to convert");
return -1;
}
{
int ret;
int fd = -1;
- int is_btrfs = 0;
u32 blocksize;
u64 total_bytes;
struct btrfs_root *root;
ret = convert_open_fs(devname, &cctx);
if (ret)
goto fail;
+ ret = convert_check_state(&cctx);
+ if (ret)
+ warning(
+ "source filesystem is not clean, running filesystem check is recommended");
ret = convert_read_used_space(&cctx);
if (ret)
goto fail;
blocksize = cctx.blocksize;
total_bytes = (u64)blocksize * (u64)cctx.block_count;
if (blocksize < 4096) {
- fprintf(stderr, "block size is too small\n");
+ error("block size is too small: %u < 4096", blocksize);
goto fail;
}
if (btrfs_check_nodesize(nodesize, blocksize, features))
goto fail;
fd = open(devname, O_RDWR);
if (fd < 0) {
- fprintf(stderr, "unable to open %s\n", devname);
+ error("unable to open %s: %s", devname, strerror(errno));
goto fail;
}
btrfs_parse_features_to_string(features_buf, features);
mkfs_cfg.stripesize = blocksize;
mkfs_cfg.features = features;
/* New convert need these space */
- mkfs_cfg.fs_uuid = malloc(BTRFS_UUID_UNPARSED_SIZE);
- mkfs_cfg.chunk_uuid = malloc(BTRFS_UUID_UNPARSED_SIZE);
- *(mkfs_cfg.fs_uuid) = '\0';
- *(mkfs_cfg.chunk_uuid) = '\0';
+ memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
+ memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE);
ret = make_btrfs(fd, &mkfs_cfg, &cctx);
if (ret) {
- fprintf(stderr, "unable to create initial ctree: %s\n",
- strerror(-ret));
+ error("unable to create initial ctree: %s", strerror(-ret));
goto fail;
}
root = open_ctree_fd(fd, devname, mkfs_cfg.super_bytenr,
OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
if (!root) {
- fprintf(stderr, "unable to open ctree\n");
+ error("unable to open ctree");
goto fail;
}
ret = init_btrfs(&mkfs_cfg, root, &cctx, datacsum, packing, noxattr);
if (ret) {
- fprintf(stderr, "unable to setup the root tree\n");
+ error("unable to setup the root tree: %d", ret);
goto fail;
}
- printf("creating %s image file.\n", cctx.convert_ops->name);
+ printf("creating %s image file\n", cctx.convert_ops->name);
ret = asprintf(&subvol_name, "%s_saved", cctx.convert_ops->name);
if (ret < 0) {
- fprintf(stderr, "error allocating subvolume name: %s_saved\n",
+ error("memory allocation failure for subvolume name: %s_saved",
cctx.convert_ops->name);
goto fail;
}
key.type = BTRFS_ROOT_ITEM_KEY;
image_root = btrfs_read_fs_root(root->fs_info, &key);
if (!image_root) {
- fprintf(stderr, "unable to create subvol\n");
+ error("unable to create image subvolume");
goto fail;
}
ret = create_image(image_root, &mkfs_cfg, &cctx, fd,
mkfs_cfg.num_bytes, "image", datacsum);
if (ret) {
- fprintf(stderr, "error during create_image %d\n", ret);
+ error("failed to create %s/image: %d", subvol_name, ret);
goto fail;
}
- printf("creating btrfs metadata.\n");
+ printf("creating btrfs metadata");
ctx.max_copy_inodes = (cctx.inodes_count - cctx.free_inodes_count);
ctx.cur_copy_inodes = 0;
}
ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx);
if (ret) {
- fprintf(stderr, "error during copy_inodes %d\n", ret);
+ error("error during copy_inodes %d", ret);
goto fail;
}
if (progress) {
}
image_root = link_subvol(root, subvol_name, CONV_IMAGE_SUBVOL_OBJECTID);
+ if (!image_root) {
+ error("unable to link subvolume %s", subvol_name);
+ goto fail;
+ }
free(subvol_name);
if (copylabel == 1) {
__strncpy_null(root->fs_info->super_copy->label,
cctx.volume_name, BTRFS_LABEL_SIZE - 1);
- fprintf(stderr, "copy label '%s'\n",
- root->fs_info->super_copy->label);
+ printf("copy label '%s'\n", root->fs_info->super_copy->label);
} else if (copylabel == -1) {
strcpy(root->fs_info->super_copy->label, fslabel);
- fprintf(stderr, "set label to '%s'\n", fslabel);
+ printf("set label to '%s'\n", fslabel);
}
ret = close_ctree(root);
if (ret) {
- fprintf(stderr, "error during close_ctree %d\n", ret);
+ error("close_ctree failed: %d", ret);
goto fail;
}
convert_close_fs(&cctx);
*/
ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize);
if (ret) {
- fprintf(stderr, "unable to migrate super block\n");
+ error("unable to migrate super block: %d", ret);
goto fail;
}
- is_btrfs = 1;
root = open_ctree_fd(fd, devname, 0,
OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
if (!root) {
- fprintf(stderr, "unable to open ctree\n");
+ error("unable to open ctree for finalization");
goto fail;
}
root->fs_info->finalize_on_close = 1;
close_ctree(root);
close(fd);
- printf("conversion complete.\n");
+ printf("conversion complete");
return 0;
fail:
clean_convert_context(&cctx);
if (fd != -1)
close(fd);
- if (is_btrfs)
- fprintf(stderr,
- "WARNING: an error occurred during chunk mapping fixup, filesystem mountable but not finalized\n");
- else
- fprintf(stderr, "conversion aborted\n");
+ warning(
+"an error occurred during conversion, filesystem is partially created but not finalized and not mountable");
return -1;
}
num_stripes = multi->num_stripes;
physical = multi->stripes[0].physical;
- kfree(multi);
+ free(multi);
if (num_stripes != 1) {
error("num stripes for bytenr %llu is not 1", bytenr);
fd = open(devname, O_RDWR);
if (fd < 0) {
- fprintf(stderr, "unable to open %s\n", devname);
+ error("unable to open %s: %s", devname, strerror(errno));
goto fail;
}
root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES);
if (!root) {
- fprintf(stderr, "unable to open ctree\n");
+ error("unable to open ctree");
goto fail;
}
ret = may_rollback(root);
if (ret < 0) {
- fprintf(stderr, "unable to do rollback\n");
+ error("unable to do rollback: %d", ret);
goto fail;
}
sectorsize = root->sectorsize;
buf = malloc(sectorsize);
if (!buf) {
- fprintf(stderr, "unable to allocate memory\n");
+ error("unable to allocate memory");
goto fail;
}
0);
btrfs_release_path(&path);
if (ret > 0) {
- fprintf(stderr,
- "ERROR: unable to convert ext2 image subvolume, is it deleted?\n");
+ error("unable to convert ext2 image subvolume, is it deleted?");
goto fail;
} else if (ret < 0) {
- fprintf(stderr,
- "ERROR: unable to open ext2_saved, id=%llu: %s\n",
+ error("unable to open ext2_saved, id %llu: %s",
(unsigned long long)key.objectid, strerror(-ret));
goto fail;
}
key.offset = (u64)-1;
image_root = btrfs_read_fs_root(root->fs_info, &key);
if (!image_root || IS_ERR(image_root)) {
- fprintf(stderr, "unable to open subvol %llu\n",
- (unsigned long long)key.objectid);
+ error("unable to open subvolume %llu: %ld",
+ (unsigned long long)key.objectid, PTR_ERR(image_root));
goto fail;
}
dir = btrfs_lookup_dir_item(NULL, image_root, &path,
root_dir, name, strlen(name), 0);
if (!dir || IS_ERR(dir)) {
- fprintf(stderr, "unable to find file %s\n", name);
+ error("unable to find file %s: %ld", name, PTR_ERR(dir));
goto fail;
}
leaf = path.nodes[0];
ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0);
if (ret) {
- fprintf(stderr, "unable to find inode item\n");
+ error("unable to find inode item: %d", ret);
goto fail;
}
leaf = path.nodes[0];
key.objectid = objectid;
key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
+ key.type = BTRFS_EXTENT_DATA_KEY;
ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0);
if (ret != 0) {
- fprintf(stderr, "unable to find first file extent\n");
+ error("unable to find first file extent: %d", ret);
btrfs_release_path(&path);
goto fail;
}
btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
if (key.objectid != objectid || key.offset != offset ||
- btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY)
+ key.type != BTRFS_EXTENT_DATA_KEY)
break;
fi = btrfs_item_ptr(leaf, path.slots[0],
btrfs_release_path(&path);
if (offset < total_bytes) {
- fprintf(stderr, "unable to build extent mapping\n");
- fprintf(stderr, "converted filesystem after balance is unable to rollback\n");
+ error("unable to build extent mapping (offset %llu, total_bytes %llu)",
+ (unsigned long long)offset,
+ (unsigned long long)total_bytes);
+ error("converted filesystem after balance is unable to rollback");
goto fail;
}
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");
+ error("no backup for the first extent");
goto fail;
}
/* force no allocation from system block group */
root->fs_info->system_allocs = -1;
trans = btrfs_start_transaction(root, 1);
- BUG_ON(!trans);
+ if (!trans) {
+ error("unable to start transaction");
+ goto fail;
+ }
/*
* recow the whole chunk tree, this will remove all chunk tree blocks
* from system block group
}
/* only extent #0 left in system block group? */
if (num_bytes > first_free) {
- fprintf(stderr, "unable to empty system block group\n");
+ error(
+ "unable to empty system block group (num_bytes %llu, first_free %llu",
+ (unsigned long long)num_bytes,
+ (unsigned long long)first_free);
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");
+ error("unable to update system chunk: %d", ret);
goto fail;
}
ret = btrfs_commit_transaction(trans, root);
- BUG_ON(ret);
+ if (ret) {
+ error("transaction commit failed: %d", ret);
+ goto fail;
+ }
ret = close_ctree(root);
if (ret) {
- fprintf(stderr, "error during close_ctree %d\n", ret);
+ error("close_ctree failed: %d", ret);
goto fail;
}
break;
ret = pwrite(fd, buf, sectorsize, bytenr);
if (ret != sectorsize) {
- fprintf(stderr,
- "error during zeroing superblock %d: %d\n",
- i, ret);
+ error("zeroing superblock mirror %d failed: %d",
+ i, ret);
goto fail;
}
}
}
ret = pread(fd, buf, sectorsize, bytenr);
if (ret < 0) {
- fprintf(stderr, "error during pread %d\n", ret);
+ error("reading superblock at %llu failed: %d",
+ (unsigned long long)bytenr, ret);
goto fail;
}
BUG_ON(ret != sectorsize);
ret = pwrite(fd, buf, sectorsize, start);
if (ret < 0) {
- fprintf(stderr, "error during pwrite %d\n", ret);
+ error("writing superblock at %llu failed: %d",
+ (unsigned long long)start, ret);
goto fail;
}
BUG_ON(ret != sectorsize);
}
ret = fsync(fd);
- if (ret) {
- fprintf(stderr, "error during fsync %d\n", ret);
+ if (ret < 0) {
+ error("fsync failed: %s", strerror(errno));
goto fail;
}
/*
*/
ret = pread(fd, buf, sectorsize, sb_bytenr);
if (ret < 0) {
- fprintf(stderr, "error during pread %d\n", ret);
+ error("reading primary superblock failed: %s",
+ strerror(errno));
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);
+ error("writing primary superblock failed: %s",
+ strerror(errno));
goto fail;
}
BUG_ON(ret != sectorsize);
ret = fsync(fd);
- if (ret) {
- fprintf(stderr, "error during fsync %d\n", ret);
+ if (ret < 0) {
+ error("fsync failed: %s", strerror(errno));
goto fail;
}
close(fd);
free(buf);
extent_io_tree_cleanup(&io_tree);
- printf("rollback complete.\n");
+ printf("rollback complete\n");
return 0;
fail:
if (fd != -1)
close(fd);
free(buf);
- fprintf(stderr, "rollback aborted.\n");
+ error("rollback aborted");
return -1;
}
printf("\t-p|--progress show converting progress (default)\n");
printf("\t-O|--features LIST comma separated list of filesystem features\n");
printf("\t--no-progress show only overview, not the detailed progress\n");
+ printf("\n");
+ printf("Supported filesystems:\n");
+ printf("\text2/3/4: %s\n", BTRFSCONVERT_EXT2 ? "yes" : "no");
}
int main(int argc, char *argv[])
case 'l':
copylabel = -1;
if (strlen(optarg) >= BTRFS_LABEL_SIZE) {
- fprintf(stderr,
- "WARNING: label too long, trimmed to %d bytes\n",
+ warning(
+ "label too long, trimmed to %d bytes",
BTRFS_LABEL_SIZE - 1);
}
__strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1);
tmp = btrfs_parse_fs_features(tmp, &features);
if (tmp) {
- fprintf(stderr,
- "Unrecognized filesystem feature '%s'\n",
+ error("unrecognized filesystem feature: %s",
tmp);
free(orig);
exit(1);
btrfs_parse_features_to_string(buf,
features & ~BTRFS_CONVERT_ALLOWED_FEATURES);
- fprintf(stderr,
- "ERROR: features not allowed for convert: %s\n",
+ error("features not allowed for convert: %s",
buf);
exit(1);
}
file = argv[optind];
ret = check_mounted(file);
if (ret < 0) {
- fprintf(stderr, "Could not check mount status: %s\n",
- strerror(-ret));
+ error("could not check mount status: %s", strerror(-ret));
return 1;
} else if (ret) {
- fprintf(stderr, "%s is mounted\n", file);
+ error("%s is mounted", file);
return 1;
}