From ed91cae19808d8d8561ec9e18c8dc0a74643c53f Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 14 Aug 2013 14:39:21 -0400 Subject: btrfs-progs: convert: add context and operations struct to allow different file systems Push out common convert operations into function pointers that we can ultimately allow other file systems to use to provide their own conversions. Signed-off-by: Jeff Mahoney Signed-off-by: David Sterba --- btrfs-convert.c | 260 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 202 insertions(+), 58 deletions(-) (limited to 'btrfs-convert.c') 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"); -- cgit v1.2.3