Re: [PATCH 4/4] btrfs-convert: split into convert/.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxx
> 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@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux