Filesystems need to provide a function open_blah that fills a struct
convert_fs with some information and three function pointers.
The function pointers are:
- cache_free_extents, which takes a struct extent_io_tree and marks all
  extents not being used by the filesystem as DIRTY
- copy_inodes, which copies the contents of the filesystem into a
  btrfs_root using CoW.
- close
There's a void* in struct convert_fs for private use by the filesystem.

libblkid is used to determine the filesystem.
---
 Makefile  |    2 +-
 convert.c |  184 +++++++++++++++++++++++++++++++++++++++++--------------------
 2 files changed, 126 insertions(+), 60 deletions(-)

diff --git a/Makefile b/Makefile
index 525676e..755cc24 100644
--- a/Makefile
+++ b/Makefile
@@ -75,7 +75,7 @@ quick-test: $(objects) quick-test.o
        gcc $(CFLAGS) -o quick-test $(objects) quick-test.o $(LDFLAGS) $(LIBS)
 
 convert: $(objects) convert.o
-       gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs $(LDFLAGS) 
$(LIBS)
+       gcc $(CFLAGS) -o btrfs-convert $(objects) convert.o -lext2fs -lblkid 
$(LDFLAGS) $(LIBS)
 
 ioctl-test: $(objects) ioctl-test.o
        gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
diff --git a/convert.c b/convert.c
index bd91990..6dfcb97 100644
--- a/convert.c
+++ b/convert.c
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <uuid/uuid.h>
 #include <linux/fs.h>
+#include <blkid/blkid.h>
 #include "kerncompat.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -42,9 +43,26 @@
 #include <ext2fs/ext2fs.h>
 #include <ext2fs/ext2_ext_attr.h>
 
+struct convert_fs {
+       u64 total_bytes;
+       u64 blocksize;
+       const char *label;
+
+       /* Close the FS */
+       int (*close)(struct convert_fs *fs);
+       /* Mark free extents as dirty */
+       int (*cache_free_extents)(struct convert_fs *fs,
+                                 struct extent_io_tree *tree);
+       /* Copy everything over */
+       int (*copy_inodes)(struct convert_fs *fs, struct btrfs_root *root,
+                          int datacsum, int packing, int noxattr);
+
+       void *privdata;
+};
+
 #define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
 #define STRIPE_LEN (64 * 1024)
-#define EXT2_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+#define ORIG_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
 
 /*
  * Open Ext2fs in readonly mode, read block allocation bitmap and
@@ -89,15 +107,16 @@ fail:
        return -1;
 }
 
-static int close_ext2fs(ext2_filsys fs)
+static int ext2_close(struct convert_fs *fs)
 {
-       ext2fs_close(fs);
+       ext2fs_close((ext2_filsys)fs->privdata);
        return 0;
 }
 
-static int ext2_cache_free_extents(ext2_filsys ext2_fs,
+static int ext2_cache_free_extents(struct convert_fs *fs,
                                   struct extent_io_tree *free_tree)
 {
+       ext2_filsys ext2_fs = fs->privdata;
        int ret = 0;
        blk_t block;
        u64 bytenr;
@@ -117,19 +136,18 @@ static int ext2_cache_free_extents(ext2_filsys ext2_fs,
 }
 
 /* mark btrfs-reserved blocks as used */
