forked from Mirrors/btrfs-progs
xattr support for the ext3->btrfs converter
parent
e80e7da3b6
commit
62a72e7d9a
394
convert.c
394
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue