From: Chris Mason <chris.mason@Oracle.COM>
To: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH 4/4] btrfs-convert: split into convert/.
Date: Tue, 23 Mar 2010 15:06:28 -0400 [thread overview]
Message-ID: <20100323190628.GI30249@think> (raw)
In-Reply-To: <20100320042746.GA17114@flcl.lan>
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
next prev parent reply other threads:[~2010-03-23 19:06 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-03-20 4:27 [PATCH 4/4] btrfs-convert: split into convert/ Sean Bartell
2010-03-23 19:06 ` Chris Mason [this message]
2010-03-23 20:09 ` Sean Bartell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100323190628.GI30249@think \
--to=chris.mason@oracle.com \
--cc=linux-btrfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.