From d931f3d86726b44f9d873f6ca40f1dfb21aa5148 Mon Sep 17 00:00:00 2001
From: Jeff Mahoney <jeffm@suse.com>
Date: Wed, 14 Aug 2013 14:39:21 -0400
Subject: [PATCH 06/10] btrfs-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 <jeffm@suse.com>
---
 btrfs-convert.c |  225 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 172 insertions(+), 53 deletions(-)

Index: btrfs-progs-v3.18.2/btrfs-convert.c
===================================================================
--- btrfs-progs-v3.18.2.orig/btrfs-convert.c
+++ btrfs-progs-v3.18.2/btrfs-convert.c
@@ -37,6 +37,7 @@
 #include "crc32c.h"
 #include "utils.h"
 #include "task-utils.h"
+#include "bitops.h"
 #include <ext2fs/ext2_fs.h>
 #include <ext2fs/ext2fs.h>
 #include <ext2fs/ext2_ext_attr.h>
@@ -79,11 +80,68 @@ 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 *cxt, const char *devname);
+	int (*alloc_block)(struct btrfs_convert_context *cxt, u64 goal,
+			   u64 *block_ret);
+	int (*test_block)(struct btrfs_convert_context *cxt, u64 block);
+	void (*free_block)(struct btrfs_convert_context *cxt, u64 block);
+	int (*copy_inodes)(struct btrfs_convert_context *cxt,
+			 struct btrfs_root *root, int datacsum,
+			 int packing, int noxattr, struct task_ctx *p);
+	void (*close_fs)(struct btrfs_convert_context *cxt);
+};
+
+struct btrfs_convert_context {
+	u32 blocksize;
+	u32 first_data_block;
+	u32 block_count;
+	u32 inodes_count;
+	u32 free_inodes_count;
+	char *volume_name;
+	struct btrfs_convert_operations *convert_ops;
+	void *fs_data;
+};
+
+static inline int convert_alloc_block(struct btrfs_convert_context *cxt,
+				      u64 goal, u64 *ret)
+{
+	return  cxt->convert_ops->alloc_block(cxt, goal, ret);
+}
+
+static inline int convert_test_block(struct btrfs_convert_context *cxt,
+				     u64 block)
+{
+	return cxt->convert_ops->test_block(cxt, block);
+}
+
+static inline void convert_free_block(struct btrfs_convert_context *cxt,
+				      u64 block)
+{
+	cxt->convert_ops->free_block(cxt, block);
+}
+
+static inline int copy_inodes(struct btrfs_convert_context *cxt,
+			      struct btrfs_root *root, int datacsum,
+			      int packing, int noxattr, struct task_ctx *p)
+{
+	return cxt->convert_ops->copy_inodes(cxt, root, datacsum, packing,
+					     noxattr, p);
+}
+
+
+static inline void convert_close_fs(struct btrfs_convert_context *cxt)
+{
+	cxt->convert_ops->close_fs(cxt);
+}
+
 /*
  * 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 *cxt, const char *name)
 {
 	errcode_t ret;
 	ext2_filsys ext2_fs;
@@ -116,20 +174,35 @@ static int open_ext2fs(const char *name,
 		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;
+	}
+
+	cxt->fs_data = ext2_fs;
+	cxt->blocksize = ext2_fs->blocksize;
+	cxt->block_count = ext2_fs->super->s_blocks_count;
+	cxt->volume_name = strndup(ext2_fs->super->s_volume_name, 16);
+	cxt->first_data_block = ext2_fs->super->s_first_data_block;
+	cxt->inodes_count = ext2_fs->super->s_inodes_count;
+	cxt->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 *cxt)
 {
-	ext2fs_close(fs);
-	return 0;
+	if (cxt->volume_name)
+		free(cxt->volume_name);
+	ext2fs_close(cxt->fs_data);
 }
 
-static int ext2_alloc_block(ext2_filsys fs, u64 goal, u64 *block_ret)
+static int ext2_alloc_block(struct btrfs_convert_context *cxt,
+			    u64 goal, u64 *block_ret)
 {
+	ext2_filsys fs = cxt->fs_data;
 	blk_t block;
 
 	if (!ext2fs_new_block(fs, goal, NULL, &block)) {
@@ -140,24 +213,25 @@ static int ext2_alloc_block(ext2_filsys
 	return -ENOSPC;
 }
 
-static int ext2_free_block(ext2_filsys fs, u64 block)
+static void ext2_free_block(struct btrfs_convert_context *cxt, u64 block)
 {
+	ext2_filsys fs = cxt->fs_data;
 	BUG_ON(block != (blk_t)block);
 	ext2fs_fast_unmark_block_bitmap(fs->block_map, block);
-	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 *cxt)
 
 {
 	int i, ret = 0;
 	blk_t block;
 	u64 bytenr;
-	u64 blocksize = ext2_fs->blocksize;
+	u64 blocksize = cxt->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 = cxt->first_data_block;
+	for (; block < cxt->block_count; block++) {
+		if (convert_test_block(cxt, block))
 			continue;
 		bytenr = block * blocksize;
 		ret = set_extent_dirty(&root->fs_info->free_space_cache,
@@ -168,7 +242,7 @@ static int cache_free_extents(struct btr
 	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 * cxt->block_count)
 			break;
 		clear_extent_dirty(&root->fs_info->free_space_cache, bytenr,
 				   bytenr + BTRFS_STRIPE_LEN - 1, 0);
@@ -1068,9 +1142,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 *cxt,
+			    struct btrfs_root *root,
+			    int datacsum, int packing, int noxattr, struct task_ctx *p)
 {
+	ext2_filsys ext2_fs = cxt->fs_data;
 	int ret;
 	errcode_t err;
 	ext2_inode_scan ext2_scan;
@@ -1121,6 +1197,14 @@ static int copy_inodes(struct btrfs_root
 	return ret;
 }
 
+static int
+ext2_test_block(struct btrfs_convert_context *cxt, u64 block)
+{
+	ext2_filsys ext2_fs = cxt->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
@@ -1133,9 +1217,9 @@ static int create_image_file_range(struc
 				   struct btrfs_root *root, u64 objectid,
 				   struct btrfs_inode_item *inode,
 				   u64 start_byte, u64 end_byte,
-				   ext2_filsys ext2_fs)
+				   struct btrfs_convert_context *cxt)
 {
-	u32 blocksize = ext2_fs->blocksize;
+	u32 blocksize = cxt->blocksize;
 	u32 block = start_byte / blocksize;
 	u32 last_block = (end_byte + blocksize - 1) / blocksize;
 	int ret = 0;
@@ -1145,7 +1229,7 @@ static int create_image_file_range(struc
 	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(cxt, block))
 			continue;
 		ret = block_iterate_proc(block, block, &data);
 		if (ret & BLOCK_ABORT) {
@@ -1170,10 +1254,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)
+static int create_image(struct btrfs_convert_context *cxt,
+			struct btrfs_root *root, const char *name)
 {
 	int ret;
 	struct btrfs_key key;
@@ -1286,7 +1370,7 @@ next:
 		if (bytenr > last_byte) {
 			ret = create_image_file_range(trans, root, objectid,
 						      &btrfs_inode, last_byte,
-						      bytenr, ext2_fs);
+						      bytenr, cxt);
 			if (ret)
 				goto fail;
 		}
@@ -1309,7 +1393,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, cxt);
 		if (ret)
 			goto fail;
 	}
@@ -1648,7 +1732,7 @@ static int init_btrfs(struct btrfs_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 */
@@ -2210,6 +2294,39 @@ err:
 	return ret;
 }
 
