Thanks for doing all this work. Which filesystems have you currently
wired up to the converter?
-chris
On Sat, Mar 20, 2010 at 12:27:46AM -0400, Sean Bartell
wrote:> No material changes are made.
> ---
> Makefile | 10 +-
> convert.c => convert/convert.c | 803
+---------------------------------------
> convert/convert.h | 76 ++++
> convert/ext2.c | 791
+++++++++++++++++++++++++++++++++++++++
> 4 files changed, 873 insertions(+), 807 deletions(-)
> rename convert.c => convert/convert.c (74%)
> create mode 100644 convert/convert.h
> create mode 100644 convert/ext2.c
>
> diff --git a/Makefile b/Makefile
> index 755cc24..c31c219 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1,6 +1,6 @@
> CC=gcc
> AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2
> -CFLAGS = -g -Werror -Os
> +CFLAGS = -g -Werror -Os -I.
> objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
> root-tree.o dir-item.o file-item.o inode-item.o \
> inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \
> @@ -29,7 +29,7 @@ endif
>
> .c.o:
> $(check) $<
> - $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $<
> + $(CC) $(DEPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c $< -o $@
>
>
> all: version $(progs) manpages
> @@ -74,8 +74,8 @@ dir-test: $(objects) dir-test.o
> 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 -lblkid
$(LDFLAGS) $(LIBS)
> +convert: $(objects) $(patsubst %.c,%.o,$(wildcard convert/*.c))
> + gcc $(CFLAGS) -o btrfs-convert $^ -lext2fs -lblkid $(LDFLAGS) $(LIBS)
>
> ioctl-test: $(objects) ioctl-test.o
> gcc $(CFLAGS) -o ioctl-test $(objects) ioctl-test.o $(LDFLAGS) $(LIBS)
> @@ -87,7 +87,7 @@ install-man:
> cd man; make install
>
> clean :
> - rm -f $(progs) cscope.out *.o .*.d btrfs-convert
> + rm -f $(progs) cscope.out *.o .*.d btrfs-convert convert/*.o convert/.*.d
> cd man; make clean
>
> install: $(progs) install-man
> diff --git a/convert.c b/convert/convert.c
> similarity index 74%
> rename from convert.c
> rename to convert/convert.c
> index 6dfcb97..aaf3c56 100644
> --- a/convert.c
> +++ b/convert/convert.c
> @@ -24,117 +24,21 @@
> #endif
> #include <stdio.h>
> #include <stdlib.h>
> -#include <sys/types.h>
> -#include <sys/stat.h>
> -#include <sys/acl.h>
> #include <fcntl.h>
> #include <unistd.h>
> -#include <uuid/uuid.h>
> -#include <linux/fs.h>
> #include <blkid/blkid.h>
> #include "kerncompat.h"
> +#include "convert.h"
> #include "ctree.h"
> #include "disk-io.h"
> #include "volumes.h"
> #include "transaction.h"
> #include "crc32c.h"
> #include "utils.h"
> -#include <ext2fs/ext2_fs.h>
> -#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 ORIG_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
>
> -/*
> - * 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)
> -{
> - errcode_t ret;
> - ext2_filsys ext2_fs;
> - ext2_ino_t ino;
> - ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs);
> - if (ret) {
> - fprintf(stderr, "ext2fs_open: %s\n", error_message(ret));
> - goto fail;
> - }
> - ret = ext2fs_read_inode_bitmap(ext2_fs);
> - if (ret) {
> - fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n",
> - error_message(ret));
> - goto fail;
> - }
> - ret = ext2fs_read_block_bitmap(ext2_fs);
> - if (ret) {
> - fprintf(stderr, "ext2fs_read_block_bitmap: %s\n",
> - error_message(ret));
> - goto fail;
> - }
> - /*
> - * search each block group for a free inode. this set up
> - * uninit block/inode bitmaps appropriately.
> - */
> - ino = 1;
> - while (ino <= ext2_fs->super->s_inodes_count) {
> - ext2_ino_t foo;
> - ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo);
> - ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
> - }
> -
> - *ret_fs = ext2_fs;
> - return 0;
> -fail:
> - return -1;
> -}
> -
> -static int ext2_close(struct convert_fs *fs)
> -{
> - ext2fs_close((ext2_filsys)fs->privdata);
> - return 0;
> -}
> -
> -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;
> - u64 blocksize = ext2_fs->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))
> - continue;
> - bytenr = block * blocksize;
> - ret = set_extent_dirty(free_tree, bytenr,
> - bytenr + blocksize - 1, 0);
> - BUG_ON(ret);
> - }
> -
> - return 0;
> -}
> -
> /* mark btrfs-reserved blocks as used */
> static void adjust_free_extents(struct convert_fs *fs,
> struct extent_io_tree *free_tree)
> @@ -267,113 +171,6 @@ struct btrfs_extent_ops extent_ops = {
> .free_extent = custom_free_extent,
> };
>
> -struct dir_iterate_data {
> - struct btrfs_trans_handle *trans;
> - struct btrfs_root *root;
> - struct btrfs_inode_item *inode;
> - u64 objectid;
> - u64 index_cnt;
> - u64 parent;
> - int errcode;
> -};
> -
> -static u8 filetype_conversion_table[EXT2_FT_MAX] = {
> - [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN,
> - [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE,
> - [EXT2_FT_DIR] = BTRFS_FT_DIR,
> - [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV,
> - [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV,
> - [EXT2_FT_FIFO] = BTRFS_FT_FIFO,
> - [EXT2_FT_SOCK] = BTRFS_FT_SOCK,
> - [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK,
> -};
> -
> -static int dir_iterate_proc(ext2_ino_t dir, int entry,
> - struct ext2_dir_entry *old,
> - int offset, int blocksize,
> - char *buf,void *priv_data)
> -{
> - int ret;
> - int file_type;
> - u64 objectid;
> - u64 inode_size;
> - char dotdot[] = "..";
> - struct btrfs_key location;
> - struct ext2_dir_entry_2 *dirent = (struct ext2_dir_entry_2 *)old;
> - struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data;
> -
> - objectid = dirent->inode + INO_OFFSET;
> - if (!strncmp(dirent->name, dotdot, dirent->name_len)) {
> - if (dirent->name_len == 2) {
> - BUG_ON(idata->parent != 0);
> - idata->parent = objectid;
> - }
> - return 0;
> - }
> - if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO)
> - return 0;
> -
> - location.objectid = objectid;
> - location.offset = 0;
> - btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
> -
> - file_type = dirent->file_type;
> - BUG_ON(file_type > EXT2_FT_SYMLINK);
> - ret = btrfs_insert_dir_item(idata->trans, idata->root,
> - dirent->name, dirent->name_len,
> - idata->objectid, &location,
> - filetype_conversion_table[file_type],
> - idata->index_cnt);
> - if (ret)
> - goto fail;
> - ret = btrfs_insert_inode_ref(idata->trans, idata->root,
> - dirent->name, dirent->name_len,
> - objectid, idata->objectid,
> - idata->index_cnt);
> - if (ret)
> - goto fail;
> - idata->index_cnt++;
> - inode_size = btrfs_stack_inode_size(idata->inode) +
> - dirent->name_len * 2;
> - btrfs_set_stack_inode_size(idata->inode, inode_size);
> - return 0;
> -fail:
> - idata->errcode = ret;
> - return BLOCK_ABORT;
> -}
> -
> -static int create_dir_entries(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - struct btrfs_inode_item *btrfs_inode,
> - ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
> -{
> - int ret;
> - errcode_t err;
> - struct dir_iterate_data data = {
> - .trans = trans,
> - .root = root,
> - .inode = btrfs_inode,
> - .objectid = objectid,
> - .index_cnt = 2,
> - .parent = 0,
> - .errcode = 0,
> - };
> -
> - err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL,
> - dir_iterate_proc, &data);
> - if (err)
> - goto error;
> - ret = data.errcode;
> - if (ret == 0 && data.parent == objectid) {
> - ret = btrfs_insert_inode_ref(trans, root, "..", 2,
> - objectid, objectid, 0);
> - }
> - return ret;
> -error:
> - fprintf(stderr, "ext2fs_dir_iterate2: %s\n",
error_message(err));
> - return -1;
> -}
> -
> static int read_disk_extent(struct btrfs_root *root, u64 bytenr,
> u64 num_bytes, char *buffer)
> {
> @@ -524,22 +321,6 @@ fail:
> return ret;
> }
>
> -struct extent_iterate_data {
> - struct btrfs_trans_handle *trans;
> - struct btrfs_root *root;
> - u64 *inode_nbytes;
> - u64 objectid;
> - int checksum, packing;
> - u64 last_file_off;
> - u64 total_size;
> - enum {EXTENT_ITERATE_TYPE_NONE, EXTENT_ITERATE_TYPE_MEM,
> - EXTENT_ITERATE_TYPE_DISK} type;
> - u64 size;
> - u64 file_off; /* always aligned to sectorsize */
> - char *data; /* for mem */
> - u64 disk_off; /* for disk */
> -};
> -
> static u64 extent_boundary(struct btrfs_root *root, u64 extent_start)
> {
> int i;
> @@ -710,9 +491,6 @@ int finish_file_extents(struct extent_iterate_data
*priv)
> return 0;
> }
>
> -int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off,
> - u64 size, char *data);
> -
> int add_file_disk_extent(struct extent_iterate_data *priv, u64 file_off,
> u64 disk_off, u64 size)
> {
> @@ -861,510 +639,6 @@ int add_file_mem_extent(struct extent_iterate_data
*priv, u64 file_off,
> return 0;
> }
>
> -static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr,
> - e2_blkcnt_t blockcnt, blk_t ref_block,
> - int ref_offset, void *priv_data)
> -{
> - struct extent_iterate_data *idata;
> - idata = (struct extent_iterate_data *)priv_data;
> - u64 blocksize = fs->blocksize;
> - int ret = add_file_disk_extent(idata, blocksize * blockcnt,
> - blocksize * *blocknr, blocksize);
> - if (ret)
> - return BLOCK_ABORT;
> - return 0;
> -}
> -
> -/*
> - * traverse file''s data blocks, record these data blocks as file
extents.
> - */
> -static int create_file_extents(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - struct btrfs_inode_item *btrfs_inode,
> - ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> - int datacsum, int packing)
> -{
> - int ret;
> - errcode_t err;
> - u64 inode_nbytes = 0;
> - u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
> - struct extent_iterate_data data;
> - ret = start_file_extents(&data, trans, root, &inode_nbytes,
objectid,
> - datacsum, packing, inode_size);
> - if (ret)
> - return ret;
> - err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
> - NULL, __block_iterate_proc, &data);
> - if (err)
> - goto error;
> - ret = finish_file_extents(&data);
> - if (ret)
> - return ret;
> - btrfs_set_stack_inode_nbytes(btrfs_inode, inode_nbytes);
> - return 0;
> -error:
> - fprintf(stderr, "ext2fs_block_iterate2: %s\n",
error_message(err));
> - return -1;
> -}
> -
> -static int create_symbol_link(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - struct btrfs_inode_item *btrfs_inode,
> - ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> - struct ext2_inode *ext2_inode)
> -{
> - int ret;
> - char *pathname;
> - u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
> - if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) {
> - btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1);
> - ret = create_file_extents(trans, root, objectid, btrfs_inode,
> - ext2_fs, ext2_ino, 1, 1);
> - btrfs_set_stack_inode_size(btrfs_inode, inode_size);
> - return ret;
> - }
> -
> - pathname = (char *)&(ext2_inode->i_block[0]);
> - BUG_ON(pathname[inode_size] != 0);
> - ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
> - pathname, inode_size + 1);
> - btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1);
> - return ret;
> -}
> -
> -/*
> - * Following xattr/acl related codes are based on codes in
> - * fs/ext3/xattr.c and fs/ext3/acl.c
> - */
> -#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr))
> -#define EXT2_XATTR_BFIRST(ptr) \
> - ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1))
> -#define EXT2_XATTR_IHDR(inode) \
> - ((struct ext2_ext_attr_header *) ((void *)(inode) + \
> - EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
> -#define EXT2_XATTR_IFIRST(inode) \
> - ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \
> - sizeof(EXT2_XATTR_IHDR(inode)->h_magic)))
> -
> -static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry,
> - const void *end)
> -{
> - struct ext2_ext_attr_entry *next;
> -
> - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> - next = EXT2_EXT_ATTR_NEXT(entry);
> - if ((void *)next >= end)
> - return -EIO;
> - entry = next;
> - }
> - return 0;
> -}
> -
> -static int ext2_xattr_check_block(const char *buf, size_t size)
> -{
> - int error;
> - struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf);
> -
> - if (header->h_magic != EXT2_EXT_ATTR_MAGIC ||
> - header->h_blocks != 1)
> - return -EIO;
> - error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size);
> - return error;
> -}
> -
> -static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry,
> - size_t size)
> -{
> - size_t value_size = entry->e_value_size;
> -
> - if (entry->e_value_block != 0 || value_size > size ||
> - entry->e_value_offs + value_size > size)
> - return -EIO;
> - return 0;
> -}
> -
> -#define EXT2_ACL_VERSION 0x0001
> -
> -typedef struct {
> - __le16 e_tag;
> - __le16 e_perm;
> - __le32 e_id;
> -} ext2_acl_entry;
> -
> -typedef struct {
> - __le16 e_tag;
> - __le16 e_perm;
> -} ext2_acl_entry_short;
> -
> -typedef struct {
> - __le32 a_version;
> -} ext2_acl_header;
> -
> -static inline int ext2_acl_count(size_t size)
> -{
> - ssize_t s;
> - size -= sizeof(ext2_acl_header);
> - s = size - 4 * sizeof(ext2_acl_entry_short);
> - if (s < 0) {
> - if (size % sizeof(ext2_acl_entry_short))
> - return -1;
> - return size / sizeof(ext2_acl_entry_short);
> - } else {
> - if (s % sizeof(ext2_acl_entry))
> - return -1;
> - return s / sizeof(ext2_acl_entry) + 4;
> - }
> -}
> -
> -#define ACL_EA_VERSION 0x0002
> -
> -typedef struct {
> - __le16 e_tag;
> - __le16 e_perm;
> - __le32 e_id;
> -} acl_ea_entry;
> -
> -typedef struct {
> - __le32 a_version;
> - acl_ea_entry a_entries[0];
> -} acl_ea_header;
> -
> -static inline size_t acl_ea_size(int count)
> -{
> - return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
> -}
> -
> -static int ext2_acl_to_xattr(void *dst, const void *src,
> - size_t dst_size, size_t src_size)
> -{
> - int i, count;
> - const void *end = src + src_size;
> - acl_ea_header *ext_acl = (acl_ea_header *)dst;
> - acl_ea_entry *dst_entry = ext_acl->a_entries;
> - ext2_acl_entry *src_entry;
> -
> - if (src_size < sizeof(ext2_acl_header))
> - goto fail;
> - if (((ext2_acl_header *)src)->a_version !> -
cpu_to_le32(EXT2_ACL_VERSION))
> - goto fail;
> - src += sizeof(ext2_acl_header);
> - count = ext2_acl_count(src_size);
> - if (count <= 0)
> - goto fail;
> -
> - BUG_ON(dst_size < acl_ea_size(count));
> - ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION);
> - for (i = 0; i < count; i++, dst_entry++) {
> - src_entry = (ext2_acl_entry *)src;
> - if (src + sizeof(ext2_acl_entry_short) > end)
> - goto fail;
> - dst_entry->e_tag = src_entry->e_tag;
> - dst_entry->e_perm = src_entry->e_perm;
> - switch (le16_to_cpu(src_entry->e_tag)) {
> - case ACL_USER_OBJ:
> - case ACL_GROUP_OBJ:
> - case ACL_MASK:
> - case ACL_OTHER:
> - src += sizeof(ext2_acl_entry_short);
> - dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
> - break;
> - case ACL_USER:
> - case ACL_GROUP:
> - src += sizeof(ext2_acl_entry);
> - if (src > end)
> - goto fail;
> - dst_entry->e_id = src_entry->e_id;
> - break;
> - default:
> - goto fail;
> - }
> - }
> - if (src != end)
> - goto fail;
> - return 0;
> -fail:
> - return -EINVAL;
> -}
> -
> -static char *xattr_prefix_table[] = {
> - [1] = "user.",
> - [2] = "system.posix_acl_access",
> - [3] = "system.posix_acl_default",
> - [4] = "trusted.",
> - [6] = "security.",
> -};
> -
> -static int copy_single_xattr(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - struct ext2_ext_attr_entry *entry,
> - const void *data, u32 datalen)
> -{
> - int ret = 0;
> - int name_len;
> - int name_index;
> - void *databuf = NULL;
> - char namebuf[XATTR_NAME_MAX + 1];
> -
> - name_index = entry->e_name_index;
> - if (name_index >= ARRAY_SIZE(xattr_prefix_table) ||
> - xattr_prefix_table[name_index] == NULL)
> - return -EOPNOTSUPP;
> - name_len = strlen(xattr_prefix_table[name_index]) +
> - entry->e_name_len;
> - if (name_len >= sizeof(namebuf))
> - return -ERANGE;
> -
> - if (name_index == 2 || name_index == 3) {
> - size_t bufsize = acl_ea_size(ext2_acl_count(datalen));
> - databuf = malloc(bufsize);
> - if (!databuf)
> - return -ENOMEM;
> - ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen);
> - if (ret)
> - goto out;
> - data = databuf;
> - datalen = bufsize;
> - }
> - strcpy(namebuf, xattr_prefix_table[name_index]);
> - strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
> - if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
> - sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
> - fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n",
> - objectid - INO_OFFSET, name_len, namebuf);
> - goto out;
> - }
> - ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len,
> - data, datalen, objectid);
> -out:
> - if (databuf)
> - free(databuf);
> - return ret;
> -}
> -
> -static int copy_extended_attrs(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - struct btrfs_inode_item *btrfs_inode,
> - ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
> -{
> - int ret = 0;
> - int inline_ea = 0;
> - errcode_t err;
> - u32 datalen;
> - u32 block_size = ext2_fs->blocksize;
> - u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super);
> - struct ext2_inode_large *ext2_inode;
> - struct ext2_ext_attr_entry *entry;
> - void *data;
> - char *buffer = NULL;
> - char inode_buf[EXT2_GOOD_OLD_INODE_SIZE];
> -
> - if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) {
> - ext2_inode = (struct ext2_inode_large *)inode_buf;
> - } else {
> - ext2_inode = (struct ext2_inode_large *)malloc(inode_size);
> - if (!ext2_inode)
> - return -ENOMEM;
> - }
> - err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode,
> - inode_size);
> - if (err) {
> - fprintf(stderr, "ext2fs_read_inode_full: %s\n",
> - error_message(err));
> - ret = -1;
> - goto out;
> - }
> -
> - if (ext2_ino > ext2_fs->super->s_first_ino &&
> - inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
> - if (EXT2_GOOD_OLD_INODE_SIZE +
> - ext2_inode->i_extra_isize > inode_size) {
> - ret = -EIO;
> - goto out;
> - }
> - if (ext2_inode->i_extra_isize != 0 &&
> - EXT2_XATTR_IHDR(ext2_inode)->h_magic => -
EXT2_EXT_ATTR_MAGIC) {
> - inline_ea = 1;
> - }
> - }
> - if (inline_ea) {
> - int total;
> - void *end = (void *)ext2_inode + inode_size;
> - entry = EXT2_XATTR_IFIRST(ext2_inode);
> - total = end - (void *)entry;
> - ret = ext2_xattr_check_names(entry, end);
> - if (ret)
> - goto out;
> - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> - ret = ext2_xattr_check_entry(entry, total);
> - if (ret)
> - goto out;
> - data = (void *)EXT2_XATTR_IFIRST(ext2_inode) +
> - entry->e_value_offs;
> - datalen = entry->e_value_size;
> - ret = copy_single_xattr(trans, root, objectid,
> - entry, data, datalen);
> - if (ret)
> - goto out;
> - entry = EXT2_EXT_ATTR_NEXT(entry);
> - }
> - }
> -
> - if (ext2_inode->i_file_acl == 0)
> - goto out;
> -
> - buffer = malloc(block_size);
> - if (!buffer) {
> - ret = -ENOMEM;
> - goto out;
> - }
> - err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer);
> - if (err) {
> - fprintf(stderr, "ext2fs_read_ext_attr: %s\n",
> - error_message(err));
> - ret = -1;
> - goto out;
> - }
> - ret = ext2_xattr_check_block(buffer, block_size);
> - if (ret)
> - goto out;
> -
> - entry = EXT2_XATTR_BFIRST(buffer);
> - while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> - ret = ext2_xattr_check_entry(entry, block_size);
> - if (ret)
> - goto out;
> - data = buffer + entry->e_value_offs;
> - datalen = entry->e_value_size;
> - ret = copy_single_xattr(trans, root, objectid,
> - entry, data, datalen);
> - if (ret)
> - goto out;
> - entry = EXT2_EXT_ATTR_NEXT(entry);
> - }
> -out:
> - if (buffer != NULL)
> - free(buffer);
> - if ((void *)ext2_inode != inode_buf)
> - free(ext2_inode);
> - return ret;
> -}
> -#define MINORBITS 20
> -#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi))
> -
> -static inline dev_t old_decode_dev(u16 val)
> -{
> - return MKDEV((val >> 8) & 255, val & 255);
> -}
> -
> -static inline dev_t new_decode_dev(u32 dev)
> -{
> - unsigned major = (dev & 0xfff00) >> 8;
> - unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
> - return MKDEV(major, minor);
> -}
> -
> -static int copy_inode_item(struct btrfs_inode_item *dst,
> - struct ext2_inode *src, u32 blocksize)
> -{
> - btrfs_set_stack_inode_generation(dst, 1);
> - btrfs_set_stack_inode_size(dst, src->i_size);
> - btrfs_set_stack_inode_nbytes(dst, 0);
> - btrfs_set_stack_inode_block_group(dst, 0);
> - btrfs_set_stack_inode_nlink(dst, src->i_links_count);
> - btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high
<< 16));
> - btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high
<< 16));
> - btrfs_set_stack_inode_mode(dst, src->i_mode);
> - btrfs_set_stack_inode_rdev(dst, 0);
> - btrfs_set_stack_inode_flags(dst, 0);
> - btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime);
> - btrfs_set_stack_timespec_nsec(&dst->atime, 0);
> - btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime);
> - btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
> - btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime);
> - btrfs_set_stack_timespec_nsec(&dst->mtime, 0);
> - btrfs_set_stack_timespec_sec(&dst->otime, 0);
> - btrfs_set_stack_timespec_nsec(&dst->otime, 0);
> -
> - if (S_ISDIR(src->i_mode)) {
> - btrfs_set_stack_inode_size(dst, 0);
> - btrfs_set_stack_inode_nlink(dst, 1);
> - }
> - if (S_ISREG(src->i_mode)) {
> - btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 |
> - (u64)src->i_size);
> - }
> - if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode)
&&
> - !S_ISLNK(src->i_mode)) {
> - if (src->i_block[0]) {
> - btrfs_set_stack_inode_rdev(dst,
> - old_decode_dev(src->i_block[0]));
> - } else {
> - btrfs_set_stack_inode_rdev(dst,
> - new_decode_dev(src->i_block[1]));
> - }
> - }
> - return 0;
> -}
> -
> -/*
> - * copy a single inode. do all the required works, such as cloning
> - * inode item, creating file extents and creating directory entries.
> - */
> -static int copy_single_inode(struct btrfs_trans_handle *trans,
> - struct btrfs_root *root, u64 objectid,
> - ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> - struct ext2_inode *ext2_inode,
> - int datacsum, int packing, int noxattr)
> -{
> - int ret;
> - struct btrfs_key inode_key;
> - struct btrfs_inode_item btrfs_inode;
> -
> - if (ext2_inode->i_links_count == 0)
> - return 0;
> -
> - copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize);
> - if (!datacsum && S_ISREG(ext2_inode->i_mode)) {
> - u32 flags = btrfs_stack_inode_flags(&btrfs_inode) |
> - BTRFS_INODE_NODATASUM;
> - btrfs_set_stack_inode_flags(&btrfs_inode, flags);
> - }
> -
> - switch (ext2_inode->i_mode & S_IFMT) {
> - case S_IFREG:
> - ret = create_file_extents(trans, root, objectid, &btrfs_inode,
> - ext2_fs, ext2_ino, datacsum, packing);
> - break;
> - case S_IFDIR:
> - ret = create_dir_entries(trans, root, objectid, &btrfs_inode,
> - ext2_fs, ext2_ino);
> - break;
> - case S_IFLNK:
> - ret = create_symbol_link(trans, root, objectid, &btrfs_inode,
> - ext2_fs, ext2_ino, ext2_inode);
> - break;
> - default:
> - ret = 0;
> - break;
> - }
> - if (ret)
> - return ret;
> -
> - if (!noxattr) {
> - ret = copy_extended_attrs(trans, root, objectid, &btrfs_inode,
> - ext2_fs, ext2_ino);
> - if (ret)
> - return ret;
> - }
> - inode_key.objectid = objectid;
> - inode_key.offset = 0;
> - btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY);
> - ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
> - return ret;
> -}
> -
> static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr,
> u64 src_bytenr, u32 num_bytes)
> {
> @@ -1388,61 +662,6 @@ fail:
> ret = -1;
> return ret;
> }
> -/*
> - * scan ext2''s inode bitmap and copy all used inode.
> - */
> -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;
> - struct ext2_inode ext2_inode;
> - ext2_ino_t ext2_ino;
> - u64 objectid;
> - struct btrfs_trans_handle *trans;
> -
> - trans = btrfs_start_transaction(root, 1);
> - if (!trans)
> - return -ENOMEM;
> - err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan);
> - if (err) {
> - fprintf(stderr, "ext2fs_open_inode_scan: %s\n",
error_message(err));
> - return -1;
> - }
> - while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino,
> - &ext2_inode))) {
> - /* no more inodes */
> - if (ext2_ino == 0)
> - break;
> - /* skip special inode in ext2fs */
> - if (ext2_ino < EXT2_GOOD_OLD_FIRST_INO &&
> - ext2_ino != EXT2_ROOT_INO)
> - continue;
> - objectid = ext2_ino + INO_OFFSET;
> - ret = copy_single_inode(trans, root,
> - objectid, ext2_fs, ext2_ino,
> - &ext2_inode, datacsum, packing,
> - noxattr);
> - if (ret)
> - return ret;
> - if (trans->blocks_used >= 4096) {
> - ret = btrfs_commit_transaction(trans, root);
> - BUG_ON(ret);
> - trans = btrfs_start_transaction(root, 1);
> - BUG_ON(!trans);
> - }
> - }
> - if (err) {
> - fprintf(stderr, "ext2fs_get_next_inode: %s\n",
error_message(err));
> - return -1;
> - }
> - ret = btrfs_commit_transaction(trans, root);
> - BUG_ON(ret);
> -
> - return ret;
> -}
>
> /*
> * Construct a range of the image file.
> @@ -2586,26 +1805,6 @@ 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 {
> diff --git a/convert/convert.h b/convert/convert.h
> new file mode 100644
> index 0000000..4f31775
> --- /dev/null
> +++ b/convert/convert.h
> @@ -0,0 +1,76 @@
> +/*
> + * Copyright (C) 2007 Oracle. All rights reserved.
> + * Copyright (C) 2010 Sean Bartell. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +#ifndef BTRFS_CONVERT_H
> +#define BTRFS_CONVERT_H
> +
> +#include "ctree.h"
> +#include "kerncompat.h"
> +#include "transaction.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;
> +};
> +
> +int ext2_open(struct convert_fs *fs, const char *name);
> +
> +struct extent_iterate_data {
> + struct btrfs_trans_handle *trans;
> + struct btrfs_root *root;
> + u64 *inode_nbytes;
> + u64 objectid;
> + int checksum, packing;
> + u64 last_file_off;
> + u64 total_size;
> + enum {EXTENT_ITERATE_TYPE_NONE, EXTENT_ITERATE_TYPE_MEM,
> + EXTENT_ITERATE_TYPE_DISK} type;
> + u64 size;
> + u64 file_off; /* always aligned to sectorsize */
> + char *data; /* for mem */
> + u64 disk_off; /* for disk */
> +};
> +
> +int start_file_extents(struct extent_iterate_data *priv,
> + struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 *inode_nbytes,
> + u64 objectid, int checksum, int packing,
> + u64 total_size);
> +int start_file_extents_range(struct extent_iterate_data *priv,
> + struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 *inode_nbytes,
> + u64 objectid, int checksum, u64 start, u64 end);
> +int finish_file_extents(struct extent_iterate_data *priv);
> +int add_file_mem_extent(struct extent_iterate_data *priv, u64 file_off,
> + u64 size, char *data);
> +int add_file_disk_extent(struct extent_iterate_data *priv, u64 file_off,
> + u64 disk_off, u64 size);
> +
> +#endif
> diff --git a/convert/ext2.c b/convert/ext2.c
> new file mode 100644
> index 0000000..7584701
> --- /dev/null
> +++ b/convert/ext2.c
> @@ -0,0 +1,791 @@
> +/*
> + * Copyright (C) 2007 Oracle. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <sys/acl.h>
> +#include <linux/fs.h>
> +#include "kerncompat.h"
> +#include "convert.h"
> +#include "ctree.h"
> +#include "disk-io.h"
> +#include "transaction.h"
> +#include <ext2fs/ext2_fs.h>
> +#include <ext2fs/ext2fs.h>
> +#include <ext2fs/ext2_ext_attr.h>
> +
> +#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO)
> +
> +/*
> + * 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)
> +{
> + errcode_t ret;
> + ext2_filsys ext2_fs;
> + ext2_ino_t ino;
> + ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs);
> + if (ret) {
> + fprintf(stderr, "ext2fs_open: %s\n", error_message(ret));
> + goto fail;
> + }
> + ret = ext2fs_read_inode_bitmap(ext2_fs);
> + if (ret) {
> + fprintf(stderr, "ext2fs_read_inode_bitmap: %s\n",
> + error_message(ret));
> + goto fail;
> + }
> + ret = ext2fs_read_block_bitmap(ext2_fs);
> + if (ret) {
> + fprintf(stderr, "ext2fs_read_block_bitmap: %s\n",
> + error_message(ret));
> + goto fail;
> + }
> + /*
> + * search each block group for a free inode. this set up
> + * uninit block/inode bitmaps appropriately.
> + */
> + ino = 1;
> + while (ino <= ext2_fs->super->s_inodes_count) {
> + ext2_ino_t foo;
> + ext2fs_new_inode(ext2_fs, ino, 0, NULL, &foo);
> + ino += EXT2_INODES_PER_GROUP(ext2_fs->super);
> + }
> +
> + *ret_fs = ext2_fs;
> + return 0;
> +fail:
> + return -1;
> +}
> +
> +static int ext2_close(struct convert_fs *fs)
> +{
> + ext2fs_close((ext2_filsys)fs->privdata);
> + return 0;
> +}
> +
> +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;
> + u64 blocksize = ext2_fs->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))
> + continue;
> + bytenr = block * blocksize;
> + ret = set_extent_dirty(free_tree, bytenr,
> + bytenr + blocksize - 1, 0);
> + BUG_ON(ret);
> + }
> +
> + return 0;
> +}
> +
> +struct dir_iterate_data {
> + struct btrfs_trans_handle *trans;
> + struct btrfs_root *root;
> + struct btrfs_inode_item *inode;
> + u64 objectid;
> + u64 index_cnt;
> + u64 parent;
> + int errcode;
> +};
> +
> +static u8 filetype_conversion_table[EXT2_FT_MAX] = {
> + [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN,
> + [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE,
> + [EXT2_FT_DIR] = BTRFS_FT_DIR,
> + [EXT2_FT_CHRDEV] = BTRFS_FT_CHRDEV,
> + [EXT2_FT_BLKDEV] = BTRFS_FT_BLKDEV,
> + [EXT2_FT_FIFO] = BTRFS_FT_FIFO,
> + [EXT2_FT_SOCK] = BTRFS_FT_SOCK,
> + [EXT2_FT_SYMLINK] = BTRFS_FT_SYMLINK,
> +};
> +
> +static int dir_iterate_proc(ext2_ino_t dir, int entry,
> + struct ext2_dir_entry *old,
> + int offset, int blocksize,
> + char *buf,void *priv_data)
> +{
> + int ret;
> + int file_type;
> + u64 objectid;
> + u64 inode_size;
> + char dotdot[] = "..";
> + struct btrfs_key location;
> + struct ext2_dir_entry_2 *dirent = (struct ext2_dir_entry_2 *)old;
> + struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data;
> +
> + objectid = dirent->inode + INO_OFFSET;
> + if (!strncmp(dirent->name, dotdot, dirent->name_len)) {
> + if (dirent->name_len == 2) {
> + BUG_ON(idata->parent != 0);
> + idata->parent = objectid;
> + }
> + return 0;
> + }
> + if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO)
> + return 0;
> +
> + location.objectid = objectid;
> + location.offset = 0;
> + btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
> +
> + file_type = dirent->file_type;
> + BUG_ON(file_type > EXT2_FT_SYMLINK);
> + ret = btrfs_insert_dir_item(idata->trans, idata->root,
> + dirent->name, dirent->name_len,
> + idata->objectid, &location,
> + filetype_conversion_table[file_type],
> + idata->index_cnt);
> + if (ret)
> + goto fail;
> + ret = btrfs_insert_inode_ref(idata->trans, idata->root,
> + dirent->name, dirent->name_len,
> + objectid, idata->objectid,
> + idata->index_cnt);
> + if (ret)
> + goto fail;
> + idata->index_cnt++;
> + inode_size = btrfs_stack_inode_size(idata->inode) +
> + dirent->name_len * 2;
> + btrfs_set_stack_inode_size(idata->inode, inode_size);
> + return 0;
> +fail:
> + idata->errcode = ret;
> + return BLOCK_ABORT;
> +}
> +
> +static int create_dir_entries(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + struct btrfs_inode_item *btrfs_inode,
> + ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
> +{
> + int ret;
> + errcode_t err;
> + struct dir_iterate_data data = {
> + .trans = trans,
> + .root = root,
> + .inode = btrfs_inode,
> + .objectid = objectid,
> + .index_cnt = 2,
> + .parent = 0,
> + .errcode = 0,
> + };
> +
> + err = ext2fs_dir_iterate2(ext2_fs, ext2_ino, 0, NULL,
> + dir_iterate_proc, &data);
> + if (err)
> + goto error;
> + ret = data.errcode;
> + if (ret == 0 && data.parent == objectid) {
> + ret = btrfs_insert_inode_ref(trans, root, "..", 2,
> + objectid, objectid, 0);
> + }
> + return ret;
> +error:
> + fprintf(stderr, "ext2fs_dir_iterate2: %s\n",
error_message(err));
> + return -1;
> +}
> +
> +static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr,
> + e2_blkcnt_t blockcnt, blk_t ref_block,
> + int ref_offset, void *priv_data)
> +{
> + struct extent_iterate_data *idata;
> + idata = (struct extent_iterate_data *)priv_data;
> + u64 blocksize = fs->blocksize;
> + int ret = add_file_disk_extent(idata, blocksize * blockcnt,
> + blocksize * *blocknr, blocksize);
> + if (ret)
> + return BLOCK_ABORT;
> + return 0;
> +}
> +
> +/*
> + * traverse file''s data blocks, record these data blocks as file
extents.
> + */
> +static int create_file_extents(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + struct btrfs_inode_item *btrfs_inode,
> + ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> + int datacsum, int packing)
> +{
> + int ret;
> + errcode_t err;
> + u64 inode_nbytes = 0;
> + u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
> + struct extent_iterate_data data;
> + ret = start_file_extents(&data, trans, root, &inode_nbytes,
objectid,
> + datacsum, packing, inode_size);
> + if (ret)
> + return ret;
> + err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
> + NULL, __block_iterate_proc, &data);
> + if (err)
> + goto error;
> + ret = finish_file_extents(&data);
> + if (ret)
> + return ret;
> + btrfs_set_stack_inode_nbytes(btrfs_inode, inode_nbytes);
> + return 0;
> +error:
> + fprintf(stderr, "ext2fs_block_iterate2: %s\n",
error_message(err));
> + return -1;
> +}
> +
> +static int create_symbol_link(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + struct btrfs_inode_item *btrfs_inode,
> + ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> + struct ext2_inode *ext2_inode)
> +{
> + int ret;
> + char *pathname;
> + u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
> + if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) {
> + btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1);
> + ret = create_file_extents(trans, root, objectid, btrfs_inode,
> + ext2_fs, ext2_ino, 1, 1);
> + btrfs_set_stack_inode_size(btrfs_inode, inode_size);
> + return ret;
> + }
> +
> + pathname = (char *)&(ext2_inode->i_block[0]);
> + BUG_ON(pathname[inode_size] != 0);
> + ret = btrfs_insert_inline_extent(trans, root, objectid, 0,
> + pathname, inode_size + 1);
> + btrfs_set_stack_inode_nbytes(btrfs_inode, inode_size + 1);
> + return ret;
> +}
> +
> +/*
> + * Following xattr/acl related codes are based on codes in
> + * fs/ext3/xattr.c and fs/ext3/acl.c
> + */
> +#define EXT2_XATTR_BHDR(ptr) ((struct ext2_ext_attr_header *)(ptr))
> +#define EXT2_XATTR_BFIRST(ptr) \
> + ((struct ext2_ext_attr_entry *)(EXT2_XATTR_BHDR(ptr) + 1))
> +#define EXT2_XATTR_IHDR(inode) \
> + ((struct ext2_ext_attr_header *) ((void *)(inode) + \
> + EXT2_GOOD_OLD_INODE_SIZE + (inode)->i_extra_isize))
> +#define EXT2_XATTR_IFIRST(inode) \
> + ((struct ext2_ext_attr_entry *) ((void *)EXT2_XATTR_IHDR(inode) + \
> + sizeof(EXT2_XATTR_IHDR(inode)->h_magic)))
> +
> +static int ext2_xattr_check_names(struct ext2_ext_attr_entry *entry,
> + const void *end)
> +{
> + struct ext2_ext_attr_entry *next;
> +
> + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> + next = EXT2_EXT_ATTR_NEXT(entry);
> + if ((void *)next >= end)
> + return -EIO;
> + entry = next;
> + }
> + return 0;
> +}
> +
> +static int ext2_xattr_check_block(const char *buf, size_t size)
> +{
> + int error;
> + struct ext2_ext_attr_header *header = EXT2_XATTR_BHDR(buf);
> +
> + if (header->h_magic != EXT2_EXT_ATTR_MAGIC ||
> + header->h_blocks != 1)
> + return -EIO;
> + error = ext2_xattr_check_names(EXT2_XATTR_BFIRST(buf), buf + size);
> + return error;
> +}
> +
> +static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry,
> + size_t size)
> +{
> + size_t value_size = entry->e_value_size;
> +
> + if (entry->e_value_block != 0 || value_size > size ||
> + entry->e_value_offs + value_size > size)
> + return -EIO;
> + return 0;
> +}
> +
> +#define EXT2_ACL_VERSION 0x0001
> +
> +typedef struct {
> + __le16 e_tag;
> + __le16 e_perm;
> + __le32 e_id;
> +} ext2_acl_entry;
> +
> +typedef struct {
> + __le16 e_tag;
> + __le16 e_perm;
> +} ext2_acl_entry_short;
> +
> +typedef struct {
> + __le32 a_version;
> +} ext2_acl_header;
> +
> +static inline int ext2_acl_count(size_t size)
> +{
> + ssize_t s;
> + size -= sizeof(ext2_acl_header);
> + s = size - 4 * sizeof(ext2_acl_entry_short);
> + if (s < 0) {
> + if (size % sizeof(ext2_acl_entry_short))
> + return -1;
> + return size / sizeof(ext2_acl_entry_short);
> + } else {
> + if (s % sizeof(ext2_acl_entry))
> + return -1;
> + return s / sizeof(ext2_acl_entry) + 4;
> + }
> +}
> +
> +#define ACL_EA_VERSION 0x0002
> +
> +typedef struct {
> + __le16 e_tag;
> + __le16 e_perm;
> + __le32 e_id;
> +} acl_ea_entry;
> +
> +typedef struct {
> + __le32 a_version;
> + acl_ea_entry a_entries[0];
> +} acl_ea_header;
> +
> +static inline size_t acl_ea_size(int count)
> +{
> + return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry);
> +}
> +
> +static int ext2_acl_to_xattr(void *dst, const void *src,
> + size_t dst_size, size_t src_size)
> +{
> + int i, count;
> + const void *end = src + src_size;
> + acl_ea_header *ext_acl = (acl_ea_header *)dst;
> + acl_ea_entry *dst_entry = ext_acl->a_entries;
> + ext2_acl_entry *src_entry;
> +
> + if (src_size < sizeof(ext2_acl_header))
> + goto fail;
> + if (((ext2_acl_header *)src)->a_version !> +
cpu_to_le32(EXT2_ACL_VERSION))
> + goto fail;
> + src += sizeof(ext2_acl_header);
> + count = ext2_acl_count(src_size);
> + if (count <= 0)
> + goto fail;
> +
> + BUG_ON(dst_size < acl_ea_size(count));
> + ext_acl->a_version = cpu_to_le32(ACL_EA_VERSION);
> + for (i = 0; i < count; i++, dst_entry++) {
> + src_entry = (ext2_acl_entry *)src;
> + if (src + sizeof(ext2_acl_entry_short) > end)
> + goto fail;
> + dst_entry->e_tag = src_entry->e_tag;
> + dst_entry->e_perm = src_entry->e_perm;
> + switch (le16_to_cpu(src_entry->e_tag)) {
> + case ACL_USER_OBJ:
> + case ACL_GROUP_OBJ:
> + case ACL_MASK:
> + case ACL_OTHER:
> + src += sizeof(ext2_acl_entry_short);
> + dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
> + break;
> + case ACL_USER:
> + case ACL_GROUP:
> + src += sizeof(ext2_acl_entry);
> + if (src > end)
> + goto fail;
> + dst_entry->e_id = src_entry->e_id;
> + break;
> + default:
> + goto fail;
> + }
> + }
> + if (src != end)
> + goto fail;
> + return 0;
> +fail:
> + return -EINVAL;
> +}
> +
> +static char *xattr_prefix_table[] = {
> + [1] = "user.",
> + [2] = "system.posix_acl_access",
> + [3] = "system.posix_acl_default",
> + [4] = "trusted.",
> + [6] = "security.",
> +};
> +
> +static int copy_single_xattr(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + struct ext2_ext_attr_entry *entry,
> + const void *data, u32 datalen)
> +{
> + int ret = 0;
> + int name_len;
> + int name_index;
> + void *databuf = NULL;
> + char namebuf[XATTR_NAME_MAX + 1];
> +
> + name_index = entry->e_name_index;
> + if (name_index >= ARRAY_SIZE(xattr_prefix_table) ||
> + xattr_prefix_table[name_index] == NULL)
> + return -EOPNOTSUPP;
> + name_len = strlen(xattr_prefix_table[name_index]) +
> + entry->e_name_len;
> + if (name_len >= sizeof(namebuf))
> + return -ERANGE;
> +
> + if (name_index == 2 || name_index == 3) {
> + size_t bufsize = acl_ea_size(ext2_acl_count(datalen));
> + databuf = malloc(bufsize);
> + if (!databuf)
> + return -ENOMEM;
> + ret = ext2_acl_to_xattr(databuf, data, bufsize, datalen);
> + if (ret)
> + goto out;
> + data = databuf;
> + datalen = bufsize;
> + }
> + strcpy(namebuf, xattr_prefix_table[name_index]);
> + strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
> + if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
> + sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
> + fprintf(stderr, "skip large xattr on inode %Lu name %.*s\n",
> + objectid - INO_OFFSET, name_len, namebuf);
> + goto out;
> + }
> + ret = btrfs_insert_xattr_item(trans, root, namebuf, name_len,
> + data, datalen, objectid);
> +out:
> + if (databuf)
> + free(databuf);
> + return ret;
> +}
> +
> +static int copy_extended_attrs(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + struct btrfs_inode_item *btrfs_inode,
> + ext2_filsys ext2_fs, ext2_ino_t ext2_ino)
> +{
> + int ret = 0;
> + int inline_ea = 0;
> + errcode_t err;
> + u32 datalen;
> + u32 block_size = ext2_fs->blocksize;
> + u32 inode_size = EXT2_INODE_SIZE(ext2_fs->super);
> + struct ext2_inode_large *ext2_inode;
> + struct ext2_ext_attr_entry *entry;
> + void *data;
> + char *buffer = NULL;
> + char inode_buf[EXT2_GOOD_OLD_INODE_SIZE];
> +
> + if (inode_size <= EXT2_GOOD_OLD_INODE_SIZE) {
> + ext2_inode = (struct ext2_inode_large *)inode_buf;
> + } else {
> + ext2_inode = (struct ext2_inode_large *)malloc(inode_size);
> + if (!ext2_inode)
> + return -ENOMEM;
> + }
> + err = ext2fs_read_inode_full(ext2_fs, ext2_ino, (void *)ext2_inode,
> + inode_size);
> + if (err) {
> + fprintf(stderr, "ext2fs_read_inode_full: %s\n",
> + error_message(err));
> + ret = -1;
> + goto out;
> + }
> +
> + if (ext2_ino > ext2_fs->super->s_first_ino &&
> + inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
> + if (EXT2_GOOD_OLD_INODE_SIZE +
> + ext2_inode->i_extra_isize > inode_size) {
> + ret = -EIO;
> + goto out;
> + }
> + if (ext2_inode->i_extra_isize != 0 &&
> + EXT2_XATTR_IHDR(ext2_inode)->h_magic => +
EXT2_EXT_ATTR_MAGIC) {
> + inline_ea = 1;
> + }
> + }
> + if (inline_ea) {
> + int total;
> + void *end = (void *)ext2_inode + inode_size;
> + entry = EXT2_XATTR_IFIRST(ext2_inode);
> + total = end - (void *)entry;
> + ret = ext2_xattr_check_names(entry, end);
> + if (ret)
> + goto out;
> + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> + ret = ext2_xattr_check_entry(entry, total);
> + if (ret)
> + goto out;
> + data = (void *)EXT2_XATTR_IFIRST(ext2_inode) +
> + entry->e_value_offs;
> + datalen = entry->e_value_size;
> + ret = copy_single_xattr(trans, root, objectid,
> + entry, data, datalen);
> + if (ret)
> + goto out;
> + entry = EXT2_EXT_ATTR_NEXT(entry);
> + }
> + }
> +
> + if (ext2_inode->i_file_acl == 0)
> + goto out;
> +
> + buffer = malloc(block_size);
> + if (!buffer) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer);
> + if (err) {
> + fprintf(stderr, "ext2fs_read_ext_attr: %s\n",
> + error_message(err));
> + ret = -1;
> + goto out;
> + }
> + ret = ext2_xattr_check_block(buffer, block_size);
> + if (ret)
> + goto out;
> +
> + entry = EXT2_XATTR_BFIRST(buffer);
> + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
> + ret = ext2_xattr_check_entry(entry, block_size);
> + if (ret)
> + goto out;
> + data = buffer + entry->e_value_offs;
> + datalen = entry->e_value_size;
> + ret = copy_single_xattr(trans, root, objectid,
> + entry, data, datalen);
> + if (ret)
> + goto out;
> + entry = EXT2_EXT_ATTR_NEXT(entry);
> + }
> +out:
> + if (buffer != NULL)
> + free(buffer);
> + if ((void *)ext2_inode != inode_buf)
> + free(ext2_inode);
> + return ret;
> +}
> +#define MINORBITS 20
> +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi))
> +
> +static inline dev_t old_decode_dev(u16 val)
> +{
> + return MKDEV((val >> 8) & 255, val & 255);
> +}
> +
> +static inline dev_t new_decode_dev(u32 dev)
> +{
> + unsigned major = (dev & 0xfff00) >> 8;
> + unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
> + return MKDEV(major, minor);
> +}
> +
> +static int copy_inode_item(struct btrfs_inode_item *dst,
> + struct ext2_inode *src, u32 blocksize)
> +{
> + btrfs_set_stack_inode_generation(dst, 1);
> + btrfs_set_stack_inode_size(dst, src->i_size);
> + btrfs_set_stack_inode_nbytes(dst, 0);
> + btrfs_set_stack_inode_block_group(dst, 0);
> + btrfs_set_stack_inode_nlink(dst, src->i_links_count);
> + btrfs_set_stack_inode_uid(dst, src->i_uid | (src->i_uid_high
<< 16));
> + btrfs_set_stack_inode_gid(dst, src->i_gid | (src->i_gid_high
<< 16));
> + btrfs_set_stack_inode_mode(dst, src->i_mode);
> + btrfs_set_stack_inode_rdev(dst, 0);
> + btrfs_set_stack_inode_flags(dst, 0);
> + btrfs_set_stack_timespec_sec(&dst->atime, src->i_atime);
> + btrfs_set_stack_timespec_nsec(&dst->atime, 0);
> + btrfs_set_stack_timespec_sec(&dst->ctime, src->i_ctime);
> + btrfs_set_stack_timespec_nsec(&dst->ctime, 0);
> + btrfs_set_stack_timespec_sec(&dst->mtime, src->i_mtime);
> + btrfs_set_stack_timespec_nsec(&dst->mtime, 0);
> + btrfs_set_stack_timespec_sec(&dst->otime, 0);
> + btrfs_set_stack_timespec_nsec(&dst->otime, 0);
> +
> + if (S_ISDIR(src->i_mode)) {
> + btrfs_set_stack_inode_size(dst, 0);
> + btrfs_set_stack_inode_nlink(dst, 1);
> + }
> + if (S_ISREG(src->i_mode)) {
> + btrfs_set_stack_inode_size(dst, (u64)src->i_size_high << 32 |
> + (u64)src->i_size);
> + }
> + if (!S_ISREG(src->i_mode) && !S_ISDIR(src->i_mode)
&&
> + !S_ISLNK(src->i_mode)) {
> + if (src->i_block[0]) {
> + btrfs_set_stack_inode_rdev(dst,
> + old_decode_dev(src->i_block[0]));
> + } else {
> + btrfs_set_stack_inode_rdev(dst,
> + new_decode_dev(src->i_block[1]));
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * copy a single inode. do all the required works, such as cloning
> + * inode item, creating file extents and creating directory entries.
> + */
> +static int copy_single_inode(struct btrfs_trans_handle *trans,
> + struct btrfs_root *root, u64 objectid,
> + ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
> + struct ext2_inode *ext2_inode,
> + int datacsum, int packing, int noxattr)
> +{
> + int ret;
> + struct btrfs_key inode_key;
> + struct btrfs_inode_item btrfs_inode;
> +
> + if (ext2_inode->i_links_count == 0)
> + return 0;
> +
> + copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize);
> + if (!datacsum && S_ISREG(ext2_inode->i_mode)) {
> + u32 flags = btrfs_stack_inode_flags(&btrfs_inode) |
> + BTRFS_INODE_NODATASUM;
> + btrfs_set_stack_inode_flags(&btrfs_inode, flags);
> + }
> +
> + switch (ext2_inode->i_mode & S_IFMT) {
> + case S_IFREG:
> + ret = create_file_extents(trans, root, objectid, &btrfs_inode,
> + ext2_fs, ext2_ino, datacsum, packing);
> + break;
> + case S_IFDIR:
> + ret = create_dir_entries(trans, root, objectid, &btrfs_inode,
> + ext2_fs, ext2_ino);
> + break;
> + case S_IFLNK:
> + ret = create_symbol_link(trans, root, objectid, &btrfs_inode,
> + ext2_fs, ext2_ino, ext2_inode);
> + break;
> + default:
> + ret = 0;
> + break;
> + }
> + if (ret)
> + return ret;
> +
> + if (!noxattr) {
> + ret = copy_extended_attrs(trans, root, objectid, &btrfs_inode,
> + ext2_fs, ext2_ino);
> + if (ret)
> + return ret;
> + }
> + inode_key.objectid = objectid;
> + inode_key.offset = 0;
> + btrfs_set_key_type(&inode_key, BTRFS_INODE_ITEM_KEY);
> + ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
> + return ret;
> +}
> +
> +/*
> + * scan ext2''s inode bitmap and copy all used inode.
> + */
> +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;
> + struct ext2_inode ext2_inode;
> + ext2_ino_t ext2_ino;
> + u64 objectid;
> + struct btrfs_trans_handle *trans;
> +
> + trans = btrfs_start_transaction(root, 1);
> + if (!trans)
> + return -ENOMEM;
> + err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan);
> + if (err) {
> + fprintf(stderr, "ext2fs_open_inode_scan: %s\n",
error_message(err));
> + return -1;
> + }
> + while (!(err = ext2fs_get_next_inode(ext2_scan, &ext2_ino,
> + &ext2_inode))) {
> + /* no more inodes */
> + if (ext2_ino == 0)
> + break;
> + /* skip special inode in ext2fs */
> + if (ext2_ino < EXT2_GOOD_OLD_FIRST_INO &&
> + ext2_ino != EXT2_ROOT_INO)
> + continue;
> + objectid = ext2_ino + INO_OFFSET;
> + ret = copy_single_inode(trans, root,
> + objectid, ext2_fs, ext2_ino,
> + &ext2_inode, datacsum, packing,
> + noxattr);
> + if (ret)
> + return ret;
> + if (trans->blocks_used >= 4096) {
> + ret = btrfs_commit_transaction(trans, root);
> + BUG_ON(ret);
> + trans = btrfs_start_transaction(root, 1);
> + BUG_ON(!trans);
> + }
> + }
> + if (err) {
> + fprintf(stderr, "ext2fs_get_next_inode: %s\n",
error_message(err));
> + return -1;
> + }
> + ret = btrfs_commit_transaction(trans, root);
> + BUG_ON(ret);
> +
> + return ret;
> +}
> +
> +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;
> +}
> --
> 1.6.4.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe
linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html