@@ -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)
@@ -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[])