summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan <yanzheng@21cn.com>2008-01-14 13:35:00 -0500
committerDavid Woodhouse <dwmw2@hera.kernel.org>2008-01-14 13:35:00 -0500
commit62a72e7d9ada7a986dd299622b2233fe2e386003 (patch)
treed766c7fb106bf3b4c9417378e4efba22f5f68237
parente80e7da3b6d5434c86d9cde22680b4a1f2d8d940 (diff)
xattr support for the ext3->btrfs converter
-rw-r--r--convert.c394
1 files changed, 361 insertions, 33 deletions
diff --git a/convert.c b/convert.c
index beb9a960..c0476203 100644
--- a/convert.c
+++ b/convert.c
@@ -25,6 +25,7 @@
#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>
@@ -35,11 +36,10 @@
#include "transaction.h"
#include "crc32c.h"
#include "utils.h"
-#include "ext2fs/ext2_fs.h"
-#include "ext2fs/ext2fs.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.
@@ -446,7 +446,8 @@ static int __block_iterate_proc(ext2_filsys fs, blk_t *blocknr,
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)
+ ext2_filsys ext2_fs, ext2_ino_t ext2_ino,
+ int datacsum, int packing)
{
int ret;
char *buffer = NULL;
@@ -454,7 +455,6 @@ static int create_file_extents(struct btrfs_trans_handle *trans,
u32 last_block;
u32 sectorsize = root->sectorsize;
u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
- u32 inode_flags = btrfs_stack_inode_flags(btrfs_inode);
struct blk_iterate_data data = {
.trans = trans,
.root = root,
@@ -463,12 +463,9 @@ static int create_file_extents(struct btrfs_trans_handle *trans,
.first_block = 0,
.disk_block = 0,
.num_blocks = 0,
- .checksum = 1,
+ .checksum = datacsum,
.errcode = 0,
};
-
- if (inode_flags & BTRFS_INODE_NODATASUM)
- data.checksum = 0;
err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
NULL, __block_iterate_proc, &data);
if (err)
@@ -476,8 +473,7 @@ static int create_file_extents(struct btrfs_trans_handle *trans,
ret = data.errcode;
if (ret)
goto fail;
-
- if (data.first_block == 0 && data.num_blocks > 0 &&
+ if (packing && data.first_block == 0 && data.num_blocks > 0 &&
inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
u64 num_bytes = data.num_blocks * sectorsize;
u64 disk_bytenr = data.disk_block * sectorsize;
@@ -528,8 +524,8 @@ static int create_symbol_link(struct btrfs_trans_handle *trans,
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);
+ 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;
}
@@ -541,6 +537,324 @@ static int create_symbol_link(struct btrfs_trans_handle *trans,
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;
+ }
+ 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;
+ }
+ strcpy(namebuf, xattr_prefix_table[name_index]);
+ strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
+ 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))
@@ -604,7 +918,7 @@ static int copy_inode_item(struct btrfs_inode_item *dst,
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,
- int datacsum)
+ int datacsum, int packing, int noxattr)
{
int ret;
errcode_t err;
@@ -631,7 +945,7 @@ static int copy_single_inode(struct btrfs_trans_handle *trans,
switch (ext2_inode.i_mode & S_IFMT) {
case S_IFREG:
ret = create_file_extents(trans, root, objectid, &btrfs_inode,
- ext2_fs, ext2_ino);
+ ext2_fs, ext2_ino, datacsum, packing);
break;
case S_IFDIR:
ret = create_dir_entries(trans, root, objectid, &btrfs_inode,
@@ -647,6 +961,13 @@ static int copy_single_inode(struct btrfs_trans_handle *trans,
}
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);
@@ -680,12 +1001,11 @@ fail:
ret = -1;
return ret;
}
-
/*
* scan ext2's inode bitmap and copy all used inode.
*/
static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
- int datacsum)
+ int datacsum, int packing, int noxattr)
{
int ret;
ext2_ino_t ext2_ino;
@@ -704,8 +1024,9 @@ static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
ext2_ino != EXT2_ROOT_INO)
continue;
objectid = ext2_ino + INO_OFFSET;
- ret = copy_single_inode(trans, root, objectid,
- ext2_fs, ext2_ino, datacsum);
+ ret = copy_single_inode(trans, root,
+ objectid, ext2_fs, ext2_ino,
+ datacsum, packing, noxattr);
if (ret)
return ret;
}
@@ -737,7 +1058,6 @@ static int lookup_extent_item(struct btrfs_trans_handle *trans,
btrfs_release_path(root, &path);
return ret;
}
-
/*
* Construct a range of ext2fs image file.
* scan block allocation bitmap, find all blocks used by the ext2fs
@@ -804,7 +1124,6 @@ static int create_image_file_range(struct btrfs_trans_handle *trans,
fail:
return 0;
}
-
/*
* Create the ext2fs image file.
*/
@@ -970,7 +1289,7 @@ again:
/*
* otime isn't used currently, so we can store some data in it.
* These data are used by do_rollback to check whether the image
- * file have been modified.
+ * file has been modified.
*/
btrfs_set_stack_timespec_sec(&btrfs_inode.otime, trans->transid);
btrfs_set_stack_timespec_nsec(&btrfs_inode.otime,
@@ -1059,7 +1378,6 @@ struct btrfs_root *create_subvol(struct btrfs_root *root, const char *name)
fail:
return NULL;
}
-
/*
* Fixup block accounting. The initial block accounting created by
* make_block_groups isn't accuracy in this case.
@@ -1219,7 +1537,8 @@ fail:
ret = -1;
return ret;
}
-int do_convert(const char *devname, int datacsum)
+
+int do_convert(const char *devname, int datacsum, int packing, int noxattr)
{
int i, fd, ret;
u32 blocksize;
@@ -1289,7 +1608,7 @@ int do_convert(const char *devname, int datacsum)
goto fail;
}
printf("creating btrfs metadata.\n");
- ret = copy_inodes(root, ext2_fs, datacsum);
+ ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr);
if (ret) {
fprintf(stderr, "error during copy_inodes %d\n", ret);
goto fail;
@@ -1420,7 +1739,8 @@ int do_rollback(const char *devname, int force)
total_bytes = btrfs_timespec_nsec(leaf, tspec);
total_bytes *= root->sectorsize;
btrfs_release_path(ext2_root, &path);
- if (total_bytes != btrfs_inode_size(leaf, inode)) {
+ if (total_bytes < first_free ||
+ total_bytes != btrfs_inode_size(leaf, inode)) {
fprintf(stderr, "image file size mismatch\n");
goto fail;
}
@@ -1431,9 +1751,9 @@ int do_rollback(const char *devname, int force)
ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0);
if (ret != 0) {
fprintf(stderr, "unable to find first file extent\n");
+ btrfs_release_path(ext2_root, &path);
goto fail;
}
-
for (offset = 0; offset < total_bytes; ) {
leaf = path.nodes[0];
if (path.slots[0] >= btrfs_header_nritems(leaf)) {
@@ -1516,11 +1836,12 @@ fail:
fprintf(stderr, "rollback aborted.\n");
return -1;
}
-
static void print_usage(void)
{
- printf("usage: btrfs-convert [-d] [-r] device\n");
+ printf("usage: btrfs-convert [-d] [-i] [-n] [-r] device\n");
printf("\t-d disable data checksum\n");
+ printf("\t-i ignore xattrs and ACLs\n");
+ printf("\t-n disable packing of small files\n");
printf("\t-r roll back to ext2fs\n");
exit(1);
}
@@ -1528,18 +1849,25 @@ static void print_usage(void)
int main(int argc, char *argv[])
{
int ret;
+ int packing = 1;
+ int noxattr = 0;
int datacsum = 1;
int rollback = 0;
char *file;
-
while(1) {
- int c = getopt(argc, argv, "dr");
+ int c = getopt(argc, argv, "dinr");
if (c < 0)
break;
switch(c) {
case 'd':
datacsum = 0;
break;
+ case 'i':
+ noxattr = 1;
+ break;
+ case 'n':
+ packing = 0;
+ break;
case 'r':
rollback = 1;
break;
@@ -1556,7 +1884,7 @@ int main(int argc, char *argv[])
if (rollback) {
ret = do_rollback(file, 0);
} else {
- ret = do_convert(file, datacsum);
+ ret = do_convert(file, datacsum, packing, noxattr);
}
return ret;
}