+static struct btrfs_convert_operations ext2_convert_ops = {
+	.name		= "ext2",
+	.open_fs	= ext2_open_fs,
+	.alloc_block	= ext2_alloc_block,
+	.copy_inodes	= ext2_copy_inodes,
+	.test_block	= ext2_test_block,
+	.free_block	= ext2_free_block,
+	.close_fs	= ext2_close_fs,
+};
+
+static struct btrfs_convert_operations *convert_operations[] = {
+	&ext2_convert_ops,
+};
+
+static int convert_open_fs(const char *devname,
+			   struct btrfs_convert_context *cxt)
+{
+	int i;
+
+	memset(cxt, 0, sizeof(*cxt));
+
+	for (i = 0; i < ARRAY_SIZE(convert_operations); i++) {
+		int ret = convert_operations[i]->open_fs(cxt, devname);
+		if (ret == 0) {
+			cxt->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,
 	       int copylabel, const char *fslabel, int progress)
 {
@@ -2219,29 +2336,24 @@ static int do_convert(const char *devnam
 	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 cxt;
+	char *subvol_name = NULL;
 	struct task_ctx ctx;
 
-	ret = open_ext2fs(devname, &ext2_fs);
-	if (ret) {
-		fprintf(stderr, "unable to open the Ext2fs\n");
+	ret = convert_open_fs(devname, &cxt);
+	if (ret)
 		goto fail;
-	}
-	blocksize = ext2_fs->blocksize;
-	total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize;
+
+	blocksize = cxt.blocksize;
+	total_bytes = (u64)blocksize * (u64)cxt.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;
-	}
 	for (i = 0; i < 7; i++) {
-		ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
+		ret = convert_alloc_block(&cxt, 0, blocks + i);
 		if (ret) {
 			fprintf(stderr, "not enough free space\n");
 			goto fail;
@@ -2254,7 +2366,7 @@ static int do_convert(const char *devnam
 		fprintf(stderr, "unable to open %s\n", devname);
 		goto fail;
 	}
-	ret = make_btrfs(fd, devname, ext2_fs->super->s_volume_name,
+	ret = make_btrfs(fd, devname, cxt.volume_name,
 			 NULL, blocks, total_bytes, blocksize, blocksize,
 			 blocksize, blocksize, 0);
 	if (ret) {
@@ -2273,7 +2385,7 @@ static int do_convert(const char *devnam
 		fprintf(stderr, "unable to open ctree\n");
 		goto fail;
 	}
-	ret = cache_free_extents(root, ext2_fs);
+	ret = cache_free_extents(root, &cxt);
 	if (ret) {
 		fprintf(stderr, "error during cache_free_extents %d\n", ret);
 		goto fail;
@@ -2282,7 +2394,7 @@ static int do_convert(const char *devnam
 	/* recover block allocation bitmap */
 	for (i = 0; i < 7; i++) {
 		blocks[i] /= blocksize;
-		ext2_free_block(ext2_fs, blocks[i]);
+		convert_free_block(&cxt, blocks[i]);
 	}
 	ret = init_btrfs(root);
 	if (ret) {
@@ -2290,15 +2402,14 @@ static int do_convert(const char *devnam
 		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 = (cxt.inodes_count - cxt.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(&cxt, root, datacsum, packing, noxattr, &ctx);
 	if (ret) {
 		fprintf(stderr, "error during copy_inodes %d\n", ret);
 		goto fail;
@@ -2307,26 +2418,38 @@ static int do_convert(const char *devnam
 		task_stop(ctx.info);
 		task_deinit(ctx.info);
 	}
-	printf("creating ext2fs image file.\n");
-	image_root = link_subvol(root, "ext2_saved",
+
+	printf("creating %s image file.\n", cxt.convert_ops->name);
+	ret = asprintf(&subvol_name, "%s_saved", cxt.convert_ops->name);
+	if (ret < 0) {
+		fprintf(stderr, "error allocating subvolume name: %s_saved\n",
+			cxt.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");
+	ret = create_image(&cxt, image_root, "image");
 	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);
+				cxt.volume_name, BTRFS_LABEL_SIZE);
 		fprintf(stderr, "copy label '%s'\n",
 				root->fs_info->super_copy->label);
 	} else if (copylabel == -1) {
-		strncpy(root->fs_info->super_copy->label, fslabel, BTRFS_LABEL_SIZE);
+		strncpy(root->fs_info->super_copy->label, fslabel,
+			BTRFS_LABEL_SIZE);
 		fprintf(stderr, "set label to '%s'\n", fslabel);
 	}
 
@@ -2341,11 +2464,11 @@ static int do_convert(const char *devnam
 		fprintf(stderr, "error during close_ctree %d\n", ret);
 		goto fail;
 	}
-	close_ext2fs(ext2_fs);
+	convert_close_fs(&cxt);
 
 	/*
 	 * 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) {
@@ -2743,7 +2866,7 @@ static void print_usage(void)
 	printf("\t-d           disable data checksum\n");
 	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");
+	printf("\t-r           roll back to source fs\n");
 	printf("\t-l LABEL     set filesystem label\n");
 	printf("\t-L           use label from converted fs\n");
 	printf("\t-p           show converting progress\n");