-static void adjust_free_extents(ext2_filsys ext2_fs,
+static void adjust_free_extents(struct convert_fs *fs,
                                struct extent_io_tree *free_tree)
 {
        int i;
        u64 bytenr;
-       u64 blocksize = ext2_fs->blocksize;
 
        clear_extent_dirty(free_tree, 0, BTRFS_SUPER_INFO_OFFSET - 1, 0);
 
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
                bytenr &= ~((u64)STRIPE_LEN - 1);
-               if (bytenr >= blocksize * ext2_fs->super->s_blocks_count)
+               if (bytenr >= fs->total_bytes)
                        break;
                clear_extent_dirty(free_tree, bytenr, bytenr + STRIPE_LEN - 1,
                                   0);
@@ -1373,9 +1391,10 @@ fail:
 /*
  * scan ext2's inode bitmap and copy all used inode.
  */
-static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
-                      int datacsum, int packing, int noxattr)
+static int ext2_copy_inodes(struct convert_fs *fs, struct btrfs_root *root,
+                           int datacsum, int packing, int noxattr)
 {
+       ext2_filsys ext2_fs = fs->privdata;
        int ret;
        errcode_t err;
        ext2_inode_scan ext2_scan;
@@ -1426,8 +1445,8 @@ static int copy_inodes(struct btrfs_root *root, 
ext2_filsys ext2_fs,
 }
 
 /*
- * Construct a range of ext2fs image file.
- * scan block allocation bitmap, find all blocks used by the ext2fs
+ * Construct a range of the image file.
+ * scan block allocation bitmap, find all blocks used by the filesystem
  * in this range and create file extents that point to these blocks.
  *
  * Note: Before calling the function, no file extent points to blocks
@@ -1465,10 +1484,10 @@ static int create_image_file_range(struct 
btrfs_trans_handle *trans,
        return ret;
 }
 /*
- * Create the ext2fs image file.
+ * Create the image file.
  */
-static int create_ext2_image(struct btrfs_root *root, const char *name,
-                            struct extent_io_tree *orig_free_tree)
+static int create_image(struct btrfs_root *root, const char *name,
+                       struct extent_io_tree *orig_free_tree)
 {
        int ret;
        struct btrfs_key key;
@@ -1620,7 +1639,7 @@ next:
        btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
        ret = btrfs_insert_dir_item(trans, root, name, strlen(name),
                                    btrfs_root_dirid(&root->root_item),
-                                   &location, EXT2_FT_REG_FILE, objectid);
+                                   &location, BTRFS_FT_REG_FILE, objectid);
        if (ret)
                goto fail;
        ret = btrfs_insert_inode_ref(trans, root, name, strlen(name),
@@ -1996,8 +2015,8 @@ 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 */
-       ret = create_subvol(trans, root, EXT2_IMAGE_SUBVOL_OBJECTID);
+       /* subvol for image file */
+       ret = create_subvol(trans, root, ORIG_IMAGE_SUBVOL_OBJECTID);
        BUG_ON(ret);
        /* subvol for data relocation */
        ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
@@ -2273,7 +2292,7 @@ fail:
 }
 
 static int relocate_extents_range(struct btrfs_root *fs_root,
-                                 struct btrfs_root *ext2_root,
+                                 struct btrfs_root *image_root,
                                  u64 start_byte, u64 end_byte)
 {
        struct btrfs_fs_info *info = fs_root->fs_info;
@@ -2320,7 +2339,7 @@ static int relocate_extents_range(struct btrfs_root 
*fs_root,
        }
        btrfs_release_path(extent_root, &path);
 again:
-       cur_root = (pass % 2 == 0) ? ext2_root : fs_root;
+       cur_root = (pass % 2 == 0) ? image_root : fs_root;
        num_extents = 0;
 
        trans = btrfs_start_transaction(cur_root, 1);
@@ -2428,7 +2447,7 @@ fail:
  * relocate data in system chunk
  */
 static int cleanup_sys_chunk(struct btrfs_root *fs_root,
-                            struct btrfs_root *ext2_root)
+                            struct btrfs_root *image_root)
 {
        struct btrfs_block_group_cache *cache;
        int i, ret = 0;
@@ -2442,7 +2461,7 @@ static int cleanup_sys_chunk(struct btrfs_root *fs_root,
 
                end_byte = cache->key.objectid + cache->key.offset;
                if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
-                       ret = relocate_extents_range(fs_root, ext2_root,
+                       ret = relocate_extents_range(fs_root, image_root,
                                                     cache->key.objectid,
                                                     end_byte);
                        if (ret)
@@ -2454,7 +2473,7 @@ static int cleanup_sys_chunk(struct btrfs_root *fs_root,
                offset = btrfs_sb_offset(i);
                offset &= ~((u64)STRIPE_LEN - 1);
 
-               ret = relocate_extents_range(fs_root, ext2_root,
+               ret = relocate_extents_range(fs_root, image_root,
                                             offset, offset + STRIPE_LEN);
                if (ret)
                        goto fail;
@@ -2567,6 +2586,55 @@ static int copy_dirtiness(struct extent_io_tree *out,
        return 0;
 }
 
+int ext2_open(struct convert_fs *fs, const char *name)
+{
+       int ret;
+       ext2_filsys ext2_fs;
+       ret = open_ext2fs(name, &ext2_fs);
+       if (ret)
+               return ret;
+
+       fs->privdata = ext2_fs;
+       fs->blocksize = ext2_fs->blocksize;
+       fs->label = ext2_fs->super->s_volume_name;
+       fs->total_bytes = ext2_fs->super->s_blocks_count * fs->blocksize;
+
+       fs->cache_free_extents = ext2_cache_free_extents;
+       fs->close = ext2_close;
+       fs->copy_inodes = ext2_copy_inodes;
+
+       return 0;
+}
+
+static int open_fs(struct convert_fs *fs, const char *devname)
+{
+       static struct {
+               const char *name; /* must match libblkid */
+               int (*open)(struct convert_fs *fs, const char *name);
+       } convert_fs_types[] = {
+               {"ext2", ext2_open},
+               {"ext3", ext2_open},
+               {"ext4", ext2_open},
+               {"ext4dev", ext2_open},
+       };
+
+       int i;
+       char *type = blkid_get_tag_value(NULL, "TYPE", devname);
+       if (!type) {
+               fprintf(stderr, "unrecognized filesystem type\n");
+               return -1;
+       }
+       for (i = 0; i < ARRAY_SIZE(convert_fs_types); i++) {
+               if (!strcmp(type, convert_fs_types[i].name)) {
+                       free(type);
+                       return convert_fs_types[i].open(fs, devname);
+               }
+       }
+       fprintf(stderr, "%s filesystems are not supported\n", type);
+       free(type);
+       return -1;
+}
+
 int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 {
        int fd, ret;
@@ -2574,31 +2642,27 @@ int do_convert(const char *devname, int datacsum, int 
packing, int noxattr)
        u64 blocks[7];
        u64 total_bytes;
        u64 super_bytenr;
-       ext2_filsys ext2_fs;
+       struct convert_fs fs;
        struct btrfs_root *root;
-       struct btrfs_root *ext2_root;
+       struct btrfs_root *image_root;
        struct extent_io_tree free_tree;
        struct extent_io_tree orig_free_tree;
 
        extent_io_tree_init(&free_tree);
        extent_io_tree_init(&orig_free_tree);
-       ret = open_ext2fs(devname, &ext2_fs);
+       fs.privdata = NULL;
+       ret = open_fs(&fs, devname);
        if (ret) {
-               fprintf(stderr, "unable to open the Ext2fs\n");
+               fprintf(stderr, "unable to open the filesystem\n");
                goto fail;
        }
-       blocksize = ext2_fs->blocksize;
-       total_bytes = (u64)ext2_fs->super->s_blocks_count * blocksize;
+       blocksize = fs.blocksize;
+       total_bytes = fs.total_bytes;
        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;
-       }
-       ret = ext2_cache_free_extents(ext2_fs, &orig_free_tree);
+       ret = fs.cache_free_extents(&fs, &orig_free_tree);
        if (ret) {
                fprintf(stderr, "error during cache_free_extents %d\n", ret);
                goto fail;
@@ -2611,7 +2675,7 @@ int do_convert(const char *devname, int datacsum, int 
packing, int noxattr)
                fprintf(stderr, "error during copy_dirtiness %d\n", ret);
                goto fail;
        }
-       adjust_free_extents(ext2_fs, &free_tree);
+       adjust_free_extents(&fs, &free_tree);
        ret = alloc_blocks(&free_tree, blocks, 7, blocksize);
        if (ret) {
                goto fail;
@@ -2622,9 +2686,8 @@ 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, devname, ext2_fs->super->s_volume_name,
-                        blocks, total_bytes, blocksize, blocksize,
-                        blocksize, blocksize);
+       ret = make_btrfs(fd, devname, fs.label, blocks, total_bytes, blocksize,
+                        blocksize, blocksize, blocksize);
        if (ret) {
                fprintf(stderr, "unable to create initial ctree\n");
                goto fail;
@@ -2649,25 +2712,25 @@ int do_convert(const char *devname, int datacsum, int 
packing, int noxattr)
                goto fail;
        }
        printf("creating btrfs metadata.\n");
-       ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr);
+       ret = fs.copy_inodes(&fs, root, datacsum, packing, noxattr);
        if (ret) {
                fprintf(stderr, "error during copy_inodes %d\n", ret);
                goto fail;
        }
-       printf("creating ext2fs image file.\n");
-       ext2_root = link_subvol(root, "ext2_saved", EXT2_IMAGE_SUBVOL_OBJECTID);
-       if (!ext2_root) {
+       printf("creating image file.\n");
+       image_root = link_subvol(root, "image_saved", 
ORIG_IMAGE_SUBVOL_OBJECTID);
+       if (!image_root) {
                fprintf(stderr, "unable to create subvol\n");
                goto fail;
        }
-       ret = create_ext2_image(ext2_root, "image", &orig_free_tree);
+       ret = create_image(image_root, "image", &orig_free_tree);
        if (ret) {
-               fprintf(stderr, "error during create_ext2_image %d\n", ret);
+               fprintf(stderr, "error during create_image %d\n", ret);
                goto fail;
        }
        extent_io_tree_cleanup(&orig_free_tree);
        printf("cleaning up system chunk.\n");
-       ret = cleanup_sys_chunk(root, ext2_root);
+       ret = cleanup_sys_chunk(root, image_root);
        if (ret) {
                fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret);
                goto fail;
@@ -2677,11 +2740,12 @@ int do_convert(const char *devname, int datacsum, int 
packing, int noxattr)
                fprintf(stderr, "error during close_ctree %d\n", ret);
                goto fail;
        }
-       close_ext2fs(ext2_fs);
+       fs.close(&fs);
+       fs.privdata = NULL;
 
        /*
         * If this step succeed, we get a mountable btrfs. Otherwise
-        * the ext2fs is left unchanged.
+        * the original filesystem is left unchanged.
         */
        ret = migrate_super_block(fd, super_bytenr, blocksize);
        if (ret) {
@@ -2706,6 +2770,8 @@ int do_convert(const char *devname, int datacsum, int 
packing, int noxattr)
        printf("conversion complete.\n");
        return 0;
 fail:
+       if (fs.privdata)
+               fs.close(&fs);
        fprintf(stderr, "conversion aborted.\n");
        return -1;
 }
@@ -2755,7 +2821,7 @@ int do_rollback(const char *devname, int force)
        int ret;
        int i;
        struct btrfs_root *root;
-       struct btrfs_root *ext2_root;
+       struct btrfs_root *image_root;
        struct btrfs_root *chunk_root;
        struct btrfs_dir_item *dir;
        struct btrfs_inode_item *inode;
@@ -2808,11 +2874,11 @@ int do_rollback(const char *devname, int force)
 
        btrfs_init_path(&path);
 
-       key.objectid = EXT2_IMAGE_SUBVOL_OBJECTID;
+       key.objectid = ORIG_IMAGE_SUBVOL_OBJECTID;
        key.type = BTRFS_ROOT_ITEM_KEY;
        key.offset = (u64)-1;
-       ext2_root = btrfs_read_fs_root(root->fs_info, &key);
-       if (!ext2_root || IS_ERR(ext2_root)) {
+       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",
                        key.objectid);
                goto fail;
@@ -2820,7 +2886,7 @@ int do_rollback(const char *devname, int force)
 
        name = "image";
        root_dir = btrfs_root_dirid(&root->root_item);
-       dir = btrfs_lookup_dir_item(NULL, ext2_root, &path,
+       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);
@@ -2828,11 +2894,11 @@ int do_rollback(const char *devname, int force)
        }
        leaf = path.nodes[0];
        btrfs_dir_item_key_to_cpu(leaf, dir, &key);
-       btrfs_release_path(ext2_root, &path);
+       btrfs_release_path(image_root, &path);
 
        objectid = key.objectid;
 
-       ret = btrfs_lookup_inode(NULL, ext2_root, &path, &key, 0);
+       ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0);
        if (ret) {
                fprintf(stderr, "unable to find inode item\n");
                goto fail;
@@ -2840,15 +2906,15 @@ int do_rollback(const char *devname, int force)
        leaf = path.nodes[0];
        inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item);
        total_bytes = btrfs_inode_size(leaf, inode);
-       btrfs_release_path(ext2_root, &path);
+       btrfs_release_path(image_root, &path);
 
        key.objectid = objectid;
        key.offset = 0;
        btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
-       ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0);
+       ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0);
        if (ret != 0) {
                fprintf(stderr, "unable to find first file extent\n");
-               btrfs_release_path(ext2_root, &path);
+               btrfs_release_path(image_root, &path);
                goto fail;
        }
 
@@ -2899,7 +2965,7 @@ next_extent:
                offset += btrfs_file_extent_num_bytes(leaf, fi);
                path.slots[0]++;
        }
-       btrfs_release_path(ext2_root, &path);
+       btrfs_release_path(image_root, &path);
 
        if (offset < total_bytes) {
                fprintf(stderr, "unable to build extent mapping\n");
@@ -3058,7 +3124,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 original filesystem\n");
 }
 
 int main(int argc, char *argv[])
-- 
1.6.4.4

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to