summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--btrfs-convert.c260
1 files changed, 202 insertions, 58 deletions
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 02e5cdba..00bac51c 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -77,11 +77,83 @@ static int after_copied_inodes(void *p)
return 0;
}
+struct btrfs_convert_context;
+struct btrfs_convert_operations {
+ const char *name;
+ int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname);
+ int (*alloc_block)(struct btrfs_convert_context *cctx, u64 goal,
+ u64 *block_ret);
+ int (*alloc_block_range)(struct btrfs_convert_context *cctx, u64 goal,
+ int num, u64 *block_ret);
+ int (*test_block)(struct btrfs_convert_context *cctx, u64 block);
+ void (*free_block)(struct btrfs_convert_context *cctx, u64 block);
+ void (*free_block_range)(struct btrfs_convert_context *cctx, u64 block,
+ int num);
+ int (*copy_inodes)(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, int datacsum,
+ int packing, int noxattr, struct task_ctx *p);
+ void (*close_fs)(struct btrfs_convert_context *cctx);
+};
+
+struct btrfs_convert_context {
+ u32 blocksize;
+ u32 first_data_block;
+ u32 block_count;
+ u32 inodes_count;
+ u32 free_inodes_count;
+ char *volume_name;
+ const struct btrfs_convert_operations *convert_ops;
+ void *fs_data;
+};
+
+static inline int convert_alloc_block(struct btrfs_convert_context *cctx,
+ u64 goal, u64 *ret)
+{
+ return cctx->convert_ops->alloc_block(cctx, goal, ret);
+}
+
+static inline int convert_alloc_block_range(struct btrfs_convert_context *cctx,
+ u64 goal, int num, u64 *ret)
+{
+ return cctx->convert_ops->alloc_block_range(cctx, goal, num, ret);
+}
+
+static inline int convert_test_block(struct btrfs_convert_context *cctx,
+ u64 block)
+{
+ return cctx->convert_ops->test_block(cctx, block);
+}
+
+static inline void convert_free_block(struct btrfs_convert_context *cctx,
+ u64 block)
+{
+ cctx->convert_ops->free_block(cctx, block);
+}
+
+static inline void convert_free_block_range(struct btrfs_convert_context *cctx,
+ u64 block, int num)
+{
+ cctx->convert_ops->free_block_range(cctx, block, num);
+}
+
+static inline int copy_inodes(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, int datacsum,
+ int packing, int noxattr, struct task_ctx *p)
+{
+ return cctx->convert_ops->copy_inodes(cctx, root, datacsum, packing,
+ noxattr, p);
+}
+
+static inline void convert_close_fs(struct btrfs_convert_context *cctx)
+{
+ cctx->convert_ops->close_fs(cctx);
+}
+
/*
* Open Ext2fs in readonly mode, read block allocation bitmap and
* inode bitmap into memory.
*/
-static int open_ext2fs(const char *name, ext2_filsys *ret_fs)
+static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
{
errcode_t ret;
ext2_filsys ext2_fs;
@@ -114,20 +186,37 @@ static int open_ext2fs(const char *name, ext2_filsys *ret_fs)
ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
}
- *ret_fs = ext2_fs;
+ if (!(ext2_fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ fprintf(stderr, "filetype feature is missing\n");
+ goto fail;
+ }
+
+ cctx->fs_data = ext2_fs;
+ cctx->blocksize = ext2_fs->blocksize;
+ cctx->block_count = ext2_fs->super->s_blocks_count;
+ cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16);
+ cctx->first_data_block = ext2_fs->super->s_first_data_block;
+ cctx->inodes_count = ext2_fs->super->s_inodes_count;
+ cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count;
return 0;
fail:
return -1;
}
-static int close_ext2fs(ext2_filsys fs)
+static void ext2_close_fs(struct btrfs_convert_context *cctx)
{
- ext2fs_close(fs);
- return 0;
+ if (cctx->volume_name) {
+ free(cctx->volume_name);
+ cctx->volume_name = NULL;
+ }
+ ext2fs_close(cctx->fs_data);
}
-static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
+static int ext2_alloc_block(struct btrfs_convert_context *cctx,
+ u64 goal, u64 *block_ret)
{
+ ext2_filsys fs = cctx->fs_data;
blk_t block;
if (!ext2fs_new_block(fs, goal, NULL, &block)) {
@@ -138,9 +227,10 @@ static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
return -ENOSPC;
}
-static int ext2_alloc_block_range(ext2_filsys fs, u64 goal, int num,
- u64 *block_ret)
+static int ext2_alloc_block_range(struct btrfs_convert_context *cctx, u64 goal,
+ int num, u64 *block_ret)
{
+ ext2_filsys fs = cctx->fs_data;
blk_t block;
ext2fs_block_bitmap bitmap = fs->block_map;
blk_t start = ext2fs_get_block_bitmap_start(bitmap);
@@ -157,31 +247,34 @@ static int ext2_alloc_block_range(ext2_filsys fs, u64 goal, int num,
return -ENOSPC;
}
-static int ext2_free_block(ext2_filsys fs, u64 block)
+static void ext2_free_block(struct btrfs_convert_context *cctx, u64 block)
{
+ ext2_filsys fs = cctx->fs_data;
+
BUG_ON(block != (blk_t)block);
ext2fs_fast_unmark_block_bitmap(fs->block_map, block);
- return 0;
}
-static int ext2_free_block_range(ext2_filsys fs, u64 block, int num)
+static void ext2_free_block_range(struct btrfs_convert_context *cctx, u64 block, int num)
{
+ ext2_filsys fs = cctx->fs_data;
+
BUG_ON(block != (blk_t)block);
ext2fs_fast_unmark_block_bitmap_range(fs->block_map, block, num);
- return 0;
}
-static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
+static int cache_free_extents(struct btrfs_root *root,
+ struct btrfs_convert_context *cctx)
{
int i, ret = 0;
blk_t block;
u64 bytenr;
- u64 blocksize = ext2_fs->blocksize;
+ u64 blocksize = cctx->blocksize;
- block = ext2_fs->super->s_first_data_block;
- for (; block < ext2_fs->super->s_blocks_count; block++) {
- if (ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block))
+ block = cctx->first_data_block;
+ for (; block < cctx->block_count; block++) {
+ if (convert_test_block(cctx, block))
continue;
bytenr = block * blocksize;
ret = set_extent_dirty(&root->fs_info->free_space_cache,
@@ -192,7 +285,7 @@ static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
bytenr = btrfs_sb_offset(i);
bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1);
- if (bytenr >= blocksize * ext2_fs->super->s_blocks_count)
+ if (bytenr >= blocksize * cctx->block_count)
break;
clear_extent_dirty(&root->fs_info->free_space_cache, bytenr,
bytenr + BTRFS_STRIPE_LEN - 1, 0);
@@ -1137,9 +1230,11 @@ fail:
/*
* scan ext2's inode bitmap and copy all used inodes.
*/
-static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
- int datacsum, int packing, int noxattr, struct task_ctx *p)
+static int ext2_copy_inodes(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root,
+ int datacsum, int packing, int noxattr, struct task_ctx *p)
{
+ ext2_filsys ext2_fs = cctx->fs_data;
int ret;
errcode_t err;
ext2_inode_scan ext2_scan;
@@ -1191,6 +1286,14 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
return ret;
}
+static int ext2_test_block(struct btrfs_convert_context *cctx, u64 block)
+{
+ ext2_filsys ext2_fs = cctx->fs_data;
+
+ BUG_ON(block != (u32)block);
+ return ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block);
+}
+
/*
* Construct a range of ext2fs image file.
* scan block allocation bitmap, find all blocks used by the ext2fs
@@ -1203,9 +1306,9 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid,
struct btrfs_inode_item *inode,
u64 start_byte, u64 end_byte,
- ext2_filsys ext2_fs, int datacsum)
+ struct btrfs_convert_context *cctx, int datacsum)
{
- u32 blocksize = ext2_fs->blocksize;
+ u32 blocksize = cctx->blocksize;
u32 block = start_byte / blocksize;
u32 last_block = (end_byte + blocksize - 1) / blocksize;
int ret = 0;
@@ -1215,7 +1318,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
data.first_block = block;
for (; start_byte < end_byte; block++, start_byte += blocksize) {
- if (!ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block))
+ if (!convert_test_block(cctx, block))
continue;
ret = block_iterate_proc(block, block, &data);
if (ret < 0)
@@ -1238,10 +1341,10 @@ fail:
return ret;
}
/*
- * Create the ext2fs image file.
+ * Create the fs image file.
*/
-static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
- const char *name, int datacsum)
+static int create_image(struct btrfs_convert_context *cctx,
+ struct btrfs_root *root, const char *name, int datacsum)
{
int ret;
struct btrfs_key key;
@@ -1362,7 +1465,7 @@ next:
if (bytenr > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- bytenr, ext2_fs,
+ bytenr, cctx,
datacsum);
if (ret)
goto fail;
@@ -1386,7 +1489,7 @@ next:
if (total_bytes > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- total_bytes, ext2_fs,
+ total_bytes, cctx,
datacsum);
if (ret)
goto fail;
@@ -1726,7 +1829,7 @@ static int init_btrfs(struct btrfs_root *root)
btrfs_set_root_dirid(&fs_info->fs_root->root_item,
BTRFS_FIRST_FREE_OBJECTID);
- /* subvol for ext2 image file */
+ /* subvol for fs image file */
ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
BUG_ON(ret);
/* subvol for data relocation */
@@ -2286,6 +2389,42 @@ err:
return ret;
}
+static const struct btrfs_convert_operations ext2_convert_ops = {
+ .name = "ext2",
+ .open_fs = ext2_open_fs,
+ .alloc_block = ext2_alloc_block,
+ .alloc_block_range = ext2_alloc_block_range,
+ .copy_inodes = ext2_copy_inodes,
+ .test_block = ext2_test_block,
+ .free_block = ext2_free_block,
+ .free_block_range = ext2_free_block_range,
+ .close_fs = ext2_close_fs,
+};
+
+static const struct btrfs_convert_operations *convert_operations[] = {
+ &ext2_convert_ops,
+};
+
+static int convert_open_fs(const char *devname,
+ struct btrfs_convert_context *cctx)
+{
+ int i;
+
+ memset(cctx, 0, sizeof(*cctx));
+
+ for (i = 0; i < ARRAY_SIZE(convert_operations); i++) {
+ int ret = convert_operations[i]->open_fs(cctx, devname);
+
+ if (ret == 0) {
+ cctx->convert_ops = convert_operations[i];
+ return ret;
+ }
+ }
+
+ fprintf(stderr, "No file system found to convert.\n");
+ return -1;
+}
+
static int do_convert(const char *devname, int datacsum, int packing, int noxattr,
u32 nodesize, int copylabel, const char *fslabel, int progress,
u64 features)
@@ -2297,38 +2436,33 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
u64 blocks[7];
u64 total_bytes;
u64 super_bytenr;
- ext2_filsys ext2_fs;
struct btrfs_root *root;
struct btrfs_root *image_root;
+ struct btrfs_convert_context cctx;
+ char *subvol_name = NULL;
struct task_ctx ctx;
char features_buf[64];
struct btrfs_mkfs_config mkfs_cfg;
- ret = open_ext2fs(devname, &ext2_fs);
- if (ret) {
- fprintf(stderr, "unable to open the Ext2fs\n");
+ ret = convert_open_fs(devname, &cctx);
+ if (ret)
goto fail;
- }
- blocksize = ext2_fs->blocksize;
- total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize;
+
+ blocksize = cctx.blocksize;
+ total_bytes = (u64)blocksize * (u64)cctx.block_count;
if (blocksize < 4096) {
fprintf(stderr, "block size is too small\n");
goto fail;
}
- if (!(ext2_fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- fprintf(stderr, "filetype feature is missing\n");
- goto fail;
- }
if (btrfs_check_nodesize(nodesize, blocksize, features))
goto fail;
blocks_per_node = nodesize / blocksize;
ret = -blocks_per_node;
for (i = 0; i < 7; i++) {
if (nodesize == blocksize)
- ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
+ ret = convert_alloc_block(&cctx, 0, blocks + i);
else
- ret = ext2_alloc_block_range(ext2_fs,
+ ret = convert_alloc_block_range(&cctx,
ret + blocks_per_node, blocks_per_node,
blocks + i);
if (ret) {
@@ -2352,7 +2486,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
printf("\tnodesize: %u\n", nodesize);
printf("\tfeatures: %s\n", features_buf);
- mkfs_cfg.label = ext2_fs->super->s_volume_name;
+ mkfs_cfg.label = cctx.volume_name;
mkfs_cfg.fs_uuid = NULL;
memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
mkfs_cfg.num_bytes = total_bytes;
@@ -2378,7 +2512,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "unable to open ctree\n");
goto fail;
}
- ret = cache_free_extents(root, ext2_fs);
+ ret = cache_free_extents(root, &cctx);
if (ret) {
fprintf(stderr, "error during cache_free_extents %d\n", ret);
goto fail;
@@ -2388,9 +2522,9 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
for (i = 0; i < 7; i++) {
blocks[i] /= blocksize;
if (nodesize == blocksize)
- ext2_free_block(ext2_fs, blocks[i]);
+ convert_free_block(&cctx, blocks[i]);
else
- ext2_free_block_range(ext2_fs, blocks[i],
+ convert_free_block_range(&cctx, blocks[i],
blocks_per_node);
}
ret = init_btrfs(root);
@@ -2399,15 +2533,14 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
goto fail;
}
printf("creating btrfs metadata.\n");
- ctx.max_copy_inodes = (ext2_fs->super->s_inodes_count
- - ext2_fs->super->s_free_inodes_count);
+ ctx.max_copy_inodes = (cctx.inodes_count - cctx.free_inodes_count);
ctx.cur_copy_inodes = 0;
if (progress) {
ctx.info = task_init(print_copied_inodes, after_copied_inodes, &ctx);
task_start(ctx.info);
}
- ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr, &ctx);
+ ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx);
if (ret) {
fprintf(stderr, "error during copy_inodes %d\n", ret);
goto fail;
@@ -2416,21 +2549,32 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
task_stop(ctx.info);
task_deinit(ctx.info);
}
- printf("creating ext2fs image file.\n");
- image_root = link_subvol(root, "ext2_saved", CONV_IMAGE_SUBVOL_OBJECTID);
+
+ 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",
+ cctx.convert_ops->name);
+ goto fail;
+ }
+
+ image_root = link_subvol(root, subvol_name, CONV_IMAGE_SUBVOL_OBJECTID);
+
+ free(subvol_name);
+
if (!image_root) {
fprintf(stderr, "unable to create subvol\n");
goto fail;
}
- ret = create_ext2_image(image_root, ext2_fs, "image", datacsum);
+ ret = create_image(&cctx, image_root, "image", datacsum);
if (ret) {
- fprintf(stderr, "error during create_ext2_image %d\n", ret);
+ fprintf(stderr, "error during create_image %d\n", ret);
goto fail;
}
memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
if (copylabel == 1) {
strncpy(root->fs_info->super_copy->label,
- ext2_fs->super->s_volume_name, 16);
+ cctx.volume_name, BTRFS_LABEL_SIZE);
fprintf(stderr, "copy label '%s'\n",
root->fs_info->super_copy->label);
} else if (copylabel == -1) {
@@ -2449,11 +2593,11 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
fprintf(stderr, "error during close_ctree %d\n", ret);
goto fail;
}
- close_ext2fs(ext2_fs);
+ convert_close_fs(&cctx);
/*
* If this step succeed, we get a mountable btrfs. Otherwise
- * the ext2fs is left unchanged.
+ * the source fs is left unchanged.
*/
ret = migrate_super_block(fd, super_bytenr, blocksize);
if (ret) {
@@ -2875,7 +3019,7 @@ static void print_usage(void)
printf("\t-i|--no-xattr ignore xattrs and ACLs\n");
printf("\t-n|--no-inline disable inlining of small files to metadata\n");
printf("\t-N|--nodesize SIZE set filesystem metadata nodesize\n");
- printf("\t-r|--rollback roll back to ext2fs\n");
+ printf("\t-r|--rollback roll back to the original filesystem\n");
printf("\t-l|--label LABEL set filesystem label\n");
printf("\t-L|--copy-label use label from converted filesystem\n");
printf("\t-p|--progress show converting progress (default)\n");