* Boston, MA 021110-1307, USA.
*/
-#define _XOPEN_SOURCE 600
-#define _GNU_SOURCE 1
-
#include "kerncompat.h"
#include <sys/ioctl.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/limits.h>
+#include <getopt.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
#include "crc32c.h"
#include "utils.h"
+#include "task-utils.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)
-#define EXT2_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+#define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID
+
+struct task_ctx {
+ uint32_t max_copy_inodes;
+ uint32_t cur_copy_inodes;
+ struct task_info *info;
+};
+
+static void *print_copied_inodes(void *p)
+{
+ struct task_ctx *priv = p;
+ const char work_indicator[] = { '.', 'o', 'O', 'o' };
+ uint32_t count = 0;
+
+ task_period_start(priv->info, 1000 /* 1s */);
+ while (1) {
+ count++;
+ printf("copy inodes [%c] [%10d/%10d]\r",
+ work_indicator[count % 4], priv->cur_copy_inodes,
+ priv->max_copy_inodes);
+ fflush(stdout);
+ task_period_wait(priv->info);
+ }
+
+ return NULL;
+}
+
+static int after_copied_inodes(void *p)
+{
+ printf("\n");
+ fflush(stdout);
+
+ return 0;
+}
/*
* Open Ext2fs in readonly mode, read block allocation bitmap and
return -ENOSPC;
}
+static int ext2_alloc_block_range(ext2_filsys fs, u64 goal, int num,
+ u64 *block_ret)
+{
+ blk_t block;
+ ext2fs_block_bitmap bitmap = fs->block_map;
+ blk_t start = ext2fs_get_block_bitmap_start(bitmap);
+ blk_t end = ext2fs_get_block_bitmap_end(bitmap);
+
+ for (block = max_t(u64, goal, start); block + num < end; block++) {
+ if (ext2fs_fast_test_block_bitmap_range(bitmap, block, num)) {
+ ext2fs_fast_mark_block_bitmap_range(bitmap, block,
+ num);
+ *block_ret = block;
+ return 0;
+ }
+ }
+ return -ENOSPC;
+}
+
static int ext2_free_block(ext2_filsys fs, u64 block)
{
BUG_ON(block != (blk_t)block);
return 0;
}
+static int ext2_free_block_range(ext2_filsys fs, u64 block, int num)
+{
+ BUG_ON(block != (blk_t)block);
+ ext2fs_fast_unmark_block_bitmap_range(fs->block_map, block, num);
+ return 0;
+}
+
static int cache_free_extents(struct btrfs_root *root, ext2_filsys ext2_fs)
{
}
static int custom_alloc_extent(struct btrfs_root *root, u64 num_bytes,
- u64 hint_byte, struct btrfs_key *ins)
+ u64 hint_byte, struct btrfs_key *ins,
+ int metadata)
{
u64 start;
u64 end;
continue;
}
+ if (metadata) {
+ BUG_ON(num_bytes != root->nodesize);
+ if (check_crossing_stripes(start, num_bytes)) {
+ last = round_down(start + num_bytes,
+ BTRFS_STRIPE_LEN);
+ continue;
+ }
+ }
clear_extent_dirty(&root->fs_info->free_space_cache,
start, start + num_bytes - 1, 0);
.free_extent = custom_free_extent,
};
+static int convert_insert_dirent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ const char *name, size_t name_len,
+ u64 dir, u64 objectid,
+ u8 file_type, u64 index_cnt,
+ struct btrfs_inode_item *inode)
+{
+ int ret;
+ u64 inode_size;
+ struct btrfs_key location = {
+ .objectid = objectid,
+ .offset = 0,
+ .type = BTRFS_INODE_ITEM_KEY,
+ };
+
+ ret = btrfs_insert_dir_item(trans, root, name, name_len,
+ dir, &location, file_type, index_cnt);
+ if (ret)
+ return ret;
+ ret = btrfs_insert_inode_ref(trans, root, name, name_len,
+ objectid, dir, index_cnt);
+ if (ret)
+ return ret;
+ inode_size = btrfs_stack_inode_size(inode) + name_len * 2;
+ btrfs_set_stack_inode_size(inode, inode_size);
+
+ return 0;
+}
+
struct dir_iterate_data {
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
int ret;
int file_type;
u64 objectid;
- u64 inode_size;
char dotdot[] = "..";
- struct btrfs_key location;
struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data;
int name_len;
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->name_len >> 8;
BUG_ON(file_type > EXT2_FT_SYMLINK);
- ret = btrfs_insert_dir_item(idata->trans, idata->root,
- dirent->name, name_len,
- idata->objectid, &location,
+
+ ret = convert_insert_dirent(idata->trans, idata->root, dirent->name,
+ name_len, idata->objectid, objectid,
filetype_conversion_table[file_type],
- idata->index_cnt);
- if (ret)
- goto fail;
- ret = btrfs_insert_inode_ref(idata->trans, idata->root,
- dirent->name, name_len,
- objectid, idata->objectid,
- idata->index_cnt);
- if (ret)
- goto fail;
+ idata->index_cnt, idata->inode);
+ if (ret < 0) {
+ idata->errcode = ret;
+ return BLOCK_ABORT;
+ }
+
idata->index_cnt++;
- inode_size = btrfs_stack_inode_size(idata->inode) +
- 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,
return ret;
}
-static int record_file_blocks(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 objectid,
- struct btrfs_inode_item *inode,
- u64 file_block, u64 disk_block,
- u64 num_blocks, int checksum)
-{
- int ret;
- u64 file_pos = file_block * root->sectorsize;
- u64 disk_bytenr = disk_block * root->sectorsize;
- u64 num_bytes = num_blocks * root->sectorsize;
- ret = btrfs_record_file_extent(trans, root, objectid, inode, file_pos,
- disk_bytenr, num_bytes);
-
- if (ret || !checksum)
- return ret;
-
- return csum_disk_extent(trans, root, disk_bytenr, num_bytes);
-}
-
struct blk_iterate_data {
struct btrfs_trans_handle *trans;
struct btrfs_root *root;
int errcode;
};
-static int block_iterate_proc(ext2_filsys ext2_fs,
- u64 disk_block, u64 file_block,
- struct blk_iterate_data *idata)
+static void init_blk_iterate_data(struct blk_iterate_data *data,
+ struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_inode_item *inode,
+ u64 objectid, int checksum)
+{
+ data->trans = trans;
+ data->root = root;
+ data->inode = inode;
+ data->objectid = objectid;
+ data->first_block = 0;
+ data->disk_block = 0;
+ data->num_blocks = 0;
+ data->boundary = (u64)-1;
+ data->checksum = checksum;
+ data->errcode = 0;
+}
+
+static int record_file_blocks(struct blk_iterate_data *data,
+ u64 file_block, u64 disk_block, u64 num_blocks)
{
int ret;
+ struct btrfs_root *root = data->root;
+ u64 file_pos = file_block * root->sectorsize;
+ u64 disk_bytenr = disk_block * root->sectorsize;
+ u64 num_bytes = num_blocks * root->sectorsize;
+ ret = btrfs_record_file_extent(data->trans, data->root,
+ data->objectid, data->inode, file_pos,
+ disk_bytenr, num_bytes);
+
+ if (ret || !data->checksum || disk_bytenr == 0)
+ return ret;
+
+ return csum_disk_extent(data->trans, data->root, disk_bytenr,
+ num_bytes);
+}
+
+static int block_iterate_proc(u64 disk_block, u64 file_block,
+ struct blk_iterate_data *idata)
+{
+ int ret = 0;
int sb_region;
int do_barrier;
struct btrfs_root *root = idata->root;
- struct btrfs_trans_handle *trans = idata->trans;
struct btrfs_block_group_cache *cache;
u64 bytenr = disk_block * root->sectorsize;
(file_block > idata->first_block + idata->num_blocks) ||
(disk_block != idata->disk_block + idata->num_blocks)) {
if (idata->num_blocks > 0) {
- ret = record_file_blocks(trans, root, idata->objectid,
- idata->inode, idata->first_block,
- idata->disk_block, idata->num_blocks,
- idata->checksum);
+ ret = record_file_blocks(idata, idata->first_block,
+ idata->disk_block,
+ idata->num_blocks);
if (ret)
goto fail;
idata->first_block += idata->num_blocks;
idata->num_blocks = 0;
}
if (file_block > idata->first_block) {
- ret = record_file_blocks(trans, root, idata->objectid,
- idata->inode, idata->first_block,
- 0, file_block - idata->first_block,
- idata->checksum);
+ ret = record_file_blocks(idata, idata->first_block,
+ 0, file_block - idata->first_block);
if (ret)
goto fail;
}
idata->boundary = bytenr / root->sectorsize;
}
idata->num_blocks++;
- return 0;
fail:
- idata->errcode = ret;
- return BLOCK_ABORT;
+ return ret;
}
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)
{
+ int ret;
struct blk_iterate_data *idata;
idata = (struct blk_iterate_data *)priv_data;
- return block_iterate_proc(fs, *blocknr, blockcnt, idata);
+ ret = block_iterate_proc(*blocknr, blockcnt, idata);
+ if (ret) {
+ idata->errcode = ret;
+ return BLOCK_ABORT;
+ }
+ return 0;
}
/*
u32 last_block;
u32 sectorsize = root->sectorsize;
u64 inode_size = btrfs_stack_inode_size(btrfs_inode);
- struct blk_iterate_data data = {
- .trans = trans,
- .root = root,
- .inode = btrfs_inode,
- .objectid = objectid,
- .first_block = 0,
- .disk_block = 0,
- .num_blocks = 0,
- .boundary = (u64)-1,
- .checksum = datacsum,
- .errcode = 0,
- };
+ struct blk_iterate_data data;
+
+ init_blk_iterate_data(&data, trans, root, btrfs_inode, objectid,
+ datacsum);
+
err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY,
NULL, __block_iterate_proc, &data);
if (err)
nbytes = btrfs_stack_inode_nbytes(btrfs_inode) + num_bytes;
btrfs_set_stack_inode_nbytes(btrfs_inode, nbytes);
} else if (data.num_blocks > 0) {
- ret = record_file_blocks(trans, root, objectid, btrfs_inode,
- data.first_block, data.disk_block,
- data.num_blocks, data.checksum);
+ ret = record_file_blocks(&data, data.first_block,
+ data.disk_block, data.num_blocks);
if (ret)
goto fail;
}
data.first_block += data.num_blocks;
last_block = (inode_size + sectorsize - 1) / sectorsize;
if (last_block > data.first_block) {
- ret = record_file_blocks(trans, root, objectid, btrfs_inode,
- data.first_block, 0, last_block -
- data.first_block, data.checksum);
+ ret = record_file_blocks(&data, data.first_block, 0,
+ last_block - data.first_block);
}
fail:
free(buffer);
#define EXT2_ACL_VERSION 0x0001
+/* 23.2.5 acl_tag_t values */
+
+#define ACL_UNDEFINED_TAG (0x00)
+#define ACL_USER_OBJ (0x01)
+#define ACL_USER (0x02)
+#define ACL_GROUP_OBJ (0x04)
+#define ACL_GROUP (0x08)
+#define ACL_MASK (0x10)
+#define ACL_OTHER (0x20)
+
+/* 23.2.7 ACL qualifier constants */
+
+#define ACL_UNDEFINED_ID ((id_t)-1)
+
typedef struct {
__le16 e_tag;
__le16 e_perm;
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)
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;
+ return btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
}
static int copy_disk_extent(struct btrfs_root *root, u64 dst_bytenr,
* scan ext2's inode bitmap and copy all used inodes.
*/
static int copy_inodes(struct btrfs_root *root, ext2_filsys ext2_fs,
- int datacsum, int packing, int noxattr)
+ int datacsum, int packing, int noxattr, struct task_ctx *p)
{
int ret;
errcode_t err;
objectid, ext2_fs, ext2_ino,
&ext2_inode, datacsum, packing,
noxattr);
+ p->cur_copy_inodes++;
if (ret)
return ret;
if (trans->blocks_used >= 4096) {
struct btrfs_root *root, u64 objectid,
struct btrfs_inode_item *inode,
u64 start_byte, u64 end_byte,
- ext2_filsys ext2_fs)
+ ext2_filsys ext2_fs, int datacsum)
{
u32 blocksize = ext2_fs->blocksize;
u32 block = start_byte / blocksize;
u32 last_block = (end_byte + blocksize - 1) / blocksize;
int ret = 0;
- struct blk_iterate_data data = {
- .trans = trans,
- .root = root,
- .inode = inode,
- .objectid = objectid,
- .first_block = block,
- .disk_block = 0,
- .num_blocks = 0,
- .boundary = (u64)-1,
- .checksum = 0,
- .errcode = 0,
- };
+ struct blk_iterate_data data;
+
+ init_blk_iterate_data(&data, trans, root, inode, objectid, datacsum);
+ data.first_block = block;
+
for (; start_byte < end_byte; block++, start_byte += blocksize) {
if (!ext2fs_fast_test_block_bitmap(ext2_fs->block_map, block))
continue;
- ret = block_iterate_proc(NULL, block, block, &data);
- if (ret & BLOCK_ABORT) {
- ret = data.errcode;
+ ret = block_iterate_proc(block, block, &data);
+ if (ret < 0)
goto fail;
- }
}
if (data.num_blocks > 0) {
- ret = record_file_blocks(trans, root, objectid, inode,
- data.first_block, data.disk_block,
- data.num_blocks, 0);
+ ret = record_file_blocks(&data, data.first_block,
+ data.disk_block, data.num_blocks);
if (ret)
goto fail;
data.first_block += data.num_blocks;
}
if (last_block > data.first_block) {
- ret = record_file_blocks(trans, root, objectid, inode,
- data.first_block, 0, last_block -
- data.first_block, 0);
+ ret = record_file_blocks(&data, data.first_block, 0,
+ last_block - data.first_block);
if (ret)
goto fail;
}
* Create the ext2fs image file.
*/
static int create_ext2_image(struct btrfs_root *root, ext2_filsys ext2_fs,
- const char *name)
+ const char *name, int datacsum)
{
int ret;
struct btrfs_key key;
u64 last_byte;
u64 first_free;
u64 total_bytes;
+ u64 flags = BTRFS_INODE_READONLY;
u32 sectorsize = root->sectorsize;
total_bytes = btrfs_super_total_bytes(fs_info->super_copy);
first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1;
first_free &= ~((u64)sectorsize - 1);
+ if (!datacsum)
+ flags |= BTRFS_INODE_NODATASUM;
memset(&btrfs_inode, 0, sizeof(btrfs_inode));
btrfs_set_stack_inode_generation(&btrfs_inode, 1);
btrfs_set_stack_inode_nlink(&btrfs_inode, 1);
btrfs_set_stack_inode_nbytes(&btrfs_inode, 0);
btrfs_set_stack_inode_mode(&btrfs_inode, S_IFREG | 0400);
- btrfs_set_stack_inode_flags(&btrfs_inode, BTRFS_INODE_NODATASUM |
- BTRFS_INODE_READONLY);
+ btrfs_set_stack_inode_flags(&btrfs_inode, flags);
btrfs_init_path(&path);
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
* special, we can't rely on relocate_extents_range to relocate it.
*/
for (last_byte = 0; last_byte < first_free; last_byte += sectorsize) {
- ret = custom_alloc_extent(root, sectorsize, 0, &key);
+ ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
if (ret)
goto fail;
ret = copy_disk_extent(root, key.objectid, last_byte,
key.objectid, sectorsize);
if (ret)
goto fail;
+ if (datacsum) {
+ ret = csum_disk_extent(trans, root, key.objectid,
+ sectorsize);
+ if (ret)
+ goto fail;
+ }
}
while(1) {
if (bytenr > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- bytenr, ext2_fs);
+ bytenr, ext2_fs,
+ datacsum);
if (ret)
goto fail;
}
if (total_bytes > last_byte) {
ret = create_image_file_range(trans, root, objectid,
&btrfs_inode, last_byte,
- total_bytes, ext2_fs);
+ total_bytes, ext2_fs,
+ datacsum);
if (ret)
goto fail;
}
btrfs_set_key_type(&location, BTRFS_INODE_ITEM_KEY);
ret = btrfs_insert_dir_item(trans, root, name, strlen(name),
btrfs_root_dirid(&root->root_item),
- &location, EXT2_FT_REG_FILE, objectid);
+ &location, BTRFS_FT_REG_FILE, objectid);
if (ret)
goto fail;
ret = btrfs_insert_inode_ref(trans, root, name, strlen(name),
BTRFS_FIRST_FREE_OBJECTID);
/* subvol for ext2 image file */
- ret = create_subvol(trans, root, EXT2_IMAGE_SUBVOL_OBJECTID);
+ ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID);
BUG_ON(ret);
/* subvol for data relocation */
ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
}
/*
- * Migrate super block to it's default position and zero 0 ~ 16k
+ * Migrate super block to its default position and zero 0 ~ 16k
*/
static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize)
{
return 0;
}
-static int prepare_system_chunk(int fd, u64 sb_bytenr, u32 sectorsize)
+static int prepare_system_chunk(int fd, u64 sb_bytenr)
{
int ret;
struct extent_buffer *buf;
struct btrfs_super_block *super;
- BUG_ON(sectorsize < sizeof(*super));
- buf = malloc(sizeof(*buf) + sectorsize);
+ BUG_ON(BTRFS_SUPER_INFO_SIZE < sizeof(*super));
+ buf = malloc(sizeof(*buf) + BTRFS_SUPER_INFO_SIZE);
if (!buf)
return -ENOMEM;
- buf->len = sectorsize;
- ret = pread(fd, buf->data, sectorsize, sb_bytenr);
- if (ret != sectorsize)
+ buf->len = BTRFS_SUPER_INFO_SIZE;
+ ret = pread(fd, buf->data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
goto fail;
super = (struct btrfs_super_block *)buf->data;
goto fail;
csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
- ret = pwrite(fd, buf->data, sectorsize, sb_bytenr);
- if (ret != sectorsize)
+ ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, sb_bytenr);
+ if (ret != BTRFS_SUPER_INFO_SIZE)
goto fail;
ret = 0;
btrfs_set_stack_inode_nbytes(&inode, nbytes);
datacsum = !(btrfs_stack_inode_flags(&inode) & BTRFS_INODE_NODATASUM);
- data = (struct blk_iterate_data) {
- .trans = trans,
- .root = root,
- .inode = &inode,
- .objectid = extent_key->objectid,
- .first_block = extent_key->offset / sectorsize,
- .disk_block = 0,
- .num_blocks = 0,
- .boundary = (u64)-1,
- .checksum = datacsum,
- .errcode = 0,
- };
+ init_blk_iterate_data(&data, trans, root, &inode, extent_key->objectid,
+ datacsum);
+ data.first_block = extent_key->offset;
cur_offset = extent_key->offset;
while (num_bytes > 0) {
ret = get_state_private(reloc_tree, bytenr, &new_pos);
BUG_ON(ret);
} else {
- ret = custom_alloc_extent(root, sectorsize, 0, &key);
+ ret = custom_alloc_extent(root, sectorsize, 0, &key, 0);
if (ret)
goto fail;
new_pos = key.objectid;
BUG_ON(ret);
}
- ret = block_iterate_proc(NULL, new_pos / sectorsize,
+ ret = block_iterate_proc(new_pos / sectorsize,
cur_offset / sectorsize, &data);
- if (ret & BLOCK_ABORT) {
- ret = data.errcode;
+ if (ret < 0)
goto fail;
- }
cur_offset += sectorsize;
bytenr += sectorsize;
}
if (data.num_blocks > 0) {
- ret = record_file_blocks(trans, root,
- extent_key->objectid, &inode,
- data.first_block, data.disk_block,
- data.num_blocks, datacsum);
+ ret = record_file_blocks(&data, data.first_block,
+ data.disk_block, data.num_blocks);
if (ret)
goto fail;
}
}
static int relocate_extents_range(struct btrfs_root *fs_root,
- struct btrfs_root *ext2_root,
+ struct btrfs_root *image_root,
u64 start_byte, u64 end_byte)
{
struct btrfs_fs_info *info = fs_root->fs_info;
}
btrfs_release_path(&path);
again:
- cur_root = (pass % 2 == 0) ? ext2_root : fs_root;
+ cur_root = (pass % 2 == 0) ? image_root : fs_root;
num_extents = 0;
trans = btrfs_start_transaction(cur_root, 1);
* relocate data in system chunk
*/
static int cleanup_sys_chunk(struct btrfs_root *fs_root,
- struct btrfs_root *ext2_root)
+ struct btrfs_root *image_root)
{
struct btrfs_block_group_cache *cache;
int i, ret = 0;
end_byte = cache->key.objectid + cache->key.offset;
if (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM) {
- ret = relocate_extents_range(fs_root, ext2_root,
+ ret = relocate_extents_range(fs_root, image_root,
cache->key.objectid,
end_byte);
if (ret)
offset = btrfs_sb_offset(i);
offset &= ~((u64)BTRFS_STRIPE_LEN - 1);
- ret = relocate_extents_range(fs_root, ext2_root,
+ ret = relocate_extents_range(fs_root, image_root,
offset, offset + BTRFS_STRIPE_LEN);
if (ret)
goto fail;
return ret;
}
-static int do_convert(const char *devname, int datacsum, int packing,
- int noxattr)
+static int do_convert(const char *devname, int datacsum, int packing, int noxattr,
+ u32 nodesize, int copylabel, const char *fslabel, int progress,
+ u64 features)
{
- int i, ret;
+ int i, ret, blocks_per_node;
int fd = -1;
u32 blocksize;
u64 blocks[7];
u64 super_bytenr;
ext2_filsys ext2_fs;
struct btrfs_root *root;
- struct btrfs_root *ext2_root;
+ struct btrfs_root *image_root;
+ struct task_ctx ctx;
+ char features_buf[64];
+ struct btrfs_mkfs_config mkfs_cfg;
ret = open_ext2fs(devname, &ext2_fs);
if (ret) {
fprintf(stderr, "filetype feature is missing\n");
goto fail;
}
+ if (btrfs_check_nodesize(nodesize, blocksize))
+ goto fail;
+ blocks_per_node = nodesize / blocksize;
+ ret = -blocks_per_node;
for (i = 0; i < 7; i++) {
- ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
+ if (nodesize == blocksize)
+ ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
+ else
+ ret = ext2_alloc_block_range(ext2_fs,
+ ret + blocks_per_node, blocks_per_node,
+ blocks + i);
if (ret) {
fprintf(stderr, "not enough free space\n");
goto fail;
fprintf(stderr, "unable to open %s\n", devname);
goto fail;
}
- ret = make_btrfs(fd, devname, ext2_fs->super->s_volume_name,
- NULL, blocks, total_bytes, blocksize, blocksize,
- blocksize, blocksize, 0);
+ btrfs_parse_features_to_string(features_buf, features);
+ if (features == BTRFS_MKFS_DEFAULT_FEATURES)
+ strcat(features_buf, " (default)");
+
+ printf("create btrfs filesystem:\n");
+ printf("\tblocksize: %u\n", blocksize);
+ printf("\tnodesize: %u\n", nodesize);
+ printf("\tfeatures: %s\n", features_buf);
+
+ mkfs_cfg.label = ext2_fs->super->s_volume_name;
+ mkfs_cfg.fs_uuid = NULL;
+ memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
+ mkfs_cfg.num_bytes = total_bytes;
+ mkfs_cfg.nodesize = nodesize;
+ mkfs_cfg.sectorsize = blocksize;
+ mkfs_cfg.stripesize = blocksize;
+ mkfs_cfg.features = features;
+
+ ret = make_btrfs(fd, &mkfs_cfg);
if (ret) {
fprintf(stderr, "unable to create initial ctree: %s\n",
strerror(-ret));
goto fail;
}
/* create a system chunk that maps the whole device */
- ret = prepare_system_chunk(fd, super_bytenr, blocksize);
+ ret = prepare_system_chunk(fd, super_bytenr);
if (ret) {
fprintf(stderr, "unable to update system chunk\n");
goto fail;
/* recover block allocation bitmap */
for (i = 0; i < 7; i++) {
blocks[i] /= blocksize;
- ext2_free_block(ext2_fs, blocks[i]);
+ if (nodesize == blocksize)
+ ext2_free_block(ext2_fs, blocks[i]);
+ else
+ ext2_free_block_range(ext2_fs, blocks[i],
+ blocks_per_node);
}
ret = init_btrfs(root);
if (ret) {
goto fail;
}
printf("creating btrfs metadata.\n");
- ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr);
+ ctx.max_copy_inodes = (ext2_fs->super->s_inodes_count
+ - ext2_fs->super->s_free_inodes_count);
+ ctx.cur_copy_inodes = 0;
+
+ if (progress) {
+ ctx.info = task_init(print_copied_inodes, after_copied_inodes, &ctx);
+ task_start(ctx.info);
+ }
+ ret = copy_inodes(root, ext2_fs, datacsum, packing, noxattr, &ctx);
if (ret) {
fprintf(stderr, "error during copy_inodes %d\n", ret);
goto fail;
}
+ if (progress) {
+ task_stop(ctx.info);
+ task_deinit(ctx.info);
+ }
printf("creating ext2fs image file.\n");
- ext2_root = link_subvol(root, "ext2_saved", EXT2_IMAGE_SUBVOL_OBJECTID);
- if (!ext2_root) {
+ image_root = link_subvol(root, "ext2_saved", CONV_IMAGE_SUBVOL_OBJECTID);
+ if (!image_root) {
fprintf(stderr, "unable to create subvol\n");
goto fail;
}
- ret = create_ext2_image(ext2_root, ext2_fs, "image");
+ ret = create_ext2_image(image_root, ext2_fs, "image", datacsum);
if (ret) {
fprintf(stderr, "error during create_ext2_image %d\n", ret);
goto fail;
}
+ memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE);
+ if (copylabel == 1) {
+ strncpy(root->fs_info->super_copy->label,
+ ext2_fs->super->s_volume_name, 16);
+ fprintf(stderr, "copy label '%s'\n",
+ root->fs_info->super_copy->label);
+ } else if (copylabel == -1) {
+ strncpy(root->fs_info->super_copy->label, fslabel, BTRFS_LABEL_SIZE);
+ fprintf(stderr, "set label to '%s'\n", fslabel);
+ }
+
printf("cleaning up system chunk.\n");
- ret = cleanup_sys_chunk(root, ext2_root);
+ ret = cleanup_sys_chunk(root, image_root);
if (ret) {
fprintf(stderr, "error during cleanup_sys_chunk %d\n", ret);
goto fail;
while (1) {
ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr,
&length, &multi, 0, NULL);
- if (ret)
+ if (ret) {
+ if (ret == -ENOENT) {
+ /* removed block group at the tail */
+ if (length == (u64)-1)
+ break;
+
+ /* removed block group in the middle */
+ goto next;
+ }
goto fail;
+ }
num_stripes = multi->num_stripes;
physical = multi->stripes[0].physical;
if (num_stripes != 1 || physical != bytenr)
goto fail;
-
+next:
bytenr += length;
if (bytenr >= total_bytes)
break;
return -1;
}
-static int do_rollback(const char *devname, int force)
+static int do_rollback(const char *devname)
{
int fd = -1;
int ret;
int i;
struct btrfs_root *root;
- struct btrfs_root *ext2_root;
+ struct btrfs_root *image_root;
struct btrfs_root *chunk_root;
struct btrfs_dir_item *dir;
struct btrfs_inode_item *inode;
btrfs_init_path(&path);
- key.objectid = EXT2_IMAGE_SUBVOL_OBJECTID;
+ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID;
key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1;
- ext2_root = btrfs_read_fs_root(root->fs_info, &key);
- if (!ext2_root || IS_ERR(ext2_root)) {
+ image_root = btrfs_read_fs_root(root->fs_info, &key);
+ if (!image_root || IS_ERR(image_root)) {
fprintf(stderr, "unable to open subvol %llu\n",
- key.objectid);
+ (unsigned long long)key.objectid);
goto fail;
}
name = "image";
root_dir = btrfs_root_dirid(&root->root_item);
- dir = btrfs_lookup_dir_item(NULL, ext2_root, &path,
+ dir = btrfs_lookup_dir_item(NULL, image_root, &path,
root_dir, name, strlen(name), 0);
if (!dir || IS_ERR(dir)) {
fprintf(stderr, "unable to find file %s\n", name);
objectid = key.objectid;
- ret = btrfs_lookup_inode(NULL, ext2_root, &path, &key, 0);
+ ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0);
if (ret) {
fprintf(stderr, "unable to find inode item\n");
goto fail;
key.objectid = objectid;
key.offset = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
- ret = btrfs_search_slot(NULL, ext2_root, &key, &path, 0, 0);
+ ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0);
if (ret != 0) {
fprintf(stderr, "unable to find first file extent\n");
btrfs_release_path(&path);
if (bytenr >= total_bytes)
break;
ret = pwrite(fd, buf, sectorsize, bytenr);
+ if (ret != sectorsize) {
+ fprintf(stderr,
+ "error during zeroing supreblock %d: %d\n",
+ i, ret);
+ goto fail;
+ }
}
sb_bytenr = (u64)-1;
static void print_usage(void)
{
- 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");
+ printf("usage: btrfs-convert [options] device\n");
+ printf("options:\n");
+ printf("\t-d|--no-datasum disable data checksum, sets NODATASUM\n");
+ printf("\t-i|--no-xattr ignore xattrs and ACLs\n");
+ printf("\t-n|--no-inline disable inlining of small files to metadata\n");
+ printf("\t-N|--nodesize SIZE set filesystem metadata nodesize\n");
+ printf("\t-r|--rollback roll back to ext2fs\n");
+ printf("\t-l|--label LABEL set filesystem label\n");
+ printf("\t-L|--copy-label use label from converted filesystem\n");
+ printf("\t-p|--progress show converting progress (default)\n");
+ printf("\t-O|--features LIST comma separated list of filesystem features\n");
+ printf("\t--no-progress show only overview, not the detailed progress\n");
}
int main(int argc, char *argv[])
int packing = 1;
int noxattr = 0;
int datacsum = 1;
+ u32 nodesize = max_t(u32, sysconf(_SC_PAGESIZE),
+ BTRFS_MKFS_DEFAULT_NODE_SIZE);
int rollback = 0;
+ int copylabel = 0;
+ int usage_error = 0;
+ int progress = 1;
char *file;
+ char *fslabel = NULL;
+ u64 features = BTRFS_MKFS_DEFAULT_FEATURES;
+
while(1) {
- int c = getopt(argc, argv, "dinr");
+ enum { GETOPT_VAL_NO_PROGRESS = 256 };
+ static const struct option long_options[] = {
+ { "no-progress", no_argument, NULL,
+ GETOPT_VAL_NO_PROGRESS },
+ { "no-datasum", no_argument, NULL, 'd' },
+ { "no-inline", no_argument, NULL, 'n' },
+ { "no-xattr", no_argument, NULL, 'i' },
+ { "rollback", no_argument, NULL, 'r' },
+ { "features", required_argument, NULL, 'O' },
+ { "progress", no_argument, NULL, 'p' },
+ { "label", required_argument, NULL, 'l' },
+ { "copy-label", no_argument, NULL, 'L' },
+ { "nodesize", required_argument, NULL, 'N' },
+ { "help", no_argument, NULL, GETOPT_VAL_HELP},
+ { NULL, 0, NULL, 0 }
+ };
+ int c = getopt_long(argc, argv, "dinN:rl:LpO:", long_options, NULL);
+
if (c < 0)
break;
switch(c) {
case 'n':
packing = 0;
break;
+ case 'N':
+ nodesize = parse_size(optarg);
+ break;
case 'r':
rollback = 1;
break;
+ case 'l':
+ copylabel = -1;
+ fslabel = strdup(optarg);
+ if (strlen(fslabel) > BTRFS_LABEL_SIZE) {
+ fprintf(stderr,
+ "warning: label too long, trimmed to %d bytes\n",
+ BTRFS_LABEL_SIZE);
+ fslabel[BTRFS_LABEL_SIZE] = 0;
+ }
+ break;
+ case 'L':
+ copylabel = 1;
+ break;
+ case 'p':
+ progress = 1;
+ break;
+ case 'O': {
+ char *orig = strdup(optarg);
+ char *tmp = orig;
+
+ tmp = btrfs_parse_fs_features(tmp, &features);
+ if (tmp) {
+ fprintf(stderr,
+ "Unrecognized filesystem feature '%s'\n",
+ tmp);
+ free(orig);
+ exit(1);
+ }
+ free(orig);
+ if (features & BTRFS_FEATURE_LIST_ALL) {
+ btrfs_list_all_fs_features(
+ ~BTRFS_CONVERT_ALLOWED_FEATURES);
+ exit(0);
+ }
+ if (features & ~BTRFS_CONVERT_ALLOWED_FEATURES) {
+ char buf[64];
+
+ btrfs_parse_features_to_string(buf,
+ features & ~BTRFS_CONVERT_ALLOWED_FEATURES);
+ fprintf(stderr,
+ "ERROR: features not allowed for convert: %s\n",
+ buf);
+ exit(1);
+ }
+
+ break;
+ }
+ case GETOPT_VAL_NO_PROGRESS:
+ progress = 0;
+ break;
+ case GETOPT_VAL_HELP:
default:
print_usage();
- return 1;
+ return c != GETOPT_VAL_HELP;
}
}
argc = argc - optind;
- if (argc != 1) {
+ set_argv0(argv);
+ if (check_argc_exact(argc, 1)) {
+ print_usage();
+ return 1;
+ }
+
+ if (rollback && (!datacsum || noxattr || !packing)) {
+ fprintf(stderr,
+ "Usage error: -d, -i, -n options do not apply to rollback\n");
+ usage_error++;
+ }
+
+ if (usage_error) {
print_usage();
return 1;
}
}
if (rollback) {
- ret = do_rollback(file, 0);
+ ret = do_rollback(file);
} else {
- ret = do_convert(file, datacsum, packing, noxattr);
+ ret = do_convert(file, datacsum, packing, noxattr, nodesize,
+ copylabel, fslabel, progress, features);
}
if (ret)
return 1;