From dee762489eabc71e4998e9bd1ae41abb77fab29b Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 28 Nov 2016 14:35:53 +0100 Subject: btrfs-progs: move btrfs-convert to own directory Signed-off-by: David Sterba --- convert/main.c | 3183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3183 insertions(+) create mode 100644 convert/main.c (limited to 'convert') diff --git a/convert/main.c b/convert/main.c new file mode 100644 index 00000000..4b4cea42 --- /dev/null +++ b/convert/main.c @@ -0,0 +1,3183 @@ +/* + * 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 "kerncompat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctree.h" +#include "disk-io.h" +#include "volumes.h" +#include "transaction.h" +#include "crc32c.h" +#include "utils.h" +#include "task-utils.h" + +#if BTRFSCONVERT_EXT2 +#include +#include +#include + +#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO) + +/* + * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag + * BIGALLOC. + * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used + * space, and btrfs-convert heavily relies on it. + */ +#ifdef HAVE_OLD_E2FSPROGS +#define EXT2FS_CLUSTER_RATIO(fs) (1) +#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s)) +#define EXT2FS_B2C(fs, blk) (blk) +#endif + +#endif + +#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; +} + +struct btrfs_convert_context; +struct btrfs_convert_operations { + const char *name; + int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); + int (*read_used_space)(struct btrfs_convert_context *cctx); + int (*copy_inodes)(struct btrfs_convert_context *cctx, + struct btrfs_root *root, int datacsum, + int packing, int noxattr, struct task_ctx *p); + void (*close_fs)(struct btrfs_convert_context *cctx); + int (*check_state)(struct btrfs_convert_context *cctx); +}; + +static void init_convert_context(struct btrfs_convert_context *cctx) +{ + cache_tree_init(&cctx->used); + cache_tree_init(&cctx->data_chunks); + cache_tree_init(&cctx->free); +} + +static void clean_convert_context(struct btrfs_convert_context *cctx) +{ + free_extent_cache_tree(&cctx->used); + free_extent_cache_tree(&cctx->data_chunks); + free_extent_cache_tree(&cctx->free); +} + +static inline int copy_inodes(struct btrfs_convert_context *cctx, + struct btrfs_root *root, int datacsum, + int packing, int noxattr, struct task_ctx *p) +{ + return cctx->convert_ops->copy_inodes(cctx, root, datacsum, packing, + noxattr, p); +} + +static inline void convert_close_fs(struct btrfs_convert_context *cctx) +{ + cctx->convert_ops->close_fs(cctx); +} + +static inline int convert_check_state(struct btrfs_convert_context *cctx) +{ + return cctx->convert_ops->check_state(cctx); +} + +static int intersect_with_sb(u64 bytenr, u64 num_bytes) +{ + int i; + u64 offset; + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + offset = btrfs_sb_offset(i); + offset &= ~((u64)BTRFS_STRIPE_LEN - 1); + + if (bytenr < offset + BTRFS_STRIPE_LEN && + bytenr + num_bytes > offset) + return 1; + } + return 0; +} + +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; +} + +static int read_disk_extent(struct btrfs_root *root, u64 bytenr, + u32 num_bytes, char *buffer) +{ + int ret; + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; + + ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr); + if (ret != num_bytes) + goto fail; + ret = 0; +fail: + if (ret > 0) + ret = -1; + return ret; +} + +static int csum_disk_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 disk_bytenr, u64 num_bytes) +{ + u32 blocksize = root->sectorsize; + u64 offset; + char *buffer; + int ret = 0; + + buffer = malloc(blocksize); + if (!buffer) + return -ENOMEM; + for (offset = 0; offset < num_bytes; offset += blocksize) { + ret = read_disk_extent(root, disk_bytenr + offset, + blocksize, buffer); + if (ret) + break; + ret = btrfs_csum_file_block(trans, + root->fs_info->csum_root, + disk_bytenr + num_bytes, + disk_bytenr + offset, + buffer, blocksize); + if (ret) + break; + } + free(buffer); + return ret; +} + +struct blk_iterate_data { + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + struct btrfs_root *convert_root; + struct btrfs_inode_item *inode; + u64 convert_ino; + u64 objectid; + u64 first_block; + u64 disk_block; + u64 num_blocks; + u64 boundary; + int checksum; + int errcode; +}; + +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) +{ + struct btrfs_key key; + + 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; + + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + data->convert_root = btrfs_read_fs_root(root->fs_info, &key); + /* Impossible as we just opened it before */ + BUG_ON(!data->convert_root || IS_ERR(data->convert_root)); + data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1; +} + +/* + * Record a file extent in original filesystem into btrfs one. + * The special point is, old disk_block can point to a reserved range. + * So here, we don't use disk_block directly but search convert_root + * to get the real disk_bytenr. + */ +static int record_file_blocks(struct blk_iterate_data *data, + u64 file_block, u64 disk_block, u64 num_blocks) +{ + int ret = 0; + struct btrfs_root *root = data->root; + struct btrfs_root *convert_root = data->convert_root; + struct btrfs_path path; + u64 file_pos = file_block * root->sectorsize; + u64 old_disk_bytenr = disk_block * root->sectorsize; + u64 num_bytes = num_blocks * root->sectorsize; + u64 cur_off = old_disk_bytenr; + + /* Hole, pass it to record_file_extent directly */ + if (old_disk_bytenr == 0) + return btrfs_record_file_extent(data->trans, root, + data->objectid, data->inode, file_pos, 0, + num_bytes); + + btrfs_init_path(&path); + + /* + * Search real disk bytenr from convert root + */ + while (cur_off < old_disk_bytenr + num_bytes) { + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + struct extent_buffer *node; + int slot; + u64 extent_disk_bytenr; + u64 extent_num_bytes; + u64 real_disk_bytenr; + u64 cur_len; + + key.objectid = data->convert_ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = cur_off; + + ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0); + if (ret < 0) + break; + if (ret > 0) { + ret = btrfs_previous_item(convert_root, &path, + data->convert_ino, + BTRFS_EXTENT_DATA_KEY); + if (ret < 0) + break; + if (ret > 0) { + ret = -ENOENT; + break; + } + } + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY || + key.objectid != data->convert_ino || + key.offset > cur_off); + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + BUG_ON(cur_off - key.offset >= extent_num_bytes); + btrfs_release_path(&path); + + if (extent_disk_bytenr) + real_disk_bytenr = cur_off - key.offset + + extent_disk_bytenr; + else + real_disk_bytenr = 0; + cur_len = min(key.offset + extent_num_bytes, + old_disk_bytenr + num_bytes) - cur_off; + ret = btrfs_record_file_extent(data->trans, data->root, + data->objectid, data->inode, file_pos, + real_disk_bytenr, cur_len); + if (ret < 0) + break; + cur_off += cur_len; + file_pos += cur_len; + + /* + * No need to care about csum + * As every byte of old fs image is calculated for csum, no + * need to waste CPU cycles now. + */ + } + btrfs_release_path(&path); + return ret; +} + +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_block_group_cache *cache; + u64 bytenr = disk_block * root->sectorsize; + + sb_region = intersect_with_sb(bytenr, root->sectorsize); + do_barrier = sb_region || disk_block >= idata->boundary; + if ((idata->num_blocks > 0 && do_barrier) || + (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(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(idata, idata->first_block, + 0, file_block - idata->first_block); + if (ret) + goto fail; + } + + if (sb_region) { + bytenr += BTRFS_STRIPE_LEN - 1; + bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1); + } else { + cache = btrfs_lookup_block_group(root->fs_info, bytenr); + BUG_ON(!cache); + bytenr = cache->key.objectid + cache->key.offset; + } + + idata->first_block = file_block; + idata->disk_block = disk_block; + idata->boundary = bytenr / root->sectorsize; + } + idata->num_blocks++; +fail: + return ret; +} + +static int create_image_file_range(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct cache_tree *used, + struct btrfs_inode_item *inode, + u64 ino, u64 bytenr, u64 *ret_len, + int datacsum) +{ + struct cache_extent *cache; + struct btrfs_block_group_cache *bg_cache; + u64 len = *ret_len; + u64 disk_bytenr; + int i; + int ret; + + if (bytenr != round_down(bytenr, root->sectorsize)) { + error("bytenr not sectorsize aligned: %llu", + (unsigned long long)bytenr); + return -EINVAL; + } + if (len != round_down(len, root->sectorsize)) { + error("length not sectorsize aligned: %llu", + (unsigned long long)len); + return -EINVAL; + } + len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE); + + /* + * Skip sb ranges first + * [0, 1M), [sb_offset(1), +64K), [sb_offset(2), +64K]. + * + * Or we will insert a hole into current image file, and later + * migrate block will fail as there is already a file extent. + */ + if (bytenr < 1024 * 1024) { + *ret_len = 1024 * 1024 - bytenr; + return 0; + } + for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { + u64 cur = btrfs_sb_offset(i); + + if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) { + *ret_len = cur + BTRFS_STRIPE_LEN - bytenr; + return 0; + } + } + for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { + u64 cur = btrfs_sb_offset(i); + + /* + * |--reserved--| + * |----range-------| + * May still need to go through file extent inserts + */ + if (bytenr < cur && bytenr + len >= cur) { + len = min_t(u64, len, cur - bytenr); + break; + } + /* + * |--reserved--| + * |---range---| + * Drop out, no need to insert anything + */ + if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) { + *ret_len = cur + BTRFS_STRIPE_LEN - bytenr; + return 0; + } + } + + cache = search_cache_extent(used, bytenr); + if (cache) { + if (cache->start <= bytenr) { + /* + * |///////Used///////| + * |<--insert--->| + * bytenr + */ + len = min_t(u64, len, cache->start + cache->size - + bytenr); + disk_bytenr = bytenr; + } else { + /* + * |//Used//| + * |<-insert-->| + * bytenr + */ + len = min(len, cache->start - bytenr); + disk_bytenr = 0; + datacsum = 0; + } + } else { + /* + * |//Used//| |EOF + * |<-insert-->| + * bytenr + */ + disk_bytenr = 0; + datacsum = 0; + } + + if (disk_bytenr) { + /* Check if the range is in a data block group */ + bg_cache = btrfs_lookup_block_group(root->fs_info, bytenr); + if (!bg_cache) + return -ENOENT; + if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA)) + return -EINVAL; + + /* The extent should never cross block group boundary */ + len = min_t(u64, len, bg_cache->key.objectid + + bg_cache->key.offset - bytenr); + } + + if (len != round_down(len, root->sectorsize)) { + error("remaining length not sectorsize aligned: %llu", + (unsigned long long)len); + return -EINVAL; + } + ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr, + disk_bytenr, len); + if (ret < 0) + return ret; + + if (datacsum) + ret = csum_disk_extent(trans, root, bytenr, len); + *ret_len = len; + return ret; +} + +/* + * Relocate old fs data in one reserved ranges + * + * Since all old fs data in reserved range is not covered by any chunk nor + * data extent, we don't need to handle any reference but add new + * extent/reference, which makes codes more clear + */ +static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct cache_tree *used, + struct btrfs_inode_item *inode, int fd, + u64 ino, u64 start, u64 len, int datacsum) +{ + u64 cur_off = start; + u64 cur_len = len; + u64 hole_start = start; + u64 hole_len; + struct cache_extent *cache; + struct btrfs_key key; + struct extent_buffer *eb; + int ret = 0; + + while (cur_off < start + len) { + cache = lookup_cache_extent(used, cur_off, cur_len); + if (!cache) + break; + cur_off = max(cache->start, cur_off); + cur_len = min(cache->start + cache->size, start + len) - + cur_off; + BUG_ON(cur_len < root->sectorsize); + + /* reserve extent for the data */ + ret = btrfs_reserve_extent(trans, root, cur_len, 0, 0, (u64)-1, + &key, 1); + if (ret < 0) + break; + + eb = malloc(sizeof(*eb) + cur_len); + if (!eb) { + ret = -ENOMEM; + break; + } + + ret = pread(fd, eb->data, cur_len, cur_off); + if (ret < cur_len) { + ret = (ret < 0 ? ret : -EIO); + free(eb); + break; + } + eb->start = key.objectid; + eb->len = key.offset; + + /* Write the data */ + ret = write_and_map_eb(trans, root, eb); + free(eb); + if (ret < 0) + break; + + /* Now handle extent item and file extent things */ + ret = btrfs_record_file_extent(trans, root, ino, inode, cur_off, + key.objectid, key.offset); + if (ret < 0) + break; + /* Finally, insert csum items */ + if (datacsum) + ret = csum_disk_extent(trans, root, key.objectid, + key.offset); + + /* Don't forget to insert hole */ + hole_len = cur_off - hole_start; + if (hole_len) { + ret = btrfs_record_file_extent(trans, root, ino, inode, + hole_start, 0, hole_len); + if (ret < 0) + break; + } + + cur_off += key.offset; + hole_start = cur_off; + cur_len = start + len - cur_off; + } + /* Last hole */ + if (start + len - hole_start > 0) + ret = btrfs_record_file_extent(trans, root, ino, inode, + hole_start, 0, start + len - hole_start); + return ret; +} + +/* + * Relocate the used ext2 data in reserved ranges + * [0,1M) + * [btrfs_sb_offset(1), +BTRFS_STRIPE_LEN) + * [btrfs_sb_offset(2), +BTRFS_STRIPE_LEN) + */ +static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct cache_tree *used, + struct btrfs_inode_item *inode, int fd, + u64 ino, u64 total_bytes, int datacsum) +{ + u64 cur_off; + u64 cur_len; + int ret = 0; + + /* 0 ~ 1M */ + cur_off = 0; + cur_len = 1024 * 1024; + ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, + cur_off, cur_len, datacsum); + if (ret < 0) + return ret; + + /* second sb(fisrt sb is included in 0~1M) */ + cur_off = btrfs_sb_offset(1); + cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off; + if (cur_off > total_bytes) + return ret; + ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, + cur_off, cur_len, datacsum); + if (ret < 0) + return ret; + + /* Last sb */ + cur_off = btrfs_sb_offset(2); + cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off; + if (cur_off > total_bytes) + return ret; + ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, + cur_off, cur_len, datacsum); + return ret; +} + +/* + * Helper for expand and merge extent_cache for wipe_one_reserved_range() to + * handle wiping a range that exists in cache. + */ +static int _expand_extent_cache(struct cache_tree *tree, + struct cache_extent *entry, + u64 min_stripe_size, int backward) +{ + struct cache_extent *ce; + int diff; + + if (entry->size >= min_stripe_size) + return 0; + diff = min_stripe_size - entry->size; + + if (backward) { + ce = prev_cache_extent(entry); + if (!ce) + goto expand_back; + if (ce->start + ce->size >= entry->start - diff) { + /* Directly merge with previous extent */ + ce->size = entry->start + entry->size - ce->start; + remove_cache_extent(tree, entry); + free(entry); + return 0; + } +expand_back: + /* No overlap, normal extent */ + if (entry->start < diff) { + error("cannot find space for data chunk layout"); + return -ENOSPC; + } + entry->start -= diff; + entry->size += diff; + return 0; + } + ce = next_cache_extent(entry); + if (!ce) + goto expand_after; + if (entry->start + entry->size + diff >= ce->start) { + /* Directly merge with next extent */ + entry->size = ce->start + ce->size - entry->start; + remove_cache_extent(tree, ce); + free(ce); + return 0; + } +expand_after: + entry->size += diff; + return 0; +} + +/* + * Remove one reserve range from given cache tree + * if min_stripe_size is non-zero, it will ensure for split case, + * all its split cache extent is no smaller than @min_strip_size / 2. + */ +static int wipe_one_reserved_range(struct cache_tree *tree, + u64 start, u64 len, u64 min_stripe_size, + int ensure_size) +{ + struct cache_extent *cache; + int ret; + + BUG_ON(ensure_size && min_stripe_size == 0); + /* + * The logical here is simplified to handle special cases only + * So we don't need to consider merge case for ensure_size + */ + BUG_ON(min_stripe_size && (min_stripe_size < len * 2 || + min_stripe_size / 2 < BTRFS_STRIPE_LEN)); + + /* Also, wipe range should already be aligned */ + BUG_ON(start != round_down(start, BTRFS_STRIPE_LEN) || + start + len != round_up(start + len, BTRFS_STRIPE_LEN)); + + min_stripe_size /= 2; + + cache = lookup_cache_extent(tree, start, len); + if (!cache) + return 0; + + if (start <= cache->start) { + /* + * |--------cache---------| + * |-wipe-| + */ + BUG_ON(start + len <= cache->start); + + /* + * The wipe size is smaller than min_stripe_size / 2, + * so the result length should still meet min_stripe_size + * And no need to do alignment + */ + cache->size -= (start + len - cache->start); + if (cache->size == 0) { + remove_cache_extent(tree, cache); + free(cache); + return 0; + } + + BUG_ON(ensure_size && cache->size < min_stripe_size); + + cache->start = start + len; + return 0; + } else if (start > cache->start && start + len < cache->start + + cache->size) { + /* + * |-------cache-----| + * |-wipe-| + */ + u64 old_start = cache->start; + u64 old_len = cache->size; + u64 insert_start = start + len; + u64 insert_len; + + cache->size = start - cache->start; + /* Expand the leading half part if needed */ + if (ensure_size && cache->size < min_stripe_size) { + ret = _expand_extent_cache(tree, cache, + min_stripe_size, 1); + if (ret < 0) + return ret; + } + + /* And insert the new one */ + insert_len = old_start + old_len - start - len; + ret = add_merge_cache_extent(tree, insert_start, insert_len); + if (ret < 0) + return ret; + + /* Expand the last half part if needed */ + if (ensure_size && insert_len < min_stripe_size) { + cache = lookup_cache_extent(tree, insert_start, + insert_len); + if (!cache || cache->start != insert_start || + cache->size != insert_len) + return -ENOENT; + ret = _expand_extent_cache(tree, cache, + min_stripe_size, 0); + } + + return ret; + } + /* + * |----cache-----| + * |--wipe-| + * Wipe len should be small enough and no need to expand the + * remaining extent + */ + cache->size = start - cache->start; + BUG_ON(ensure_size && cache->size < min_stripe_size); + return 0; +} + +/* + * Remove reserved ranges from given cache_tree + * + * It will remove the following ranges + * 1) 0~1M + * 2) 2nd superblock, +64K (make sure chunks are 64K aligned) + * 3) 3rd superblock, +64K + * + * @min_stripe must be given for safety check + * and if @ensure_size is given, it will ensure affected cache_extent will be + * larger than min_stripe_size + */ +static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size, + int ensure_size) +{ + int ret; + + ret = wipe_one_reserved_range(tree, 0, 1024 * 1024, min_stripe_size, + ensure_size); + if (ret < 0) + return ret; + ret = wipe_one_reserved_range(tree, btrfs_sb_offset(1), + BTRFS_STRIPE_LEN, min_stripe_size, ensure_size); + if (ret < 0) + return ret; + ret = wipe_one_reserved_range(tree, btrfs_sb_offset(2), + BTRFS_STRIPE_LEN, min_stripe_size, ensure_size); + return ret; +} + +static int calculate_available_space(struct btrfs_convert_context *cctx) +{ + struct cache_tree *used = &cctx->used; + struct cache_tree *data_chunks = &cctx->data_chunks; + struct cache_tree *free = &cctx->free; + struct cache_extent *cache; + u64 cur_off = 0; + /* + * Twice the minimal chunk size, to allow later wipe_reserved_ranges() + * works without need to consider overlap + */ + u64 min_stripe_size = 2 * 16 * 1024 * 1024; + int ret; + + /* Calculate data_chunks */ + for (cache = first_cache_extent(used); cache; + cache = next_cache_extent(cache)) { + u64 cur_len; + + if (cache->start + cache->size < cur_off) + continue; + if (cache->start > cur_off + min_stripe_size) + cur_off = cache->start; + cur_len = max(cache->start + cache->size - cur_off, + min_stripe_size); + ret = add_merge_cache_extent(data_chunks, cur_off, cur_len); + if (ret < 0) + goto out; + cur_off += cur_len; + } + /* + * remove reserved ranges, so we won't ever bother relocating an old + * filesystem extent to other place. + */ + ret = wipe_reserved_ranges(data_chunks, min_stripe_size, 1); + if (ret < 0) + goto out; + + cur_off = 0; + /* + * Calculate free space + * Always round up the start bytenr, to avoid metadata extent corss + * stripe boundary, as later mkfs_convert() won't have all the extent + * allocation check + */ + for (cache = first_cache_extent(data_chunks); cache; + cache = next_cache_extent(cache)) { + if (cache->start < cur_off) + continue; + if (cache->start > cur_off) { + u64 insert_start; + u64 len; + + len = cache->start - round_up(cur_off, + BTRFS_STRIPE_LEN); + insert_start = round_up(cur_off, BTRFS_STRIPE_LEN); + + ret = add_merge_cache_extent(free, insert_start, len); + if (ret < 0) + goto out; + } + cur_off = cache->start + cache->size; + } + /* Don't forget the last range */ + if (cctx->total_bytes > cur_off) { + u64 len = cctx->total_bytes - cur_off; + u64 insert_start; + + insert_start = round_up(cur_off, BTRFS_STRIPE_LEN); + + ret = add_merge_cache_extent(free, insert_start, len); + if (ret < 0) + goto out; + } + + /* Remove reserved bytes */ + ret = wipe_reserved_ranges(free, min_stripe_size, 0); +out: + return ret; +} + +/* + * Read used space, and since we have the used space, + * calcuate data_chunks and free for later mkfs + */ +static int convert_read_used_space(struct btrfs_convert_context *cctx) +{ + int ret; + + ret = cctx->convert_ops->read_used_space(cctx); + if (ret) + return ret; + + ret = calculate_available_space(cctx); + return ret; +} + +/* + * Create the fs image file of old filesystem. + * + * This is completely fs independent as we have cctx->used, only + * need to create file extents pointing to all the positions. + */ +static int create_image(struct btrfs_root *root, + struct btrfs_mkfs_config *cfg, + struct btrfs_convert_context *cctx, int fd, + u64 size, char *name, int datacsum) +{ + struct btrfs_inode_item buf; + struct btrfs_trans_handle *trans; + struct btrfs_path path; + struct btrfs_key key; + struct cache_extent *cache; + struct cache_tree used_tmp; + u64 cur; + u64 ino; + u64 flags = BTRFS_INODE_READONLY; + int ret; + + if (!datacsum) + flags |= BTRFS_INODE_NODATASUM; + + trans = btrfs_start_transaction(root, 1); + if (!trans) + return -ENOMEM; + + cache_tree_init(&used_tmp); + btrfs_init_path(&path); + + ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID, + &ino); + if (ret < 0) + goto out; + ret = btrfs_new_inode(trans, root, ino, 0400 | S_IFREG); + if (ret < 0) + goto out; + ret = btrfs_change_inode_flags(trans, root, ino, flags); + if (ret < 0) + goto out; + ret = btrfs_add_link(trans, root, ino, BTRFS_FIRST_FREE_OBJECTID, name, + strlen(name), BTRFS_FT_REG_FILE, NULL, 1); + if (ret < 0) + goto out; + + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); + if (ret) { + ret = (ret > 0 ? -ENOENT : ret); + goto out; + } + read_extent_buffer(path.nodes[0], &buf, + btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), + sizeof(buf)); + btrfs_release_path(&path); + + /* + * Create a new used space cache, which doesn't contain the reserved + * range + */ + for (cache = first_cache_extent(&cctx->used); cache; + cache = next_cache_extent(cache)) { + ret = add_cache_extent(&used_tmp, cache->start, cache->size); + if (ret < 0) + goto out; + } + ret = wipe_reserved_ranges(&used_tmp, 0, 0); + if (ret < 0) + goto out; + + /* + * Start from 1M, as 0~1M is reserved, and create_image_file_range() + * can't handle bytenr 0(will consider it as a hole) + */ + cur = 1024 * 1024; + while (cur < size) { + u64 len = size - cur; + + ret = create_image_file_range(trans, root, &used_tmp, + &buf, ino, cur, &len, datacsum); + if (ret < 0) + goto out; + cur += len; + } + /* Handle the reserved ranges */ + ret = migrate_reserved_ranges(trans, root, &cctx->used, &buf, fd, ino, + cfg->num_bytes, datacsum); + + + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + ret = btrfs_search_slot(trans, root, &key, &path, 0, 1); + if (ret) { + ret = (ret > 0 ? -ENOENT : ret); + goto out; + } + btrfs_set_stack_inode_size(&buf, cfg->num_bytes); + write_extent_buffer(path.nodes[0], &buf, + btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), + sizeof(buf)); +out: + free_extent_cache_tree(&used_tmp); + btrfs_release_path(&path); + btrfs_commit_transaction(trans, root); + return ret; +} + +static struct btrfs_root* link_subvol(struct btrfs_root *root, + const char *base, u64 root_objectid) +{ + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *new_root = NULL; + struct btrfs_path path; + struct btrfs_inode_item *inode_item; + struct extent_buffer *leaf; + struct btrfs_key key; + u64 dirid = btrfs_root_dirid(&root->root_item); + u64 index = 2; + char buf[BTRFS_NAME_LEN + 1]; /* for snprintf null */ + int len; + int i; + int ret; + + len = strlen(base); + if (len == 0 || len > BTRFS_NAME_LEN) + return NULL; + + btrfs_init_path(&path); + key.objectid = dirid; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret <= 0) { + error("search for DIR_INDEX dirid %llu failed: %d", + (unsigned long long)dirid, ret); + goto fail; + } + + if (path.slots[0] > 0) { + path.slots[0]--; + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) + index = key.offset + 1; + } + btrfs_release_path(&path); + + trans = btrfs_start_transaction(root, 1); + if (!trans) { + error("unable to start transaction"); + goto fail; + } + + key.objectid = dirid; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + + ret = btrfs_lookup_inode(trans, root, &path, &key, 1); + if (ret) { + error("search for INODE_ITEM %llu failed: %d", + (unsigned long long)dirid, ret); + goto fail; + } + leaf = path.nodes[0]; + inode_item = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_inode_item); + + key.objectid = root_objectid; + key.offset = (u64)-1; + key.type = BTRFS_ROOT_ITEM_KEY; + + memcpy(buf, base, len); + for (i = 0; i < 1024; i++) { + ret = btrfs_insert_dir_item(trans, root, buf, len, + dirid, &key, BTRFS_FT_DIR, index); + if (ret != -EEXIST) + break; + len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", base, i); + if (len < 1 || len > BTRFS_NAME_LEN) { + ret = -EINVAL; + break; + } + } + if (ret) + goto fail; + + btrfs_set_inode_size(leaf, inode_item, len * 2 + + btrfs_inode_size(leaf, inode_item)); + btrfs_mark_buffer_dirty(leaf); + btrfs_release_path(&path); + + /* add the backref first */ + ret = btrfs_add_root_ref(trans, tree_root, root_objectid, + BTRFS_ROOT_BACKREF_KEY, + root->root_key.objectid, + dirid, index, buf, len); + if (ret) { + error("unable to add root backref for %llu: %d", + root->root_key.objectid, ret); + goto fail; + } + + /* now add the forward ref */ + ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid, + BTRFS_ROOT_REF_KEY, root_objectid, + dirid, index, buf, len); + if (ret) { + error("unable to add root ref for %llu: %d", + root->root_key.objectid, ret); + goto fail; + } + + ret = btrfs_commit_transaction(trans, root); + if (ret) { + error("transaction commit failed: %d", ret); + goto fail; + } + + new_root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(new_root)) { + error("unable to fs read root: %lu", PTR_ERR(new_root)); + new_root = NULL; + } +fail: + btrfs_init_path(&path); + return new_root; +} + +static int create_subvol(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 root_objectid) +{ + struct extent_buffer *tmp; + struct btrfs_root *new_root; + struct btrfs_key key; + struct btrfs_root_item root_item; + int ret; + + ret = btrfs_copy_root(trans, root, root->node, &tmp, + root_objectid); + if (ret) + return ret; + + memcpy(&root_item, &root->root_item, sizeof(root_item)); + btrfs_set_root_bytenr(&root_item, tmp->start); + btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); + btrfs_set_root_generation(&root_item, trans->transid); + free_extent_buffer(tmp); + + key.objectid = root_objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = trans->transid; + ret = btrfs_insert_root(trans, root->fs_info->tree_root, + &key, &root_item); + + key.offset = (u64)-1; + new_root = btrfs_read_fs_root(root->fs_info, &key); + if (!new_root || IS_ERR(new_root)) { + error("unable to fs read root: %lu", PTR_ERR(new_root)); + return PTR_ERR(new_root); + } + + ret = btrfs_make_root_dir(trans, new_root, BTRFS_FIRST_FREE_OBJECTID); + + return ret; +} + +/* + * New make_btrfs() has handle system and meta chunks quite well. + * So only need to add remaining data chunks. + */ +static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_mkfs_config *cfg, + struct btrfs_convert_context *cctx) +{ + struct btrfs_root *extent_root = fs_info->extent_root; + struct cache_tree *data_chunks = &cctx->data_chunks; + struct cache_extent *cache; + u64 max_chunk_size; + int ret = 0; + + /* + * Don't create data chunk over 10% of the convert device + * And for single chunk, don't create chunk larger than 1G. + */ + max_chunk_size = cfg->num_bytes / 10; + max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size); + max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize); + + for (cache = first_cache_extent(data_chunks); cache; + cache = next_cache_extent(cache)) { + u64 cur = cache->start; + + while (cur < cache->start + cache->size) { + u64 len; + u64 cur_backup = cur; + + len = min(max_chunk_size, + cache->start + cache->size - cur); + ret = btrfs_alloc_data_chunk(trans, extent_root, + &cur_backup, len, + BTRFS_BLOCK_GROUP_DATA, 1); + if (ret < 0) + break; + ret = btrfs_make_block_group(trans, extent_root, 0, + BTRFS_BLOCK_GROUP_DATA, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, + cur, len); + if (ret < 0) + break; + cur += len; + } + } + return ret; +} + +/* + * Init the temp btrfs to a operational status. + * + * It will fix the extent usage accounting(XXX: Do we really need?) and + * insert needed data chunks, to ensure all old fs data extents are covered + * by DATA chunks, preventing wrong chunks are allocated. + * + * And also create convert image subvolume and relocation tree. + * (XXX: Not need again?) + * But the convert image subvolume is *NOT* linked to fs tree yet. + */ +static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root, + struct btrfs_convert_context *cctx, int datacsum, + int packing, int noxattr) +{ + struct btrfs_key location; + struct btrfs_trans_handle *trans; + struct btrfs_fs_info *fs_info = root->fs_info; + int ret; + + /* + * Don't alloc any metadata/system chunk, as we don't want + * any meta/sys chunk allcated before all data chunks are inserted. + * Or we screw up the chunk layout just like the old implement. + */ + fs_info->avoid_sys_chunk_alloc = 1; + fs_info->avoid_meta_chunk_alloc = 1; + trans = btrfs_start_transaction(root, 1); + if (!trans) { + error("unable to start transaction"); + ret = -EINVAL; + goto err; + } + ret = btrfs_fix_block_accounting(trans, root); + if (ret) + goto err; + ret = make_convert_data_block_groups(trans, fs_info, cfg, cctx); + if (ret) + goto err; + ret = btrfs_make_root_dir(trans, fs_info->tree_root, + BTRFS_ROOT_TREE_DIR_OBJECTID); + if (ret) + goto err; + memcpy(&location, &root->root_key, sizeof(location)); + location.offset = (u64)-1; + ret = btrfs_insert_dir_item(trans, fs_info->tree_root, "default", 7, + btrfs_super_root_dir(fs_info->super_copy), + &location, BTRFS_FT_DIR, 0); + if (ret) + goto err; + ret = btrfs_insert_inode_ref(trans, fs_info->tree_root, "default", 7, + location.objectid, + btrfs_super_root_dir(fs_info->super_copy), 0); + if (ret) + goto err; + btrfs_set_root_dirid(&fs_info->fs_root->root_item, + BTRFS_FIRST_FREE_OBJECTID); + + /* subvol for fs image file */ + ret = create_subvol(trans, root, CONV_IMAGE_SUBVOL_OBJECTID); + if (ret < 0) { + error("failed to create subvolume image root: %d", ret); + goto err; + } + /* subvol for data relocation tree */ + ret = create_subvol(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID); + if (ret < 0) { + error("failed to create DATA_RELOC root: %d", ret); + goto err; + } + + ret = btrfs_commit_transaction(trans, root); + fs_info->avoid_sys_chunk_alloc = 0; + fs_info->avoid_meta_chunk_alloc = 0; +err: + return ret; +} + +/* + * Migrate super block to its default position and zero 0 ~ 16k + */ +static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) +{ + int ret; + struct extent_buffer *buf; + struct btrfs_super_block *super; + u32 len; + u32 bytenr; + + buf = malloc(sizeof(*buf) + sectorsize); + if (!buf) + return -ENOMEM; + + buf->len = sectorsize; + ret = pread(fd, buf->data, sectorsize, old_bytenr); + if (ret != sectorsize) + goto fail; + + super = (struct btrfs_super_block *)buf->data; + BUG_ON(btrfs_super_bytenr(super) != old_bytenr); + btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET); + if (ret != sectorsize) + goto fail; + + ret = fsync(fd); + if (ret) + goto fail; + + memset(buf->data, 0, sectorsize); + for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { + len = BTRFS_SUPER_INFO_OFFSET - bytenr; + if (len > sectorsize) + len = sectorsize; + ret = pwrite(fd, buf->data, len, bytenr); + if (ret != len) { + fprintf(stderr, "unable to zero fill device\n"); + break; + } + bytenr += len; + } + ret = 0; + fsync(fd); +fail: + free(buf); + if (ret > 0) + ret = -1; + return ret; +} + +static int prepare_system_chunk_sb(struct btrfs_super_block *super) +{ + struct btrfs_chunk *chunk; + struct btrfs_disk_key *key; + u32 sectorsize = btrfs_super_sectorsize(super); + + key = (struct btrfs_disk_key *)(super->sys_chunk_array); + chunk = (struct btrfs_chunk *)(super->sys_chunk_array + + sizeof(struct btrfs_disk_key)); + + btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_disk_key_offset(key, 0); + + btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super)); + btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN); + btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); + btrfs_set_stack_chunk_io_align(chunk, sectorsize); + btrfs_set_stack_chunk_io_width(chunk, sectorsize); + btrfs_set_stack_chunk_sector_size(chunk, sectorsize); + btrfs_set_stack_chunk_num_stripes(chunk, 1); + btrfs_set_stack_chunk_sub_stripes(chunk, 0); + chunk->stripe.devid = super->dev_item.devid; + btrfs_set_stack_stripe_offset(&chunk->stripe, 0); + memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); + btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); + return 0; +} + +#if BTRFSCONVERT_EXT2 + +/* + * Open Ext2fs in readonly mode, read block allocation bitmap and + * inode bitmap into memory. + */ +static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) +{ + errcode_t ret; + ext2_filsys ext2_fs; + ext2_ino_t ino; + u32 ro_feature; + + ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); + if (ret) { + fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); + return -1; + } + /* + * We need to know exactly the used space, some RO compat flags like + * BIGALLOC will affect how used space is present. + * So we need manuall check any unsupported RO compat flags + */ + ro_feature = ext2_fs->super->s_feature_ro_compat; + if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + error( +"unsupported RO features detected: %x, abort convert to avoid possible corruption", + ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP); + 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); + } + + if (!(ext2_fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + error("filetype feature is missing"); + goto fail; + } + + cctx->fs_data = ext2_fs; + cctx->blocksize = ext2_fs->blocksize; + cctx->block_count = ext2_fs->super->s_blocks_count; + cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count; + cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16); + cctx->first_data_block = ext2_fs->super->s_first_data_block; + cctx->inodes_count = ext2_fs->super->s_inodes_count; + cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count; + return 0; +fail: + ext2fs_close(ext2_fs); + return -1; +} + +static int __ext2_add_one_block(ext2_filsys fs, char *bitmap, + unsigned long group_nr, struct cache_tree *used) +{ + unsigned long offset; + unsigned i; + int ret = 0; + + offset = fs->super->s_first_data_block; + offset /= EXT2FS_CLUSTER_RATIO(fs); + offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super); + for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) { + if (ext2fs_test_bit(i, bitmap)) { + u64 start; + + start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs); + start *= fs->blocksize; + ret = add_merge_cache_extent(used, start, + fs->blocksize); + if (ret < 0) + break; + } + } + return ret; +} + +/* + * Read all used ext2 space into cctx->used cache tree + */ +static int ext2_read_used_space(struct btrfs_convert_context *cctx) +{ + ext2_filsys fs = (ext2_filsys)cctx->fs_data; + blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); + struct cache_tree *used_tree = &cctx->used; + char *block_bitmap = NULL; + unsigned long i; + int block_nbytes; + int ret = 0; + + block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; + /* Shouldn't happen */ + BUG_ON(!fs->block_map); + + block_bitmap = malloc(block_nbytes); + if (!block_bitmap) + return -ENOMEM; + + for (i = 0; i < fs->group_desc_count; i++) { + ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr, + block_nbytes * 8, block_bitmap); + if (ret) { + error("fail to get bitmap from ext2, %s", + strerror(-ret)); + break; + } + ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree); + if (ret < 0) { + error("fail to build used space tree, %s", + strerror(-ret)); + break; + } + blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super); + } + + free(block_bitmap); + return ret; +} + +static void ext2_close_fs(struct btrfs_convert_context *cctx) +{ + if (cctx->volume_name) { + free(cctx->volume_name); + cctx->volume_name = NULL; + } + ext2fs_close(cctx->fs_data); +} + +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 ext2_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 ext2_dir_iterate_proc(ext2_ino_t dir, int entry, + struct ext2_dir_entry *dirent, + int offset, int blocksize, + char *buf,void *priv_data) +{ + int ret; + int file_type; + u64 objectid; + char dotdot[] = ".."; + struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data; + int name_len; + + name_len = dirent->name_len & 0xFF; + + objectid = dirent->inode + INO_OFFSET; + if (!strncmp(dirent->name, dotdot, name_len)) { + if (name_len == 2) { + BUG_ON(idata->parent != 0); + idata->parent = objectid; + } + return 0; + } + if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO) + return 0; + + file_type = dirent->name_len >> 8; + BUG_ON(file_type > EXT2_FT_SYMLINK); + + ret = convert_insert_dirent(idata->trans, idata->root, dirent->name, + name_len, idata->objectid, objectid, + ext2_filetype_conversion_table[file_type], + idata->index_cnt, idata->inode); + if (ret < 0) { + idata->errcode = ret; + return BLOCK_ABORT; + } + + idata->index_cnt++; + return 0; +} + +static int ext2_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, + ext2_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 ext2_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; + ret = block_iterate_proc(*blocknr, blockcnt, idata); + if (ret) { + idata->errcode = ret; + return BLOCK_ABORT; + } + return 0; +} + +/* + * traverse file's data blocks, record these data blocks as file extents. + */ +static int ext2_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; + char *buffer = NULL; + errcode_t err; + u32 last_block; + u32 sectorsize = root->sectorsize; + u64 inode_size = btrfs_stack_inode_size(btrfs_inode); + 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, ext2_block_iterate_proc, &data); + if (err) + goto error; + ret = data.errcode; + if (ret) + goto fail; + 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; + u64 nbytes; + + buffer = malloc(num_bytes); + if (!buffer) + return -ENOMEM; + ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); + if (ret) + goto fail; + if (num_bytes > inode_size) + num_bytes = inode_size; + ret = btrfs_insert_inline_extent(trans, root, objectid, + 0, buffer, num_bytes); + if (ret) + goto fail; + 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(&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(&data, data.first_block, 0, + last_block - data.first_block); + } +fail: + free(buffer); + return ret; +error: + fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); + return -1; +} + +static int ext2_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 = ext2_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 + +/* 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; + __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 ext2_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; + } + strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX); + 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: + free(databuf); + return ret; +} + +static int ext2_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 = ext2_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 = ext2_copy_single_xattr(trans, root, objectid, + entry, data, datalen); + if (ret) + goto out; + entry = EXT2_EXT_ATTR_NEXT(entry); + } +out: + 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 void ext2_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_sequence(dst, 0); + btrfs_set_stack_inode_transid(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])); + } + } + memset(&dst->reserved, 0, sizeof(dst->reserved)); +} +static int ext2_check_state(struct btrfs_convert_context *cctx) +{ + ext2_filsys fs = cctx->fs_data; + + if (!(fs->super->s_state & EXT2_VALID_FS)) + return 1; + else if (fs->super->s_state & EXT2_ERROR_FS) + return 1; + else + return 0; +} + +/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */ +#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \ + if (ext2_inode->i_flags & EXT2_##name##_FL) \ + flags |= BTRFS_INODE_##name; \ +}) + +/* + * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags + * + * Only a subset of EXT_*_FL is supported in btrfs. + */ +static void ext2_convert_inode_flags(struct btrfs_inode_item *dst, + struct ext2_inode *src) +{ + u64 flags = 0; + + COPY_ONE_EXT2_FLAG(flags, src, APPEND); + COPY_ONE_EXT2_FLAG(flags, src, SYNC); + COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE); + COPY_ONE_EXT2_FLAG(flags, src, NODUMP); + COPY_ONE_EXT2_FLAG(flags, src, NOATIME); + COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC); + btrfs_set_stack_inode_flags(dst, flags); +} + +/* + * copy a single inode. do all the required works, such as cloning + * inode item, creating file extents and creating directory entries. + */ +static int ext2_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_inode_item btrfs_inode; + + if (ext2_inode->i_links_count == 0) + return 0; + + ext2_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); + } + ext2_convert_inode_flags(&btrfs_inode, ext2_inode); + + switch (ext2_inode->i_mode & S_IFMT) { + case S_IFREG: + ret = ext2_create_file_extents(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino, datacsum, packing); + break; + case S_IFDIR: + ret = ext2_create_dir_entries(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino); + break; + case S_IFLNK: + ret = ext2_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 = ext2_copy_extended_attrs(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino); + if (ret) + return ret; + } + return btrfs_insert_inode(trans, root, objectid, &btrfs_inode); +} + +/* + * scan ext2's inode bitmap and copy all used inodes. + */ +static int ext2_copy_inodes(struct btrfs_convert_context *cctx, + struct btrfs_root *root, + int datacsum, int packing, int noxattr, struct task_ctx *p) +{ + ext2_filsys ext2_fs = cctx->fs_data; + 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 = ext2_copy_single_inode(trans, root, + objectid, ext2_fs, ext2_ino, + &ext2_inode, datacsum, packing, + noxattr); + p->cur_copy_inodes++; + 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); + ext2fs_close_inode_scan(ext2_scan); + + return ret; +} + +static const struct btrfs_convert_operations ext2_convert_ops = { + .name = "ext2", + .open_fs = ext2_open_fs, + .read_used_space = ext2_read_used_space, + .copy_inodes = ext2_copy_inodes, + .close_fs = ext2_close_fs, + .check_state = ext2_check_state, +}; + +#endif + +static const struct btrfs_convert_operations *convert_operations[] = { +#if BTRFSCONVERT_EXT2 + &ext2_convert_ops, +#endif +}; + +static int convert_open_fs(const char *devname, + struct btrfs_convert_context *cctx) +{ + int i; + + memset(cctx, 0, sizeof(*cctx)); + + for (i = 0; i < ARRAY_SIZE(convert_operations); i++) { + int ret = convert_operations[i]->open_fs(cctx, devname); + + if (ret == 0) { + cctx->convert_ops = convert_operations[i]; + return ret; + } + } + + error("no file system found to convert"); + return -1; +} + +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 ret; + int fd = -1; + u32 blocksize; + u64 total_bytes; + struct btrfs_root *root; + struct btrfs_root *image_root; + struct btrfs_convert_context cctx; + struct btrfs_key key; + char *subvol_name = NULL; + struct task_ctx ctx; + char features_buf[64]; + struct btrfs_mkfs_config mkfs_cfg; + + init_convert_context(&cctx); + ret = convert_open_fs(devname, &cctx); + if (ret) + goto fail; + ret = convert_check_state(&cctx); + if (ret) + warning( + "source filesystem is not clean, running filesystem check is recommended"); + ret = convert_read_used_space(&cctx); + if (ret) + goto fail; + + blocksize = cctx.blocksize; + total_bytes = (u64)blocksize * (u64)cctx.block_count; + if (blocksize < 4096) { + error("block size is too small: %u < 4096", blocksize); + goto fail; + } + if (btrfs_check_nodesize(nodesize, blocksize, features)) + goto fail; + fd = open(devname, O_RDWR); + if (fd < 0) { + error("unable to open %s: %s", devname, strerror(errno)); + goto fail; + } + 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 = cctx.volume_name; + mkfs_cfg.num_bytes = total_bytes; + mkfs_cfg.nodesize = nodesize; + mkfs_cfg.sectorsize = blocksize; + mkfs_cfg.stripesize = blocksize; + mkfs_cfg.features = features; + /* New convert need these space */ + memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); + memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); + + ret = make_btrfs(fd, &mkfs_cfg, &cctx); + if (ret) { + error("unable to create initial ctree: %s", strerror(-ret)); + goto fail; + } + + root = open_ctree_fd(fd, devname, mkfs_cfg.super_bytenr, + OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL); + if (!root) { + error("unable to open ctree"); + goto fail; + } + ret = init_btrfs(&mkfs_cfg, root, &cctx, datacsum, packing, noxattr); + if (ret) { + error("unable to setup the root tree: %d", ret); + goto fail; + } + + printf("creating %s image file\n", cctx.convert_ops->name); + ret = asprintf(&subvol_name, "%s_saved", cctx.convert_ops->name); + if (ret < 0) { + error("memory allocation failure for subvolume name: %s_saved", + cctx.convert_ops->name); + goto fail; + } + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.offset = (u64)-1; + key.type = BTRFS_ROOT_ITEM_KEY; + image_root = btrfs_read_fs_root(root->fs_info, &key); + if (!image_root) { + error("unable to create image subvolume"); + goto fail; + } + ret = create_image(image_root, &mkfs_cfg, &cctx, fd, + mkfs_cfg.num_bytes, "image", datacsum); + if (ret) { + error("failed to create %s/image: %d", subvol_name, ret); + goto fail; + } + + printf("creating btrfs metadata"); + ctx.max_copy_inodes = (cctx.inodes_count - cctx.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(&cctx, root, datacsum, packing, noxattr, &ctx); + if (ret) { + error("error during copy_inodes %d", ret); + goto fail; + } + if (progress) { + task_stop(ctx.info); + task_deinit(ctx.info); + } + + image_root = link_subvol(root, subvol_name, CONV_IMAGE_SUBVOL_OBJECTID); + if (!image_root) { + error("unable to link subvolume %s", subvol_name); + goto fail; + } + + free(subvol_name); + + memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE); + if (copylabel == 1) { + __strncpy_null(root->fs_info->super_copy->label, + cctx.volume_name, BTRFS_LABEL_SIZE - 1); + printf("copy label '%s'\n", root->fs_info->super_copy->label); + } else if (copylabel == -1) { + strcpy(root->fs_info->super_copy->label, fslabel); + printf("set label to '%s'\n", fslabel); + } + + ret = close_ctree(root); + if (ret) { + error("close_ctree failed: %d", ret); + goto fail; + } + convert_close_fs(&cctx); + clean_convert_context(&cctx); + + /* + * If this step succeed, we get a mountable btrfs. Otherwise + * the source fs is left unchanged. + */ + ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize); + if (ret) { + error("unable to migrate super block: %d", ret); + goto fail; + } + + root = open_ctree_fd(fd, devname, 0, + OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL); + if (!root) { + error("unable to open ctree for finalization"); + goto fail; + } + root->fs_info->finalize_on_close = 1; + close_ctree(root); + close(fd); + + printf("conversion complete"); + return 0; +fail: + clean_convert_context(&cctx); + if (fd != -1) + close(fd); + warning( +"an error occurred during conversion, filesystem is partially created but not finalized and not mountable"); + return -1; +} + +/* + * Check if a non 1:1 mapped chunk can be rolled back. + * For new convert, it's OK while for old convert it's not. + */ +static int may_rollback_chunk(struct btrfs_fs_info *fs_info, u64 bytenr) +{ + struct btrfs_block_group_cache *bg; + struct btrfs_key key; + struct btrfs_path path; + struct btrfs_root *extent_root = fs_info->extent_root; + u64 bg_start; + u64 bg_end; + int ret; + + bg = btrfs_lookup_first_block_group(fs_info, bytenr); + if (!bg) + return -ENOENT; + bg_start = bg->key.objectid; + bg_end = bg->key.objectid + bg->key.offset; + + key.objectid = bg_end; + key.type = BTRFS_METADATA_ITEM_KEY; + key.offset = 0; + btrfs_init_path(&path); + + ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); + if (ret < 0) + return ret; + + while (1) { + struct btrfs_extent_item *ei; + + ret = btrfs_previous_extent_item(extent_root, &path, bg_start); + if (ret > 0) { + ret = 0; + break; + } + if (ret < 0) + break; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + if (key.type == BTRFS_METADATA_ITEM_KEY) + continue; + /* Now it's EXTENT_ITEM_KEY only */ + ei = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_extent_item); + /* + * Found data extent, means this is old convert must follow 1:1 + * mapping. + */ + if (btrfs_extent_flags(path.nodes[0], ei) + & BTRFS_EXTENT_FLAG_DATA) { + ret = -EINVAL; + break; + } + } + btrfs_release_path(&path); + return ret; +} + +static int may_rollback(struct btrfs_root *root) +{ + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_multi_bio *multi = NULL; + u64 bytenr; + u64 length; + u64 physical; + u64 total_bytes; + int num_stripes; + int ret; + + if (btrfs_super_num_devices(info->super_copy) != 1) + goto fail; + + bytenr = BTRFS_SUPER_INFO_OFFSET; + total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy); + + while (1) { + ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr, + &length, &multi, 0, NULL); + 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; + free(multi); + + if (num_stripes != 1) { + error("num stripes for bytenr %llu is not 1", bytenr); + goto fail; + } + + /* + * Extra check for new convert, as metadata chunk from new + * convert is much more free than old convert, it doesn't need + * to do 1:1 mapping. + */ + if (physical != bytenr) { + /* + * Check if it's a metadata chunk and has only metadata + * extent. + */ + ret = may_rollback_chunk(info, bytenr); + if (ret < 0) + goto fail; + } +next: + bytenr += length; + if (bytenr >= total_bytes) + break; + } + return 0; +fail: + return -1; +} + +static int do_rollback(const char *devname) +{ + int fd = -1; + int ret; + int i; + struct btrfs_root *root; + struct btrfs_root *image_root; + struct btrfs_root *chunk_root; + struct btrfs_dir_item *dir; + struct btrfs_inode_item *inode; + struct btrfs_file_extent_item *fi; + struct btrfs_trans_handle *trans; + struct extent_buffer *leaf; + struct btrfs_block_group_cache *cache1; + struct btrfs_block_group_cache *cache2; + struct btrfs_key key; + struct btrfs_path path; + struct extent_io_tree io_tree; + char *buf = NULL; + char *name; + u64 bytenr; + u64 num_bytes; + u64 root_dir; + u64 objectid; + u64 offset; + u64 start; + u64 end; + u64 sb_bytenr; + u64 first_free; + u64 total_bytes; + u32 sectorsize; + + extent_io_tree_init(&io_tree); + + fd = open(devname, O_RDWR); + if (fd < 0) { + error("unable to open %s: %s", devname, strerror(errno)); + goto fail; + } + root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES); + if (!root) { + error("unable to open ctree"); + goto fail; + } + ret = may_rollback(root); + if (ret < 0) { + error("unable to do rollback: %d", ret); + goto fail; + } + + sectorsize = root->sectorsize; + buf = malloc(sectorsize); + if (!buf) { + error("unable to allocate memory"); + goto fail; + } + + btrfs_init_path(&path); + + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_BACKREF_KEY; + key.offset = BTRFS_FS_TREE_OBJECTID; + ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0, + 0); + btrfs_release_path(&path); + if (ret > 0) { + error("unable to convert ext2 image subvolume, is it deleted?"); + goto fail; + } else if (ret < 0) { + error("unable to open ext2_saved, id %llu: %s", + (unsigned long long)key.objectid, strerror(-ret)); + goto fail; + } + + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + image_root = btrfs_read_fs_root(root->fs_info, &key); + if (!image_root || IS_ERR(image_root)) { + error("unable to open subvolume %llu: %ld", + (unsigned long long)key.objectid, PTR_ERR(image_root)); + goto fail; + } + + name = "image"; + root_dir = btrfs_root_dirid(&root->root_item); + dir = btrfs_lookup_dir_item(NULL, image_root, &path, + root_dir, name, strlen(name), 0); + if (!dir || IS_ERR(dir)) { + error("unable to find file %s: %ld", name, PTR_ERR(dir)); + goto fail; + } + leaf = path.nodes[0]; + btrfs_dir_item_key_to_cpu(leaf, dir, &key); + btrfs_release_path(&path); + + objectid = key.objectid; + + ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0); + if (ret) { + error("unable to find inode item: %d", ret); + goto fail; + } + leaf = path.nodes[0]; + inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); + total_bytes = btrfs_inode_size(leaf, inode); + btrfs_release_path(&path); + + key.objectid = objectid; + key.offset = 0; + key.type = BTRFS_EXTENT_DATA_KEY; + ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0); + if (ret != 0) { + error("unable to find first file extent: %d", ret); + btrfs_release_path(&path); + goto fail; + } + + /* build mapping tree for the relocated blocks */ + for (offset = 0; offset < total_bytes; ) { + leaf = path.nodes[0]; + if (path.slots[0] >= btrfs_header_nritems(leaf)) { + ret = btrfs_next_leaf(root, &path); + if (ret != 0) + break; + continue; + } + + btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); + if (key.objectid != objectid || key.offset != offset || + key.type != BTRFS_EXTENT_DATA_KEY) + break; + + fi = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) + break; + if (btrfs_file_extent_compression(leaf, fi) || + btrfs_file_extent_encryption(leaf, fi) || + btrfs_file_extent_other_encoding(leaf, fi)) + break; + + bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + /* skip holes and direct mapped extents */ + if (bytenr == 0 || bytenr == offset) + goto next_extent; + + bytenr += btrfs_file_extent_offset(leaf, fi); + num_bytes = btrfs_file_extent_num_bytes(leaf, fi); + + cache1 = btrfs_lookup_block_group(root->fs_info, offset); + cache2 = btrfs_lookup_block_group(root->fs_info, + offset + num_bytes - 1); + /* + * Here we must take consideration of old and new convert + * behavior. + * For old convert case, sign, there is no consist chunk type + * that will cover the extent. META/DATA/SYS are all possible. + * Just ensure relocate one is in SYS chunk. + * For new convert case, they are all covered by DATA chunk. + * + * So, there is not valid chunk type check for it now. + */ + if (cache1 != cache2) + break; + + set_extent_bits(&io_tree, offset, offset + num_bytes - 1, + EXTENT_LOCKED, GFP_NOFS); + set_state_private(&io_tree, offset, bytenr); +next_extent: + offset += btrfs_file_extent_num_bytes(leaf, fi); + path.slots[0]++; + } + btrfs_release_path(&path); + + if (offset < total_bytes) { + error("unable to build extent mapping (offset %llu, total_bytes %llu)", + (unsigned long long)offset, + (unsigned long long)total_bytes); + error("converted filesystem after balance is unable to rollback"); + goto fail; + } + + first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1; + first_free &= ~((u64)sectorsize - 1); + /* backup for extent #0 should exist */ + if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) { + error("no backup for the first extent"); + goto fail; + } + /* force no allocation from system block group */ + root->fs_info->system_allocs = -1; + trans = btrfs_start_transaction(root, 1); + if (!trans) { + error("unable to start transaction"); + goto fail; + } + /* + * recow the whole chunk tree, this will remove all chunk tree blocks + * from system block group + */ + chunk_root = root->fs_info->chunk_root; + memset(&key, 0, sizeof(key)); + while (1) { + ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); + if (ret < 0) + break; + + ret = btrfs_next_leaf(chunk_root, &path); + if (ret) + break; + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + btrfs_release_path(&path); + } + btrfs_release_path(&path); + + offset = 0; + num_bytes = 0; + while(1) { + cache1 = btrfs_lookup_block_group(root->fs_info, offset); + if (!cache1) + break; + + if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) + num_bytes += btrfs_block_group_used(&cache1->item); + + offset = cache1->key.objectid + cache1->key.offset; + } + /* only extent #0 left in system block group? */ + if (num_bytes > first_free) { + error( + "unable to empty system block group (num_bytes %llu, first_free %llu", + (unsigned long long)num_bytes, + (unsigned long long)first_free); + goto fail; + } + /* create a system chunk that maps the whole device */ + ret = prepare_system_chunk_sb(root->fs_info->super_copy); + if (ret) { + error("unable to update system chunk: %d", ret); + goto fail; + } + + ret = btrfs_commit_transaction(trans, root); + if (ret) { + error("transaction commit failed: %d", ret); + goto fail; + } + + ret = close_ctree(root); + if (ret) { + error("close_ctree failed: %d", ret); + goto fail; + } + + /* zero btrfs super block mirrors */ + memset(buf, 0, sectorsize); + for (i = 1 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { + bytenr = btrfs_sb_offset(i); + if (bytenr >= total_bytes) + break; + ret = pwrite(fd, buf, sectorsize, bytenr); + if (ret != sectorsize) { + error("zeroing superblock mirror %d failed: %d", + i, ret); + goto fail; + } + } + + sb_bytenr = (u64)-1; + /* copy all relocated blocks back */ + while(1) { + ret = find_first_extent_bit(&io_tree, 0, &start, &end, + EXTENT_LOCKED); + if (ret) + break; + + ret = get_state_private(&io_tree, start, &bytenr); + BUG_ON(ret); + + clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED, + GFP_NOFS); + + while (start <= end) { + if (start == BTRFS_SUPER_INFO_OFFSET) { + sb_bytenr = bytenr; + goto next_sector; + } + ret = pread(fd, buf, sectorsize, bytenr); + if (ret < 0) { + error("reading superblock at %llu failed: %d", + (unsigned long long)bytenr, ret); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = pwrite(fd, buf, sectorsize, start); + if (ret < 0) { + error("writing superblock at %llu failed: %d", + (unsigned long long)start, ret); + goto fail; + } + BUG_ON(ret != sectorsize); +next_sector: + start += sectorsize; + bytenr += sectorsize; + } + } + + ret = fsync(fd); + if (ret < 0) { + error("fsync failed: %s", strerror(errno)); + goto fail; + } + /* + * finally, overwrite btrfs super block. + */ + ret = pread(fd, buf, sectorsize, sb_bytenr); + if (ret < 0) { + error("reading primary superblock failed: %s", + strerror(errno)); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); + if (ret < 0) { + error("writing primary superblock failed: %s", + strerror(errno)); + goto fail; + } + BUG_ON(ret != sectorsize); + ret = fsync(fd); + if (ret < 0) { + error("fsync failed: %s", strerror(errno)); + goto fail; + } + + close(fd); + free(buf); + extent_io_tree_cleanup(&io_tree); + printf("rollback complete\n"); + return 0; + +fail: + if (fd != -1) + close(fd); + free(buf); + error("rollback aborted"); + return -1; +} + +static void print_usage(void) +{ + 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 the original filesystem\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"); + printf("\n"); + printf("Supported filesystems:\n"); + printf("\text2/3/4: %s\n", BTRFSCONVERT_EXT2 ? "yes" : "no"); +} + +int main(int argc, char *argv[]) +{ + int ret; + 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[BTRFS_LABEL_SIZE]; + u64 features = BTRFS_MKFS_DEFAULT_FEATURES; + + while(1) { + 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 'd': + datacsum = 0; + break; + case 'i': + noxattr = 1; + break; + case 'n': + packing = 0; + break; + case 'N': + nodesize = parse_size(optarg); + break; + case 'r': + rollback = 1; + break; + case 'l': + copylabel = -1; + if (strlen(optarg) >= BTRFS_LABEL_SIZE) { + warning( + "label too long, trimmed to %d bytes", + BTRFS_LABEL_SIZE - 1); + } + __strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1); + 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) { + error("unrecognized filesystem feature: %s", + 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); + error("features not allowed for convert: %s", + buf); + exit(1); + } + + break; + } + case GETOPT_VAL_NO_PROGRESS: + progress = 0; + break; + case GETOPT_VAL_HELP: + default: + print_usage(); + return c != GETOPT_VAL_HELP; + } + } + set_argv0(argv); + if (check_argc_exact(argc - optind, 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; + } + + file = argv[optind]; + ret = check_mounted(file); + if (ret < 0) { + error("could not check mount status: %s", strerror(-ret)); + return 1; + } else if (ret) { + error("%s is mounted", file); + return 1; + } + + if (rollback) { + ret = do_rollback(file); + } else { + ret = do_convert(file, datacsum, packing, noxattr, nodesize, + copylabel, fslabel, progress, features); + } + if (ret) + return 1; + return 0; +} -- cgit v1.2.3 From 8583b613095a46b000e5ee333d6bf58cad0c2248 Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Thu, 8 Dec 2016 19:26:19 +0530 Subject: btrfs-progs: convert: Prevent accounting blocks beyond end of device When looping across data block bitmap, __ext2_add_one_block() may add blocks which do not exist on the underlying disk. This commit prevents this from happening by checking the block index against the maximum block count that was present in the ext4 filesystem instance that is being converted. Signed-off-by: Chandan Rajendra Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 4b4cea42..1148a369 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1525,6 +1525,9 @@ static int __ext2_add_one_block(ext2_filsys fs, char *bitmap, offset /= EXT2FS_CLUSTER_RATIO(fs); offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super); for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) { + if ((i + offset) >= ext2fs_blocks_count(fs->super)) + break; + if (ext2fs_test_bit(i, bitmap)) { u64 start; -- cgit v1.2.3 From 71ffdb50eccf409f3f50389ca6f2057db105e0f2 Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Thu, 8 Dec 2016 19:26:20 +0530 Subject: btrfs-progs: convert: Fix migrate_super_block() to work with 64k sectorsize migrate_super_block() uses sectorsize to refer to the size of the superblock. Hence on 64k sectorsize filesystems, it ends up computing checksum beyond the super block length (i.e. BTRFS_SUPER_INFO_SIZE). This commit fixes the bug by using BTRFS_SUPER_INFO_SIZE instead of sectorsize of the underlying filesystem. Signed-off-by: Chandan Rajendra Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 1148a369..fd6f77b2 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1360,7 +1360,7 @@ err: /* * Migrate super block to its default position and zero 0 ~ 16k */ -static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) +static int migrate_super_block(int fd, u64 old_bytenr) { int ret; struct extent_buffer *buf; @@ -1368,13 +1368,13 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) u32 len; u32 bytenr; - buf = malloc(sizeof(*buf) + sectorsize); + buf = malloc(sizeof(*buf) + BTRFS_SUPER_INFO_SIZE); if (!buf) return -ENOMEM; - buf->len = sectorsize; - ret = pread(fd, buf->data, sectorsize, old_bytenr); - if (ret != sectorsize) + buf->len = BTRFS_SUPER_INFO_SIZE; + ret = pread(fd, buf->data, BTRFS_SUPER_INFO_SIZE, old_bytenr); + if (ret != BTRFS_SUPER_INFO_SIZE) goto fail; super = (struct btrfs_super_block *)buf->data; @@ -1382,19 +1382,20 @@ static int migrate_super_block(int fd, u64 old_bytenr, u32 sectorsize) btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, BTRFS_SUPER_INFO_OFFSET); - if (ret != sectorsize) + ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, + BTRFS_SUPER_INFO_OFFSET); + if (ret != BTRFS_SUPER_INFO_SIZE) goto fail; ret = fsync(fd); if (ret) goto fail; - memset(buf->data, 0, sectorsize); + memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE); for (bytenr = 0; bytenr < BTRFS_SUPER_INFO_OFFSET; ) { len = BTRFS_SUPER_INFO_OFFSET - bytenr; - if (len > sectorsize) - len = sectorsize; + if (len > BTRFS_SUPER_INFO_SIZE) + len = BTRFS_SUPER_INFO_SIZE; ret = pwrite(fd, buf->data, len, bytenr); if (ret != len) { fprintf(stderr, "unable to zero fill device\n"); @@ -2519,7 +2520,7 @@ static int do_convert(const char *devname, int datacsum, int packing, * If this step succeed, we get a mountable btrfs. Otherwise * the source fs is left unchanged. */ - ret = migrate_super_block(fd, mkfs_cfg.super_bytenr, blocksize); + ret = migrate_super_block(fd, mkfs_cfg.super_bytenr); if (ret) { error("unable to migrate super block: %d", ret); goto fail; -- cgit v1.2.3 From 95ecaf0d61ae0bd5f70d6ddf79de3a36a24f816d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 19 Dec 2016 14:56:37 +0800 Subject: btrfs-progs: file-item: Fix wrong file extents inserted If we specify NO_HOLES incompat feature when converting, the result image still uses hole file extents. And further more, the hole is incorrect as its disk_num_bytes is not zero. The problem is at btrfs_insert_file_extent() which doesn't check if we are going to insert hole file extent. Modify it to skip hole file extents to allow it follow restrict NO_HOLES flag. And since no_holes flag can be triggered on half-way, so current fsck won't report such error, as it consider it as old file holes. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index fd6f77b2..15f14af2 100644 --- a/convert/main.c +++ b/convert/main.c @@ -336,7 +336,7 @@ static int record_file_blocks(struct blk_iterate_data *data, key.offset > cur_off); fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); - extent_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi); + extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); BUG_ON(cur_off - key.offset >= extent_num_bytes); btrfs_release_path(&path); -- cgit v1.2.3 From c855e60f7ef8f980a465bcadc72caeba70fd10b7 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 24 Jan 2017 19:36:15 +0100 Subject: btrfs-progs: mkfs/convert: separate the convert part from make_btrfs The regulare mkfs_btrfs does not anything special about convert and just passes the arguments. Make that two functions. Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 15f14af2..8d9f29fa 100644 --- a/convert/main.c +++ b/convert/main.c @@ -2431,7 +2431,7 @@ static int do_convert(const char *devname, int datacsum, int packing, memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); - ret = make_btrfs(fd, &mkfs_cfg, &cctx); + ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx); if (ret) { error("unable to create initial ctree: %s", strerror(-ret)); goto fail; -- cgit v1.2.3 From 1c880f34f13e51aae9704449603f74680e442cf6 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 25 Jan 2017 16:35:00 +0100 Subject: btrfs-progs: move help defines to own header Signed-off-by: David Sterba --- convert/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 8d9f29fa..fa05485a 100644 --- a/convert/main.c +++ b/convert/main.c @@ -37,6 +37,7 @@ #include "crc32c.h" #include "utils.h" #include "task-utils.h" +#include "help.h" #if BTRFSCONVERT_EXT2 #include -- cgit v1.2.3 From ad7c469ac33dce8b9a602e5dbc63cc3cf96b0d48 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 25 Jan 2017 16:54:35 +0100 Subject: btrfs-progs: move mkfs definitions to own header Create a header for filesystem creation API, the config and the main entry function. Signed-off-by: David Sterba --- convert/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index fa05485a..3135ee31 100644 --- a/convert/main.c +++ b/convert/main.c @@ -38,6 +38,7 @@ #include "utils.h" #include "task-utils.h" #include "help.h" +#include "mkfs/common.h" #if BTRFSCONVERT_EXT2 #include -- cgit v1.2.3 From 5f276edfdaaec5ec21e48694768b7c200583736c Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 25 Jan 2017 17:09:10 +0100 Subject: btrfs-progs: move convert definitions to own header Create a header for filesystem conversion API, the config and the main entry function. Signed-off-by: David Sterba --- convert/common.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ convert/main.c | 1 + 2 files changed, 56 insertions(+) create mode 100644 convert/common.h (limited to 'convert') diff --git a/convert/common.h b/convert/common.h new file mode 100644 index 00000000..236e9a80 --- /dev/null +++ b/convert/common.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/* + * Defines and function declarations for users of the mkfs API, no internal + * defintions. + */ + +#ifndef __BTRFS_CONVERT_COMMON_H__ +#define __BTRFS_CONVERT_COMMON_H__ + +#include "kerncompat.h" +#include "common-defs.h" + +struct btrfs_mkfs_config; + +struct btrfs_convert_context { + u32 blocksize; + u32 first_data_block; + u32 block_count; + u32 inodes_count; + u32 free_inodes_count; + u64 total_bytes; + char *volume_name; + const struct btrfs_convert_operations *convert_ops; + + /* The accurate used space of old filesystem */ + struct cache_tree used; + + /* Batched ranges which must be covered by data chunks */ + struct cache_tree data_chunks; + + /* Free space which is not covered by data_chunks */ + struct cache_tree free; + + void *fs_data; +}; + +int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, + struct btrfs_convert_context *cctx); + +#endif diff --git a/convert/main.c b/convert/main.c index 3135ee31..48fb2f95 100644 --- a/convert/main.c +++ b/convert/main.c @@ -39,6 +39,7 @@ #include "task-utils.h" #include "help.h" #include "mkfs/common.h" +#include "convert/common.h" #if BTRFSCONVERT_EXT2 #include -- cgit v1.2.3 From 203c52a850f82d686cdff144f3cd296da0b5a1ed Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 25 Jan 2017 17:21:02 +0100 Subject: btrfs-progs: convert: move common api implementation to own file Lots of moved code but no actual changes. Signed-off-by: David Sterba --- convert/common.c | 882 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ convert/common.h | 1 + 2 files changed, 883 insertions(+) create mode 100644 convert/common.c (limited to 'convert') diff --git a/convert/common.c b/convert/common.c new file mode 100644 index 00000000..7a9b5962 --- /dev/null +++ b/convert/common.c @@ -0,0 +1,882 @@ +/* + * 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 +#include +#include "disk-io.h" +#include "volumes.h" +#include "utils.h" +#include "mkfs/common.h" +#include "convert/common.h" + +/* + * Reserve space from free_tree. + * The algorithm is very simple, find the first cache_extent with enough space + * and allocate from its beginning. + */ +static int reserve_free_space(struct cache_tree *free_tree, u64 len, + u64 *ret_start) +{ + struct cache_extent *cache; + int found = 0; + + ASSERT(ret_start != NULL); + cache = first_cache_extent(free_tree); + while (cache) { + if (cache->size > len) { + found = 1; + *ret_start = cache->start; + + cache->size -= len; + if (cache->size == 0) { + remove_cache_extent(free_tree, cache); + free(cache); + } else { + cache->start += len; + } + break; + } + cache = next_cache_extent(cache); + } + if (!found) + return -ENOSPC; + return 0; +} + +static inline int write_temp_super(int fd, struct btrfs_super_block *sb, + u64 sb_bytenr) +{ + u32 crc = ~(u32)0; + int ret; + + crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, + BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); + btrfs_csum_final(crc, &sb->csum[0]); + ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr); + if (ret < BTRFS_SUPER_INFO_SIZE) + ret = (ret < 0 ? -errno : -EIO); + else + ret = 0; + return ret; +} + +/* + * Setup temporary superblock at cfg->super_bynter + * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr + * + * For now sys chunk array will be empty and dev_item is empty too. + * They will be re-initialized at temp chunk tree setup. + * + * The superblock signature is not valid, denotes a partially created + * filesystem, needs to be finalized. + */ +static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg, + u64 root_bytenr, u64 chunk_bytenr) +{ + unsigned char chunk_uuid[BTRFS_UUID_SIZE]; + char super_buf[BTRFS_SUPER_INFO_SIZE]; + struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf; + int ret; + + memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE); + cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize); + + if (*cfg->fs_uuid) { + if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) { + error("cound not parse UUID: %s", cfg->fs_uuid); + ret = -EINVAL; + goto out; + } + if (!test_uuid_unique(cfg->fs_uuid)) { + error("non-unique UUID: %s", cfg->fs_uuid); + ret = -EINVAL; + goto out; + } + } else { + uuid_generate(super->fsid); + uuid_unparse(super->fsid, cfg->fs_uuid); + } + uuid_generate(chunk_uuid); + uuid_unparse(chunk_uuid, cfg->chunk_uuid); + + btrfs_set_super_bytenr(super, cfg->super_bytenr); + btrfs_set_super_num_devices(super, 1); + btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL); + btrfs_set_super_generation(super, 1); + btrfs_set_super_root(super, root_bytenr); + btrfs_set_super_chunk_root(super, chunk_bytenr); + btrfs_set_super_total_bytes(super, cfg->num_bytes); + /* + * Temporary filesystem will only have 6 tree roots: + * chunk tree, root tree, extent_tree, device tree, fs tree + * and csum tree. + */ + btrfs_set_super_bytes_used(super, 6 * cfg->nodesize); + btrfs_set_super_sectorsize(super, cfg->sectorsize); + btrfs_set_super_leafsize(super, cfg->nodesize); + btrfs_set_super_nodesize(super, cfg->nodesize); + btrfs_set_super_stripesize(super, cfg->stripesize); + btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32); + btrfs_set_super_chunk_root(super, chunk_bytenr); + btrfs_set_super_cache_generation(super, -1); + btrfs_set_super_incompat_flags(super, cfg->features); + if (cfg->label) + __strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1); + + /* Sys chunk array will be re-initialized at chunk tree init time */ + super->sys_chunk_array_size = 0; + + ret = write_temp_super(fd, super, cfg->super_bytenr); +out: + return ret; +} + +static int setup_temp_extent_buffer(struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + u64 bytenr, u64 owner) +{ + unsigned char fsid[BTRFS_FSID_SIZE]; + unsigned char chunk_uuid[BTRFS_UUID_SIZE]; + int ret; + + ret = uuid_parse(cfg->fs_uuid, fsid); + if (ret) + return -EINVAL; + ret = uuid_parse(cfg->chunk_uuid, chunk_uuid); + if (ret) + return -EINVAL; + + memset(buf->data, 0, cfg->nodesize); + buf->len = cfg->nodesize; + btrfs_set_header_bytenr(buf, bytenr); + btrfs_set_header_generation(buf, 1); + btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(buf, owner); + btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN); + write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf), + BTRFS_UUID_SIZE); + write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); + return 0; +} + +static void insert_temp_root_item(struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + int *slot, u32 *itemoff, u64 objectid, + u64 bytenr) +{ + struct btrfs_root_item root_item; + struct btrfs_inode_item *inode_item; + struct btrfs_disk_key disk_key; + + btrfs_set_header_nritems(buf, *slot + 1); + (*itemoff) -= sizeof(root_item); + memset(&root_item, 0, sizeof(root_item)); + inode_item = &root_item.inode; + btrfs_set_stack_inode_generation(inode_item, 1); + btrfs_set_stack_inode_size(inode_item, 3); + btrfs_set_stack_inode_nlink(inode_item, 1); + btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize); + btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); + btrfs_set_root_refs(&root_item, 1); + btrfs_set_root_used(&root_item, cfg->nodesize); + btrfs_set_root_generation(&root_item, 1); + btrfs_set_root_bytenr(&root_item, bytenr); + + memset(&disk_key, 0, sizeof(disk_key)); + btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); + btrfs_set_disk_key_objectid(&disk_key, objectid); + btrfs_set_disk_key_offset(&disk_key, 0); + + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, *slot), + sizeof(root_item)); + (*slot)++; +} + +/* + * Setup an extent buffer for tree block. + */ +static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf, + u64 bytenr) +{ + int ret; + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + + /* Temporary extent buffer is always mapped 1:1 on disk */ + ret = pwrite(fd, buf->data, buf->len, bytenr); + if (ret < buf->len) + ret = (ret < 0 ? ret : -EIO); + else + ret = 0; + return ret; +} + +static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 root_bytenr, u64 extent_bytenr, + u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr) +{ + struct extent_buffer *buf = NULL; + u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + int slot = 0; + int ret; + + /* + * Provided bytenr must in ascending order, or tree root will have a + * bad key order. + */ + if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr && + dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) { + error("bad tree bytenr order: " + "root < extent %llu < %llu, " + "extent < dev %llu < %llu, " + "dev < fs %llu < %llu, " + "fs < csum %llu < %llu", + (unsigned long long)root_bytenr, + (unsigned long long)extent_bytenr, + (unsigned long long)extent_bytenr, + (unsigned long long)dev_bytenr, + (unsigned long long)dev_bytenr, + (unsigned long long)fs_bytenr, + (unsigned long long)fs_bytenr, + (unsigned long long)csum_bytenr); + return -EINVAL; + } + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + + ret = setup_temp_extent_buffer(buf, cfg, root_bytenr, + BTRFS_ROOT_TREE_OBJECTID); + if (ret < 0) + goto out; + + insert_temp_root_item(buf, cfg, &slot, &itemoff, + BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr); + insert_temp_root_item(buf, cfg, &slot, &itemoff, + BTRFS_DEV_TREE_OBJECTID, dev_bytenr); + insert_temp_root_item(buf, cfg, &slot, &itemoff, + BTRFS_FS_TREE_OBJECTID, fs_bytenr); + insert_temp_root_item(buf, cfg, &slot, &itemoff, + BTRFS_CSUM_TREE_OBJECTID, csum_bytenr); + + ret = write_temp_extent_buffer(fd, buf, root_bytenr); +out: + free(buf); + return ret; +} + +static int insert_temp_dev_item(int fd, struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + int *slot, u32 *itemoff) +{ + struct btrfs_disk_key disk_key; + struct btrfs_dev_item *dev_item; + char super_buf[BTRFS_SUPER_INFO_SIZE]; + unsigned char dev_uuid[BTRFS_UUID_SIZE]; + unsigned char fsid[BTRFS_FSID_SIZE]; + struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf; + int ret; + + ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr); + if (ret < BTRFS_SUPER_INFO_SIZE) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + btrfs_set_header_nritems(buf, *slot + 1); + (*itemoff) -= sizeof(*dev_item); + /* setup device item 1, 0 is for replace case */ + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, 1); + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item)); + + dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item); + /* Generate device uuid */ + uuid_generate(dev_uuid); + write_extent_buffer(buf, dev_uuid, + (unsigned long)btrfs_device_uuid(dev_item), + BTRFS_UUID_SIZE); + uuid_parse(cfg->fs_uuid, fsid); + write_extent_buffer(buf, fsid, + (unsigned long)btrfs_device_fsid(dev_item), + BTRFS_FSID_SIZE); + btrfs_set_device_id(buf, dev_item, 1); + btrfs_set_device_generation(buf, dev_item, 0); + btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes); + /* + * The number must match the initial SYSTEM and META chunk size + */ + btrfs_set_device_bytes_used(buf, dev_item, + BTRFS_MKFS_SYSTEM_GROUP_SIZE + + BTRFS_CONVERT_META_GROUP_SIZE); + btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize); + btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize); + btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize); + btrfs_set_device_type(buf, dev_item, 0); + + /* Super dev_item is not complete, copy the complete one to sb */ + read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item, + sizeof(*dev_item)); + ret = write_temp_super(fd, super, cfg->super_bytenr); + (*slot)++; +out: + return ret; +} + +static int insert_temp_chunk_item(int fd, struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + int *slot, u32 *itemoff, u64 start, u64 len, + u64 type) +{ + struct btrfs_chunk *chunk; + struct btrfs_disk_key disk_key; + char super_buf[BTRFS_SUPER_INFO_SIZE]; + struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf; + int ret = 0; + + ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, + cfg->super_bytenr); + if (ret < BTRFS_SUPER_INFO_SIZE) { + ret = (ret < 0 ? ret : -EIO); + return ret; + } + + btrfs_set_header_nritems(buf, *slot + 1); + (*itemoff) -= btrfs_chunk_item_size(1); + btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, start); + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), + btrfs_chunk_item_size(1)); + + chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk); + btrfs_set_chunk_length(buf, chunk, len); + btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_chunk_stripe_len(buf, chunk, BTRFS_STRIPE_LEN); + btrfs_set_chunk_type(buf, chunk, type); + btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_num_stripes(buf, chunk, 1); + /* TODO: Support DUP profile for system chunk */ + btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); + /* We are doing 1:1 mapping, so start is its dev offset */ + btrfs_set_stripe_offset_nr(buf, chunk, 0, start); + write_extent_buffer(buf, &sb->dev_item.uuid, + (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0), + BTRFS_UUID_SIZE); + (*slot)++; + + /* + * If it's system chunk, also copy it to super block. + */ + if (type & BTRFS_BLOCK_GROUP_SYSTEM) { + char *cur; + u32 array_size; + + cur = (char *)sb->sys_chunk_array + + btrfs_super_sys_array_size(sb); + memcpy(cur, &disk_key, sizeof(disk_key)); + cur += sizeof(disk_key); + read_extent_buffer(buf, cur, (unsigned long int)chunk, + btrfs_chunk_item_size(1)); + array_size = btrfs_super_sys_array_size(sb); + array_size += btrfs_chunk_item_size(1) + + sizeof(disk_key); + btrfs_set_super_sys_array_size(sb, array_size); + + ret = write_temp_super(fd, sb, cfg->super_bytenr); + } + return ret; +} + +static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 sys_chunk_start, u64 meta_chunk_start, + u64 chunk_bytenr) +{ + struct extent_buffer *buf = NULL; + u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + int slot = 0; + int ret; + + /* Must ensure SYS chunk starts before META chunk */ + if (meta_chunk_start < sys_chunk_start) { + error("wrong chunk order: meta < system %llu < %llu", + (unsigned long long)meta_chunk_start, + (unsigned long long)sys_chunk_start); + return -EINVAL; + } + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr, + BTRFS_CHUNK_TREE_OBJECTID); + if (ret < 0) + goto out; + + ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff); + if (ret < 0) + goto out; + ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff, + sys_chunk_start, + BTRFS_MKFS_SYSTEM_GROUP_SIZE, + BTRFS_BLOCK_GROUP_SYSTEM); + if (ret < 0) + goto out; + ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff, + meta_chunk_start, + BTRFS_CONVERT_META_GROUP_SIZE, + BTRFS_BLOCK_GROUP_METADATA); + if (ret < 0) + goto out; + ret = write_temp_extent_buffer(fd, buf, chunk_bytenr); + +out: + free(buf); + return ret; +} + +static void insert_temp_dev_extent(struct extent_buffer *buf, + int *slot, u32 *itemoff, u64 start, u64 len) +{ + struct btrfs_dev_extent *dev_extent; + struct btrfs_disk_key disk_key; + + btrfs_set_header_nritems(buf, *slot + 1); + (*itemoff) -= sizeof(*dev_extent); + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); + btrfs_set_disk_key_objectid(&disk_key, 1); + btrfs_set_disk_key_offset(&disk_key, start); + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent)); + + dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent); + btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, + BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_length(buf, dev_extent, len); + btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start); + btrfs_set_dev_extent_chunk_tree(buf, dev_extent, + BTRFS_CHUNK_TREE_OBJECTID); + (*slot)++; +} + +static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 sys_chunk_start, u64 meta_chunk_start, + u64 dev_bytenr) +{ + struct extent_buffer *buf = NULL; + u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + int slot = 0; + int ret; + + /* Must ensure SYS chunk starts before META chunk */ + if (meta_chunk_start < sys_chunk_start) { + error("wrong chunk order: meta < system %llu < %llu", + (unsigned long long)meta_chunk_start, + (unsigned long long)sys_chunk_start); + return -EINVAL; + } + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr, + BTRFS_DEV_TREE_OBJECTID); + if (ret < 0) + goto out; + insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); + insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start, + BTRFS_CONVERT_META_GROUP_SIZE); + ret = write_temp_extent_buffer(fd, buf, dev_bytenr); +out: + free(buf); + return ret; +} + +static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 fs_bytenr) +{ + struct extent_buffer *buf = NULL; + int ret; + + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr, + BTRFS_FS_TREE_OBJECTID); + if (ret < 0) + goto out; + /* + * Temporary fs tree is completely empty. + */ + ret = write_temp_extent_buffer(fd, buf, fs_bytenr); +out: + free(buf); + return ret; +} + +static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 csum_bytenr) +{ + struct extent_buffer *buf = NULL; + int ret; + + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr, + BTRFS_CSUM_TREE_OBJECTID); + if (ret < 0) + goto out; + /* + * Temporary csum tree is completely empty. + */ + ret = write_temp_extent_buffer(fd, buf, csum_bytenr); +out: + free(buf); + return ret; +} + +/* + * Insert one temporary extent item. + * + * NOTE: if skinny_metadata is not enabled, this function must be called + * after all other trees are initialized. + * Or fs without skinny-metadata will be screwed up. + */ +static int insert_temp_extent_item(int fd, struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + int *slot, u32 *itemoff, u64 bytenr, + u64 ref_root) +{ + struct extent_buffer *tmp; + struct btrfs_extent_item *ei; + struct btrfs_extent_inline_ref *iref; + struct btrfs_disk_key disk_key; + struct btrfs_disk_key tree_info_key; + struct btrfs_tree_block_info *info; + int itemsize; + int skinny_metadata = cfg->features & + BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA; + int ret; + + if (skinny_metadata) + itemsize = sizeof(*ei) + sizeof(*iref); + else + itemsize = sizeof(*ei) + sizeof(*iref) + + sizeof(struct btrfs_tree_block_info); + + btrfs_set_header_nritems(buf, *slot + 1); + *(itemoff) -= itemsize; + + if (skinny_metadata) { + btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, 0); + } else { + btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, cfg->nodesize); + } + btrfs_set_disk_key_objectid(&disk_key, bytenr); + + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize); + + ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item); + btrfs_set_extent_refs(buf, ei, 1); + btrfs_set_extent_generation(buf, ei, 1); + btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK); + + if (skinny_metadata) { + iref = (struct btrfs_extent_inline_ref *)(ei + 1); + } else { + info = (struct btrfs_tree_block_info *)(ei + 1); + iref = (struct btrfs_extent_inline_ref *)(info + 1); + } + btrfs_set_extent_inline_ref_type(buf, iref, + BTRFS_TREE_BLOCK_REF_KEY); + btrfs_set_extent_inline_ref_offset(buf, iref, ref_root); + + (*slot)++; + if (skinny_metadata) + return 0; + + /* + * Lastly, check the tree block key by read the tree block + * Since we do 1:1 mapping for convert case, we can directly + * read the bytenr from disk + */ + tmp = malloc(sizeof(*tmp) + cfg->nodesize); + if (!tmp) + return -ENOMEM; + ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root); + if (ret < 0) + goto out; + ret = pread(fd, tmp->data, cfg->nodesize, bytenr); + if (ret < cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + if (btrfs_header_nritems(tmp) == 0) { + btrfs_set_disk_key_type(&tree_info_key, 0); + btrfs_set_disk_key_objectid(&tree_info_key, 0); + btrfs_set_disk_key_offset(&tree_info_key, 0); + } else { + btrfs_item_key(tmp, &tree_info_key, 0); + } + btrfs_set_tree_block_key(buf, info, &tree_info_key); + +out: + free(tmp); + return ret; +} + +static void insert_temp_block_group(struct extent_buffer *buf, + struct btrfs_mkfs_config *cfg, + int *slot, u32 *itemoff, + u64 bytenr, u64 len, u64 used, u64 flag) +{ + struct btrfs_block_group_item bgi; + struct btrfs_disk_key disk_key; + + btrfs_set_header_nritems(buf, *slot + 1); + (*itemoff) -= sizeof(bgi); + btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY); + btrfs_set_disk_key_objectid(&disk_key, bytenr); + btrfs_set_disk_key_offset(&disk_key, len); + btrfs_set_item_key(buf, &disk_key, *slot); + btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi)); + + btrfs_set_block_group_flags(&bgi, flag); + btrfs_set_block_group_used(&bgi, used); + btrfs_set_block_group_chunk_objectid(&bgi, + BTRFS_FIRST_CHUNK_TREE_OBJECTID); + write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot), + sizeof(bgi)); + (*slot)++; +} + +static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg, + u64 chunk_bytenr, u64 root_bytenr, + u64 extent_bytenr, u64 dev_bytenr, + u64 fs_bytenr, u64 csum_bytenr) +{ + struct extent_buffer *buf = NULL; + u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + int slot = 0; + int ret; + + /* + * We must ensure provided bytenr are in ascending order, + * or extent tree key order will be broken. + */ + if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr && + extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr && + fs_bytenr < csum_bytenr)) { + error("bad tree bytenr order: " + "chunk < root %llu < %llu, " + "root < extent %llu < %llu, " + "extent < dev %llu < %llu, " + "dev < fs %llu < %llu, " + "fs < csum %llu < %llu", + (unsigned long long)chunk_bytenr, + (unsigned long long)root_bytenr, + (unsigned long long)root_bytenr, + (unsigned long long)extent_bytenr, + (unsigned long long)extent_bytenr, + (unsigned long long)dev_bytenr, + (unsigned long long)dev_bytenr, + (unsigned long long)fs_bytenr, + (unsigned long long)fs_bytenr, + (unsigned long long)csum_bytenr); + return -EINVAL; + } + buf = malloc(sizeof(*buf) + cfg->nodesize); + if (!buf) + return -ENOMEM; + + ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr, + BTRFS_EXTENT_TREE_OBJECTID); + if (ret < 0) + goto out; + + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID); + if (ret < 0) + goto out; + + insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr, + BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize, + BTRFS_BLOCK_GROUP_SYSTEM); + + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + root_bytenr, BTRFS_ROOT_TREE_OBJECTID); + if (ret < 0) + goto out; + + /* 5 tree block used, root, extent, dev, fs and csum*/ + insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr, + BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5, + BTRFS_BLOCK_GROUP_METADATA); + + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID); + if (ret < 0) + goto out; + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + dev_bytenr, BTRFS_DEV_TREE_OBJECTID); + if (ret < 0) + goto out; + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + fs_bytenr, BTRFS_FS_TREE_OBJECTID); + if (ret < 0) + goto out; + ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, + csum_bytenr, BTRFS_CSUM_TREE_OBJECTID); + if (ret < 0) + goto out; + + ret = write_temp_extent_buffer(fd, buf, extent_bytenr); +out: + free(buf); + return ret; +} + +/* + * Improved version of make_btrfs(). + * + * This one will + * 1) Do chunk allocation to avoid used data + * And after this function, extent type matches chunk type + * 2) Better structured code + * No super long hand written codes to initialized all tree blocks + * Split into small blocks and reuse codes. + * TODO: Reuse tree operation facilities by introducing new flags + */ +int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, + struct btrfs_convert_context *cctx) +{ + struct cache_tree *free = &cctx->free; + struct cache_tree *used = &cctx->used; + u64 sys_chunk_start; + u64 meta_chunk_start; + /* chunk tree bytenr, in system chunk */ + u64 chunk_bytenr; + /* metadata trees bytenr, in metadata chunk */ + u64 root_bytenr; + u64 extent_bytenr; + u64 dev_bytenr; + u64 fs_bytenr; + u64 csum_bytenr; + int ret; + + /* Shouldn't happen */ + BUG_ON(cache_tree_empty(used)); + + /* + * reserve space for temporary superblock first + * Here we allocate a little larger space, to keep later + * free space will be STRIPE_LEN aligned + */ + ret = reserve_free_space(free, BTRFS_STRIPE_LEN, + &cfg->super_bytenr); + if (ret < 0) + goto out; + + /* + * Then reserve system chunk space + * TODO: Change system group size depending on cctx->total_bytes. + * If using current 4M, it can only handle less than one TB for + * worst case and then run out of sys space. + */ + ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE, + &sys_chunk_start); + if (ret < 0) + goto out; + ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE, + &meta_chunk_start); + if (ret < 0) + goto out; + + /* + * Allocated meta/sys chunks will be mapped 1:1 with device offset. + * + * Inside the allocated metadata chunk, the layout will be: + * | offset | contents | + * ------------------------------------- + * | +0 | tree root | + * | +nodesize | extent root | + * | +nodesize * 2 | device root | + * | +nodesize * 3 | fs tree | + * | +nodesize * 4 | csum tree | + * ------------------------------------- + * Inside the allocated system chunk, the layout will be: + * | offset | contents | + * ------------------------------------- + * | +0 | chunk root | + * ------------------------------------- + */ + chunk_bytenr = sys_chunk_start; + root_bytenr = meta_chunk_start; + extent_bytenr = meta_chunk_start + cfg->nodesize; + dev_bytenr = meta_chunk_start + cfg->nodesize * 2; + fs_bytenr = meta_chunk_start + cfg->nodesize * 3; + csum_bytenr = meta_chunk_start + cfg->nodesize * 4; + + ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr); + if (ret < 0) + goto out; + + ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr, + dev_bytenr, fs_bytenr, csum_bytenr); + if (ret < 0) + goto out; + ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start, + chunk_bytenr); + if (ret < 0) + goto out; + ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start, + dev_bytenr); + if (ret < 0) + goto out; + ret = setup_temp_fs_tree(fd, cfg, fs_bytenr); + if (ret < 0) + goto out; + ret = setup_temp_csum_tree(fd, cfg, csum_bytenr); + if (ret < 0) + goto out; + /* + * Setup extent tree last, since it may need to read tree block key + * for non-skinny metadata case. + */ + ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr, + extent_bytenr, dev_bytenr, fs_bytenr, + csum_bytenr); +out: + return ret; +} + diff --git a/convert/common.h b/convert/common.h index 236e9a80..55a65547 100644 --- a/convert/common.h +++ b/convert/common.h @@ -24,6 +24,7 @@ #include "kerncompat.h" #include "common-defs.h" +#include "extent-cache.h" struct btrfs_mkfs_config; -- cgit v1.2.3 From 14f9565c11828841274c516c71d11ab6a9e2fe58 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 26 Jan 2017 13:13:36 +0100 Subject: btrfs-progs: move fs features declarations to own header from utils Signed-off-by: David Sterba --- convert/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 48fb2f95..39c6eeb4 100644 --- a/convert/main.c +++ b/convert/main.c @@ -40,6 +40,7 @@ #include "help.h" #include "mkfs/common.h" #include "convert/common.h" +#include "fsfeatures.h" #if BTRFSCONVERT_EXT2 #include -- cgit v1.2.3 From 2798a5df33d7749b223ffff38956a5c615bef874 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 27 Jan 2017 15:47:17 +0100 Subject: btrfs-progs: convert: move definitions for interal conversion API to own file Signed-off-by: David Sterba --- convert/main.c | 50 ++++++--------------------------- convert/source-fs.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 convert/source-fs.h (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 39c6eeb4..70de44b6 100644 --- a/convert/main.c +++ b/convert/main.c @@ -40,6 +40,7 @@ #include "help.h" #include "mkfs/common.h" #include "convert/common.h" +#include "convert/source-fs.h" #include "fsfeatures.h" #if BTRFSCONVERT_EXT2 @@ -63,14 +64,6 @@ #endif -#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; @@ -98,26 +91,14 @@ static int after_copied_inodes(void *p) return 0; } -struct btrfs_convert_context; -struct btrfs_convert_operations { - const char *name; - int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); - int (*read_used_space)(struct btrfs_convert_context *cctx); - int (*copy_inodes)(struct btrfs_convert_context *cctx, - struct btrfs_root *root, int datacsum, - int packing, int noxattr, struct task_ctx *p); - void (*close_fs)(struct btrfs_convert_context *cctx); - int (*check_state)(struct btrfs_convert_context *cctx); -}; - -static void init_convert_context(struct btrfs_convert_context *cctx) +void init_convert_context(struct btrfs_convert_context *cctx) { cache_tree_init(&cctx->used); cache_tree_init(&cctx->data_chunks); cache_tree_init(&cctx->free); } -static void clean_convert_context(struct btrfs_convert_context *cctx) +void clean_convert_context(struct btrfs_convert_context *cctx) { free_extent_cache_tree(&cctx->used); free_extent_cache_tree(&cctx->data_chunks); @@ -158,7 +139,7 @@ static int intersect_with_sb(u64 bytenr, u64 num_bytes) return 0; } -static int convert_insert_dirent(struct btrfs_trans_handle *trans, +int convert_insert_dirent(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *name, size_t name_len, u64 dir, u64 objectid, @@ -187,7 +168,7 @@ static int convert_insert_dirent(struct btrfs_trans_handle *trans, return 0; } -static int read_disk_extent(struct btrfs_root *root, u64 bytenr, +int read_disk_extent(struct btrfs_root *root, u64 bytenr, u32 num_bytes, char *buffer) { int ret; @@ -232,22 +213,7 @@ static int csum_disk_extent(struct btrfs_trans_handle *trans, return ret; } -struct blk_iterate_data { - struct btrfs_trans_handle *trans; - struct btrfs_root *root; - struct btrfs_root *convert_root; - struct btrfs_inode_item *inode; - u64 convert_ino; - u64 objectid; - u64 first_block; - u64 disk_block; - u64 num_blocks; - u64 boundary; - int checksum; - int errcode; -}; - -static void init_blk_iterate_data(struct blk_iterate_data *data, +void init_blk_iterate_data(struct blk_iterate_data *data, struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_inode_item *inode, @@ -281,7 +247,7 @@ static void init_blk_iterate_data(struct blk_iterate_data *data, * So here, we don't use disk_block directly but search convert_root * to get the real disk_bytenr. */ -static int record_file_blocks(struct blk_iterate_data *data, +int record_file_blocks(struct blk_iterate_data *data, u64 file_block, u64 disk_block, u64 num_blocks) { int ret = 0; @@ -369,7 +335,7 @@ static int record_file_blocks(struct blk_iterate_data *data, return ret; } -static int block_iterate_proc(u64 disk_block, u64 file_block, +int block_iterate_proc(u64 disk_block, u64 file_block, struct blk_iterate_data *idata) { int ret = 0; diff --git a/convert/source-fs.h b/convert/source-fs.h new file mode 100644 index 00000000..b6a159e5 --- /dev/null +++ b/convert/source-fs.h @@ -0,0 +1,81 @@ +/* + * 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_SOURCE_FS_H__ +#define __BTRFS_CONVERT_SOURCE_FS_H__ + +#include "kerncompat.h" + +#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; +}; + +struct btrfs_convert_context; +struct btrfs_root; +struct btrfs_trans_handle; +struct btrfs_inode_item; + +struct btrfs_convert_operations { + const char *name; + int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); + int (*read_used_space)(struct btrfs_convert_context *cctx); + int (*copy_inodes)(struct btrfs_convert_context *cctx, + struct btrfs_root *root, int datacsum, + int packing, int noxattr, struct task_ctx *p); + void (*close_fs)(struct btrfs_convert_context *cctx); + int (*check_state)(struct btrfs_convert_context *cctx); +}; + +struct blk_iterate_data { + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + struct btrfs_root *convert_root; + struct btrfs_inode_item *inode; + u64 convert_ino; + u64 objectid; + u64 first_block; + u64 disk_block; + u64 num_blocks; + u64 boundary; + int checksum; + int errcode; +}; + +void init_convert_context(struct btrfs_convert_context *cctx); +void clean_convert_context(struct btrfs_convert_context *cctx); +int block_iterate_proc(u64 disk_block, u64 file_block, + struct blk_iterate_data *idata); +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); +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 read_disk_extent(struct btrfs_root *root, u64 bytenr, + u32 num_bytes, char *buffer); +int record_file_blocks(struct blk_iterate_data *data, + u64 file_block, u64 disk_block, u64 num_blocks); + +#endif -- cgit v1.2.3 From 27e927e775943947f4e6c4a72d7b39a5f977cb0c Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 27 Jan 2017 16:51:03 +0100 Subject: btrfs-progs: convert: move ext2 definitions out of main Signed-off-by: David Sterba --- convert/main.c | 76 +-------------------------------- convert/source-ext2.h | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 75 deletions(-) create mode 100644 convert/source-ext2.h (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 70de44b6..5eaa2517 100644 --- a/convert/main.c +++ b/convert/main.c @@ -41,29 +41,9 @@ #include "mkfs/common.h" #include "convert/common.h" #include "convert/source-fs.h" +#include "convert/source-ext2.h" #include "fsfeatures.h" -#if BTRFSCONVERT_EXT2 -#include -#include -#include - -#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO) - -/* - * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag - * BIGALLOC. - * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used - * space, and btrfs-convert heavily relies on it. - */ -#ifdef HAVE_OLD_E2FSPROGS -#define EXT2FS_CLUSTER_RATIO(fs) (1) -#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s)) -#define EXT2FS_B2C(fs, blk) (blk) -#endif - -#endif - static void *print_copied_inodes(void *p) { struct task_ctx *priv = p; @@ -1564,16 +1544,6 @@ static void ext2_close_fs(struct btrfs_convert_context *cctx) ext2fs_close(cctx->fs_data); } -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 ext2_filetype_conversion_table[EXT2_FT_MAX] = { [EXT2_FT_UNKNOWN] = BTRFS_FT_UNKNOWN, [EXT2_FT_REG_FILE] = BTRFS_FT_REG_FILE, @@ -1816,37 +1786,6 @@ static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry, return 0; } -#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; - __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; @@ -1863,19 +1802,6 @@ static inline int ext2_acl_count(size_t size) } } -#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); diff --git a/convert/source-ext2.h b/convert/source-ext2.h new file mode 100644 index 00000000..20a278ed --- /dev/null +++ b/convert/source-ext2.h @@ -0,0 +1,113 @@ +/* + * 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_SOURCE_EXT2_H__ +#define __BTRFS_CONVERT_SOURCE_EXT2_H__ + +#if BTRFSCONVERT_EXT2 + +#include "kerncompat.h" + +#include +#include +#include +#include "convert/source-fs.h" + +#define INO_OFFSET (BTRFS_FIRST_FREE_OBJECTID - EXT2_ROOT_INO) + +/* + * Compatibility code for e2fsprogs 1.41 which doesn't support RO compat flag + * BIGALLOC. + * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used + * space, and btrfs-convert heavily relies on it. + */ +#ifdef HAVE_OLD_E2FSPROGS +#define EXT2FS_CLUSTER_RATIO(fs) (1) +#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s)) +#define EXT2FS_B2C(fs, blk) (blk) +#endif + +/* + * 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))) + +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; +}; + +#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; + __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; + +#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; + +#endif /* BTRFSCONVERT_EXT2 */ + +#endif -- cgit v1.2.3 From 03c085861b8692cd6f7d3307cadaab7ee4654fdb Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 27 Jan 2017 16:56:37 +0100 Subject: btrfs-progs: convert: move ext2 conversion out of main.c Signed-off-by: David Sterba --- convert/main.c | 849 +----------------------------------------------- convert/source-ext2.c | 870 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 871 insertions(+), 848 deletions(-) create mode 100644 convert/source-ext2.c (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 5eaa2517..105c4c12 100644 --- a/convert/main.c +++ b/convert/main.c @@ -41,7 +41,6 @@ #include "mkfs/common.h" #include "convert/common.h" #include "convert/source-fs.h" -#include "convert/source-ext2.h" #include "fsfeatures.h" static void *print_copied_inodes(void *p) @@ -1392,853 +1391,7 @@ static int prepare_system_chunk_sb(struct btrfs_super_block *super) return 0; } -#if BTRFSCONVERT_EXT2 - -/* - * Open Ext2fs in readonly mode, read block allocation bitmap and - * inode bitmap into memory. - */ -static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) -{ - errcode_t ret; - ext2_filsys ext2_fs; - ext2_ino_t ino; - u32 ro_feature; - - ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); - if (ret) { - fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); - return -1; - } - /* - * We need to know exactly the used space, some RO compat flags like - * BIGALLOC will affect how used space is present. - * So we need manuall check any unsupported RO compat flags - */ - ro_feature = ext2_fs->super->s_feature_ro_compat; - if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { - error( -"unsupported RO features detected: %x, abort convert to avoid possible corruption", - ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP); - 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); - } - - if (!(ext2_fs->super->s_feature_incompat & - EXT2_FEATURE_INCOMPAT_FILETYPE)) { - error("filetype feature is missing"); - goto fail; - } - - cctx->fs_data = ext2_fs; - cctx->blocksize = ext2_fs->blocksize; - cctx->block_count = ext2_fs->super->s_blocks_count; - cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count; - cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16); - cctx->first_data_block = ext2_fs->super->s_first_data_block; - cctx->inodes_count = ext2_fs->super->s_inodes_count; - cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count; - return 0; -fail: - ext2fs_close(ext2_fs); - return -1; -} - -static int __ext2_add_one_block(ext2_filsys fs, char *bitmap, - unsigned long group_nr, struct cache_tree *used) -{ - unsigned long offset; - unsigned i; - int ret = 0; - - offset = fs->super->s_first_data_block; - offset /= EXT2FS_CLUSTER_RATIO(fs); - offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super); - for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) { - if ((i + offset) >= ext2fs_blocks_count(fs->super)) - break; - - if (ext2fs_test_bit(i, bitmap)) { - u64 start; - - start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs); - start *= fs->blocksize; - ret = add_merge_cache_extent(used, start, - fs->blocksize); - if (ret < 0) - break; - } - } - return ret; -} - -/* - * Read all used ext2 space into cctx->used cache tree - */ -static int ext2_read_used_space(struct btrfs_convert_context *cctx) -{ - ext2_filsys fs = (ext2_filsys)cctx->fs_data; - blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); - struct cache_tree *used_tree = &cctx->used; - char *block_bitmap = NULL; - unsigned long i; - int block_nbytes; - int ret = 0; - - block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; - /* Shouldn't happen */ - BUG_ON(!fs->block_map); - - block_bitmap = malloc(block_nbytes); - if (!block_bitmap) - return -ENOMEM; - - for (i = 0; i < fs->group_desc_count; i++) { - ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr, - block_nbytes * 8, block_bitmap); - if (ret) { - error("fail to get bitmap from ext2, %s", - strerror(-ret)); - break; - } - ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree); - if (ret < 0) { - error("fail to build used space tree, %s", - strerror(-ret)); - break; - } - blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super); - } - - free(block_bitmap); - return ret; -} - -static void ext2_close_fs(struct btrfs_convert_context *cctx) -{ - if (cctx->volume_name) { - free(cctx->volume_name); - cctx->volume_name = NULL; - } - ext2fs_close(cctx->fs_data); -} - -static u8 ext2_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 ext2_dir_iterate_proc(ext2_ino_t dir, int entry, - struct ext2_dir_entry *dirent, - int offset, int blocksize, - char *buf,void *priv_data) -{ - int ret; - int file_type; - u64 objectid; - char dotdot[] = ".."; - struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data; - int name_len; - - name_len = dirent->name_len & 0xFF; - - objectid = dirent->inode + INO_OFFSET; - if (!strncmp(dirent->name, dotdot, name_len)) { - if (name_len == 2) { - BUG_ON(idata->parent != 0); - idata->parent = objectid; - } - return 0; - } - if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO) - return 0; - - file_type = dirent->name_len >> 8; - BUG_ON(file_type > EXT2_FT_SYMLINK); - - ret = convert_insert_dirent(idata->trans, idata->root, dirent->name, - name_len, idata->objectid, objectid, - ext2_filetype_conversion_table[file_type], - idata->index_cnt, idata->inode); - if (ret < 0) { - idata->errcode = ret; - return BLOCK_ABORT; - } - - idata->index_cnt++; - return 0; -} - -static int ext2_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, - ext2_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 ext2_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; - ret = block_iterate_proc(*blocknr, blockcnt, idata); - if (ret) { - idata->errcode = ret; - return BLOCK_ABORT; - } - return 0; -} - -/* - * traverse file's data blocks, record these data blocks as file extents. - */ -static int ext2_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; - char *buffer = NULL; - errcode_t err; - u32 last_block; - u32 sectorsize = root->sectorsize; - u64 inode_size = btrfs_stack_inode_size(btrfs_inode); - 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, ext2_block_iterate_proc, &data); - if (err) - goto error; - ret = data.errcode; - if (ret) - goto fail; - 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; - u64 nbytes; - - buffer = malloc(num_bytes); - if (!buffer) - return -ENOMEM; - ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); - if (ret) - goto fail; - if (num_bytes > inode_size) - num_bytes = inode_size; - ret = btrfs_insert_inline_extent(trans, root, objectid, - 0, buffer, num_bytes); - if (ret) - goto fail; - 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(&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(&data, data.first_block, 0, - last_block - data.first_block); - } -fail: - free(buffer); - return ret; -error: - fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); - return -1; -} - -static int ext2_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 = ext2_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; -} - -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; - } -} - -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 ext2_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; - } - strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX); - 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: - free(databuf); - return ret; -} - -static int ext2_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 = ext2_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 = ext2_copy_single_xattr(trans, root, objectid, - entry, data, datalen); - if (ret) - goto out; - entry = EXT2_EXT_ATTR_NEXT(entry); - } -out: - 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 void ext2_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_sequence(dst, 0); - btrfs_set_stack_inode_transid(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])); - } - } - memset(&dst->reserved, 0, sizeof(dst->reserved)); -} -static int ext2_check_state(struct btrfs_convert_context *cctx) -{ - ext2_filsys fs = cctx->fs_data; - - if (!(fs->super->s_state & EXT2_VALID_FS)) - return 1; - else if (fs->super->s_state & EXT2_ERROR_FS) - return 1; - else - return 0; -} - -/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */ -#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \ - if (ext2_inode->i_flags & EXT2_##name##_FL) \ - flags |= BTRFS_INODE_##name; \ -}) - -/* - * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags - * - * Only a subset of EXT_*_FL is supported in btrfs. - */ -static void ext2_convert_inode_flags(struct btrfs_inode_item *dst, - struct ext2_inode *src) -{ - u64 flags = 0; - - COPY_ONE_EXT2_FLAG(flags, src, APPEND); - COPY_ONE_EXT2_FLAG(flags, src, SYNC); - COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE); - COPY_ONE_EXT2_FLAG(flags, src, NODUMP); - COPY_ONE_EXT2_FLAG(flags, src, NOATIME); - COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC); - btrfs_set_stack_inode_flags(dst, flags); -} - -/* - * copy a single inode. do all the required works, such as cloning - * inode item, creating file extents and creating directory entries. - */ -static int ext2_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_inode_item btrfs_inode; - - if (ext2_inode->i_links_count == 0) - return 0; - - ext2_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); - } - ext2_convert_inode_flags(&btrfs_inode, ext2_inode); - - switch (ext2_inode->i_mode & S_IFMT) { - case S_IFREG: - ret = ext2_create_file_extents(trans, root, objectid, - &btrfs_inode, ext2_fs, ext2_ino, datacsum, packing); - break; - case S_IFDIR: - ret = ext2_create_dir_entries(trans, root, objectid, - &btrfs_inode, ext2_fs, ext2_ino); - break; - case S_IFLNK: - ret = ext2_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 = ext2_copy_extended_attrs(trans, root, objectid, - &btrfs_inode, ext2_fs, ext2_ino); - if (ret) - return ret; - } - return btrfs_insert_inode(trans, root, objectid, &btrfs_inode); -} - -/* - * scan ext2's inode bitmap and copy all used inodes. - */ -static int ext2_copy_inodes(struct btrfs_convert_context *cctx, - struct btrfs_root *root, - int datacsum, int packing, int noxattr, struct task_ctx *p) -{ - ext2_filsys ext2_fs = cctx->fs_data; - 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 = ext2_copy_single_inode(trans, root, - objectid, ext2_fs, ext2_ino, - &ext2_inode, datacsum, packing, - noxattr); - p->cur_copy_inodes++; - 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); - ext2fs_close_inode_scan(ext2_scan); - - return ret; -} - -static const struct btrfs_convert_operations ext2_convert_ops = { - .name = "ext2", - .open_fs = ext2_open_fs, - .read_used_space = ext2_read_used_space, - .copy_inodes = ext2_copy_inodes, - .close_fs = ext2_close_fs, - .check_state = ext2_check_state, -}; - -#endif +const struct btrfs_convert_operations ext2_convert_ops; static const struct btrfs_convert_operations *convert_operations[] = { #if BTRFSCONVERT_EXT2 diff --git a/convert/source-ext2.c b/convert/source-ext2.c new file mode 100644 index 00000000..f286d074 --- /dev/null +++ b/convert/source-ext2.c @@ -0,0 +1,870 @@ +/* + * 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. + */ + +#if BTRFSCONVERT_EXT2 + +#include "kerncompat.h" +#include "disk-io.h" +#include "transaction.h" +#include "utils.h" +#include "convert/common.h" +#include "convert/source-ext2.h" + +/* + * Open Ext2fs in readonly mode, read block allocation bitmap and + * inode bitmap into memory. + */ +static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) +{ + errcode_t ret; + ext2_filsys ext2_fs; + ext2_ino_t ino; + u32 ro_feature; + + ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); + if (ret) { + fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); + return -1; + } + /* + * We need to know exactly the used space, some RO compat flags like + * BIGALLOC will affect how used space is present. + * So we need manuall check any unsupported RO compat flags + */ + ro_feature = ext2_fs->super->s_feature_ro_compat; + if (ro_feature & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { + error( +"unsupported RO features detected: %x, abort convert to avoid possible corruption", + ro_feature & ~EXT2_LIB_FEATURE_COMPAT_SUPP); + 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); + } + + if (!(ext2_fs->super->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + error("filetype feature is missing"); + goto fail; + } + + cctx->fs_data = ext2_fs; + cctx->blocksize = ext2_fs->blocksize; + cctx->block_count = ext2_fs->super->s_blocks_count; + cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count; + cctx->volume_name = strndup(ext2_fs->super->s_volume_name, 16); + cctx->first_data_block = ext2_fs->super->s_first_data_block; + cctx->inodes_count = ext2_fs->super->s_inodes_count; + cctx->free_inodes_count = ext2_fs->super->s_free_inodes_count; + return 0; +fail: + ext2fs_close(ext2_fs); + return -1; +} + +static int __ext2_add_one_block(ext2_filsys fs, char *bitmap, + unsigned long group_nr, struct cache_tree *used) +{ + unsigned long offset; + unsigned i; + int ret = 0; + + offset = fs->super->s_first_data_block; + offset /= EXT2FS_CLUSTER_RATIO(fs); + offset += group_nr * EXT2_CLUSTERS_PER_GROUP(fs->super); + for (i = 0; i < EXT2_CLUSTERS_PER_GROUP(fs->super); i++) { + if ((i + offset) >= ext2fs_blocks_count(fs->super)) + break; + + if (ext2fs_test_bit(i, bitmap)) { + u64 start; + + start = (i + offset) * EXT2FS_CLUSTER_RATIO(fs); + start *= fs->blocksize; + ret = add_merge_cache_extent(used, start, + fs->blocksize); + if (ret < 0) + break; + } + } + return ret; +} + +/* + * Read all used ext2 space into cctx->used cache tree + */ +static int ext2_read_used_space(struct btrfs_convert_context *cctx) +{ + ext2_filsys fs = (ext2_filsys)cctx->fs_data; + blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); + struct cache_tree *used_tree = &cctx->used; + char *block_bitmap = NULL; + unsigned long i; + int block_nbytes; + int ret = 0; + + block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; + /* Shouldn't happen */ + BUG_ON(!fs->block_map); + + block_bitmap = malloc(block_nbytes); + if (!block_bitmap) + return -ENOMEM; + + for (i = 0; i < fs->group_desc_count; i++) { + ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr, + block_nbytes * 8, block_bitmap); + if (ret) { + error("fail to get bitmap from ext2, %s", + strerror(-ret)); + break; + } + ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree); + if (ret < 0) { + error("fail to build used space tree, %s", + strerror(-ret)); + break; + } + blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super); + } + + free(block_bitmap); + return ret; +} + +static void ext2_close_fs(struct btrfs_convert_context *cctx) +{ + if (cctx->volume_name) { + free(cctx->volume_name); + cctx->volume_name = NULL; + } + ext2fs_close(cctx->fs_data); +} + +static u8 ext2_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 ext2_dir_iterate_proc(ext2_ino_t dir, int entry, + struct ext2_dir_entry *dirent, + int offset, int blocksize, + char *buf,void *priv_data) +{ + int ret; + int file_type; + u64 objectid; + char dotdot[] = ".."; + struct dir_iterate_data *idata = (struct dir_iterate_data *)priv_data; + int name_len; + + name_len = dirent->name_len & 0xFF; + + objectid = dirent->inode + INO_OFFSET; + if (!strncmp(dirent->name, dotdot, name_len)) { + if (name_len == 2) { + BUG_ON(idata->parent != 0); + idata->parent = objectid; + } + return 0; + } + if (dirent->inode < EXT2_GOOD_OLD_FIRST_INO) + return 0; + + file_type = dirent->name_len >> 8; + BUG_ON(file_type > EXT2_FT_SYMLINK); + + ret = convert_insert_dirent(idata->trans, idata->root, dirent->name, + name_len, idata->objectid, objectid, + ext2_filetype_conversion_table[file_type], + idata->index_cnt, idata->inode); + if (ret < 0) { + idata->errcode = ret; + return BLOCK_ABORT; + } + + idata->index_cnt++; + return 0; +} + +static int ext2_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, + ext2_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 ext2_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; + ret = block_iterate_proc(*blocknr, blockcnt, idata); + if (ret) { + idata->errcode = ret; + return BLOCK_ABORT; + } + return 0; +} + +/* + * traverse file's data blocks, record these data blocks as file extents. + */ +static int ext2_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; + char *buffer = NULL; + errcode_t err; + u32 last_block; + u32 sectorsize = root->sectorsize; + u64 inode_size = btrfs_stack_inode_size(btrfs_inode); + 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, ext2_block_iterate_proc, &data); + if (err) + goto error; + ret = data.errcode; + if (ret) + goto fail; + 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; + u64 nbytes; + + buffer = malloc(num_bytes); + if (!buffer) + return -ENOMEM; + ret = read_disk_extent(root, disk_bytenr, num_bytes, buffer); + if (ret) + goto fail; + if (num_bytes > inode_size) + num_bytes = inode_size; + ret = btrfs_insert_inline_extent(trans, root, objectid, + 0, buffer, num_bytes); + if (ret) + goto fail; + 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(&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(&data, data.first_block, 0, + last_block - data.first_block); + } +fail: + free(buffer); + return ret; +error: + fprintf(stderr, "ext2fs_block_iterate2: %s\n", error_message(err)); + return -1; +} + +static int ext2_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 = ext2_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; +} + +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; + } +} + +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 ext2_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; + } + strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX); + 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: + free(databuf); + return ret; +} + +static int ext2_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 = ext2_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 = ext2_copy_single_xattr(trans, root, objectid, + entry, data, datalen); + if (ret) + goto out; + entry = EXT2_EXT_ATTR_NEXT(entry); + } +out: + 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 void ext2_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_sequence(dst, 0); + btrfs_set_stack_inode_transid(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])); + } + } + memset(&dst->reserved, 0, sizeof(dst->reserved)); +} +static int ext2_check_state(struct btrfs_convert_context *cctx) +{ + ext2_filsys fs = cctx->fs_data; + + if (!(fs->super->s_state & EXT2_VALID_FS)) + return 1; + else if (fs->super->s_state & EXT2_ERROR_FS) + return 1; + else + return 0; +} + +/* EXT2_*_FL to BTRFS_INODE_FLAG_* stringification helper */ +#define COPY_ONE_EXT2_FLAG(flags, ext2_inode, name) ({ \ + if (ext2_inode->i_flags & EXT2_##name##_FL) \ + flags |= BTRFS_INODE_##name; \ +}) + +/* + * Convert EXT2_*_FL to corresponding BTRFS_INODE_* flags + * + * Only a subset of EXT_*_FL is supported in btrfs. + */ +static void ext2_convert_inode_flags(struct btrfs_inode_item *dst, + struct ext2_inode *src) +{ + u64 flags = 0; + + COPY_ONE_EXT2_FLAG(flags, src, APPEND); + COPY_ONE_EXT2_FLAG(flags, src, SYNC); + COPY_ONE_EXT2_FLAG(flags, src, IMMUTABLE); + COPY_ONE_EXT2_FLAG(flags, src, NODUMP); + COPY_ONE_EXT2_FLAG(flags, src, NOATIME); + COPY_ONE_EXT2_FLAG(flags, src, DIRSYNC); + btrfs_set_stack_inode_flags(dst, flags); +} + +/* + * copy a single inode. do all the required works, such as cloning + * inode item, creating file extents and creating directory entries. + */ +static int ext2_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_inode_item btrfs_inode; + + if (ext2_inode->i_links_count == 0) + return 0; + + ext2_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); + } + ext2_convert_inode_flags(&btrfs_inode, ext2_inode); + + switch (ext2_inode->i_mode & S_IFMT) { + case S_IFREG: + ret = ext2_create_file_extents(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino, datacsum, packing); + break; + case S_IFDIR: + ret = ext2_create_dir_entries(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino); + break; + case S_IFLNK: + ret = ext2_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 = ext2_copy_extended_attrs(trans, root, objectid, + &btrfs_inode, ext2_fs, ext2_ino); + if (ret) + return ret; + } + return btrfs_insert_inode(trans, root, objectid, &btrfs_inode); +} + +/* + * scan ext2's inode bitmap and copy all used inodes. + */ +static int ext2_copy_inodes(struct btrfs_convert_context *cctx, + struct btrfs_root *root, + int datacsum, int packing, int noxattr, struct task_ctx *p) +{ + ext2_filsys ext2_fs = cctx->fs_data; + 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 = ext2_copy_single_inode(trans, root, + objectid, ext2_fs, ext2_ino, + &ext2_inode, datacsum, packing, + noxattr); + p->cur_copy_inodes++; + 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); + ext2fs_close_inode_scan(ext2_scan); + + return ret; +} + +const struct btrfs_convert_operations ext2_convert_ops = { + .name = "ext2", + .open_fs = ext2_open_fs, + .read_used_space = ext2_read_used_space, + .copy_inodes = ext2_copy_inodes, + .close_fs = ext2_close_fs, + .check_state = ext2_check_state, +}; + +#endif /* BTRFSCONVERT_EXT2 */ -- cgit v1.2.3 From 3a724cbde0f3e9ff94bff787a9185a030c7f7073 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 27 Jan 2017 15:47:17 +0100 Subject: btrfs-progs: convert: move implementation for interal conversion API to own file Signed-off-by: David Sterba --- convert/main.c | 246 ----------------------------------------------- convert/source-fs.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 246 deletions(-) create mode 100644 convert/source-fs.c (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 105c4c12..aa0d805b 100644 --- a/convert/main.c +++ b/convert/main.c @@ -70,20 +70,6 @@ static int after_copied_inodes(void *p) return 0; } -void init_convert_context(struct btrfs_convert_context *cctx) -{ - cache_tree_init(&cctx->used); - cache_tree_init(&cctx->data_chunks); - cache_tree_init(&cctx->free); -} - -void clean_convert_context(struct btrfs_convert_context *cctx) -{ - free_extent_cache_tree(&cctx->used); - free_extent_cache_tree(&cctx->data_chunks); - free_extent_cache_tree(&cctx->free); -} - static inline int copy_inodes(struct btrfs_convert_context *cctx, struct btrfs_root *root, int datacsum, int packing, int noxattr, struct task_ctx *p) @@ -102,67 +88,6 @@ static inline int convert_check_state(struct btrfs_convert_context *cctx) return cctx->convert_ops->check_state(cctx); } -static int intersect_with_sb(u64 bytenr, u64 num_bytes) -{ - int i; - u64 offset; - - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - offset = btrfs_sb_offset(i); - offset &= ~((u64)BTRFS_STRIPE_LEN - 1); - - if (bytenr < offset + BTRFS_STRIPE_LEN && - bytenr + num_bytes > offset) - return 1; - } - return 0; -} - -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; -} - -int read_disk_extent(struct btrfs_root *root, u64 bytenr, - u32 num_bytes, char *buffer) -{ - int ret; - struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; - - ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr); - if (ret != num_bytes) - goto fail; - ret = 0; -fail: - if (ret > 0) - ret = -1; - return ret; -} - static int csum_disk_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 disk_bytenr, u64 num_bytes) @@ -192,177 +117,6 @@ static int csum_disk_extent(struct btrfs_trans_handle *trans, return ret; } -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) -{ - struct btrfs_key key; - - 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; - - key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; - key.type = BTRFS_ROOT_ITEM_KEY; - key.offset = (u64)-1; - data->convert_root = btrfs_read_fs_root(root->fs_info, &key); - /* Impossible as we just opened it before */ - BUG_ON(!data->convert_root || IS_ERR(data->convert_root)); - data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1; -} - -/* - * Record a file extent in original filesystem into btrfs one. - * The special point is, old disk_block can point to a reserved range. - * So here, we don't use disk_block directly but search convert_root - * to get the real disk_bytenr. - */ -int record_file_blocks(struct blk_iterate_data *data, - u64 file_block, u64 disk_block, u64 num_blocks) -{ - int ret = 0; - struct btrfs_root *root = data->root; - struct btrfs_root *convert_root = data->convert_root; - struct btrfs_path path; - u64 file_pos = file_block * root->sectorsize; - u64 old_disk_bytenr = disk_block * root->sectorsize; - u64 num_bytes = num_blocks * root->sectorsize; - u64 cur_off = old_disk_bytenr; - - /* Hole, pass it to record_file_extent directly */ - if (old_disk_bytenr == 0) - return btrfs_record_file_extent(data->trans, root, - data->objectid, data->inode, file_pos, 0, - num_bytes); - - btrfs_init_path(&path); - - /* - * Search real disk bytenr from convert root - */ - while (cur_off < old_disk_bytenr + num_bytes) { - struct btrfs_key key; - struct btrfs_file_extent_item *fi; - struct extent_buffer *node; - int slot; - u64 extent_disk_bytenr; - u64 extent_num_bytes; - u64 real_disk_bytenr; - u64 cur_len; - - key.objectid = data->convert_ino; - key.type = BTRFS_EXTENT_DATA_KEY; - key.offset = cur_off; - - ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0); - if (ret < 0) - break; - if (ret > 0) { - ret = btrfs_previous_item(convert_root, &path, - data->convert_ino, - BTRFS_EXTENT_DATA_KEY); - if (ret < 0) - break; - if (ret > 0) { - ret = -ENOENT; - break; - } - } - node = path.nodes[0]; - slot = path.slots[0]; - btrfs_item_key_to_cpu(node, &key, slot); - BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY || - key.objectid != data->convert_ino || - key.offset > cur_off); - fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); - extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); - extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); - BUG_ON(cur_off - key.offset >= extent_num_bytes); - btrfs_release_path(&path); - - if (extent_disk_bytenr) - real_disk_bytenr = cur_off - key.offset + - extent_disk_bytenr; - else - real_disk_bytenr = 0; - cur_len = min(key.offset + extent_num_bytes, - old_disk_bytenr + num_bytes) - cur_off; - ret = btrfs_record_file_extent(data->trans, data->root, - data->objectid, data->inode, file_pos, - real_disk_bytenr, cur_len); - if (ret < 0) - break; - cur_off += cur_len; - file_pos += cur_len; - - /* - * No need to care about csum - * As every byte of old fs image is calculated for csum, no - * need to waste CPU cycles now. - */ - } - btrfs_release_path(&path); - return ret; -} - -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_block_group_cache *cache; - u64 bytenr = disk_block * root->sectorsize; - - sb_region = intersect_with_sb(bytenr, root->sectorsize); - do_barrier = sb_region || disk_block >= idata->boundary; - if ((idata->num_blocks > 0 && do_barrier) || - (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(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(idata, idata->first_block, - 0, file_block - idata->first_block); - if (ret) - goto fail; - } - - if (sb_region) { - bytenr += BTRFS_STRIPE_LEN - 1; - bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1); - } else { - cache = btrfs_lookup_block_group(root->fs_info, bytenr); - BUG_ON(!cache); - bytenr = cache->key.objectid + cache->key.offset; - } - - idata->first_block = file_block; - idata->disk_block = disk_block; - idata->boundary = bytenr / root->sectorsize; - } - idata->num_blocks++; -fail: - return ret; -} - static int create_image_file_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct cache_tree *used, diff --git a/convert/source-fs.c b/convert/source-fs.c new file mode 100644 index 00000000..f2952777 --- /dev/null +++ b/convert/source-fs.c @@ -0,0 +1,270 @@ +/* + * 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 "kerncompat.h" +#include +#include "internal.h" +#include "disk-io.h" +#include "volumes.h" +#include "convert/common.h" +#include "convert/source-fs.h" + +static int intersect_with_sb(u64 bytenr, u64 num_bytes) +{ + int i; + u64 offset; + + for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { + offset = btrfs_sb_offset(i); + offset &= ~((u64)BTRFS_STRIPE_LEN - 1); + + if (bytenr < offset + BTRFS_STRIPE_LEN && + bytenr + num_bytes > offset) + return 1; + } + return 0; +} + +void init_convert_context(struct btrfs_convert_context *cctx) +{ + cache_tree_init(&cctx->used); + cache_tree_init(&cctx->data_chunks); + cache_tree_init(&cctx->free); +} + +void clean_convert_context(struct btrfs_convert_context *cctx) +{ + free_extent_cache_tree(&cctx->used); + free_extent_cache_tree(&cctx->data_chunks); + free_extent_cache_tree(&cctx->free); +} + +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_block_group_cache *cache; + u64 bytenr = disk_block * root->sectorsize; + + sb_region = intersect_with_sb(bytenr, root->sectorsize); + do_barrier = sb_region || disk_block >= idata->boundary; + if ((idata->num_blocks > 0 && do_barrier) || + (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(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(idata, idata->first_block, + 0, file_block - idata->first_block); + if (ret) + goto fail; + } + + if (sb_region) { + bytenr += BTRFS_STRIPE_LEN - 1; + bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1); + } else { + cache = btrfs_lookup_block_group(root->fs_info, bytenr); + BUG_ON(!cache); + bytenr = cache->key.objectid + cache->key.offset; + } + + idata->first_block = file_block; + idata->disk_block = disk_block; + idata->boundary = bytenr / root->sectorsize; + } + idata->num_blocks++; +fail: + return ret; +} + +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) +{ + struct btrfs_key key; + + 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; + + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + data->convert_root = btrfs_read_fs_root(root->fs_info, &key); + /* Impossible as we just opened it before */ + BUG_ON(!data->convert_root || IS_ERR(data->convert_root)); + data->convert_ino = BTRFS_FIRST_FREE_OBJECTID + 1; +} + +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; +} + +int read_disk_extent(struct btrfs_root *root, u64 bytenr, + u32 num_bytes, char *buffer) +{ + int ret; + struct btrfs_fs_devices *fs_devs = root->fs_info->fs_devices; + + ret = pread(fs_devs->latest_bdev, buffer, num_bytes, bytenr); + if (ret != num_bytes) + goto fail; + ret = 0; +fail: + if (ret > 0) + ret = -1; + return ret; +} + +/* + * Record a file extent in original filesystem into btrfs one. + * The special point is, old disk_block can point to a reserved range. + * So here, we don't use disk_block directly but search convert_root + * to get the real disk_bytenr. + */ +int record_file_blocks(struct blk_iterate_data *data, + u64 file_block, u64 disk_block, u64 num_blocks) +{ + int ret = 0; + struct btrfs_root *root = data->root; + struct btrfs_root *convert_root = data->convert_root; + struct btrfs_path path; + u64 file_pos = file_block * root->sectorsize; + u64 old_disk_bytenr = disk_block * root->sectorsize; + u64 num_bytes = num_blocks * root->sectorsize; + u64 cur_off = old_disk_bytenr; + + /* Hole, pass it to record_file_extent directly */ + if (old_disk_bytenr == 0) + return btrfs_record_file_extent(data->trans, root, + data->objectid, data->inode, file_pos, 0, + num_bytes); + + btrfs_init_path(&path); + + /* + * Search real disk bytenr from convert root + */ + while (cur_off < old_disk_bytenr + num_bytes) { + struct btrfs_key key; + struct btrfs_file_extent_item *fi; + struct extent_buffer *node; + int slot; + u64 extent_disk_bytenr; + u64 extent_num_bytes; + u64 real_disk_bytenr; + u64 cur_len; + + key.objectid = data->convert_ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = cur_off; + + ret = btrfs_search_slot(NULL, convert_root, &key, &path, 0, 0); + if (ret < 0) + break; + if (ret > 0) { + ret = btrfs_previous_item(convert_root, &path, + data->convert_ino, + BTRFS_EXTENT_DATA_KEY); + if (ret < 0) + break; + if (ret > 0) { + ret = -ENOENT; + break; + } + } + node = path.nodes[0]; + slot = path.slots[0]; + btrfs_item_key_to_cpu(node, &key, slot); + BUG_ON(key.type != BTRFS_EXTENT_DATA_KEY || + key.objectid != data->convert_ino || + key.offset > cur_off); + fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item); + extent_disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi); + extent_num_bytes = btrfs_file_extent_num_bytes(node, fi); + BUG_ON(cur_off - key.offset >= extent_num_bytes); + btrfs_release_path(&path); + + if (extent_disk_bytenr) + real_disk_bytenr = cur_off - key.offset + + extent_disk_bytenr; + else + real_disk_bytenr = 0; + cur_len = min(key.offset + extent_num_bytes, + old_disk_bytenr + num_bytes) - cur_off; + ret = btrfs_record_file_extent(data->trans, data->root, + data->objectid, data->inode, file_pos, + real_disk_bytenr, cur_len); + if (ret < 0) + break; + cur_off += cur_len; + file_pos += cur_len; + + /* + * No need to care about csum + * As every byte of old fs image is calculated for csum, no + * need to waste CPU cycles now. + */ + } + btrfs_release_path(&path); + return ret; +} + -- cgit v1.2.3 From 0a5d16eca7167af06c896df64a2d301a6af4c6b4 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 10:11:22 +0100 Subject: btrfs-progs: mkfs: make list of source fs more visible Signed-off-by: David Sterba --- convert/main.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index aa0d805b..2f6ec428 100644 --- a/convert/main.c +++ b/convert/main.c @@ -43,6 +43,14 @@ #include "convert/source-fs.h" #include "fsfeatures.h" +const struct btrfs_convert_operations ext2_convert_ops; + +static const struct btrfs_convert_operations *convert_operations[] = { +#if BTRFSCONVERT_EXT2 + &ext2_convert_ops, +#endif +}; + static void *print_copied_inodes(void *p) { struct task_ctx *priv = p; @@ -1145,14 +1153,6 @@ static int prepare_system_chunk_sb(struct btrfs_super_block *super) return 0; } -const struct btrfs_convert_operations ext2_convert_ops; - -static const struct btrfs_convert_operations *convert_operations[] = { -#if BTRFSCONVERT_EXT2 - &ext2_convert_ops, -#endif -}; - static int convert_open_fs(const char *devname, struct btrfs_convert_context *cctx) { -- cgit v1.2.3 From c0acc29c990cbf6e1ea0a2d7565984b0318584a2 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 10:33:01 +0100 Subject: btrfs-progs: convert: use wider types types for inode counts for progress reports Signed-off-by: David Sterba --- convert/main.c | 9 +++++---- convert/source-fs.h | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 2f6ec428..5a2da312 100644 --- a/convert/main.c +++ b/convert/main.c @@ -55,14 +55,15 @@ static void *print_copied_inodes(void *p) { struct task_ctx *priv = p; const char work_indicator[] = { '.', 'o', 'O', 'o' }; - uint32_t count = 0; + u64 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); + printf("copy inodes [%c] [%10llu/%10llu]\r", + work_indicator[count % 4], + (unsigned long long)priv->cur_copy_inodes, + (unsigned long long)priv->max_copy_inodes); fflush(stdout); task_period_wait(priv->info); } diff --git a/convert/source-fs.h b/convert/source-fs.h index b6a159e5..41d1153d 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -22,8 +22,8 @@ #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID struct task_ctx { - uint32_t max_copy_inodes; - uint32_t cur_copy_inodes; + u64 max_copy_inodes; + u64 cur_copy_inodes; struct task_info *info; }; -- cgit v1.2.3 From f989881ccd464455f20bfbbe9d32e5a3d13b6360 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 10:36:16 +0100 Subject: btrfs-progs: convert: update some forward declarations Add missing and reorder so they come next to the structures using them. Signed-off-by: David Sterba --- convert/source-fs.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/source-fs.h b/convert/source-fs.h index 41d1153d..d074eada 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -21,6 +21,8 @@ #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID +struct task_info; + struct task_ctx { u64 max_copy_inodes; u64 cur_copy_inodes; @@ -28,9 +30,6 @@ struct task_ctx { }; struct btrfs_convert_context; -struct btrfs_root; -struct btrfs_trans_handle; -struct btrfs_inode_item; struct btrfs_convert_operations { const char *name; @@ -43,6 +42,10 @@ struct btrfs_convert_operations { int (*check_state)(struct btrfs_convert_context *cctx); }; +struct btrfs_trans_handle; +struct btrfs_root; +struct btrfs_inode_item; + struct blk_iterate_data { struct btrfs_trans_handle *trans; struct btrfs_root *root; -- cgit v1.2.3 From cf83a41a02e2f1f86f6b3c10abe0bf051a151c62 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 13:41:36 +0100 Subject: btrfs-progs: convert: move struct initialization to the init function The context is zeroed in convert_open_fs after and overwrites the rbtree initialization, which accidentally is the same (NULL). Signed-off-by: David Sterba --- convert/main.c | 2 -- convert/source-fs.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 5a2da312..b0d72d3e 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1159,8 +1159,6 @@ static int convert_open_fs(const char *devname, { int i; - memset(cctx, 0, sizeof(*cctx)); - for (i = 0; i < ARRAY_SIZE(convert_operations); i++) { int ret = convert_operations[i]->open_fs(cctx, devname); diff --git a/convert/source-fs.c b/convert/source-fs.c index f2952777..e87b861c 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -40,6 +40,8 @@ static int intersect_with_sb(u64 bytenr, u64 num_bytes) void init_convert_context(struct btrfs_convert_context *cctx) { + memset(cctx, 0, sizeof(*cctx)); + cache_tree_init(&cctx->used); cache_tree_init(&cctx->data_chunks); cache_tree_init(&cctx->free); -- cgit v1.2.3 From 6b74f2fbe308cc2c57fc1199395e06e1932ed9af Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 14:11:19 +0100 Subject: btrfs-progs: convert: use fixed lenght array for source fs name Signed-off-by: David Sterba --- convert/source-fs.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-fs.h b/convert/source-fs.h index d074eada..3ee429af 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -31,8 +31,10 @@ struct task_ctx { struct btrfs_convert_context; +#define SOURCE_FS_NAME_LEN (16) + struct btrfs_convert_operations { - const char *name; + const char name[SOURCE_FS_NAME_LEN]; int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); int (*read_used_space)(struct btrfs_convert_context *cctx); int (*copy_inodes)(struct btrfs_convert_context *cctx, -- cgit v1.2.3 From 4927a9b268710e219bb1bd62ae74340f5ee3891c Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 14:20:30 +0100 Subject: btrfs-progs: convert: use on-stack buffer for subvol name dir Get rid of dynamic allocation. Signed-off-by: David Sterba --- convert/main.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index b0d72d3e..5edb297e 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1184,7 +1184,7 @@ static int do_convert(const char *devname, int datacsum, int packing, struct btrfs_root *image_root; struct btrfs_convert_context cctx; struct btrfs_key key; - char *subvol_name = NULL; + char subvol_name[SOURCE_FS_NAME_LEN + 8]; struct task_ctx ctx; char features_buf[64]; struct btrfs_mkfs_config mkfs_cfg; @@ -1252,12 +1252,8 @@ static int do_convert(const char *devname, int datacsum, int packing, } printf("creating %s image file\n", cctx.convert_ops->name); - ret = asprintf(&subvol_name, "%s_saved", cctx.convert_ops->name); - if (ret < 0) { - error("memory allocation failure for subvolume name: %s_saved", + snprintf(subvol_name, sizeof(subvol_name), "%s_saved", cctx.convert_ops->name); - goto fail; - } key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; key.offset = (u64)-1; key.type = BTRFS_ROOT_ITEM_KEY; @@ -1298,8 +1294,6 @@ static int do_convert(const char *devname, int datacsum, int packing, goto fail; } - free(subvol_name); - memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE); if (copylabel == 1) { __strncpy_null(root->fs_info->super_copy->label, -- cgit v1.2.3 From 5bcff79f14c4fd13bd4d4fef6cab2ef99e8b3f25 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 16:52:07 +0100 Subject: btrfs-progs: convert: remove unused includes Signed-off-by: David Sterba --- convert/main.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 5edb297e..b2b24ae4 100644 --- a/convert/main.c +++ b/convert/main.c @@ -18,23 +18,17 @@ #include "kerncompat.h" -#include -#include #include #include #include -#include #include #include -#include -#include #include #include "ctree.h" #include "disk-io.h" #include "volumes.h" #include "transaction.h" -#include "crc32c.h" #include "utils.h" #include "task-utils.h" #include "help.h" -- cgit v1.2.3 From 9368a774560c8c913c46f7cdf36bb11da5688338 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 16:58:57 +0100 Subject: btrfs-progs: convert: better error handling in ext2_read_used_space Signed-off-by: David Sterba --- convert/source-ext2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index f286d074..29222a37 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -136,8 +136,11 @@ static int ext2_read_used_space(struct btrfs_convert_context *cctx) int ret = 0; block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; - /* Shouldn't happen */ - BUG_ON(!fs->block_map); + if (!block_nbytes) { + error("EXT2_CLUSTERS_PER_GROUP too small: %llu", + (unsigned long long)(EXT2_CLUSTERS_PER_GROUP(fs->super))); + return -EINVAL; + } block_bitmap = malloc(block_nbytes); if (!block_bitmap) -- cgit v1.2.3 From c45a060f3e2d7a46935f92397bf561cd56d3127f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 17:14:03 +0100 Subject: btrfs-progs: convert: use helper for special inode number check Signed-off-by: David Sterba --- convert/source-ext2.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 29222a37..fb93fc77 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -802,6 +802,13 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, return btrfs_insert_inode(trans, root, objectid, &btrfs_inode); } +static int ext2_is_special_inode(ext2_ino_t ino) +{ + if (ino < EXT2_GOOD_OLD_FIRST_INO && ino != EXT2_ROOT_INO) + return 1; + return 0; +} + /* * scan ext2's inode bitmap and copy all used inodes. */ @@ -831,9 +838,7 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, /* 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) + if (ext2_is_special_inode(ext2_ino)) continue; objectid = ext2_ino + INO_OFFSET; ret = ext2_copy_single_inode(trans, root, -- cgit v1.2.3 From 0c362b9031e5c0441f40ef3535572505d52006f2 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 17:52:15 +0100 Subject: btrfs-progs: convert: use bit field for convert flags Use one flag field instead of several variables. The change cascades down to the callchain and modifies several functions. Signed-off-by: David Sterba --- convert/main.c | 65 +++++++++++++++++++++++++++------------------------ convert/source-ext2.c | 27 +++++++++++---------- convert/source-fs.h | 10 ++++++-- 3 files changed, 58 insertions(+), 44 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index b2b24ae4..f5c560c4 100644 --- a/convert/main.c +++ b/convert/main.c @@ -74,11 +74,10 @@ static int after_copied_inodes(void *p) } static inline int copy_inodes(struct btrfs_convert_context *cctx, - struct btrfs_root *root, int datacsum, - int packing, int noxattr, struct task_ctx *p) + struct btrfs_root *root, u32 convert_flags, + struct task_ctx *p) { - return cctx->convert_ops->copy_inodes(cctx, root, datacsum, packing, - noxattr, p); + return cctx->convert_ops->copy_inodes(cctx, root, convert_flags, p); } static inline void convert_close_fs(struct btrfs_convert_context *cctx) @@ -125,7 +124,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, struct cache_tree *used, struct btrfs_inode_item *inode, u64 ino, u64 bytenr, u64 *ret_len, - int datacsum) + u32 convert_flags) { struct cache_extent *cache; struct btrfs_block_group_cache *bg_cache; @@ -133,6 +132,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, u64 disk_bytenr; int i; int ret; + u32 datacsum = convert_flags & CONVERT_FLAG_DATACSUM; if (bytenr != round_down(bytenr, root->sectorsize)) { error("bytenr not sectorsize aligned: %llu", @@ -259,7 +259,8 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct cache_tree *used, struct btrfs_inode_item *inode, int fd, - u64 ino, u64 start, u64 len, int datacsum) + u64 ino, u64 start, u64 len, + u32 convert_flags) { u64 cur_off = start; u64 cur_len = len; @@ -312,7 +313,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, if (ret < 0) break; /* Finally, insert csum items */ - if (datacsum) + if (convert_flags & CONVERT_FLAG_DATACSUM) ret = csum_disk_extent(trans, root, key.objectid, key.offset); @@ -346,7 +347,7 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct cache_tree *used, struct btrfs_inode_item *inode, int fd, - u64 ino, u64 total_bytes, int datacsum) + u64 ino, u64 total_bytes, u32 convert_flags) { u64 cur_off; u64 cur_len; @@ -356,7 +357,7 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, cur_off = 0; cur_len = 1024 * 1024; ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, datacsum); + cur_off, cur_len, convert_flags); if (ret < 0) return ret; @@ -366,7 +367,7 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, if (cur_off > total_bytes) return ret; ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, datacsum); + cur_off, cur_len, convert_flags); if (ret < 0) return ret; @@ -376,7 +377,7 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, if (cur_off > total_bytes) return ret; ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, datacsum); + cur_off, cur_len, convert_flags); return ret; } @@ -670,7 +671,7 @@ static int convert_read_used_space(struct btrfs_convert_context *cctx) static int create_image(struct btrfs_root *root, struct btrfs_mkfs_config *cfg, struct btrfs_convert_context *cctx, int fd, - u64 size, char *name, int datacsum) + u64 size, char *name, u32 convert_flags) { struct btrfs_inode_item buf; struct btrfs_trans_handle *trans; @@ -683,7 +684,7 @@ static int create_image(struct btrfs_root *root, u64 flags = BTRFS_INODE_READONLY; int ret; - if (!datacsum) + if (!(convert_flags & CONVERT_FLAG_DATACSUM)) flags |= BTRFS_INODE_NODATASUM; trans = btrfs_start_transaction(root, 1); @@ -745,15 +746,15 @@ static int create_image(struct btrfs_root *root, u64 len = size - cur; ret = create_image_file_range(trans, root, &used_tmp, - &buf, ino, cur, &len, datacsum); + &buf, ino, cur, &len, + convert_flags); if (ret < 0) goto out; cur += len; } /* Handle the reserved ranges */ ret = migrate_reserved_ranges(trans, root, &cctx->used, &buf, fd, ino, - cfg->num_bytes, datacsum); - + cfg->num_bytes, convert_flags); key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; @@ -997,8 +998,7 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, * But the convert image subvolume is *NOT* linked to fs tree yet. */ static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root, - struct btrfs_convert_context *cctx, int datacsum, - int packing, int noxattr) + struct btrfs_convert_context *cctx, u32 convert_flags) { struct btrfs_key location; struct btrfs_trans_handle *trans; @@ -1166,9 +1166,8 @@ static int convert_open_fs(const char *devname, return -1; } -static int do_convert(const char *devname, int datacsum, int packing, - int noxattr, u32 nodesize, int copylabel, const char *fslabel, - int progress, u64 features) +static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, + const char *fslabel, int progress, u64 features) { int ret; int fd = -1; @@ -1239,7 +1238,7 @@ static int do_convert(const char *devname, int datacsum, int packing, error("unable to open ctree"); goto fail; } - ret = init_btrfs(&mkfs_cfg, root, &cctx, datacsum, packing, noxattr); + ret = init_btrfs(&mkfs_cfg, root, &cctx, convert_flags); if (ret) { error("unable to setup the root tree: %d", ret); goto fail; @@ -1257,7 +1256,8 @@ static int do_convert(const char *devname, int datacsum, int packing, goto fail; } ret = create_image(image_root, &mkfs_cfg, &cctx, fd, - mkfs_cfg.num_bytes, "image", datacsum); + mkfs_cfg.num_bytes, "image", + convert_flags); if (ret) { error("failed to create %s/image: %d", subvol_name, ret); goto fail; @@ -1272,7 +1272,7 @@ static int do_convert(const char *devname, int datacsum, int packing, &ctx); task_start(ctx.info); } - ret = copy_inodes(&cctx, root, datacsum, packing, noxattr, &ctx); + ret = copy_inodes(&cctx, root, convert_flags, &ctx); if (ret) { error("error during copy_inodes %d", ret); goto fail; @@ -1289,11 +1289,11 @@ static int do_convert(const char *devname, int datacsum, int packing, } memset(root->fs_info->super_copy->label, 0, BTRFS_LABEL_SIZE); - if (copylabel == 1) { + if (convert_flags & CONVERT_FLAG_COPY_LABEL) { __strncpy_null(root->fs_info->super_copy->label, cctx.volume_name, BTRFS_LABEL_SIZE - 1); printf("copy label '%s'\n", root->fs_info->super_copy->label); - } else if (copylabel == -1) { + } else if (convert_flags & CONVERT_FLAG_SET_LABEL) { strcpy(root->fs_info->super_copy->label, fslabel); printf("set label to '%s'\n", fslabel); } @@ -1886,7 +1886,7 @@ int main(int argc, char *argv[]) rollback = 1; break; case 'l': - copylabel = -1; + copylabel = CONVERT_FLAG_SET_LABEL; if (strlen(optarg) >= BTRFS_LABEL_SIZE) { warning( "label too long, trimmed to %d bytes", @@ -1895,7 +1895,7 @@ int main(int argc, char *argv[]) __strncpy_null(fslabel, optarg, BTRFS_LABEL_SIZE - 1); break; case 'L': - copylabel = 1; + copylabel = CONVERT_FLAG_COPY_LABEL; break; case 'p': progress = 1; @@ -1968,8 +1968,13 @@ int main(int argc, char *argv[]) if (rollback) { ret = do_rollback(file); } else { - ret = do_convert(file, datacsum, packing, noxattr, nodesize, - copylabel, fslabel, progress, features); + u32 cf = 0; + + cf |= datacsum ? CONVERT_FLAG_DATACSUM : 0; + cf |= packing ? CONVERT_FLAG_INLINE_DATA : 0; + cf |= noxattr ? 0 : CONVERT_FLAG_XATTR; + cf |= copylabel; + ret = do_convert(file, cf, nodesize, fslabel, progress, features); } if (ret) return 1; diff --git a/convert/source-ext2.c b/convert/source-ext2.c index fb93fc77..cfafb3bf 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -282,7 +282,7 @@ static int ext2_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) + u32 convert_flags) { int ret; char *buffer = NULL; @@ -293,7 +293,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, struct blk_iterate_data data; init_blk_iterate_data(&data, trans, root, btrfs_inode, objectid, - datacsum); + convert_flags & CONVERT_FLAG_DATACSUM); err = ext2fs_block_iterate2(ext2_fs, ext2_ino, BLOCK_FLAG_DATA_ONLY, NULL, ext2_block_iterate_proc, &data); @@ -302,8 +302,9 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, ret = data.errcode; if (ret) goto fail; - if (packing && data.first_block == 0 && data.num_blocks > 0 && - inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { + if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && 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; u64 nbytes; @@ -354,7 +355,9 @@ static int ext2_create_symbol_link(struct btrfs_trans_handle *trans, if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) { btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1); ret = ext2_create_file_extents(trans, root, objectid, - btrfs_inode, ext2_fs, ext2_ino, 1, 1); + btrfs_inode, ext2_fs, ext2_ino, + CONVERT_FLAG_DATACSUM | + CONVERT_FLAG_INLINE_DATA); btrfs_set_stack_inode_size(btrfs_inode, inode_size); return ret; } @@ -757,7 +760,7 @@ static int ext2_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) + u32 convert_flags) { int ret; struct btrfs_inode_item btrfs_inode; @@ -766,7 +769,8 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, return 0; ext2_copy_inode_item(&btrfs_inode, ext2_inode, ext2_fs->blocksize); - if (!datacsum && S_ISREG(ext2_inode->i_mode)) { + if (!(convert_flags & CONVERT_FLAG_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); @@ -776,7 +780,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, switch (ext2_inode->i_mode & S_IFMT) { case S_IFREG: ret = ext2_create_file_extents(trans, root, objectid, - &btrfs_inode, ext2_fs, ext2_ino, datacsum, packing); + &btrfs_inode, ext2_fs, ext2_ino, convert_flags); break; case S_IFDIR: ret = ext2_create_dir_entries(trans, root, objectid, @@ -793,7 +797,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, if (ret) return ret; - if (!noxattr) { + if (convert_flags & CONVERT_FLAG_XATTR) { ret = ext2_copy_extended_attrs(trans, root, objectid, &btrfs_inode, ext2_fs, ext2_ino); if (ret) @@ -814,7 +818,7 @@ static int ext2_is_special_inode(ext2_ino_t ino) */ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, struct btrfs_root *root, - int datacsum, int packing, int noxattr, struct task_ctx *p) + u32 convert_flags, struct task_ctx *p) { ext2_filsys ext2_fs = cctx->fs_data; int ret; @@ -843,8 +847,7 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, objectid = ext2_ino + INO_OFFSET; ret = ext2_copy_single_inode(trans, root, objectid, ext2_fs, ext2_ino, - &ext2_inode, datacsum, packing, - noxattr); + &ext2_inode, convert_flags); p->cur_copy_inodes++; if (ret) return ret; diff --git a/convert/source-fs.h b/convert/source-fs.h index 3ee429af..f3f96d07 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -33,13 +33,19 @@ struct btrfs_convert_context; #define SOURCE_FS_NAME_LEN (16) +#define CONVERT_FLAG_DATACSUM (1U << 0) +#define CONVERT_FLAG_INLINE_DATA (1U << 1) +#define CONVERT_FLAG_XATTR (1U << 2) +#define CONVERT_FLAG_COPY_LABEL (1U << 3) +#define CONVERT_FLAG_SET_LABEL (1U << 4) + struct btrfs_convert_operations { const char name[SOURCE_FS_NAME_LEN]; int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); int (*read_used_space)(struct btrfs_convert_context *cctx); int (*copy_inodes)(struct btrfs_convert_context *cctx, - struct btrfs_root *root, int datacsum, - int packing, int noxattr, struct task_ctx *p); + struct btrfs_root *root, u32 covert_flags, + struct task_ctx *p); void (*close_fs)(struct btrfs_convert_context *cctx); int (*check_state)(struct btrfs_convert_context *cctx); }; -- cgit v1.2.3 From f6dfc0263dff83c28fb4af7de8876722eb2202ac Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 30 Jan 2017 18:00:39 +0100 Subject: btrfs-progs: build: add stub makefile to convert A shortcut to compile btrfs-convert from inside the directory. Signed-off-by: David Sterba --- convert/Makefile | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 convert/Makefile (limited to 'convert') diff --git a/convert/Makefile b/convert/Makefile new file mode 100644 index 00000000..66dd7d0b --- /dev/null +++ b/convert/Makefile @@ -0,0 +1,7 @@ +include ../Makefile.inc + +all: + $(MAKE) -C .. btrfs-convert + +clean: + -$(RM) -f -- *.o *.o.d -- cgit v1.2.3 From 39aa42147a2431fb7f5425dfc3446088d97e8352 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 31 Jan 2017 11:08:09 +0100 Subject: btrfs-progs: convert: rename ext2 function to create a symlink Signed-off-by: David Sterba --- convert/source-ext2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index cfafb3bf..96ac95da 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -343,7 +343,7 @@ error: return -1; } -static int ext2_create_symbol_link(struct btrfs_trans_handle *trans, +static int ext2_create_symlink(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, @@ -787,7 +787,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans, &btrfs_inode, ext2_fs, ext2_ino); break; case S_IFLNK: - ret = ext2_create_symbol_link(trans, root, objectid, + ret = ext2_create_symlink(trans, root, objectid, &btrfs_inode, ext2_fs, ext2_ino, ext2_inode); break; default: -- cgit v1.2.3 From 1f739d73e38a44a51f27e3b84ff50a9b08fcc399 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 1 Feb 2017 11:42:01 +0100 Subject: btrfs-progs: convert: move internal bg size definition Signed-off-by: David Sterba --- convert/common.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 7a9b5962..999e0c27 100644 --- a/convert/common.c +++ b/convert/common.c @@ -22,6 +22,8 @@ #include "mkfs/common.h" #include "convert/common.h" +#define BTRFS_CONVERT_META_GROUP_SIZE SZ_32M + /* * Reserve space from free_tree. * The algorithm is very simple, find the first cache_extent with enough space -- cgit v1.2.3 From e945e27712782de2dab3a28bc0a813e72c9f684f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 1 Feb 2017 14:15:08 +0100 Subject: btrfs-progs: mkfs: clear whole mkfs_cfg at once Zero out the structure and set only the requested fields. Signed-off-by: David Sterba --- convert/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index f5c560c4..42b69df3 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1216,15 +1216,13 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, printf("\tnodesize: %u\n", nodesize); printf("\tfeatures: %s\n", features_buf); + memset(&mkfs_cfg, 0, sizeof(mkfs_cfg)); mkfs_cfg.label = cctx.volume_name; mkfs_cfg.num_bytes = total_bytes; mkfs_cfg.nodesize = nodesize; mkfs_cfg.sectorsize = blocksize; mkfs_cfg.stripesize = blocksize; mkfs_cfg.features = features; - /* New convert need these space */ - memset(mkfs_cfg.chunk_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); - memset(mkfs_cfg.fs_uuid, 0, BTRFS_UUID_UNPARSED_SIZE); ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx); if (ret) { -- cgit v1.2.3 From 52319450e70828181d1dd92f9cca1bc38611038e Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 1 Feb 2017 15:06:04 +0100 Subject: btrfs-progs: convert: rename members that clash with other functions Rename 'free' and also 'used' for consistency. Signed-off-by: David Sterba --- convert/common.c | 12 ++++++------ convert/common.h | 4 ++-- convert/main.c | 10 +++++----- convert/source-ext2.c | 2 +- convert/source-fs.c | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 999e0c27..d37d2b26 100644 --- a/convert/common.c +++ b/convert/common.c @@ -782,8 +782,8 @@ out: int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, struct btrfs_convert_context *cctx) { - struct cache_tree *free = &cctx->free; - struct cache_tree *used = &cctx->used; + struct cache_tree *free_space = &cctx->free_space; + struct cache_tree *used_space = &cctx->used_space; u64 sys_chunk_start; u64 meta_chunk_start; /* chunk tree bytenr, in system chunk */ @@ -797,14 +797,14 @@ int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, int ret; /* Shouldn't happen */ - BUG_ON(cache_tree_empty(used)); + BUG_ON(cache_tree_empty(used_space)); /* * reserve space for temporary superblock first * Here we allocate a little larger space, to keep later * free space will be STRIPE_LEN aligned */ - ret = reserve_free_space(free, BTRFS_STRIPE_LEN, + ret = reserve_free_space(free_space, BTRFS_STRIPE_LEN, &cfg->super_bytenr); if (ret < 0) goto out; @@ -815,11 +815,11 @@ int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, * If using current 4M, it can only handle less than one TB for * worst case and then run out of sys space. */ - ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE, + ret = reserve_free_space(free_space, BTRFS_MKFS_SYSTEM_GROUP_SIZE, &sys_chunk_start); if (ret < 0) goto out; - ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE, + ret = reserve_free_space(free_space, BTRFS_CONVERT_META_GROUP_SIZE, &meta_chunk_start); if (ret < 0) goto out; diff --git a/convert/common.h b/convert/common.h index 55a65547..0d3adeaa 100644 --- a/convert/common.h +++ b/convert/common.h @@ -39,13 +39,13 @@ struct btrfs_convert_context { const struct btrfs_convert_operations *convert_ops; /* The accurate used space of old filesystem */ - struct cache_tree used; + struct cache_tree used_space; /* Batched ranges which must be covered by data chunks */ struct cache_tree data_chunks; /* Free space which is not covered by data_chunks */ - struct cache_tree free; + struct cache_tree free_space; void *fs_data; }; diff --git a/convert/main.c b/convert/main.c index 42b69df3..b115e24f 100644 --- a/convert/main.c +++ b/convert/main.c @@ -567,9 +567,9 @@ static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size, static int calculate_available_space(struct btrfs_convert_context *cctx) { - struct cache_tree *used = &cctx->used; + struct cache_tree *used = &cctx->used_space; struct cache_tree *data_chunks = &cctx->data_chunks; - struct cache_tree *free = &cctx->free; + struct cache_tree *free = &cctx->free_space; struct cache_extent *cache; u64 cur_off = 0; /* @@ -727,7 +727,7 @@ static int create_image(struct btrfs_root *root, * Create a new used space cache, which doesn't contain the reserved * range */ - for (cache = first_cache_extent(&cctx->used); cache; + for (cache = first_cache_extent(&cctx->used_space); cache; cache = next_cache_extent(cache)) { ret = add_cache_extent(&used_tmp, cache->start, cache->size); if (ret < 0) @@ -753,8 +753,8 @@ static int create_image(struct btrfs_root *root, cur += len; } /* Handle the reserved ranges */ - ret = migrate_reserved_ranges(trans, root, &cctx->used, &buf, fd, ino, - cfg->num_bytes, convert_flags); + ret = migrate_reserved_ranges(trans, root, &cctx->used_space, &buf, fd, + ino, cfg->num_bytes, convert_flags); key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 96ac95da..824d45d0 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -129,7 +129,7 @@ static int ext2_read_used_space(struct btrfs_convert_context *cctx) { ext2_filsys fs = (ext2_filsys)cctx->fs_data; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); - struct cache_tree *used_tree = &cctx->used; + struct cache_tree *used_tree = &cctx->used_space; char *block_bitmap = NULL; unsigned long i; int block_nbytes; diff --git a/convert/source-fs.c b/convert/source-fs.c index e87b861c..8217c893 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -42,16 +42,16 @@ void init_convert_context(struct btrfs_convert_context *cctx) { memset(cctx, 0, sizeof(*cctx)); - cache_tree_init(&cctx->used); + cache_tree_init(&cctx->used_space); cache_tree_init(&cctx->data_chunks); - cache_tree_init(&cctx->free); + cache_tree_init(&cctx->free_space); } void clean_convert_context(struct btrfs_convert_context *cctx) { - free_extent_cache_tree(&cctx->used); + free_extent_cache_tree(&cctx->used_space); free_extent_cache_tree(&cctx->data_chunks); - free_extent_cache_tree(&cctx->free); + free_extent_cache_tree(&cctx->free_space); } int block_iterate_proc(u64 disk_block, u64 file_block, -- cgit v1.2.3 From ca92914804be310293c0b447de9fcd79ffb728ce Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 1 Feb 2017 15:41:06 +0100 Subject: btrfs-progs: convert: improve assert in make_convert_btrfs Signed-off-by: David Sterba --- convert/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index d37d2b26..d87d75ee 100644 --- a/convert/common.c +++ b/convert/common.c @@ -796,8 +796,8 @@ int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, u64 csum_bytenr; int ret; - /* Shouldn't happen */ - BUG_ON(cache_tree_empty(used_space)); + /* Source filesystem must be opened, checked and analyzed in advance */ + ASSERT(!cache_tree_empty(used_space)); /* * reserve space for temporary superblock first -- cgit v1.2.3 From 6e1510e0fee5cca94d3a5fc14fcfe69218968d7a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 9 Feb 2017 17:39:44 +0100 Subject: btrfs-progs: remove unused argument from btrfs_csum_data Signed-off-by: David Sterba --- convert/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index d87d75ee..40bf32cf 100644 --- a/convert/common.c +++ b/convert/common.c @@ -64,7 +64,7 @@ static inline int write_temp_super(int fd, struct btrfs_super_block *sb, u32 crc = ~(u32)0; int ret; - crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, + crc = btrfs_csum_data((char *)sb + BTRFS_CSUM_SIZE, crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); btrfs_csum_final(crc, &sb->csum[0]); ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr); -- cgit v1.2.3 From b53af22c24515f6e92f694f066f2ac91a6bc90de Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 9 Feb 2017 17:42:02 +0100 Subject: btrfs-progs: remove unused argument from write_and_map_eb Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index b115e24f..40fc30a1 100644 --- a/convert/main.c +++ b/convert/main.c @@ -302,7 +302,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, eb->len = key.offset; /* Write the data */ - ret = write_and_map_eb(trans, root, eb); + ret = write_and_map_eb(root, eb); free(eb); if (ret < 0) break; -- cgit v1.2.3 From aee9207ece3050c8929ee6a0f8dbe314c8f9a3a1 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 9 Feb 2017 17:42:02 +0100 Subject: btrfs-progs: remove unused argument from set_extent_bits Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 40fc30a1..57db3f55 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1627,7 +1627,7 @@ static int do_rollback(const char *devname) break; set_extent_bits(&io_tree, offset, offset + num_bytes - 1, - EXTENT_LOCKED, GFP_NOFS); + EXTENT_LOCKED); set_state_private(&io_tree, offset, bytenr); next_extent: offset += btrfs_file_extent_num_bytes(leaf, fi); -- cgit v1.2.3 From 0acce60a2837fa5cfefda9bc55df08ae13fa0ab0 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 9 Feb 2017 17:42:02 +0100 Subject: btrfs-progs: remove unused argument from clear_extent_dirty Signed-off-by: David Sterba --- convert/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 57db3f55..dc6047e6 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1741,8 +1741,7 @@ next_extent: ret = get_state_private(&io_tree, start, &bytenr); BUG_ON(ret); - clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED, - GFP_NOFS); + clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED); while (start <= end) { if (start == BTRFS_SUPER_INFO_OFFSET) { -- cgit v1.2.3 From 0117fdf1fc1bafb11b61e13dfc9a07c0899af781 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 22 Feb 2017 15:50:32 +0800 Subject: btrfs-progs: convert: Add comment for the overall convert design Convert is now a little complex due to that fact we need to separate metadata and data chunks for different profiles. Add a comment with ascii art explaining the whole design and point out the really complex part, so any newcomers interested in convert can get a quick overview of it before digging into the hard to read code. Signed-off-by: Qu Wenruo [ wording and formatting adjustments ] Signed-off-by: David Sterba --- convert/main.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index dc6047e6..38cfdead 100644 --- a/convert/main.c +++ b/convert/main.c @@ -16,6 +16,70 @@ * Boston, MA 021110-1307, USA. */ +/* + * Btrfs convert design: + * + * The overall design of btrfs convert is like the following: + * + * |<------------------Old fs----------------------------->| + * |<- used ->| |<- used ->| |<- used ->| + * || + * \/ + * |<---------------Btrfs fs------------------------------>| + * |<- Old data chunk ->|< new chunk (D/M/S)>|<- ODC ->| + * |<-Old-FE->| |<-Old-FE->|<- Btrfs extents ->|<-Old-FE->| + * + * ODC = Old data chunk, btrfs chunks containing old fs data + * Mapped 1:1 (logical address == device offset) + * Old-FE = file extents pointing to old fs. + * + * So old fs used space is (mostly) kept as is, while btrfs will insert + * its chunk (Data/Meta/Sys) into large enough free space. + * In this way, we can create different profiles for metadata/data for + * converted fs. + * + * We must reserve and relocate 3 ranges for btrfs: + * * [0, 1M) - area never used for any data except the first + * superblock + * * [btrfs_sb_offset(1), +64K) - 1st superblock backup copy + * * [btrfs_sb_offset(2), +64K) - 2nd, dtto + * + * Most work is spent handling corner cases around these reserved ranges. + * + * Detailed workflow is: + * 1) Scan old fs used space and calculate data chunk layout + * 1.1) Scan old fs + * We can a map used space of old fs + * + * 1.2) Calculate data chunk layout - this is the hard part + * New data chunks must meet 3 conditions using result fomr 1.1 + * a. Large enough to be a chunk + * b. Doesn't intersect reserved ranges + * c. Covers all the remaining old fs used space + * + * NOTE: This can be simplified if we don't need to handle backup supers + * + * 1.3) Calculate usable space for new btrfs chunks + * Btrfs chunk usable space must meet 3 conditions using result from 1.2 + * a. Large enough to be a chunk + * b. Doesn't intersect reserved ranges + * c. Doesn't cover any data chunks in 1.1 + * + * 2) Create basic btrfs filesystem structure + * Initial metadata and sys chunks are inserted in the first availabe + * space found in step 1.3 + * Then insert all data chunks into the basic btrfs + * + * 3) Create convert image + * We need to relocate reserved ranges here. + * After this step, the convert image is done, and we can use the image + * as reflink source to create old files + * + * 4) Iterate old fs to create files + * We just reflink file extents from old fs to newly created files on + * btrfs. + */ + #include "kerncompat.h" #include -- cgit v1.2.3 From 714cda6136b90b3159bed9f077ede9608fbca49f Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 22 Feb 2017 16:46:18 +0800 Subject: btrfs-progs: convert: Introduce simple range structure for convert reserved ranges Introduce a new strucutre, simple_range, to present one contingous range. Also, use such structure to define btrfs_reserved_ranges(), which convert and rollback will use. Suggested-by: David Sterba Signed-off-by: Qu Wenruo [ split hunks to new file structure ] Signed-off-by: David Sterba --- convert/source-fs.c | 6 ++++++ convert/source-fs.h | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'convert') diff --git a/convert/source-fs.c b/convert/source-fs.c index 8217c893..7cf515b0 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -22,6 +22,12 @@ #include "convert/common.h" #include "convert/source-fs.h" +struct simple_range btrfs_reserved_ranges[3] = { + { 0, SZ_1M }, + { BTRFS_SB_MIRROR_OFFSET(1), SZ_64K }, + { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K } +}; + static int intersect_with_sb(u64 bytenr, u64 num_bytes) { int i; diff --git a/convert/source-fs.h b/convert/source-fs.h index f3f96d07..9f611150 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -21,6 +21,19 @@ #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID +/* + * Reresents a simple contiguous range. + * + * For multiple or non-contiguous ranges, use extent_cache_tree from + * extent-cache.c + */ +struct simple_range { + u64 start; + u64 len; +}; + +extern struct simple_range btrfs_reserved_ranges[3]; + struct task_info; struct task_ctx { @@ -89,4 +102,14 @@ int read_disk_extent(struct btrfs_root *root, u64 bytenr, int record_file_blocks(struct blk_iterate_data *data, u64 file_block, u64 disk_block, u64 num_blocks); +/* + * Simple range functions + * + * Get range end (exclusive) + */ +static inline u64 range_end(struct simple_range *range) +{ + return (range->start + range->len); +} + #endif -- cgit v1.2.3 From e8d2163e06e82dcc0f0987006fc08ecea08bc896 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 23 Feb 2017 10:22:22 +0800 Subject: btrfs-progs: convert: Use reserved ranges array to cleanup open code Since we have reserved ranges array now, we can use them to skip all these open codes. And add some comment and asciidoc art for related part. Signed-off-by: Qu Wenruo [ port to v4.10 ] Signed-off-by: David Sterba --- convert/main.c | 137 ++++++++++++++++++++++++++------------------------------- 1 file changed, 63 insertions(+), 74 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 38cfdead..8fcfd36b 100644 --- a/convert/main.c +++ b/convert/main.c @@ -211,47 +211,40 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, len = min_t(u64, len, BTRFS_MAX_EXTENT_SIZE); /* - * Skip sb ranges first - * [0, 1M), [sb_offset(1), +64K), [sb_offset(2), +64K]. + * Skip reserved ranges first * * Or we will insert a hole into current image file, and later * migrate block will fail as there is already a file extent. */ - if (bytenr < 1024 * 1024) { - *ret_len = 1024 * 1024 - bytenr; - return 0; - } - for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { - u64 cur = btrfs_sb_offset(i); + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *reserved = &btrfs_reserved_ranges[i]; - if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) { - *ret_len = cur + BTRFS_STRIPE_LEN - bytenr; + /* + * |-- reserved --| + * |--range---| + * or + * |---- reserved ----| + * |-- range --| + * Skip to reserved range end + */ + if (bytenr >= reserved->start && bytenr < range_end(reserved)) { + *ret_len = range_end(reserved) - bytenr; return 0; } - } - for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { - u64 cur = btrfs_sb_offset(i); /* - * |--reserved--| + * |---reserved---| * |----range-------| - * May still need to go through file extent inserts + * Leading part may still create a file extent */ - if (bytenr < cur && bytenr + len >= cur) { - len = min_t(u64, len, cur - bytenr); + if (bytenr < reserved->start && + bytenr + len >= range_end(reserved)) { + len = min_t(u64, len, reserved->start - bytenr); break; } - /* - * |--reserved--| - * |---range---| - * Drop out, no need to insert anything - */ - if (bytenr >= cur && bytenr < cur + BTRFS_STRIPE_LEN) { - *ret_len = cur + BTRFS_STRIPE_LEN - bytenr; - return 0; - } } + /* Check if we are going to insert regular file extent, or hole */ cache = search_cache_extent(used, bytenr); if (cache) { if (cache->start <= bytenr) { @@ -259,6 +252,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, * |///////Used///////| * |<--insert--->| * bytenr + * Insert one real file extent */ len = min_t(u64, len, cache->start + cache->size - bytenr); @@ -268,6 +262,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, * |//Used//| * |<-insert-->| * bytenr + * Insert one hole */ len = min(len, cache->start - bytenr); disk_bytenr = 0; @@ -278,6 +273,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, * |//Used//| |EOF * |<-insert-->| * bytenr + * Insert one hole */ disk_bytenr = 0; datacsum = 0; @@ -323,24 +319,31 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct cache_tree *used, struct btrfs_inode_item *inode, int fd, - u64 ino, u64 start, u64 len, + u64 ino, struct simple_range *range, u32 convert_flags) { - u64 cur_off = start; - u64 cur_len = len; - u64 hole_start = start; + u64 cur_off = range->start; + u64 cur_len = range->len; + u64 hole_start = range->start; u64 hole_len; struct cache_extent *cache; struct btrfs_key key; struct extent_buffer *eb; int ret = 0; - while (cur_off < start + len) { + /* + * It's possible that there are holes in reserved range: + * |<---------------- Reserved range ---------------------->| + * |<- Old fs data ->| |<- Old fs data ->| + * So here we need to iterate through old fs used space and only + * migrate ranges that covered by old fs data. + */ + while (cur_off < range_end(range)) { cache = lookup_cache_extent(used, cur_off, cur_len); if (!cache) break; cur_off = max(cache->start, cur_off); - cur_len = min(cache->start + cache->size, start + len) - + cur_len = min(cache->start + cache->size, range_end(range)) - cur_off; BUG_ON(cur_len < root->sectorsize); @@ -392,20 +395,22 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, cur_off += key.offset; hole_start = cur_off; - cur_len = start + len - cur_off; + cur_len = range_end(range) - cur_off; } - /* Last hole */ - if (start + len - hole_start > 0) + /* + * Last hole + * |<---- reserved -------->| + * |<- Old fs data ->| | + * | Hole | + */ + if (range_end(range) - hole_start > 0) ret = btrfs_record_file_extent(trans, root, ino, inode, - hole_start, 0, start + len - hole_start); + hole_start, 0, range_end(range) - hole_start); return ret; } /* * Relocate the used ext2 data in reserved ranges - * [0,1M) - * [btrfs_sb_offset(1), +BTRFS_STRIPE_LEN) - * [btrfs_sb_offset(2), +BTRFS_STRIPE_LEN) */ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -413,35 +418,20 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, struct btrfs_inode_item *inode, int fd, u64 ino, u64 total_bytes, u32 convert_flags) { - u64 cur_off; - u64 cur_len; + int i; int ret = 0; - /* 0 ~ 1M */ - cur_off = 0; - cur_len = 1024 * 1024; - ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, convert_flags); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *range = &btrfs_reserved_ranges[i]; - /* second sb(fisrt sb is included in 0~1M) */ - cur_off = btrfs_sb_offset(1); - cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off; - if (cur_off > total_bytes) - return ret; - ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, convert_flags); - if (ret < 0) - return ret; + if (range->start > total_bytes) + return ret; + ret = migrate_one_reserved_range(trans, root, used, inode, fd, + ino, range, convert_flags); + if (ret < 0) + return ret; + } - /* Last sb */ - cur_off = btrfs_sb_offset(2); - cur_len = min(total_bytes, cur_off + BTRFS_STRIPE_LEN) - cur_off; - if (cur_off > total_bytes) - return ret; - ret = migrate_one_reserved_range(trans, root, used, inode, fd, ino, - cur_off, cur_len, convert_flags); return ret; } @@ -614,18 +604,17 @@ static int wipe_one_reserved_range(struct cache_tree *tree, static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size, int ensure_size) { + int i; int ret; - ret = wipe_one_reserved_range(tree, 0, 1024 * 1024, min_stripe_size, - ensure_size); - if (ret < 0) - return ret; - ret = wipe_one_reserved_range(tree, btrfs_sb_offset(1), - BTRFS_STRIPE_LEN, min_stripe_size, ensure_size); - if (ret < 0) - return ret; - ret = wipe_one_reserved_range(tree, btrfs_sb_offset(2), - BTRFS_STRIPE_LEN, min_stripe_size, ensure_size); + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *range = &btrfs_reserved_ranges[i]; + + ret = wipe_one_reserved_range(tree, range->start, range->len, + min_stripe_size, ensure_size); + if (ret < 0) + return ret; + } return ret; } -- cgit v1.2.3 From b82b12265b976bdb044a752bfd27b2494edfffbd Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 23 Feb 2017 15:17:16 +0800 Subject: btrfs-progs: convert: Introduce function to read out btrfs reserved range Introduce a new function, read_reserved_ranges(), to allow later rollback to use these data to do rollback. Signed-off-by: Qu Wenruo --- convert/main.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 8fcfd36b..69279506 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1513,6 +1513,36 @@ fail: return -1; } +/* + * Read out data of convert image which is in btrfs reserved ranges so we can + * use them to overwrite the ranges during rollback. + */ +static int read_reserved_ranges(struct btrfs_root *root, u64 ino, + u64 total_bytes, char *reserved_ranges[]) +{ + int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *range = &btrfs_reserved_ranges[i]; + + if (range->start + range->len >= total_bytes) + break; + ret = btrfs_read_file(root, ino, range->start, range->len, + reserved_ranges[i]); + if (ret < range->len) { + error( + "failed to read data of convert image, offset=%llu len=%llu ret=%d", + range->start, range->len, ret); + if (ret >= 0) + ret = -EIO; + break; + } + ret = 0; + } + return ret; +} + static int do_rollback(const char *devname) { int fd = -1; -- cgit v1.2.3 From 1170ac3079005a99098e48777513e5ab09b64d55 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 23 Feb 2017 15:46:46 +0800 Subject: btrfs-progs: convert: Introduce function to check if convert image is able to be rolled back Introduce a function, check_convert_image() to check if that image is rollback-able. This means all file extents except one of the btrfs reserved ranges, must be mapped 1:1 on disk. 1:1 mapped file extents must match the following conditions: 1) Their file_offset(key.offset) matches its disk_bytenr 2) The corresponding chunk must be mapped 1:1 on disk That's to say, it's a SINGLE chunk, and chunk logical matches with stripe physical. Above 2 conditions ensured that file extent lies the exactly the same position as in the old filesystem. For data in reserved ranges of btrfs, they are relocated to new places, and in that case, we use btrfs_read_file() to read out the content for later rollback use. Signed-off-by: Qu Wenruo --- convert/main.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 69279506..d4cda478 100644 --- a/convert/main.c +++ b/convert/main.c @@ -88,6 +88,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" @@ -1543,6 +1544,185 @@ static int read_reserved_ranges(struct btrfs_root *root, u64 ino, return ret; } +static bool is_subset_of_reserved_ranges(u64 start, u64 len) +{ + int i; + bool ret = false; + + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *range = &btrfs_reserved_ranges[i]; + + if (start >= range->start && start + len <= range_end(range)) { + ret = true; + break; + } + } + return ret; +} + +static bool is_chunk_direct_mapped(struct btrfs_fs_info *fs_info, u64 start) +{ + struct cache_extent *ce; + struct map_lookup *map; + bool ret = false; + + ce = search_cache_extent(&fs_info->mapping_tree.cache_tree, start); + if (!ce) + goto out; + if (ce->start > start || ce->start + ce->size < start) + goto out; + + map = container_of(ce, struct map_lookup, ce); + + /* Not SINGLE chunk */ + if (map->num_stripes != 1) + goto out; + + /* Chunk's logical doesn't match with phisical, not 1:1 mapped */ + if (map->ce.start != map->stripes[0].physical) + goto out; + ret = true; +out: + return ret; +} + +/* + * Iterate all file extents of the convert image. + * + * All file extents except ones in btrfs_reserved_ranges must be mapped 1:1 + * on disk. (Means thier file_offset must match their on disk bytenr) + * + * File extents in reserved ranges can be relocated to other place, and in + * that case we will read them out for later use. + */ +static int check_image_file_extents(struct btrfs_root *image_root, u64 ino, + u64 total_size, char *reserved_ranges[]) +{ + struct btrfs_key key; + struct btrfs_path path; + struct btrfs_fs_info *fs_info = image_root->fs_info; + u64 checked_bytes = 0; + int ret; + + key.objectid = ino; + key.offset = 0; + key.type = BTRFS_EXTENT_DATA_KEY; + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0); + /* + * It's possible that some fs doesn't store any (including sb) + * data into 0~1M range, and NO_HOLES is enabled. + * + * So we only need to check if ret < 0 + */ + if (ret < 0) { + error("failed to iterate file extents at offset 0: %s", + strerror(-ret)); + btrfs_release_path(&path); + return ret; + } + + /* Loop from the first file extents */ + while (1) { + struct btrfs_file_extent_item *fi; + struct extent_buffer *leaf = path.nodes[0]; + u64 disk_bytenr; + u64 file_offset; + u64 ram_bytes; + int slot = path.slots[0]; + + if (slot >= btrfs_header_nritems(leaf)) + goto next; + btrfs_item_key_to_cpu(leaf, &key, slot); + + /* + * Iteration is done, exit normally, we have extra check out of + * the loop + */ + if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) { + ret = 0; + break; + } + file_offset = key.offset; + fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); + if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) { + ret = -EINVAL; + error( + "ino %llu offset %llu doesn't have a regular file extent", + ino, file_offset); + break; + } + if (btrfs_file_extent_compression(leaf, fi) || + btrfs_file_extent_encryption(leaf, fi) || + btrfs_file_extent_other_encoding(leaf, fi)) { + ret = -EINVAL; + error( + "ino %llu offset %llu doesn't have a plain file extent", + ino, file_offset); + break; + } + + disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi); + + checked_bytes += ram_bytes; + /* Skip hole */ + if (disk_bytenr == 0) + goto next; + + /* + * Most file extents must be 1:1 mapped, which means 2 things: + * 1) File extent file offset == disk_bytenr + * 2) That data chunk's logical == chunk's physical + * + * So file extent's file offset == physical position on disk. + * + * And after rolling back btrfs reserved range, other part + * remains what old fs used to be. + */ + if (file_offset != disk_bytenr || + !is_chunk_direct_mapped(fs_info, disk_bytenr)) { + /* + * Only file extent in btrfs reserved ranges are + * allowed to be non-1:1 mapped + */ + if (!is_subset_of_reserved_ranges(file_offset, + ram_bytes)) { + ret = -EINVAL; + error( + "ino %llu offset %llu file extent should not be relocated", + ino, file_offset); + break; + } + } +next: + ret = btrfs_next_item(image_root, &path); + if (ret) { + if (ret > 0) + ret = 0; + break; + } + } + btrfs_release_path(&path); + /* + * For HOLES mode (without NO_HOLES), we must ensure file extents + * cover the whole range of the image + */ + if (!ret && !btrfs_fs_incompat(fs_info, NO_HOLES)) { + if (checked_bytes != total_size) { + ret = -EINVAL; + error("inode %llu has some file extents not checked", + ino); + } + } + + /* So far so good, read old data located in btrfs reserved ranges */ + ret = read_reserved_ranges(image_root, ino, total_size, + reserved_ranges); + return ret; +} + static int do_rollback(const char *devname) { int fd = -1; -- cgit v1.2.3 From 6d881033e1651a59970fa52d365b3268decab1d1 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 23 Feb 2017 16:21:14 +0800 Subject: btrfs-progs: convert: Rework rollback Rework rollback to a more easy to understand way. New convert behavior makes us to have a more flex chunk layout, which only data chunk containing old fs data will be at the same physical location, while new chunks (data/meta/sys) can be mapped anywhere else. This behavior makes old rollback behavior can't handle it. As old behavior assumes all data/meta is mapped in a large chunk, which is mapped 1:1 on disk. So rework rollback to handle new convert behavior, enhance the check by only checking all file extents of convert image, only to check if these file extents and therir chunks are mapped 1:1. This new rollback check behavior can handle both new and old convert behavior, as the new behavior is a superset of old behavior. Further more, introduce a simple rollback mechanisim: 1) Read reserved data (offset = file offset) from convert image 2) Write reserved data into disk (offset = physical offset) Since old fs image is a valid fs, and we only need to rollback superblocks (btrfs reserved ranges), then we just read out data in reserved range, and write it back. Due to the fact that all other file extents of converted image is mapped 1:1 on disk, we put the missing piece back, then the fs is as good as old one. Then what we do in btrfs is just another dream. With this new rollback mechanisim, we can open btrfs read-only, so we won't cause any damage to current btrfs, until the final piece (0~1M, containing 1st super block) is put back. Signed-off-by: Qu Wenruo [ port to v4.10 ] Signed-off-by: David Sterba --- convert/main.c | 592 ++++++++++++++------------------------------------------- 1 file changed, 139 insertions(+), 453 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index d4cda478..73c9d889 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1172,36 +1172,6 @@ fail: return ret; } -static int prepare_system_chunk_sb(struct btrfs_super_block *super) -{ - struct btrfs_chunk *chunk; - struct btrfs_disk_key *key; - u32 sectorsize = btrfs_super_sectorsize(super); - - key = (struct btrfs_disk_key *)(super->sys_chunk_array); - chunk = (struct btrfs_chunk *)(super->sys_chunk_array + - sizeof(struct btrfs_disk_key)); - - btrfs_set_disk_key_objectid(key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_type(key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_disk_key_offset(key, 0); - - btrfs_set_stack_chunk_length(chunk, btrfs_super_total_bytes(super)); - btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN); - btrfs_set_stack_chunk_type(chunk, BTRFS_BLOCK_GROUP_SYSTEM); - btrfs_set_stack_chunk_io_align(chunk, sectorsize); - btrfs_set_stack_chunk_io_width(chunk, sectorsize); - btrfs_set_stack_chunk_sector_size(chunk, sectorsize); - btrfs_set_stack_chunk_num_stripes(chunk, 1); - btrfs_set_stack_chunk_sub_stripes(chunk, 0); - chunk->stripe.devid = super->dev_item.devid; - btrfs_set_stack_stripe_offset(&chunk->stripe, 0); - memcpy(chunk->stripe.dev_uuid, super->dev_item.uuid, BTRFS_UUID_SIZE); - btrfs_set_super_sys_array_size(super, sizeof(*key) + sizeof(*chunk)); - return 0; -} - static int convert_open_fs(const char *devname, struct btrfs_convert_context *cctx) { @@ -1389,131 +1359,6 @@ fail: return -1; } -/* - * Check if a non 1:1 mapped chunk can be rolled back. - * For new convert, it's OK while for old convert it's not. - */ -static int may_rollback_chunk(struct btrfs_fs_info *fs_info, u64 bytenr) -{ - struct btrfs_block_group_cache *bg; - struct btrfs_key key; - struct btrfs_path path; - struct btrfs_root *extent_root = fs_info->extent_root; - u64 bg_start; - u64 bg_end; - int ret; - - bg = btrfs_lookup_first_block_group(fs_info, bytenr); - if (!bg) - return -ENOENT; - bg_start = bg->key.objectid; - bg_end = bg->key.objectid + bg->key.offset; - - key.objectid = bg_end; - key.type = BTRFS_METADATA_ITEM_KEY; - key.offset = 0; - btrfs_init_path(&path); - - ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0); - if (ret < 0) - return ret; - - while (1) { - struct btrfs_extent_item *ei; - - ret = btrfs_previous_extent_item(extent_root, &path, bg_start); - if (ret > 0) { - ret = 0; - break; - } - if (ret < 0) - break; - - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - if (key.type == BTRFS_METADATA_ITEM_KEY) - continue; - /* Now it's EXTENT_ITEM_KEY only */ - ei = btrfs_item_ptr(path.nodes[0], path.slots[0], - struct btrfs_extent_item); - /* - * Found data extent, means this is old convert must follow 1:1 - * mapping. - */ - if (btrfs_extent_flags(path.nodes[0], ei) - & BTRFS_EXTENT_FLAG_DATA) { - ret = -EINVAL; - break; - } - } - btrfs_release_path(&path); - return ret; -} - -static int may_rollback(struct btrfs_root *root) -{ - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_multi_bio *multi = NULL; - u64 bytenr; - u64 length; - u64 physical; - u64 total_bytes; - int num_stripes; - int ret; - - if (btrfs_super_num_devices(info->super_copy) != 1) - goto fail; - - bytenr = BTRFS_SUPER_INFO_OFFSET; - total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy); - - while (1) { - ret = btrfs_map_block(&info->mapping_tree, WRITE, bytenr, - &length, &multi, 0, NULL); - 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; - free(multi); - - if (num_stripes != 1) { - error("num stripes for bytenr %llu is not 1", bytenr); - goto fail; - } - - /* - * Extra check for new convert, as metadata chunk from new - * convert is much more free than old convert, it doesn't need - * to do 1:1 mapping. - */ - if (physical != bytenr) { - /* - * Check if it's a metadata chunk and has only metadata - * extent. - */ - ret = may_rollback_chunk(info, bytenr); - if (ret < 0) - goto fail; - } -next: - bytenr += length; - if (bytenr >= total_bytes) - break; - } - return 0; -fail: - return -1; -} - /* * Read out data of convert image which is in btrfs reserved ranges so we can * use them to overwrite the ranges during rollback. @@ -1595,8 +1440,8 @@ out: * File extents in reserved ranges can be relocated to other place, and in * that case we will read them out for later use. */ -static int check_image_file_extents(struct btrfs_root *image_root, u64 ino, - u64 total_size, char *reserved_ranges[]) +static int check_convert_image(struct btrfs_root *image_root, u64 ino, + u64 total_size, char *reserved_ranges[]) { struct btrfs_key key; struct btrfs_path path; @@ -1723,354 +1568,195 @@ next: return ret; } +/* + * btrfs rollback is just reverted convert: + * |<---------------Btrfs fs------------------------------>| + * |<- Old data chunk ->|< new chunk (D/M/S)>|<- ODC ->| + * |<-Old-FE->| |<-Old-FE->|<- Btrfs extents ->|<-Old-FE->| + * || + * \/ + * |<------------------Old fs----------------------------->| + * |<- used ->| |<- used ->| |<- used ->| + * + * However things are much easier than convert, we don't really need to + * do the complex space calculation, but only to handle btrfs reserved space + * + * |<---------------------------Btrfs fs----------------------------->| + * | RSV 1 | | Old | | RSV 2 | | Old | | RSV 3 | + * | 0~1M | | Fs | | SB2 + 64K | | Fs | | SB3 + 64K | + * + * On the other hande, the converted fs image in btrfs is a completely + * valid old fs. + * + * |<-----------------Converted fs image in btrfs-------------------->| + * | RSV 1 | | Old | | RSV 2 | | Old | | RSV 3 | + * | Relocated | | Fs | | Relocated | | Fs | | Relocated | + * + * Used space in fs image should be at the same physical position on disk. + * We only need to recover the data in reserved ranges, so the whole + * old fs is back. + * + * The idea to rollback is also straightforward, we just "read" out the data + * of reserved ranges, and write them back to there they should be. + * Then the old fs is back. + */ static int do_rollback(const char *devname) { - int fd = -1; - int ret; - int i; struct btrfs_root *root; struct btrfs_root *image_root; - struct btrfs_root *chunk_root; - struct btrfs_dir_item *dir; - struct btrfs_inode_item *inode; - struct btrfs_file_extent_item *fi; - struct btrfs_trans_handle *trans; - struct extent_buffer *leaf; - struct btrfs_block_group_cache *cache1; - struct btrfs_block_group_cache *cache2; + struct btrfs_fs_info *fs_info; struct btrfs_key key; struct btrfs_path path; - struct extent_io_tree io_tree; - char *buf = NULL; - char *name; - u64 bytenr; - u64 num_bytes; - u64 root_dir; - u64 objectid; - u64 offset; - u64 start; - u64 end; - u64 sb_bytenr; - u64 first_free; + struct btrfs_dir_item *dir; + struct btrfs_inode_item *inode_item; + char *image_name = "image"; + char *reserved_ranges[ARRAY_SIZE(btrfs_reserved_ranges)] = { NULL }; u64 total_bytes; - u32 sectorsize; + u64 fsize; + u64 root_dir; + u64 ino; + int fd = -1; + int ret; + int i; - extent_io_tree_init(&io_tree); + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + struct simple_range *range = &btrfs_reserved_ranges[i]; + reserved_ranges[i] = calloc(1, range->len); + if (!reserved_ranges[i]) { + ret = -ENOMEM; + goto free_mem; + } + } fd = open(devname, O_RDWR); if (fd < 0) { error("unable to open %s: %s", devname, strerror(errno)); - goto fail; + ret = -EIO; + goto free_mem; } + fsize = lseek(fd, 0, SEEK_END); root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES); if (!root) { error("unable to open ctree"); - goto fail; - } - ret = may_rollback(root); - if (ret < 0) { - error("unable to do rollback: %d", ret); - goto fail; + ret = -EIO; + goto free_mem; } + fs_info = root->fs_info; - sectorsize = root->sectorsize; - buf = malloc(sectorsize); - if (!buf) { - error("unable to allocate memory"); - goto fail; - } - - btrfs_init_path(&path); - + /* + * Search root backref first, or after subvolume deletion (orphan), + * we can still rollback the image. + */ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; key.type = BTRFS_ROOT_BACKREF_KEY; key.offset = BTRFS_FS_TREE_OBJECTID; - ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key, &path, 0, - 0); + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0); btrfs_release_path(&path); if (ret > 0) { - error("unable to convert ext2 image subvolume, is it deleted?"); - goto fail; + error("unable to find ext2 image subvolume, is it deleted?"); + ret = -ENOENT; + goto close_fs; } else if (ret < 0) { - error("unable to open ext2_saved, id %llu: %s", - (unsigned long long)key.objectid, strerror(-ret)); - goto fail; + error("failed to find ext2 image subvolume: %s", + strerror(-ret)); + goto close_fs; } + /* Search convert subvolume */ key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; - image_root = btrfs_read_fs_root(root->fs_info, &key); - if (!image_root || IS_ERR(image_root)) { - error("unable to open subvolume %llu: %ld", - (unsigned long long)key.objectid, PTR_ERR(image_root)); - goto fail; + image_root = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(image_root)) { + ret = PTR_ERR(image_root); + error("failed to open convert image subvolume: %s", + strerror(-ret)); + goto close_fs; } - name = "image"; - root_dir = btrfs_root_dirid(&root->root_item); - dir = btrfs_lookup_dir_item(NULL, image_root, &path, - root_dir, name, strlen(name), 0); + /* Search the image file */ + root_dir = btrfs_root_dirid(&image_root->root_item); + dir = btrfs_lookup_dir_item(NULL, image_root, &path, root_dir, + image_name, strlen(image_name), 0); + if (!dir || IS_ERR(dir)) { - error("unable to find file %s: %ld", name, PTR_ERR(dir)); - goto fail; + btrfs_release_path(&path); + if (dir) + ret = PTR_ERR(dir); + else + ret = -ENOENT; + error("failed to locate file %s: %s", image_name, + strerror(-ret)); + goto close_fs; } - leaf = path.nodes[0]; - btrfs_dir_item_key_to_cpu(leaf, dir, &key); + btrfs_dir_item_key_to_cpu(path.nodes[0], dir, &key); btrfs_release_path(&path); - objectid = key.objectid; + /* Get total size of the original image */ + ino = key.objectid; ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0); - if (ret) { - error("unable to find inode item: %d", ret); - goto fail; - } - leaf = path.nodes[0]; - inode = btrfs_item_ptr(leaf, path.slots[0], struct btrfs_inode_item); - total_bytes = btrfs_inode_size(leaf, inode); - btrfs_release_path(&path); - key.objectid = objectid; - key.offset = 0; - key.type = BTRFS_EXTENT_DATA_KEY; - ret = btrfs_search_slot(NULL, image_root, &key, &path, 0, 0); - if (ret != 0) { - error("unable to find first file extent: %d", ret); + if (ret < 0) { btrfs_release_path(&path); - goto fail; - } - - /* build mapping tree for the relocated blocks */ - for (offset = 0; offset < total_bytes; ) { - leaf = path.nodes[0]; - if (path.slots[0] >= btrfs_header_nritems(leaf)) { - ret = btrfs_next_leaf(root, &path); - if (ret != 0) - break; - continue; - } - - btrfs_item_key_to_cpu(leaf, &key, path.slots[0]); - if (key.objectid != objectid || key.offset != offset || - key.type != BTRFS_EXTENT_DATA_KEY) - break; - - fi = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG) - break; - if (btrfs_file_extent_compression(leaf, fi) || - btrfs_file_extent_encryption(leaf, fi) || - btrfs_file_extent_other_encoding(leaf, fi)) - break; - - bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); - /* skip holes and direct mapped extents */ - if (bytenr == 0 || bytenr == offset) - goto next_extent; - - bytenr += btrfs_file_extent_offset(leaf, fi); - num_bytes = btrfs_file_extent_num_bytes(leaf, fi); - - cache1 = btrfs_lookup_block_group(root->fs_info, offset); - cache2 = btrfs_lookup_block_group(root->fs_info, - offset + num_bytes - 1); - /* - * Here we must take consideration of old and new convert - * behavior. - * For old convert case, sign, there is no consist chunk type - * that will cover the extent. META/DATA/SYS are all possible. - * Just ensure relocate one is in SYS chunk. - * For new convert case, they are all covered by DATA chunk. - * - * So, there is not valid chunk type check for it now. - */ - if (cache1 != cache2) - break; - - set_extent_bits(&io_tree, offset, offset + num_bytes - 1, - EXTENT_LOCKED); - set_state_private(&io_tree, offset, bytenr); -next_extent: - offset += btrfs_file_extent_num_bytes(leaf, fi); - path.slots[0]++; + error("unable to find inode %llu: %s", ino, strerror(-ret)); + goto close_fs; } + inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_inode_item); + total_bytes = btrfs_inode_size(path.nodes[0], inode_item); btrfs_release_path(&path); - if (offset < total_bytes) { - error("unable to build extent mapping (offset %llu, total_bytes %llu)", - (unsigned long long)offset, - (unsigned long long)total_bytes); - error("converted filesystem after balance is unable to rollback"); - goto fail; + /* Check if we can rollback the image */ + ret = check_convert_image(image_root, ino, total_bytes, reserved_ranges); + if (ret < 0) { + error("old fs image can't be rolled back"); + goto close_fs; } +close_fs: + btrfs_release_path(&path); + close_ctree_fs_info(fs_info); + if (ret) + goto free_mem; - first_free = BTRFS_SUPER_INFO_OFFSET + 2 * sectorsize - 1; - first_free &= ~((u64)sectorsize - 1); - /* backup for extent #0 should exist */ - if(!test_range_bit(&io_tree, 0, first_free - 1, EXTENT_LOCKED, 1)) { - error("no backup for the first extent"); - goto fail; - } - /* force no allocation from system block group */ - root->fs_info->system_allocs = -1; - trans = btrfs_start_transaction(root, 1); - if (!trans) { - error("unable to start transaction"); - goto fail; - } /* - * recow the whole chunk tree, this will remove all chunk tree blocks - * from system block group + * Everything is OK, just write back old fs data into btrfs reserved + * ranges + * + * Here, we starts from the backup blocks first, so if something goes + * wrong, the fs is still mountable */ - chunk_root = root->fs_info->chunk_root; - memset(&key, 0, sizeof(key)); - while (1) { - ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1); - if (ret < 0) - break; - - ret = btrfs_next_leaf(chunk_root, &path); - if (ret) - break; - - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - btrfs_release_path(&path); - } - btrfs_release_path(&path); - - offset = 0; - num_bytes = 0; - while(1) { - cache1 = btrfs_lookup_block_group(root->fs_info, offset); - if (!cache1) - break; - - if (cache1->flags & BTRFS_BLOCK_GROUP_SYSTEM) - num_bytes += btrfs_block_group_used(&cache1->item); - - offset = cache1->key.objectid + cache1->key.offset; - } - /* only extent #0 left in system block group? */ - if (num_bytes > first_free) { - error( - "unable to empty system block group (num_bytes %llu, first_free %llu", - (unsigned long long)num_bytes, - (unsigned long long)first_free); - goto fail; - } - /* create a system chunk that maps the whole device */ - ret = prepare_system_chunk_sb(root->fs_info->super_copy); - if (ret) { - error("unable to update system chunk: %d", ret); - goto fail; - } - - ret = btrfs_commit_transaction(trans, root); - if (ret) { - error("transaction commit failed: %d", ret); - goto fail; - } - ret = close_ctree(root); - if (ret) { - error("close_ctree failed: %d", ret); - goto fail; - } - - /* zero btrfs super block mirrors */ - memset(buf, 0, sectorsize); - for (i = 1 ; i < BTRFS_SUPER_MIRROR_MAX; i++) { - bytenr = btrfs_sb_offset(i); - if (bytenr >= total_bytes) - break; - ret = pwrite(fd, buf, sectorsize, bytenr); - if (ret != sectorsize) { - error("zeroing superblock mirror %d failed: %d", - i, ret); - goto fail; - } - } - - sb_bytenr = (u64)-1; - /* copy all relocated blocks back */ - while(1) { - ret = find_first_extent_bit(&io_tree, 0, &start, &end, - EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(&io_tree, start, &bytenr); - BUG_ON(ret); + for (i = ARRAY_SIZE(btrfs_reserved_ranges) - 1; i >= 0; i--) { + u64 real_size; + struct simple_range *range = &btrfs_reserved_ranges[i]; - clear_extent_bits(&io_tree, start, end, EXTENT_LOCKED); + if (range_end(range) >= fsize) + continue; - while (start <= end) { - if (start == BTRFS_SUPER_INFO_OFFSET) { - sb_bytenr = bytenr; - goto next_sector; - } - ret = pread(fd, buf, sectorsize, bytenr); - if (ret < 0) { - error("reading superblock at %llu failed: %d", - (unsigned long long)bytenr, ret); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = pwrite(fd, buf, sectorsize, start); - if (ret < 0) { - error("writing superblock at %llu failed: %d", - (unsigned long long)start, ret); - goto fail; - } - BUG_ON(ret != sectorsize); -next_sector: - start += sectorsize; - bytenr += sectorsize; + real_size = min(range_end(range), fsize) - range->start; + ret = pwrite(fd, reserved_ranges[i], real_size, range->start); + if (ret < real_size) { + if (ret < 0) + ret = -errno; + else + ret = -EIO; + error("failed to recover range [%llu, %llu): %s", + range->start, real_size, strerror(-ret)); + goto free_mem; } + ret = 0; } - ret = fsync(fd); - if (ret < 0) { - error("fsync failed: %s", strerror(errno)); - goto fail; - } - /* - * finally, overwrite btrfs super block. - */ - ret = pread(fd, buf, sectorsize, sb_bytenr); - if (ret < 0) { - error("reading primary superblock failed: %s", - strerror(errno)); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); - if (ret < 0) { - error("writing primary superblock failed: %s", - strerror(errno)); - goto fail; - } - BUG_ON(ret != sectorsize); - ret = fsync(fd); - if (ret < 0) { - error("fsync failed: %s", strerror(errno)); - goto fail; - } - - close(fd); - free(buf); - extent_io_tree_cleanup(&io_tree); - printf("rollback complete\n"); - return 0; - -fail: - if (fd != -1) - close(fd); - free(buf); - error("rollback aborted"); - return -1; +free_mem: + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) + free(reserved_ranges[i]); + if (ret) + error("rollback failed"); + else + printf("rollback succeeded\n"); + return ret; } static void print_usage(void) -- cgit v1.2.3 From 153d4e6c01b53855d01b5d0c99886ba31b48bad1 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 12 Mar 2017 19:46:52 +0200 Subject: btrfs-progs: add missing limits headers Build under musl libc fails because of missing PATH_MAX and XATTR_NAME_MAX macro declarations. Add the required headers. Signed-off-by: Baruch Siach --- convert/source-ext2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 824d45d0..1b0576b1 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -17,6 +17,7 @@ #if BTRFSCONVERT_EXT2 #include "kerncompat.h" +#include #include "disk-io.h" #include "transaction.h" #include "utils.h" -- cgit v1.2.3 From 89d5eb55274e0777fec8f5fe01789c32556dee16 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 16 Mar 2017 11:18:31 +0800 Subject: btrfs-progs: convert: Make btrfs_reserved_ranges const Since btrfs_reserved_ranges array is just used to store btrfs reserved ranges, no one will nor should modify them at run time, make them static and const will be better. This also eliminates the use of immediate number 3. Signed-off-by: Qu Wenruo [ definition stays in source-fs.c ] Signed-off-by: David Sterba --- convert/main.c | 16 ++++++++-------- convert/source-fs.c | 2 +- convert/source-fs.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 73c9d889..96358c62 100644 --- a/convert/main.c +++ b/convert/main.c @@ -218,7 +218,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, * migrate block will fail as there is already a file extent. */ for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *reserved = &btrfs_reserved_ranges[i]; + const struct simple_range *reserved = &btrfs_reserved_ranges[i]; /* * |-- reserved --| @@ -320,7 +320,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct cache_tree *used, struct btrfs_inode_item *inode, int fd, - u64 ino, struct simple_range *range, + u64 ino, const struct simple_range *range, u32 convert_flags) { u64 cur_off = range->start; @@ -423,7 +423,7 @@ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, int ret = 0; for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; if (range->start > total_bytes) return ret; @@ -609,7 +609,7 @@ static int wipe_reserved_ranges(struct cache_tree *tree, u64 min_stripe_size, int ret; for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; ret = wipe_one_reserved_range(tree, range->start, range->len, min_stripe_size, ensure_size); @@ -1370,7 +1370,7 @@ static int read_reserved_ranges(struct btrfs_root *root, u64 ino, int ret = 0; for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; if (range->start + range->len >= total_bytes) break; @@ -1395,7 +1395,7 @@ static bool is_subset_of_reserved_ranges(u64 start, u64 len) bool ret = false; for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; if (start >= range->start && start + len <= range_end(range)) { ret = true; @@ -1620,7 +1620,7 @@ static int do_rollback(const char *devname) int i; for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; reserved_ranges[i] = calloc(1, range->len); if (!reserved_ranges[i]) { @@ -1730,7 +1730,7 @@ close_fs: for (i = ARRAY_SIZE(btrfs_reserved_ranges) - 1; i >= 0; i--) { u64 real_size; - struct simple_range *range = &btrfs_reserved_ranges[i]; + const struct simple_range *range = &btrfs_reserved_ranges[i]; if (range_end(range) >= fsize) continue; diff --git a/convert/source-fs.c b/convert/source-fs.c index 7cf515b0..80e4e418 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -22,7 +22,7 @@ #include "convert/common.h" #include "convert/source-fs.h" -struct simple_range btrfs_reserved_ranges[3] = { +const struct simple_range btrfs_reserved_ranges[3] = { { 0, SZ_1M }, { BTRFS_SB_MIRROR_OFFSET(1), SZ_64K }, { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K } diff --git a/convert/source-fs.h b/convert/source-fs.h index 9f611150..ca32d159 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -32,7 +32,7 @@ struct simple_range { u64 len; }; -extern struct simple_range btrfs_reserved_ranges[3]; +extern const struct simple_range btrfs_reserved_ranges[3]; struct task_info; @@ -107,7 +107,7 @@ int record_file_blocks(struct blk_iterate_data *data, * * Get range end (exclusive) */ -static inline u64 range_end(struct simple_range *range) +static inline u64 range_end(const struct simple_range *range) { return (range->start + range->len); } -- cgit v1.2.3 From 52bbb015e5a21743478024becb1c3dacea822adb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 21 Mar 2017 09:00:55 +0800 Subject: btrfs-progs: convert: Add missing return for HOLE mode when checking convert image In check_convert_image(), for normal HOLE case, if the file extents are smaller than image size, we set ret to -EINVAL and print error message. But forget to return. This patch adds the missing return to fix it. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 96358c62..c56382e9 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1559,6 +1559,7 @@ next: ret = -EINVAL; error("inode %llu has some file extents not checked", ino); + return ret; } } -- cgit v1.2.3 From 8636132c6d3e0b916a6877c327314f211498ab88 Mon Sep 17 00:00:00 2001 From: "Lakshmipathi.G" Date: Mon, 26 Jun 2017 14:43:31 +0200 Subject: btrfs-progs: convert: widen int types in convert context The u32 types in the convert context might not be enough for some very large filesytems (20TB). Use 64bit types to be safe. Signed-off-by: Lakshmipathi.G Signed-off-by: David Sterba --- convert/common.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'convert') diff --git a/convert/common.h b/convert/common.h index 0d3adeaa..2f4ea485 100644 --- a/convert/common.h +++ b/convert/common.h @@ -30,10 +30,10 @@ struct btrfs_mkfs_config; struct btrfs_convert_context { u32 blocksize; - u32 first_data_block; - u32 block_count; - u32 inodes_count; - u32 free_inodes_count; + u64 first_data_block; + u64 block_count; + u64 inodes_count; + u64 free_inodes_count; u64 total_bytes; char *volume_name; const struct btrfs_convert_operations *convert_ops; -- cgit v1.2.3 From 324d4c1857ac8282e838d1fe7c1a9b65b67f0d40 Mon Sep 17 00:00:00 2001 From: "Lakshmipathi.G" Date: Sat, 3 Jun 2017 15:27:45 +0530 Subject: btrfs-progs: convert: Add larger device support With larger file system (in this case its 22TB), ext2fs_open() returns EXT2_ET_CANT_USE_LEGACY_BITMAPS error message with ext2fs_read_block_bitmap(). To overcome this issue, (a) we need pass EXT2_FLAG_64BITS flag with ext2fs_open. (b) use 64-bit functions like ext2fs_get_block_bitmap_range2, ext2fs_inode_data_blocks2,ext2fs_read_ext_attr2 (c) use 64bit types with btrfs_convert_context fields Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=194795 Signed-off-by: Lakshmipathi.G Signed-off-by: David Sterba --- convert/source-ext2.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 1b0576b1..275cb89f 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -34,8 +34,9 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) ext2_filsys ext2_fs; ext2_ino_t ino; u32 ro_feature; + int open_flag = EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS; - ret = ext2fs_open(name, 0, 0, 0, unix_io_manager, &ext2_fs); + ret = ext2fs_open(name, open_flag, 0, 0, unix_io_manager, &ext2_fs); if (ret) { fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); return -1; @@ -148,7 +149,7 @@ static int ext2_read_used_space(struct btrfs_convert_context *cctx) return -ENOMEM; for (i = 0; i < fs->group_desc_count; i++) { - ret = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr, + ret = ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr, block_nbytes * 8, block_bitmap); if (ret) { error("fail to get bitmap from ext2, %s", @@ -353,7 +354,7 @@ static int ext2_create_symlink(struct btrfs_trans_handle *trans, int ret; char *pathname; u64 inode_size = btrfs_stack_inode_size(btrfs_inode); - if (ext2fs_inode_data_blocks(ext2_fs, ext2_inode)) { + if (ext2fs_inode_data_blocks2(ext2_fs, ext2_inode)) { btrfs_set_stack_inode_size(btrfs_inode, inode_size + 1); ret = ext2_create_file_extents(trans, root, objectid, btrfs_inode, ext2_fs, ext2_ino, @@ -627,9 +628,9 @@ static int ext2_copy_extended_attrs(struct btrfs_trans_handle *trans, ret = -ENOMEM; goto out; } - err = ext2fs_read_ext_attr(ext2_fs, ext2_inode->i_file_acl, buffer); + err = ext2fs_read_ext_attr2(ext2_fs, ext2_inode->i_file_acl, buffer); if (err) { - fprintf(stderr, "ext2fs_read_ext_attr: %s\n", + fprintf(stderr, "ext2fs_read_ext_attr2: %s\n", error_message(err)); ret = -1; goto out; -- cgit v1.2.3 From 188f9c889a062307cf18fdacf91381f29f12bf6d Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Wed, 28 Jun 2017 15:29:49 -0600 Subject: Btrfs-progs: convert: do not clear nodatasum flag in inode item With the current btrfs-convert, if we convert a ext4 without data checksum, it'd not set nodatasum flag in inode item, nor create csum item, reading file ends up with checksum errors. Signed-off-by: Liu Bo Signed-off-by: David Sterba --- convert/source-ext2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 275cb89f..38c3cd33 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -743,7 +743,7 @@ static int ext2_check_state(struct btrfs_convert_context *cctx) static void ext2_convert_inode_flags(struct btrfs_inode_item *dst, struct ext2_inode *src) { - u64 flags = 0; + u64 flags = btrfs_stack_inode_flags(dst); COPY_ONE_EXT2_FLAG(flags, src, APPEND); COPY_ONE_EXT2_FLAG(flags, src, SYNC); -- cgit v1.2.3 From 1d46d6ba2c888b55c064d5c3c4cb49902e201279 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Mon, 26 Jun 2017 17:55:02 -0600 Subject: Btrfs-progs: convert: do not clear header rev So btrfs_set_header_flags() vs btrfs_set_header_flag, the difference is sort of similar to "=" vs "|=", when creating and initialising a new extent buffer, convert uses the former one which clears header_rev by accident. Signed-off-by: Liu Bo Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 40bf32cf..f0dd2cfc 100644 --- a/convert/common.c +++ b/convert/common.c @@ -167,7 +167,7 @@ static int setup_temp_extent_buffer(struct extent_buffer *buf, btrfs_set_header_generation(buf, 1); btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(buf, owner); - btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN); + btrfs_set_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN); write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf), BTRFS_UUID_SIZE); write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); -- cgit v1.2.3 From 3c7a04e59272837b8d70f2cfdfd7173d53c72616 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 17 May 2017 16:42:50 +0800 Subject: btrfs-progs: Remove deprecated leafsize usage Leafsize is deprecated for a long time, and kernel has already updated ctree.h to rename sb->leafsize to sb->__unused_leafsize. This patch will remove normal users of leafsize: 1) Remove leafsize member from btrfs_root structure Now only root->nodesize and root->sectorisze. No longer root->leafsize. 2) Remove @leafsize parameter from btrfs_setup_root() function Since no root->leafsize, no need for @leafsize parameter. The remaining user of leafsize will be: 1) btrfs inspect-internal dump-super Reformat the "leafsize" output to "leafsize (deprecated)" and use le32_to_cpu() to do the cast manually. 2) mkfs We still need to set sb->__unused_leafsize to nodesize. Do the manual cast too. 3) convert Same as mkfs, these two superblock setup should be merged later Signed-off-by: Qu Wenruo --- convert/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index f0dd2cfc..3860f3b9 100644 --- a/convert/common.c +++ b/convert/common.c @@ -128,7 +128,7 @@ static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg, */ btrfs_set_super_bytes_used(super, 6 * cfg->nodesize); btrfs_set_super_sectorsize(super, cfg->sectorsize); - btrfs_set_super_leafsize(super, cfg->nodesize); + super->__unused_leafsize = cpu_to_le32(cfg->nodesize); btrfs_set_super_nodesize(super, cfg->nodesize); btrfs_set_super_stripesize(super, cfg->stripesize); btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32); -- cgit v1.2.3 From 2b50d3b82ab8f776a00a62a6c9ca5bc61541163b Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 18 May 2017 10:11:46 +0800 Subject: btrfs-progs: Refactor sectorsize in convert/source-fs.c Signed-off-by: Qu Wenruo --- convert/source-fs.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'convert') diff --git a/convert/source-fs.c b/convert/source-fs.c index 80e4e418..59e36095 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -68,9 +68,10 @@ int block_iterate_proc(u64 disk_block, u64 file_block, int do_barrier; struct btrfs_root *root = idata->root; struct btrfs_block_group_cache *cache; - u64 bytenr = disk_block * root->sectorsize; + u32 sectorsize = root->fs_info->sectorsize; + u64 bytenr = disk_block * sectorsize; - sb_region = intersect_with_sb(bytenr, root->sectorsize); + sb_region = intersect_with_sb(bytenr, sectorsize); do_barrier = sb_region || disk_block >= idata->boundary; if ((idata->num_blocks > 0 && do_barrier) || (file_block > idata->first_block + idata->num_blocks) || @@ -102,7 +103,7 @@ int block_iterate_proc(u64 disk_block, u64 file_block, idata->first_block = file_block; idata->disk_block = disk_block; - idata->boundary = bytenr / root->sectorsize; + idata->boundary = bytenr / sectorsize; } idata->num_blocks++; fail: @@ -195,9 +196,10 @@ int record_file_blocks(struct blk_iterate_data *data, struct btrfs_root *root = data->root; struct btrfs_root *convert_root = data->convert_root; struct btrfs_path path; - u64 file_pos = file_block * root->sectorsize; - u64 old_disk_bytenr = disk_block * root->sectorsize; - u64 num_bytes = num_blocks * root->sectorsize; + u32 sectorsize = root->fs_info->sectorsize; + u64 file_pos = file_block * sectorsize; + u64 old_disk_bytenr = disk_block * sectorsize; + u64 num_bytes = num_blocks * sectorsize; u64 cur_off = old_disk_bytenr; /* Hole, pass it to record_file_extent directly */ -- cgit v1.2.3 From df4ad8281069e3c2faf4771a04f94b9d01ff8ca2 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 18 May 2017 10:13:27 +0800 Subject: btrfs-progs: Refactor sectorsize users in convert/main.c Signed-off-by: Qu Wenruo --- convert/main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index c56382e9..bf050132 100644 --- a/convert/main.c +++ b/convert/main.c @@ -159,7 +159,7 @@ static int csum_disk_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 disk_bytenr, u64 num_bytes) { - u32 blocksize = root->sectorsize; + u32 blocksize = root->fs_info->sectorsize; u64 offset; char *buffer; int ret = 0; @@ -199,12 +199,12 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, int ret; u32 datacsum = convert_flags & CONVERT_FLAG_DATACSUM; - if (bytenr != round_down(bytenr, root->sectorsize)) { + if (bytenr != round_down(bytenr, root->fs_info->sectorsize)) { error("bytenr not sectorsize aligned: %llu", (unsigned long long)bytenr); return -EINVAL; } - if (len != round_down(len, root->sectorsize)) { + if (len != round_down(len, root->fs_info->sectorsize)) { error("length not sectorsize aligned: %llu", (unsigned long long)len); return -EINVAL; @@ -293,7 +293,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, bg_cache->key.offset - bytenr); } - if (len != round_down(len, root->sectorsize)) { + if (len != round_down(len, root->fs_info->sectorsize)) { error("remaining length not sectorsize aligned: %llu", (unsigned long long)len); return -EINVAL; @@ -346,7 +346,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, cur_off = max(cache->start, cur_off); cur_len = min(cache->start + cache->size, range_end(range)) - cur_off; - BUG_ON(cur_len < root->sectorsize); + BUG_ON(cur_len < root->fs_info->sectorsize); /* reserve extent for the data */ ret = btrfs_reserve_extent(trans, root, cur_len, 0, 0, (u64)-1, @@ -1011,7 +1011,8 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, */ max_chunk_size = cfg->num_bytes / 10; max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size); - max_chunk_size = round_down(max_chunk_size, extent_root->sectorsize); + max_chunk_size = round_down(max_chunk_size, + extent_root->fs_info->sectorsize); for (cache = first_cache_extent(data_chunks); cache; cache = next_cache_extent(cache)) { -- cgit v1.2.3 From ec5a427feca56f5587ba668358ebbbe0422c9c39 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 18 May 2017 10:14:19 +0800 Subject: btrfs-progs: Refactor sectorsize users in convert/source-ext2.c Signed-off-by: Qu Wenruo --- convert/source-ext2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 38c3cd33..c6f9f28e 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -290,7 +290,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, char *buffer = NULL; errcode_t err; u32 last_block; - u32 sectorsize = root->sectorsize; + u32 sectorsize = root->fs_info->sectorsize; u64 inode_size = btrfs_stack_inode_size(btrfs_inode); struct blk_iterate_data data; -- cgit v1.2.3 From 790b5950f3d34b84498feaf00b8646eff02f1803 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 13 Jun 2017 17:19:21 +0800 Subject: btrfs-progs: Refactor write_and_map_eb to use btrfs_fs_info Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index bf050132..103cd5cd 100644 --- a/convert/main.c +++ b/convert/main.c @@ -370,7 +370,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, eb->len = key.offset; /* Write the data */ - ret = write_and_map_eb(root, eb); + ret = write_and_map_eb(root->fs_info, eb); free(eb); if (ret < 0) break; -- cgit v1.2.3 From 0544aafcbf48a8b70090cc515b9d130bac10554e Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 13 Jun 2017 17:19:35 +0800 Subject: btrfs-progs: Refactor chunk creation functions to use btrfs_fs_info 4 functions are involved in this refactor: btrfs_make_block_group() btrfs_make_block_groups(), btrfs_alloc_chunk, btrfs_alloc_data_chunk(). Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 103cd5cd..4f65765d 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1024,12 +1024,12 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, len = min(max_chunk_size, cache->start + cache->size - cur); - ret = btrfs_alloc_data_chunk(trans, extent_root, + ret = btrfs_alloc_data_chunk(trans, fs_info, &cur_backup, len, BTRFS_BLOCK_GROUP_DATA, 1); if (ret < 0) break; - ret = btrfs_make_block_group(trans, extent_root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_DATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, cur, len); -- cgit v1.2.3 From 1639cc2a511cb6f2a40b706b3c41e5cb4e01b809 Mon Sep 17 00:00:00 2001 From: Adam Buchbinder Date: Wed, 12 Jul 2017 13:05:38 -0700 Subject: btrfs-progs: convert: Fix data race when reporting progress The status display was reading the state while the task was updating it. Use a mutex to prevent the race. This race was detected using ThreadSanitizer and misc-tests/005-convert-progress-thread-crash. ================== WARNING: ThreadSanitizer: data race Write of size 8 by main thread: #0 ext2_copy_inodes btrfs-progs/convert/source-ext2.c:853 #1 copy_inodes btrfs-progs/convert/main.c:145 #2 do_convert btrfs-progs/convert/main.c:1297 #3 main btrfs-progs/convert/main.c:1924 Previous read of size 8 by thread T1: #0 print_copied_inodes btrfs-progs/convert/main.c:124 Location is stack of main thread. Thread T1 (running) created by main thread at: #0 pthread_create #1 task_start btrfs-progs/task-utils.c:50 #2 do_convert btrfs-progs/convert/main.c:1295 #3 main btrfs-progs/convert/main.c:1924 SUMMARY: ThreadSanitizer: data race btrfs-progs/convert/source-ext2.c:853 in ext2_copy_inodes Signed-off-by: Adam Buchbinder Signed-off-by: David Sterba --- convert/main.c | 8 ++++++++ convert/source-ext2.c | 3 +++ convert/source-fs.h | 3 +++ 3 files changed, 14 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 4f65765d..0deccd9c 100644 --- a/convert/main.c +++ b/convert/main.c @@ -88,6 +88,7 @@ #include #include #include +#include #include #include "ctree.h" @@ -119,10 +120,12 @@ static void *print_copied_inodes(void *p) task_period_start(priv->info, 1000 /* 1s */); while (1) { count++; + pthread_mutex_lock(&priv->mutex); printf("copy inodes [%c] [%10llu/%10llu]\r", work_indicator[count % 4], (unsigned long long)priv->cur_copy_inodes, (unsigned long long)priv->max_copy_inodes); + pthread_mutex_unlock(&priv->mutex); fflush(stdout); task_period_wait(priv->info); } @@ -1287,6 +1290,11 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, } printf("creating btrfs metadata"); + ret = pthread_mutex_init(&ctx.mutex, NULL); + if (ret) { + error("failed to initialize mutex: %d", ret); + goto fail; + } ctx.max_copy_inodes = (cctx.inodes_count - cctx.free_inodes_count); ctx.cur_copy_inodes = 0; diff --git a/convert/source-ext2.c b/convert/source-ext2.c index c6f9f28e..24744e22 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -18,6 +18,7 @@ #include "kerncompat.h" #include +#include #include "disk-io.h" #include "transaction.h" #include "utils.h" @@ -850,7 +851,9 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, ret = ext2_copy_single_inode(trans, root, objectid, ext2_fs, ext2_ino, &ext2_inode, convert_flags); + pthread_mutex_lock(&p->mutex); p->cur_copy_inodes++; + pthread_mutex_unlock(&p->mutex); if (ret) return ret; if (trans->blocks_used >= 4096) { diff --git a/convert/source-fs.h b/convert/source-fs.h index ca32d159..3a6fa46c 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -18,6 +18,8 @@ #define __BTRFS_CONVERT_SOURCE_FS_H__ #include "kerncompat.h" +#include + #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID @@ -37,6 +39,7 @@ extern const struct simple_range btrfs_reserved_ranges[3]; struct task_info; struct task_ctx { + pthread_mutex_t mutex; u64 max_copy_inodes; u64 cur_copy_inodes; struct task_info *info; -- cgit v1.2.3 From 77083770759166dd42f1cc17f173148b51ae7d28 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 25 Jul 2017 16:54:41 -0400 Subject: btrfs-progs: convert: properly handle reserved ranges while iterating files Commit 522ef705e38 (btrfs-progs: convert: Introduce function to calculate the available space) changed how we handle migrating file data so that we never have btrfs space associated with the reserved ranges. This works pretty well and when we iterate over the file blocks, the associations are redirected to the migrated locations. This commit missed the case in block_iterate_proc where we just check for intersection with a superblock location before looking up a block group. intersect_with_sb checks to see if the range intersects with a stripe containing a superblock but, in fact, we've reserved the full 0-1MB range at the start of the disk. So a file block located at e.g. 160kB will fall in the reserved region but won't be excepted in block_iterate_block. We ultimately hit a BUG_ON when we fail to look up the block group for that location. This is reproducible using convert-tests/003-ext4-basic. The fix is to have intersect_with_sb and block_iterate_proc understand the full size of the reserved ranges. Since we use the range to determine the boundary for the block iterator, let's just return the boundary. 0 isn't a valid boundary and means that we proceed normally with block group lookup. Cc: Qu Wenruo Signed-off-by: Jeff Mahoney Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-fs.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'convert') diff --git a/convert/source-fs.c b/convert/source-fs.c index 59e36095..4755efb2 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -28,18 +28,16 @@ const struct simple_range btrfs_reserved_ranges[3] = { { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K } }; -static int intersect_with_sb(u64 bytenr, u64 num_bytes) +static u64 intersect_with_reserved(u64 bytenr, u64 num_bytes) { int i; - u64 offset; - for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { - offset = btrfs_sb_offset(i); - offset &= ~((u64)BTRFS_STRIPE_LEN - 1); + for (i = 0; i < ARRAY_SIZE(btrfs_reserved_ranges); i++) { + const struct simple_range *range = &btrfs_reserved_ranges[i]; - if (bytenr < offset + BTRFS_STRIPE_LEN && - bytenr + num_bytes > offset) - return 1; + if (bytenr < range_end(range) && + bytenr + num_bytes >= range->start) + return range_end(range); } return 0; } @@ -64,15 +62,15 @@ int block_iterate_proc(u64 disk_block, u64 file_block, struct blk_iterate_data *idata) { int ret = 0; - int sb_region; + u64 reserved_boundary; int do_barrier; struct btrfs_root *root = idata->root; struct btrfs_block_group_cache *cache; u32 sectorsize = root->fs_info->sectorsize; u64 bytenr = disk_block * sectorsize; - sb_region = intersect_with_sb(bytenr, sectorsize); - do_barrier = sb_region || disk_block >= idata->boundary; + reserved_boundary = intersect_with_reserved(bytenr, sectorsize); + do_barrier = reserved_boundary || disk_block >= idata->boundary; if ((idata->num_blocks > 0 && do_barrier) || (file_block > idata->first_block + idata->num_blocks) || (disk_block != idata->disk_block + idata->num_blocks)) { @@ -92,9 +90,8 @@ int block_iterate_proc(u64 disk_block, u64 file_block, goto fail; } - if (sb_region) { - bytenr += BTRFS_STRIPE_LEN - 1; - bytenr &= ~((u64)BTRFS_STRIPE_LEN - 1); + if (reserved_boundary) { + bytenr = reserved_boundary; } else { cache = btrfs_lookup_block_group(root->fs_info, bytenr); BUG_ON(!cache); -- cgit v1.2.3 From 10726f7423e9179d367fb6eb9e37d8ffd91a7f66 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 25 Jul 2017 16:54:42 -0400 Subject: btrfs-progs: convert: add missing newlines for printfs There are two printfs with missing newlines that end up making the output wonky. Signed-off-by: Jeff Mahoney Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 0deccd9c..800fb1e2 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1289,7 +1289,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, goto fail; } - printf("creating btrfs metadata"); + printf("creating btrfs metadata\n"); ret = pthread_mutex_init(&ctx.mutex, NULL); if (ret) { error("failed to initialize mutex: %d", ret); @@ -1357,7 +1357,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, close_ctree(root); close(fd); - printf("conversion complete"); + printf("conversion complete\n"); return 0; fail: clean_convert_context(&cctx); -- cgit v1.2.3 From f2c14eb282a87d7e622538f76dfbc356cb73c1d4 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 27 Jul 2017 11:47:21 -0400 Subject: btrfs-progs: convert: use search_cache_extent in migrate_one_reserved_range When we are looking for extents in migrate_one_reserved_range, it's likely that there will be multiple extents that fall into the 0-1MB range. If lookup_cache_extent is called with a range that covers multiple cache entries, it will return the first entry it encounters while searching from the top of the tree that happens to fall in that range. That means that we can end up skipping regions within that range, resulting in a file system image that can't be rolled back since it wasn't all migrated properly. This is reproducible using convert-tests/008-readonly-image. There was a range from 0-160kB, but the only entry that was returned began at ~ 280kB. The fix is to use search_cache_extent to iterate through multiple regions within that range. Signed-off-by: Jeff Mahoney Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 800fb1e2..03da9e49 100644 --- a/convert/main.c +++ b/convert/main.c @@ -343,10 +343,12 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, * migrate ranges that covered by old fs data. */ while (cur_off < range_end(range)) { - cache = lookup_cache_extent(used, cur_off, cur_len); + cache = search_cache_extent(used, cur_off); if (!cache) break; cur_off = max(cache->start, cur_off); + if (cur_off >= range_end(range)) + break; cur_len = min(cache->start + cache->size, range_end(range)) - cur_off; BUG_ON(cur_len < root->fs_info->sectorsize); -- cgit v1.2.3 From cbaa70b265aa4161d92fe87fde586e1660905be2 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 27 Jul 2017 11:17:00 +0300 Subject: btrfs-progs: Use named constants for common sizes There multiple places where we use well-known sizes - 1,8,16,32 megabytes. We also have them defined as constants in the sizes.h header. So let's use them. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- convert/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 03da9e49..43ee0be3 100644 --- a/convert/main.c +++ b/convert/main.c @@ -635,7 +635,7 @@ static int calculate_available_space(struct btrfs_convert_context *cctx) * Twice the minimal chunk size, to allow later wipe_reserved_ranges() * works without need to consider overlap */ - u64 min_stripe_size = 2 * 16 * 1024 * 1024; + u64 min_stripe_size = SZ_32M; int ret; /* Calculate data_chunks */ @@ -800,7 +800,7 @@ static int create_image(struct btrfs_root *root, * Start from 1M, as 0~1M is reserved, and create_image_file_range() * can't handle bytenr 0(will consider it as a hole) */ - cur = 1024 * 1024; + cur = SZ_1M; while (cur < size) { u64 len = size - cur; @@ -1015,7 +1015,7 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, * And for single chunk, don't create chunk larger than 1G. */ max_chunk_size = cfg->num_bytes / 10; - max_chunk_size = min((u64)(1024 * 1024 * 1024), max_chunk_size); + max_chunk_size = min((u64)(SZ_1G), max_chunk_size); max_chunk_size = round_down(max_chunk_size, extent_root->fs_info->sectorsize); @@ -1597,7 +1597,7 @@ next: * | RSV 1 | | Old | | RSV 2 | | Old | | RSV 3 | * | 0~1M | | Fs | | SB2 + 64K | | Fs | | SB3 + 64K | * - * On the other hande, the converted fs image in btrfs is a completely + * On the other hand, the converted fs image in btrfs is a completely * valid old fs. * * |<-----------------Converted fs image in btrfs-------------------->| -- cgit v1.2.3 From 99340c2ef762f3da92e4bb4ce3b2f6d5fc6ef9d0 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 22 Aug 2017 18:30:43 +0200 Subject: btrfs-progs: convert: add support for converting reiserfs This patch adds support to convert reiserfs file systems in-place to btrfs. It will convert extended attribute files to btrfs extended attributes, translate ACLs, coalesce tails that consist of multiple items into one item, and convert tails that are too big into indirect files. This requires that libreiserfscore 3.6.27 be available. Signed-off-by: Jeff Mahoney Signed-off-by: David Sterba --- convert/main.c | 13 +- convert/source-reiserfs.c | 1015 +++++++++++++++++++++++++++++++++++++++++++++ convert/source-reiserfs.h | 106 +++++ 3 files changed, 1130 insertions(+), 4 deletions(-) create mode 100644 convert/source-reiserfs.c create mode 100644 convert/source-reiserfs.h (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 43ee0be3..22884bd5 100644 --- a/convert/main.c +++ b/convert/main.c @@ -103,12 +103,16 @@ #include "convert/source-fs.h" #include "fsfeatures.h" -const struct btrfs_convert_operations ext2_convert_ops; +extern const struct btrfs_convert_operations ext2_convert_ops; +extern const struct btrfs_convert_operations reiserfs_convert_ops; static const struct btrfs_convert_operations *convert_operations[] = { #if BTRFSCONVERT_EXT2 &ext2_convert_ops, #endif +#if BTRFSCONVERT_REISERFS + &reiserfs_convert_ops, +#endif }; static void *print_copied_inodes(void *p) @@ -416,7 +420,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, } /* - * Relocate the used ext2 data in reserved ranges + * Relocate the used source fs data in reserved ranges */ static int migrate_reserved_ranges(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -1666,11 +1670,11 @@ static int do_rollback(const char *devname) ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0); btrfs_release_path(&path); if (ret > 0) { - error("unable to find ext2 image subvolume, is it deleted?"); + error("unable to find source fs image subvolume, is it deleted?"); ret = -ENOENT; goto close_fs; } else if (ret < 0) { - error("failed to find ext2 image subvolume: %s", + error("failed to find source fs image subvolume: %s", strerror(-ret)); goto close_fs; } @@ -1788,6 +1792,7 @@ static void print_usage(void) printf("\n"); printf("Supported filesystems:\n"); printf("\text2/3/4: %s\n", BTRFSCONVERT_EXT2 ? "yes" : "no"); + printf("\treiserfs: %s\n", BTRFSCONVERT_REISERFS ? "yes" : "no"); } int main(int argc, char *argv[]) diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c new file mode 100644 index 00000000..8b3286f6 --- /dev/null +++ b/convert/source-reiserfs.c @@ -0,0 +1,1015 @@ +/* + * 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. + */ + +#if BTRFSCONVERT_REISERFS + +#include "kerncompat.h" +#include +#include +#include +#include +#include +#include "disk-io.h" +#include "transaction.h" +#include "utils.h" +#include "bitops.h" +#include "convert/common.h" +#include "convert/source-reiserfs.h" + +static inline u8 mode_to_file_type(u32 mode) +{ + switch (mode & S_IFMT) { + case S_IFREG: return BTRFS_FT_REG_FILE; + case S_IFDIR: return BTRFS_FT_DIR; + case S_IFCHR: return BTRFS_FT_CHRDEV; + case S_IFBLK: return BTRFS_FT_BLKDEV; + case S_IFIFO: return BTRFS_FT_FIFO; + case S_IFSOCK: return BTRFS_FT_SOCK; + case S_IFLNK: return BTRFS_FT_SYMLINK; + }; + + return BTRFS_FT_UNKNOWN; +} + +struct reiserfs_convert_info { + bool copy_attrs; + struct reiserfs_key privroot_key; + struct reiserfs_key xattr_key; + + /* only set during copy_inodes */ + struct task_ctx *progress; + + /* used to track hardlinks */ + unsigned used_slots; + unsigned alloced_slots; + u64 *objectids; +}; + +static u32 reiserfs_count_objectids(reiserfs_filsys_t fs) +{ + struct reiserfs_super_block *sb = fs->fs_ondisk_sb; + u32 count = 0; + u32 *map; + int i; + + if (fs->fs_format == REISERFS_FORMAT_3_6) + map = (u32 *) (sb + 1); + else + map = (u32 *)((struct reiserfs_super_block_v1 *)sb + 1); + + for (i = 0; i < get_sb_oid_cursize(sb); i += 2) + count += le32_to_cpu(map[i + 1]) - (le32_to_cpu(map[i]) + 1); + + return count; +} + + +static int reiserfs_open_fs(struct btrfs_convert_context *cxt, const char *name) +{ + struct reiserfs_convert_info *info; + reiserfs_filsys_t fs; + long error; + + fs = reiserfs_open(name, O_RDONLY, &error, NULL, 0); + if (!fs) + return -1; + + error = reiserfs_open_ondisk_bitmap(fs); + if (error) { + reiserfs_close(fs); + return -1; + } + + cxt->fs_data = fs; + cxt->blocksize = fs->fs_blocksize; + cxt->block_count = get_sb_block_count(fs->fs_ondisk_sb); + cxt->total_bytes = cxt->blocksize * cxt->block_count; + cxt->volume_name = strndup(fs->fs_ondisk_sb->s_label, 16); + cxt->first_data_block = 0; + cxt->inodes_count = reiserfs_count_objectids(fs); + cxt->free_inodes_count = 0; + info = calloc(1, sizeof(*info)); + if (!info) { + reiserfs_close(fs); + return -1; + } + + /* + * Inode attributes are somewhat of a hack on reiserfs and it was + * once possible to have garbage in the flags field. A superblock + * field now indicates that the field has been cleared and can + * be considered valid, but only on v3.6 format file systems. + */ + if (fs->fs_format == REISERFS_FORMAT_3_6 && + get_sb_v2_flag(fs->fs_ondisk_sb, reiserfs_attrs_cleared)) + info->copy_attrs = true; + + fs->fs_vp = info; + return 0; +} + +static void reiserfs_close_fs(struct btrfs_convert_context *cxt) +{ + reiserfs_filsys_t fs = cxt->fs_data; + struct reiserfs_convert_info *info = fs->fs_vp; + + if (info) { + if (info->objectids) + free(info->objectids); + free(info); + fs->fs_vp = NULL; + } + + /* We don't want changes to be persistent */ + fs->fs_bitmap2->bm_dirty = 0; + + reiserfs_close(fs); +} + +static int compare_objectids(const void *p1, const void *p2) +{ + u64 v1 = *(u64 *)p1; + u64 v2 = *(u64 *)p2; + + if (v1 > v2) + return 1; + else if (v1 < v2) + return -1; + return 0; +} + +static int lookup_cached_objectid(reiserfs_filsys_t fs, u64 objectid) +{ + struct reiserfs_convert_info *info = fs->fs_vp; + u64 *result; + + if (!info->objectids) + return 0; + result = bsearch(&objectid, info->objectids, info->used_slots, + sizeof(u64), compare_objectids); + return result != NULL; +} + +static int insert_cached_objectid(reiserfs_filsys_t fs, u64 objectid) +{ + struct reiserfs_convert_info *info = fs->fs_vp; + + if (info->used_slots + 1 >= info->alloced_slots) { + u64 *objectids = realloc(info->objectids, + (info->alloced_slots + 1000) * sizeof(u64)); + + if (!objectids) + return -ENOMEM; + info->objectids = objectids; + info->alloced_slots += 1000; + } + info->objectids[info->used_slots++] = objectid; + + qsort(info->objectids, info->used_slots, sizeof(u64), compare_objectids); + return 0; +} + +static int reiserfs_locate_privroot(reiserfs_filsys_t fs) +{ + int err; + unsigned generation; + struct reiserfs_convert_info *info = fs->fs_vp; + struct reiserfs_key key = root_dir_key; + + err = reiserfs_find_entry(fs, &key, ".reiserfs_priv", + &generation, &info->privroot_key); + if (err == 1) { + err = reiserfs_find_entry(fs, &info->privroot_key, "xattrs", + &generation, &info->xattr_key); + if (err != 1) + memset(&info->xattr_key, 0, sizeof(info->xattr_key)); + } + + return 0; +} + +static void reiserfs_convert_inode_flags(struct btrfs_inode_item *inode, + const struct stat_data *sd) +{ + u16 attrs = sd_v2_sd_attrs(sd); + u64 new_flags = 0; + + if (attrs & FS_IMMUTABLE_FL) + new_flags |= BTRFS_INODE_IMMUTABLE; + + if (attrs & FS_APPEND_FL) + new_flags |= BTRFS_INODE_APPEND; + + if (attrs & FS_SYNC_FL) + new_flags |= BTRFS_INODE_SYNC; + + if (attrs & FS_NOATIME_FL) + new_flags |= BTRFS_INODE_NOATIME; + + if (attrs & FS_NODUMP_FL) + new_flags |= BTRFS_INODE_NODUMP; + + if (attrs & FS_NODUMP_FL) + new_flags |= BTRFS_INODE_NODUMP; + + btrfs_set_stack_inode_flags(inode, new_flags); + +} + +static void reiserfs_copy_inode_item(struct btrfs_inode_item *inode, + struct item_head *ih, void *stat_data, + bool copy_inode_flags) +{ + u32 mode; + u32 rdev = 0; + + memset(inode, 0, sizeof(*inode)); + btrfs_set_stack_inode_generation(inode, 1); + if (get_ih_key_format(ih) == KEY_FORMAT_1) { + struct stat_data_v1 *sd = stat_data; + + mode = sd_v1_mode(sd); + btrfs_set_stack_inode_size(inode, sd_v1_size(sd)); + btrfs_set_stack_inode_nlink(inode, sd_v1_nlink(sd)); + btrfs_set_stack_inode_uid(inode, sd_v1_uid(sd)); + btrfs_set_stack_inode_gid(inode, sd_v1_gid(sd)); + btrfs_set_stack_timespec_sec(&inode->atime, sd_v1_atime(sd)); + btrfs_set_stack_timespec_sec(&inode->ctime, sd_v1_ctime(sd)); + btrfs_set_stack_timespec_sec(&inode->mtime, sd_v1_mtime(sd)); + + if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)) + rdev = new_decode_dev(sd_v1_rdev(sd)); + } else { + struct stat_data *sd = stat_data; + + mode = sd_v2_mode(sd); + btrfs_set_stack_inode_size(inode, sd_v2_size(sd)); + btrfs_set_stack_inode_nlink(inode, sd_v2_nlink(sd)); + btrfs_set_stack_inode_uid(inode, sd_v2_uid(sd)); + btrfs_set_stack_inode_gid(inode, sd_v2_gid(sd)); + btrfs_set_stack_timespec_sec(&inode->atime, sd_v2_atime(sd)); + btrfs_set_stack_timespec_sec(&inode->ctime, sd_v2_ctime(sd)); + btrfs_set_stack_timespec_sec(&inode->mtime, sd_v2_mtime(sd)); + + if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)) + rdev = new_decode_dev(sd_v2_rdev(sd)); + + if (copy_inode_flags) + reiserfs_convert_inode_flags(inode, sd); + + } + if (S_ISDIR(mode)) { + btrfs_set_stack_inode_size(inode, 0); + btrfs_set_stack_inode_nlink(inode, 1); + } + btrfs_set_stack_inode_mode(inode, mode); + btrfs_set_stack_inode_rdev(inode, rdev); +} + +struct reiserfs_blk_iterate_data { + struct blk_iterate_data blk_data; + char *inline_data; + u64 inline_offset; + u32 inline_length; +}; + +static void init_reiserfs_blk_iterate_data( + struct reiserfs_blk_iterate_data *data, + struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_inode_item *inode, + u64 objectid, u32 convert_flags) +{ + init_blk_iterate_data(&data->blk_data, trans, root, inode, objectid, + convert_flags & CONVERT_FLAG_DATACSUM); + data->inline_data = NULL; + data->inline_offset = (u64)-1; + data->inline_length = 0; +} + +static int reiserfs_record_indirect_extent(reiserfs_filsys_t fs, u64 position, + u64 size, int num_ptrs, + u32 *ptrs, void *data) +{ + struct reiserfs_blk_iterate_data *bdata = data; + u32 file_block = position / fs->fs_blocksize; + int i; + int ret = 0; + + for (i = 0; i < num_ptrs; i++, file_block++) { + u32 block = d32_get(ptrs, i); + + ret = block_iterate_proc(block, file_block, &bdata->blk_data); + if (ret) + break; + } + + return ret; +} + +/* + * Unlike btrfs inline extents, reiserfs can have multiple inline extents. + * This handles concatanating multiple tails into one inline extent + * for insertion. + */ +static int reiserfs_record_direct_extent(reiserfs_filsys_t fs, __u64 position, + __u64 size, const char *body, + size_t len, void *data) +{ + struct reiserfs_blk_iterate_data *bdata = data; + char *inline_data; + + if (bdata->inline_offset == (u64)-1) + bdata->inline_offset = position; + else if (bdata->inline_offset + bdata->inline_length != position) { + /* + * This condition shouldn't actually happen, but better to + * catch it than break silently. + */ + error( +"source fs contains file with multiple tails but they are not contiguous"); + return -EINVAL; + } + + inline_data = realloc(bdata->inline_data, bdata->inline_length + len); + if (!inline_data) + return -ENOMEM; + + bdata->inline_data = inline_data; + memcpy(bdata->inline_data + bdata->inline_length, body, len); + bdata->inline_length += len; + + return 0; +} + +static int convert_direct(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, const char *body, + u32 length, u64 offset, u32 convert_flags) +{ + struct btrfs_key key; + u32 sectorsize = root->fs_info->sectorsize; + int ret; + struct extent_buffer *eb; + + BUG_ON(length > sectorsize); + ret = btrfs_reserve_extent(trans, root, sectorsize, + 0, 0, -1ULL, &key, 1); + if (ret) + return ret; + + eb = alloc_extent_buffer(&root->fs_info->extent_cache, key.objectid, + sectorsize); + + if (!eb) + return -ENOMEM; + + write_extent_buffer(eb, body, 0, length); + ret = write_and_map_eb(root->fs_info, eb); + free_extent_buffer(eb); + if (ret) + return ret; + + return btrfs_record_file_extent(trans, root, objectid, inode, offset, + key.objectid, sectorsize); +} + +static int reiserfs_convert_tail(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_inode_item *inode, + u64 objectid, u64 offset, + const void *body, unsigned length, + u32 convert_flags) +{ + u64 isize; + int ret; + + if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root)) + return convert_direct(trans, root, objectid, inode, body, + length, offset, convert_flags); + + ret = btrfs_insert_inline_extent(trans, root, objectid, + offset, body, length); + if (ret) + return ret; + + isize = btrfs_stack_inode_nbytes(inode); + btrfs_set_stack_inode_nbytes(inode, isize + length); + + return 0; +} + +static inline u32 block_count(u64 size, u32 blocksize) +{ + return round_up(size, blocksize) / blocksize; +} + +static int reiserfs_record_file_extents(reiserfs_filsys_t fs, + struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 objectid, + struct btrfs_inode_item *inode, + struct reiserfs_key *sd_key, + u32 convert_flags) + +{ + int ret; + u32 blocksize = fs->fs_blocksize; + u64 inode_size = btrfs_stack_inode_size(inode); + u32 last_block; + struct reiserfs_blk_iterate_data data; + + init_reiserfs_blk_iterate_data(&data, trans, root, inode, + objectid, convert_flags); + + ret = reiserfs_iterate_file_data(fs, sd_key, + reiserfs_record_indirect_extent, + reiserfs_record_direct_extent, &data); + if (ret) + return ret; + + /* + * blk_iterate_block has no idea that we're done iterating, so record + * the final range if any. This range can end and still have a tail + * after it. + */ + if (data.blk_data.num_blocks) { + ret = record_file_blocks(&data.blk_data, + data.blk_data.first_block, + data.blk_data.disk_block, + data.blk_data.num_blocks); + if (ret) + goto fail; + data.blk_data.first_block += data.blk_data.num_blocks; + data.blk_data.num_blocks = 0; + } + + /* + * Handle a hole at the end of the file. ReiserFS will + * not write a tail followed by a hole but it will write a hole + * followed by a tail. + */ + last_block = block_count(inode_size - data.inline_length, blocksize); + if (last_block > data.blk_data.first_block) { + ret = record_file_blocks(&data.blk_data, + data.blk_data.first_block, 0, + last_block - data.blk_data.first_block); + if (ret) + goto fail; + } + + if (data.inline_length) { + ret = reiserfs_convert_tail(trans, root, inode, objectid, + data.inline_offset, + data.inline_data, + data.inline_length, convert_flags); + if (ret) + goto fail; + } + + ret = 0; +fail: + return ret; +} + +#define OID_OFFSET (BTRFS_FIRST_FREE_OBJECTID - REISERFS_ROOT_OBJECTID) +static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, + u32 convert_flags, u32 deh_dirid, + u32 deh_objectid, u8 *type); + +struct reiserfs_dirent_data { + u64 index; + u32 convert_flags; + struct btrfs_inode_item *inode; + struct btrfs_root *root; +}; + +static int reiserfs_copy_dirent(reiserfs_filsys_t fs, + const struct reiserfs_key *dir_short_key, + const char *name, size_t len, + __u32 deh_dirid, __u32 deh_objectid, + void *cb_data) +{ + int ret; + u8 type; + struct btrfs_trans_handle *trans; + u64 objectid = deh_objectid + OID_OFFSET; + struct reiserfs_convert_info *info = fs->fs_vp; + struct reiserfs_dirent_data *dirent_data = cb_data; + struct btrfs_root *root = dirent_data->root; + __u32 dir_objectid = get_key_objectid(dir_short_key) + OID_OFFSET; + + /* + * These are the extended attributes and shouldn't appear as files + * in the converted file systems. + */ + if (deh_objectid == get_key_objectid(&info->privroot_key)) + return 0; + + ret = reiserfs_copy_meta(fs, root, dirent_data->convert_flags, + deh_dirid, deh_objectid, &type); + if (ret) { + error( + "an error occured while converting \"%.*s\", reiserfs key [%u %u]: %s", + (int)len, name, deh_dirid, deh_objectid, + strerror(-ret)); + return ret; + } + trans = btrfs_start_transaction(root, 1); + if (!trans) + return -ENOMEM; + + ret = convert_insert_dirent(trans, root, name, len, dir_objectid, + objectid, type, dirent_data->index++, + dirent_data->inode); + return btrfs_commit_transaction(trans, root); +} + +static int reiserfs_copy_symlink(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *btrfs_inode, + reiserfs_filsys_t fs, + struct reiserfs_path *sd_path) +{ + INITIALIZE_REISERFS_PATH(path); + struct item_head *ih = tp_item_head(sd_path); + struct reiserfs_key key = ih->ih_key; + int ret; + char *symlink; + int len; + + set_key_uniqueness(&key, type2uniqueness(TYPE_DIRECT)); + set_key_offset_v1(&key, 1); + + ret = reiserfs_search_by_key_3(fs, &key, &path); + if (ret != ITEM_FOUND) { + ret = -ENOENT; + goto fail; + } + + symlink = tp_item_body(&path); + len = get_ih_item_len(tp_item_head(&path)); + + ret = btrfs_insert_inline_extent(trans, root, objectid, 0, + symlink, len + 1); + btrfs_set_stack_inode_nbytes(btrfs_inode, len + 1); +fail: + pathrelse(&path); + return ret; +} + +static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, + u32 convert_flags, u32 deh_dirid, + u32 deh_objectid, u8 *type) +{ + INITIALIZE_REISERFS_PATH(path); + int ret = 0; + struct item_head *ih; + struct reiserfs_key key; + struct btrfs_inode_item btrfs_inode; + struct btrfs_trans_handle *trans = NULL; + struct reiserfs_convert_info *info = fs->fs_vp; + u32 mode; + u64 objectid = deh_objectid + OID_OFFSET; + u64 parent = deh_dirid + OID_OFFSET; + struct reiserfs_dirent_data dirent_data = { + .index = 2, + .convert_flags = convert_flags, + .inode = &btrfs_inode, + .root = root, + }; + + /* The root directory's dirid in reiserfs points to an object + * that does't exist. In btrfs it's self-referential. + */ + if (deh_dirid == REISERFS_ROOT_PARENT_OBJECTID) + parent = objectid; + + set_key_dirid(&key, deh_dirid); + set_key_objectid(&key, deh_objectid); + set_key_offset_v2(&key, 0); + set_key_type_v2(&key, TYPE_STAT_DATA); + + ret = reiserfs_search_by_key_3(fs, &key, &path); + if (ret != ITEM_FOUND) { + ret = -ENOENT; + goto fail; + } + + ih = tp_item_head(&path); + if (!is_stat_data_ih(ih)) { + ret = -EINVAL; + goto fail; + } + + reiserfs_copy_inode_item(&btrfs_inode, ih, tp_item_body(&path), + info->copy_attrs); + mode = btrfs_stack_inode_mode(&btrfs_inode); + *type = mode_to_file_type(mode); + + if (S_ISREG(mode)) { + /* Inodes with hardlinks should only be inserted once */ + if (btrfs_stack_inode_nlink(&btrfs_inode) > 1) { + if (lookup_cached_objectid(fs, deh_objectid)) { + ret = 0; + goto fail; /* Not a failure */ + } + ret = insert_cached_objectid(fs, deh_objectid); + if (ret) + goto fail; + } + } + + if (!(convert_flags & CONVERT_FLAG_DATACSUM)) { + u32 flags = btrfs_stack_inode_flags(&btrfs_inode) | + BTRFS_INODE_NODATASUM; + btrfs_set_stack_inode_flags(&btrfs_inode, flags); + } + + switch (mode & S_IFMT) { + case S_IFREG: + trans = btrfs_start_transaction(root, 1); + if (!trans) { + ret = -ENOMEM; + goto fail; + } + ret = reiserfs_record_file_extents(fs, trans, root, objectid, + &btrfs_inode, &ih->ih_key, + convert_flags); + if (ret) + goto fail; + break; + case S_IFDIR: + ret = reiserfs_iterate_dir(fs, &ih->ih_key, + reiserfs_copy_dirent, &dirent_data); + if (ret) + goto fail; + trans = btrfs_start_transaction(root, 1); + if (!trans) { + ret = -ENOMEM; + goto fail; + } + + ret = btrfs_insert_inode_ref(trans, root, "..", 2, parent, + objectid, 0); + break; + case S_IFLNK: + trans = btrfs_start_transaction(root, 1); + if (!trans) { + ret = -ENOMEM; + goto fail; + } + ret = reiserfs_copy_symlink(trans, root, objectid, + &btrfs_inode, fs, &path); + if (ret) + goto fail; + break; + default: + trans = btrfs_start_transaction(root, 1); + if (!trans) { + ret = -ENOMEM; + goto fail; + } + } + + ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode); + if (ret) + goto fail; + ret = btrfs_commit_transaction(trans, root); + info->progress->cur_copy_inodes++; + +fail: + pathrelse(&path); + return ret; +} + +struct reiserfs_xattr_data { + struct btrfs_root *root; + struct btrfs_trans_handle *trans; + u64 target_oid; + const char *name; + size_t namelen; + void *body; + size_t len; +}; + +static int reiserfs_xattr_indirect_fn(reiserfs_filsys_t fs, u64 position, + u64 size, int num_blocks, + u32 *blocks, void *data) +{ + int i; + struct reiserfs_xattr_data *xa_data = data; + size_t alloc = min(position + num_blocks * fs->fs_blocksize, size); + char *body; + + if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) - + sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { + fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n", + xa_data->target_oid, (int)xa_data->namelen, + xa_data->name); + return -E2BIG; + } + + body = realloc(xa_data->body, alloc); + if (!body) + return -ENOMEM; + + xa_data->body = body; + xa_data->len = alloc; + + for (i = 0; i < num_blocks; i++) { + int ret; + u32 block = d32_get(blocks, i); + u64 offset = (u64)block * fs->fs_blocksize; + size_t chunk = min_t(u64, size - position, fs->fs_blocksize); + char *buffer = xa_data->body + position; + + ret = read_disk_extent(xa_data->root, offset, chunk, buffer); + if (ret) + return ret; + position += chunk; + } + + return 0; +} + +static int reiserfs_xattr_direct_fn(reiserfs_filsys_t fs, __u64 position, + __u64 size, const char *body, size_t len, + void *data) +{ + struct reiserfs_xattr_data *xa_data = data; + char *newbody; + + if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) - + sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { + fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n", + xa_data->target_oid, (int)xa_data->namelen, + xa_data->name); + return -E2BIG; + } + + newbody = realloc(xa_data->body, position + len); + if (!newbody) + return -ENOMEM; + xa_data->body = newbody; + xa_data->len = position + len; + memcpy(xa_data->body + position, body, len); + return 0; +} + +static int reiserfs_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; + struct reiserfs_acl_entry *src_entry; + + if (src_size < sizeof(struct reiserfs_acl_header)) + goto fail; + if (((struct reiserfs_acl_header *)src)->a_version != + cpu_to_le32(REISERFS_ACL_VERSION)) + goto fail; + src += sizeof(struct reiserfs_acl_header); + count = reiserfs_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 = (struct reiserfs_acl_entry *)src; + if (src + sizeof(struct reiserfs_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(struct reiserfs_acl_entry_short); + dst_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); + break; + case ACL_USER: + case ACL_GROUP: + src += sizeof(struct reiserfs_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 int reiserfs_copy_one_xattr(reiserfs_filsys_t fs, + const struct reiserfs_key *dir_short_key, + const char *name, size_t namelen, + __u32 deh_dirid, + __u32 deh_objectid, void *cb_data) +{ + struct reiserfs_convert_info *info = fs->fs_vp; + struct reiserfs_xattr_data *xa_data = cb_data; + struct reiserfs_key key = { + .k2_dir_id = deh_dirid, + .k2_objectid = deh_objectid, + }; + void *body = NULL; + int len; + int ret; + + xa_data->name = name; + xa_data->namelen = namelen; + + ret = reiserfs_iterate_file_data(fs, &key, reiserfs_xattr_indirect_fn, + reiserfs_xattr_direct_fn, cb_data); + if (ret) + goto out; + + if (!reiserfs_check_xattr(xa_data->body, xa_data->len)) { + fprintf(stderr, + "skip corrupted xattr on objectid %u name %.*s\n", + deh_objectid, (int)xa_data->namelen, + xa_data->name); + goto out; + } + + body = xa_data->body + sizeof(struct reiserfs_xattr_header); + len = xa_data->len - sizeof(struct reiserfs_xattr_header); + + if (!strncmp("system.posix_acl_default", name, namelen) || + !strncmp("system.posix_acl_access", name, namelen)) { + size_t bufsize = acl_ea_size(ext2_acl_count(len)); + char *databuf = malloc(bufsize); + + if (!databuf) + goto out; + ret = reiserfs_acl_to_xattr(databuf, body, bufsize, len); + if (ret) + goto out; + body = databuf; + len = bufsize; + } + + ret = btrfs_insert_xattr_item(xa_data->trans, xa_data->root, + name, namelen, body, len, + xa_data->target_oid); + + info->progress->cur_copy_inodes++; +out: + if (body && + body != xa_data->body + sizeof(struct reiserfs_xattr_header)) + free(body); + if (xa_data->body) + free(xa_data->body); + xa_data->body = NULL; + xa_data->len = 0; + + return ret; +} + +static int reiserfs_copy_xattr_dir(reiserfs_filsys_t fs, + const struct reiserfs_key *dir_short_key, + const char *name, size_t len, + __u32 deh_dirid, __u32 deh_objectid, + void *cb_data) +{ + struct reiserfs_convert_info *info = fs->fs_vp; + struct reiserfs_xattr_data *xa_data = cb_data; + struct reiserfs_key dir_key = { + .k2_dir_id = deh_dirid, + .k2_objectid = deh_objectid, + }; + int ret, err; + + errno = 0; + xa_data->target_oid = strtoull(name, NULL, 16); + if (xa_data->target_oid == ULLONG_MAX && errno) + return -errno; + + xa_data->target_oid += OID_OFFSET; + + xa_data->trans = btrfs_start_transaction(xa_data->root, 1); + if (!xa_data->trans) + return -ENOMEM; + + ret = reiserfs_iterate_dir(fs, &dir_key, + reiserfs_copy_one_xattr, xa_data); + + err = btrfs_commit_transaction(xa_data->trans, xa_data->root); + info->progress->cur_copy_inodes++; + xa_data->trans = NULL; + return ret ?: err; +} + +static int reiserfs_copy_xattrs(reiserfs_filsys_t fs, struct btrfs_root *root) +{ + struct reiserfs_convert_info *info = fs->fs_vp; + struct reiserfs_xattr_data data = { + .root = root, + }; + + if (get_key_objectid(&info->xattr_key) == 0) + return 0; + + return reiserfs_iterate_dir(fs, &info->xattr_key, + reiserfs_copy_xattr_dir, &data); +} + +static int reiserfs_copy_inodes(struct btrfs_convert_context *cxt, + struct btrfs_root *root, + u32 convert_flags, + struct task_ctx *p) +{ + reiserfs_filsys_t fs = cxt->fs_data; + struct reiserfs_convert_info *info = fs->fs_vp; + int ret; + u8 type; + + info->progress = p; + + ret = reiserfs_locate_privroot(fs); + if (ret) + goto out; + + ret = reiserfs_copy_meta(fs, root, convert_flags, + REISERFS_ROOT_PARENT_OBJECTID, + REISERFS_ROOT_OBJECTID, &type); + if (ret) + goto out; + + if (convert_flags & CONVERT_FLAG_XATTR) + ret = reiserfs_copy_xattrs(fs, root); + +out: + info->progress = NULL; + return ret; +} + +static int reiserfs_read_used_space(struct btrfs_convert_context *cxt) +{ + reiserfs_filsys_t fs = cxt->fs_data; + u64 start, end = 0; + unsigned int size = get_sb_block_count(fs->fs_ondisk_sb); + unsigned long *bitmap = (unsigned long *)fs->fs_bitmap2->bm_map; + int ret = 0; + + /* + * We have the entire bitmap loaded so we can just ping pong with + * ffz and ffs + */ + while (end < size) { + u64 offset, length; + + start = find_next_bit(bitmap, size, end); + if (start >= size) + break; + end = find_next_zero_bit(bitmap, size, start); + if (end > size) + end = size; + offset = start * fs->fs_blocksize; + length = (end - start) * fs->fs_blocksize; + ret = add_merge_cache_extent(&cxt->used_space, offset, length); + if (ret < 0) + break; + } + + return ret; +} + +static int reiserfs_check_state(struct btrfs_convert_context *cxt) +{ + return 0; +} + +const struct btrfs_convert_operations reiserfs_convert_ops = { + .name = "reiserfs", + .open_fs = reiserfs_open_fs, + .read_used_space = reiserfs_read_used_space, + .copy_inodes = reiserfs_copy_inodes, + .close_fs = reiserfs_close_fs, + .check_state = reiserfs_check_state, +}; + +#endif /* BTRFSCONVERT_REISERFS */ diff --git a/convert/source-reiserfs.h b/convert/source-reiserfs.h new file mode 100644 index 00000000..c960c2b6 --- /dev/null +++ b/convert/source-reiserfs.h @@ -0,0 +1,106 @@ +/* + * 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_SOURCE_REISERFS_H__ +#define __BTRFS_CONVERT_SOURCE_REISERFS_H__ + +#if BTRFSCONVERT_REISERFS + +#include "kerncompat.h" +#include +#include +#include +#include +#include +#include "convert/source-fs.h" + +#define REISERFS_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; + __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; + +#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 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; + } +} + + +static inline size_t acl_ea_size(int count) +{ + return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); +} + +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); +} + +#endif /* BTRFSCONVERT_REISERFS */ + +#endif -- cgit v1.2.3 From 385bbf35e245bfa71c83d6c41e563ce4ed22663e Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 22 Aug 2017 20:30:43 +0200 Subject: btrfs-progs: convert: move duplicated acl code to common header The ACL types and macros are same for both source filesystem. Signed-off-by: David Sterba --- convert/source-ext2.h | 42 ----------------------------------------- convert/source-fs.h | 47 ++++++++++++++++++++++++++++++++++++++++++++++ convert/source-reiserfs.h | 48 ----------------------------------------------- 3 files changed, 47 insertions(+), 90 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.h b/convert/source-ext2.h index 20a278ed..80833b21 100644 --- a/convert/source-ext2.h +++ b/convert/source-ext2.h @@ -66,48 +66,6 @@ struct dir_iterate_data { #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; - __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; - -#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; - #endif /* BTRFSCONVERT_EXT2 */ #endif diff --git a/convert/source-fs.h b/convert/source-fs.h index 3a6fa46c..a2b21982 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -55,6 +55,53 @@ struct btrfs_convert_context; #define CONVERT_FLAG_COPY_LABEL (1U << 3) #define CONVERT_FLAG_SET_LABEL (1U << 4) +/* 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) + +#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; + +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 size_t acl_ea_size(int count) +{ + return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); +} + struct btrfs_convert_operations { const char name[SOURCE_FS_NAME_LEN]; int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); diff --git a/convert/source-reiserfs.h b/convert/source-reiserfs.h index c960c2b6..10b0ceb6 100644 --- a/convert/source-reiserfs.h +++ b/convert/source-reiserfs.h @@ -29,48 +29,6 @@ #define REISERFS_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; - __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; - -#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 int ext2_acl_count(size_t size) { ssize_t s; @@ -87,12 +45,6 @@ static inline int ext2_acl_count(size_t size) } } - -static inline size_t acl_ea_size(int count) -{ - return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); -} - static inline dev_t new_decode_dev(u32 dev) { unsigned major = (dev & 0xfff00) >> 8; -- cgit v1.2.3 From c05c70c77ca797549e45c971bc3c02bb3ec79e6e Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 22 Aug 2017 20:34:36 +0200 Subject: btrfs-progs: convert: move acl helper to common source file There were 2 copies of ext2_acl_count and acl_ea_size. Signed-off-by: David Sterba --- convert/source-ext2.c | 21 --------------------- convert/source-fs.c | 17 +++++++++++++++++ convert/source-fs.h | 2 ++ convert/source-reiserfs.h | 16 ---------------- 4 files changed, 19 insertions(+), 37 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 24744e22..cdaf6698 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -424,27 +424,6 @@ static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry, return 0; } -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; - } -} - -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) { diff --git a/convert/source-fs.c b/convert/source-fs.c index 4755efb2..d6dfc077 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -28,6 +28,23 @@ const struct simple_range btrfs_reserved_ranges[3] = { { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K } }; +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; + } +} + static u64 intersect_with_reserved(u64 bytenr, u64 num_bytes) { int i; diff --git a/convert/source-fs.h b/convert/source-fs.h index a2b21982..bc693162 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -102,6 +102,8 @@ static inline size_t acl_ea_size(int count) return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); } +int ext2_acl_count(size_t size); + struct btrfs_convert_operations { const char name[SOURCE_FS_NAME_LEN]; int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); diff --git a/convert/source-reiserfs.h b/convert/source-reiserfs.h index 10b0ceb6..0fa476a9 100644 --- a/convert/source-reiserfs.h +++ b/convert/source-reiserfs.h @@ -29,22 +29,6 @@ #define REISERFS_ACL_VERSION 0x0001 -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; - } -} - static inline dev_t new_decode_dev(u32 dev) { unsigned major = (dev & 0xfff00) >> 8; -- cgit v1.2.3 From 4856215f51b4cc7f4bdaef07fc30bba1e8525653 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 22 Aug 2017 20:43:49 +0200 Subject: btrfs-progs: convert: move and rename dev_t helpers to common file Signed-off-by: David Sterba --- convert/source-ext2.c | 11 +---------- convert/source-fs.c | 8 ++++++++ convert/source-fs.h | 9 ++++++++- convert/source-reiserfs.c | 4 ++-- convert/source-reiserfs.h | 9 --------- 5 files changed, 19 insertions(+), 22 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index cdaf6698..f1075da5 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -638,21 +638,12 @@ out: 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 void ext2_copy_inode_item(struct btrfs_inode_item *dst, struct ext2_inode *src, u32 blocksize) { @@ -692,7 +683,7 @@ static void ext2_copy_inode_item(struct btrfs_inode_item *dst, old_decode_dev(src->i_block[0])); } else { btrfs_set_stack_inode_rdev(dst, - new_decode_dev(src->i_block[1])); + decode_dev(src->i_block[1])); } } memset(&dst->reserved, 0, sizeof(dst->reserved)); diff --git a/convert/source-fs.c b/convert/source-fs.c index d6dfc077..b6d08370 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -28,6 +28,14 @@ const struct simple_range btrfs_reserved_ranges[3] = { { BTRFS_SB_MIRROR_OFFSET(2), SZ_64K } }; +dev_t decode_dev(u32 dev) +{ + unsigned major = (dev & 0xfff00) >> 8; + unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00); + + return MKDEV(major, minor); +} + int ext2_acl_count(size_t size) { ssize_t s; diff --git a/convert/source-fs.h b/convert/source-fs.h index bc693162..23f33567 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -18,9 +18,9 @@ #define __BTRFS_CONVERT_SOURCE_FS_H__ #include "kerncompat.h" +#include #include - #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID /* @@ -104,6 +104,13 @@ static inline size_t acl_ea_size(int count) int ext2_acl_count(size_t size); +#ifndef MKDEV +#define MINORBITS 20 +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) +#endif + +dev_t decode_dev(u32 dev); + struct btrfs_convert_operations { const char name[SOURCE_FS_NAME_LEN]; int (*open_fs)(struct btrfs_convert_context *cctx, const char *devname); diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 8b3286f6..22a35904 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -251,7 +251,7 @@ static void reiserfs_copy_inode_item(struct btrfs_inode_item *inode, btrfs_set_stack_timespec_sec(&inode->mtime, sd_v1_mtime(sd)); if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)) - rdev = new_decode_dev(sd_v1_rdev(sd)); + rdev = decode_dev(sd_v1_rdev(sd)); } else { struct stat_data *sd = stat_data; @@ -265,7 +265,7 @@ static void reiserfs_copy_inode_item(struct btrfs_inode_item *inode, btrfs_set_stack_timespec_sec(&inode->mtime, sd_v2_mtime(sd)); if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)) - rdev = new_decode_dev(sd_v2_rdev(sd)); + rdev = decode_dev(sd_v2_rdev(sd)); if (copy_inode_flags) reiserfs_convert_inode_flags(inode, sd); diff --git a/convert/source-reiserfs.h b/convert/source-reiserfs.h index 0fa476a9..8de11b9c 100644 --- a/convert/source-reiserfs.h +++ b/convert/source-reiserfs.h @@ -24,19 +24,10 @@ #include #include #include -#include #include "convert/source-fs.h" #define REISERFS_ACL_VERSION 0x0001 -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); -} - #endif /* BTRFSCONVERT_REISERFS */ #endif -- cgit v1.2.3 From 1fe20f4c64033ed9f5cd21e18bbde9b62fd229d8 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 24 Aug 2017 15:48:48 +0200 Subject: btrfs-progs: convert: move reiserfs struct definitions to header Signed-off-by: David Sterba --- convert/source-reiserfs.c | 39 --------------------------------------- convert/source-reiserfs.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 39 deletions(-) (limited to 'convert') diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 22a35904..d6389bf9 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -44,20 +44,6 @@ static inline u8 mode_to_file_type(u32 mode) return BTRFS_FT_UNKNOWN; } -struct reiserfs_convert_info { - bool copy_attrs; - struct reiserfs_key privroot_key; - struct reiserfs_key xattr_key; - - /* only set during copy_inodes */ - struct task_ctx *progress; - - /* used to track hardlinks */ - unsigned used_slots; - unsigned alloced_slots; - u64 *objectids; -}; - static u32 reiserfs_count_objectids(reiserfs_filsys_t fs) { struct reiserfs_super_block *sb = fs->fs_ondisk_sb; @@ -279,13 +265,6 @@ static void reiserfs_copy_inode_item(struct btrfs_inode_item *inode, btrfs_set_stack_inode_rdev(inode, rdev); } -struct reiserfs_blk_iterate_data { - struct blk_iterate_data blk_data; - char *inline_data; - u64 inline_offset; - u32 inline_length; -}; - static void init_reiserfs_blk_iterate_data( struct reiserfs_blk_iterate_data *data, struct btrfs_trans_handle *trans, @@ -485,18 +464,10 @@ fail: return ret; } -#define OID_OFFSET (BTRFS_FIRST_FREE_OBJECTID - REISERFS_ROOT_OBJECTID) static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, u32 convert_flags, u32 deh_dirid, u32 deh_objectid, u8 *type); -struct reiserfs_dirent_data { - u64 index; - u32 convert_flags; - struct btrfs_inode_item *inode; - struct btrfs_root *root; -}; - static int reiserfs_copy_dirent(reiserfs_filsys_t fs, const struct reiserfs_key *dir_short_key, const char *name, size_t len, @@ -696,16 +667,6 @@ fail: return ret; } -struct reiserfs_xattr_data { - struct btrfs_root *root; - struct btrfs_trans_handle *trans; - u64 target_oid; - const char *name; - size_t namelen; - void *body; - size_t len; -}; - static int reiserfs_xattr_indirect_fn(reiserfs_filsys_t fs, u64 position, u64 size, int num_blocks, u32 *blocks, void *data) diff --git a/convert/source-reiserfs.h b/convert/source-reiserfs.h index 8de11b9c..a4e14392 100644 --- a/convert/source-reiserfs.h +++ b/convert/source-reiserfs.h @@ -28,6 +28,46 @@ #define REISERFS_ACL_VERSION 0x0001 +#define OID_OFFSET (BTRFS_FIRST_FREE_OBJECTID - REISERFS_ROOT_OBJECTID) + +struct reiserfs_convert_info { + bool copy_attrs; + struct reiserfs_key privroot_key; + struct reiserfs_key xattr_key; + + /* only set during copy_inodes */ + struct task_ctx *progress; + + /* used to track hardlinks */ + unsigned used_slots; + unsigned alloced_slots; + u64 *objectids; +}; + +struct reiserfs_blk_iterate_data { + struct blk_iterate_data blk_data; + char *inline_data; + u64 inline_offset; + u32 inline_length; +}; + +struct reiserfs_dirent_data { + u64 index; + u32 convert_flags; + struct btrfs_inode_item *inode; + struct btrfs_root *root; +}; + +struct reiserfs_xattr_data { + struct btrfs_root *root; + struct btrfs_trans_handle *trans; + u64 target_oid; + const char *name; + size_t namelen; + void *body; + size_t len; +}; + #endif /* BTRFSCONVERT_REISERFS */ #endif -- cgit v1.2.3 From 448999d84ddc2eaf36938176fb5091d2c2029e2f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 28 Aug 2017 16:48:16 +0200 Subject: btrfs-progs: add crude error handling when transaction start fails Currently transaction bugs out insided btrfs_start_transaction in case of error, we want to lift the error handling to the callers. This patch adds the BUG_ON anywhere it's been missing so far. This is not the best way of course. Transforming BUG_ON to a proper error handling highly depends on the caller and should be dealt with case by case. Signed-off-by: David Sterba --- convert/main.c | 10 +++++----- convert/source-ext2.c | 6 +++--- convert/source-reiserfs.c | 24 ++++++++++++------------ 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 22884bd5..7ec6202d 100644 --- a/convert/main.c +++ b/convert/main.c @@ -751,8 +751,8 @@ static int create_image(struct btrfs_root *root, flags |= BTRFS_INODE_NODATASUM; trans = btrfs_start_transaction(root, 1); - if (!trans) - return -ENOMEM; + if (IS_ERR(trans)) + return PTR_ERR(trans); cache_tree_init(&used_tmp); btrfs_init_path(&path); @@ -881,7 +881,7 @@ static struct btrfs_root* link_subvol(struct btrfs_root *root, btrfs_release_path(&path); trans = btrfs_start_transaction(root, 1); - if (!trans) { + if (IS_ERR(trans)) { error("unable to start transaction"); goto fail; } @@ -1077,9 +1077,9 @@ static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root, fs_info->avoid_sys_chunk_alloc = 1; fs_info->avoid_meta_chunk_alloc = 1; trans = btrfs_start_transaction(root, 1); - if (!trans) { + if (IS_ERR(trans)) { error("unable to start transaction"); - ret = -EINVAL; + ret = PTR_ERR(trans); goto err; } ret = btrfs_fix_block_accounting(trans, root); diff --git a/convert/source-ext2.c b/convert/source-ext2.c index f1075da5..559669ff 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -803,8 +803,8 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, struct btrfs_trans_handle *trans; trans = btrfs_start_transaction(root, 1); - if (!trans) - return -ENOMEM; + if (IS_ERR(trans)) + return PTR_ERR(trans); err = ext2fs_open_inode_scan(ext2_fs, 0, &ext2_scan); if (err) { fprintf(stderr, "ext2fs_open_inode_scan: %s\n", error_message(err)); @@ -830,7 +830,7 @@ static int ext2_copy_inodes(struct btrfs_convert_context *cctx, ret = btrfs_commit_transaction(trans, root); BUG_ON(ret); trans = btrfs_start_transaction(root, 1); - BUG_ON(!trans); + BUG_ON(IS_ERR(trans)); } } if (err) { diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index d6389bf9..be79d8e2 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -500,8 +500,8 @@ static int reiserfs_copy_dirent(reiserfs_filsys_t fs, return ret; } trans = btrfs_start_transaction(root, 1); - if (!trans) - return -ENOMEM; + if (IS_ERR(trans)) + return PTR_ERR(trans); ret = convert_insert_dirent(trans, root, name, len, dir_objectid, objectid, type, dirent_data->index++, @@ -613,8 +613,8 @@ static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, switch (mode & S_IFMT) { case S_IFREG: trans = btrfs_start_transaction(root, 1); - if (!trans) { - ret = -ENOMEM; + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); goto fail; } ret = reiserfs_record_file_extents(fs, trans, root, objectid, @@ -629,8 +629,8 @@ static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, if (ret) goto fail; trans = btrfs_start_transaction(root, 1); - if (!trans) { - ret = -ENOMEM; + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); goto fail; } @@ -639,8 +639,8 @@ static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, break; case S_IFLNK: trans = btrfs_start_transaction(root, 1); - if (!trans) { - ret = -ENOMEM; + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); goto fail; } ret = reiserfs_copy_symlink(trans, root, objectid, @@ -650,8 +650,8 @@ static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, break; default: trans = btrfs_start_transaction(root, 1); - if (!trans) { - ret = -ENOMEM; + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); goto fail; } } @@ -872,8 +872,8 @@ static int reiserfs_copy_xattr_dir(reiserfs_filsys_t fs, xa_data->target_oid += OID_OFFSET; xa_data->trans = btrfs_start_transaction(xa_data->root, 1); - if (!xa_data->trans) - return -ENOMEM; + if (IS_ERR(xa_data->trans)) + return PTR_ERR(xa_data->trans); ret = reiserfs_iterate_dir(fs, &dir_key, reiserfs_copy_one_xattr, xa_data); -- cgit v1.2.3 From 87adfe8254b7d785d0a523166e4319d17f5fecbc Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 29 Aug 2017 19:42:10 +0200 Subject: btrfs-progs: convert: don't print message when fs is not recognized, ext2 Bad magic error means it's not the desired filesystem so the error message is just noise. Signed-off-by: David Sterba --- convert/source-ext2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 559669ff..44d5e3af 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -39,7 +39,8 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) ret = ext2fs_open(name, open_flag, 0, 0, unix_io_manager, &ext2_fs); if (ret) { - fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); + if (ret != EXT2_ET_BAD_MAGIC) + fprintf(stderr, "ext2fs_open: %s\n", error_message(ret)); return -1; } /* -- cgit v1.2.3 From 29faad633948e86ec8840653f1692dda8937b84f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 12 Sep 2017 15:23:05 +0200 Subject: btrfs-progs: convert: use correct string printing for errcode_t Signed-off-by: David Sterba --- convert/source-ext2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 44d5e3af..e9277213 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -155,7 +155,8 @@ static int ext2_read_used_space(struct btrfs_convert_context *cctx) block_nbytes * 8, block_bitmap); if (ret) { error("fail to get bitmap from ext2, %s", - strerror(-ret)); + error_message(ret)); + ret = -EINVAL; break; } ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree); -- cgit v1.2.3 From 8a820b57656c1a062c6445f872a9169fa0d9b381 Mon Sep 17 00:00:00 2001 From: Yingyi Luo Date: Fri, 15 Sep 2017 10:49:21 -0700 Subject: btrfs-progs: convert: move link_subvol out of main link_subvol() is moved to inode.c and renamed as btrfs_mksubvol(). The change cascades down to the callchain. Signed-off-by: Yingyi Luo Signed-off-by: David Sterba --- convert/main.c | 126 +-------------------------------------------------------- 1 file changed, 2 insertions(+), 124 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 7ec6202d..0babf0e8 100644 --- a/convert/main.c +++ b/convert/main.c @@ -838,129 +838,6 @@ out: return ret; } -static struct btrfs_root* link_subvol(struct btrfs_root *root, - const char *base, u64 root_objectid) -{ - struct btrfs_trans_handle *trans; - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_root *tree_root = fs_info->tree_root; - struct btrfs_root *new_root = NULL; - struct btrfs_path path; - struct btrfs_inode_item *inode_item; - struct extent_buffer *leaf; - struct btrfs_key key; - u64 dirid = btrfs_root_dirid(&root->root_item); - u64 index = 2; - char buf[BTRFS_NAME_LEN + 1]; /* for snprintf null */ - int len; - int i; - int ret; - - len = strlen(base); - if (len == 0 || len > BTRFS_NAME_LEN) - return NULL; - - btrfs_init_path(&path); - key.objectid = dirid; - key.type = BTRFS_DIR_INDEX_KEY; - key.offset = (u64)-1; - - ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); - if (ret <= 0) { - error("search for DIR_INDEX dirid %llu failed: %d", - (unsigned long long)dirid, ret); - goto fail; - } - - if (path.slots[0] > 0) { - path.slots[0]--; - btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); - if (key.objectid == dirid && key.type == BTRFS_DIR_INDEX_KEY) - index = key.offset + 1; - } - btrfs_release_path(&path); - - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - error("unable to start transaction"); - goto fail; - } - - key.objectid = dirid; - key.offset = 0; - key.type = BTRFS_INODE_ITEM_KEY; - - ret = btrfs_lookup_inode(trans, root, &path, &key, 1); - if (ret) { - error("search for INODE_ITEM %llu failed: %d", - (unsigned long long)dirid, ret); - goto fail; - } - leaf = path.nodes[0]; - inode_item = btrfs_item_ptr(leaf, path.slots[0], - struct btrfs_inode_item); - - key.objectid = root_objectid; - key.offset = (u64)-1; - key.type = BTRFS_ROOT_ITEM_KEY; - - memcpy(buf, base, len); - for (i = 0; i < 1024; i++) { - ret = btrfs_insert_dir_item(trans, root, buf, len, - dirid, &key, BTRFS_FT_DIR, index); - if (ret != -EEXIST) - break; - len = snprintf(buf, ARRAY_SIZE(buf), "%s%d", base, i); - if (len < 1 || len > BTRFS_NAME_LEN) { - ret = -EINVAL; - break; - } - } - if (ret) - goto fail; - - btrfs_set_inode_size(leaf, inode_item, len * 2 + - btrfs_inode_size(leaf, inode_item)); - btrfs_mark_buffer_dirty(leaf); - btrfs_release_path(&path); - - /* add the backref first */ - ret = btrfs_add_root_ref(trans, tree_root, root_objectid, - BTRFS_ROOT_BACKREF_KEY, - root->root_key.objectid, - dirid, index, buf, len); - if (ret) { - error("unable to add root backref for %llu: %d", - root->root_key.objectid, ret); - goto fail; - } - - /* now add the forward ref */ - ret = btrfs_add_root_ref(trans, tree_root, root->root_key.objectid, - BTRFS_ROOT_REF_KEY, root_objectid, - dirid, index, buf, len); - if (ret) { - error("unable to add root ref for %llu: %d", - root->root_key.objectid, ret); - goto fail; - } - - ret = btrfs_commit_transaction(trans, root); - if (ret) { - error("transaction commit failed: %d", ret); - goto fail; - } - - new_root = btrfs_read_fs_root(fs_info, &key); - if (IS_ERR(new_root)) { - error("unable to fs read root: %lu", PTR_ERR(new_root)); - new_root = NULL; - } -fail: - btrfs_init_path(&path); - return new_root; -} - static int create_subvol(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 root_objectid) { @@ -1319,7 +1196,8 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, task_deinit(ctx.info); } - image_root = link_subvol(root, subvol_name, CONV_IMAGE_SUBVOL_OBJECTID); + image_root = btrfs_mksubvol(root, subvol_name, + CONV_IMAGE_SUBVOL_OBJECTID); if (!image_root) { error("unable to link subvolume %s", subvol_name); goto fail; -- cgit v1.2.3 From fe667f6e7d0a3215bf671bfd70b0eb18d6e9c1a6 Mon Sep 17 00:00:00 2001 From: Yingyi Luo Date: Fri, 15 Sep 2017 11:17:31 -0700 Subject: btrfs-progs: add a parameter to btrfs_mksubvol A convert parameter is added as a flag to indicate if btrfs_mksubvol() is used for btrfs-convert. The change cascades down to the callchain. Signed-off-by: Yingyi Luo Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 0babf0e8..882daf7c 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1197,7 +1197,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, } image_root = btrfs_mksubvol(root, subvol_name, - CONV_IMAGE_SUBVOL_OBJECTID); + CONV_IMAGE_SUBVOL_OBJECTID, true); if (!image_root) { error("unable to link subvolume %s", subvol_name); goto fail; -- cgit v1.2.3 From 3efc459166ad8d104912ec06c4ec9e0db6b7ef79 Mon Sep 17 00:00:00 2001 From: Su Yue Date: Mon, 28 Aug 2017 15:08:09 +0800 Subject: btrfs-progs: check: adjustments for further repair For code reuse, btrfs_insert_dir_item() now calls inserts_with_overflow() even if the dir_item existed. Add a parameter @ignore_existed to btrfs_add_link(). If @ignore_existed is not zero, btrfs_add_link() continues to do link. Signed-off-by: Su Yue Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 882daf7c..67616c4b 100644 --- a/convert/main.c +++ b/convert/main.c @@ -768,7 +768,7 @@ static int create_image(struct btrfs_root *root, if (ret < 0) goto out; ret = btrfs_add_link(trans, root, ino, BTRFS_FIRST_FREE_OBJECTID, name, - strlen(name), BTRFS_FT_REG_FILE, NULL, 1); + strlen(name), BTRFS_FT_REG_FILE, NULL, 1, 0); if (ret < 0) goto out; -- cgit v1.2.3 From da2659b090af383dea6dec71d2d4e128c643d455 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 26 Oct 2017 15:28:37 +0800 Subject: btrfs-progs: convert: Open the fs readonly for rollback For rollback, we only needs to open the fs to check if it meets the condition to rollback. And this RW read makes us failed to rollback btrfs with v2 space cache. In fact, we don't even start a transaction during rollback. So open the fs RO for rollback, to avoid v2 space cache problem. Reported-by: Gu Jinxiang Reviewed-by: Gu JinXiang Tested-by: Gu JinXiang Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 67616c4b..af285531 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1529,7 +1529,13 @@ static int do_rollback(const char *devname) goto free_mem; } fsize = lseek(fd, 0, SEEK_END); - root = open_ctree_fd(fd, devname, 0, OPEN_CTREE_WRITES); + + /* + * For rollback, we don't really need to write anything so open it + * read-only. The write part will happen after we close the + * filesystem. + */ + root = open_ctree_fd(fd, devname, 0, 0); if (!root) { error("unable to open ctree"); ret = -EIO; -- cgit v1.2.3 From 658cc4daa6ed6e6463cba2f30731abd9a91a7b2d Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 31 Oct 2017 14:57:54 +0200 Subject: btrfs-progs: convert: add missing types header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build with musl libc needs the sys/types.h header for the dev_t type, since this header is not included indirectly. This fixes the following build failure: In file included from convert/source-fs.c:23:0: ./convert/source-fs.h:112:1: error: unknown type name ‘dev_t’ dev_t decode_dev(u32 dev); ^~~~~ convert/source-fs.c:31:1: error: unknown type name ‘dev_t’ dev_t decode_dev(u32 dev) ^~~~~ Signed-off-by: Baruch Siach Signed-off-by: David Sterba --- convert/source-fs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/source-fs.h b/convert/source-fs.h index 23f33567..4e5babef 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -19,6 +19,7 @@ #include "kerncompat.h" #include +#include #include #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID -- cgit v1.2.3 From e8f9653fc04513db59b0d1438367ecee1552db9d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 29 Nov 2017 21:48:05 +0800 Subject: btrfs-progs: convert: Fix a bug in rollback check which overwrite return value Commit 1170ac307900 ("btrfs-progs: convert: Introduce function to check if convert image is able to be rolled back") reworked rollback check condition, by checking 1:1 mapping of each file extent. The idea itself has nothing wrong, but error handler is not implemented correctly, which over writes the return value and always try to rollback the fs even it fails to pass the check. Fix it by correctly return the error before rollback the fs. Fixes: 1170ac307900 ("btrfs-progs: convert: Introduce function to check if convert image is able to be rolled back") Reported-by: Jeff Mahoney Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index af285531..89f92611 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1443,6 +1443,8 @@ next: } } btrfs_release_path(&path); + if (ret) + return ret; /* * For HOLES mode (without NO_HOLES), we must ensure file extents * cover the whole range of the image -- cgit v1.2.3 From e4df433b8a2d5ef944ab4764a5ad237bcdbd1fdc Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jan 2018 13:54:21 -0800 Subject: btrfs-progs: treewide: Replace strerror(errno) with %m. As btrfs is specific to Linux, %m can be used instead of strerror(errno) in format strings. This has some size reduction benefits for embedded systems. glibc, musl, and uclibc-ng all support %m as a modifier to printf. A quick glance at the BIONIC libc source indicates that it has support for %m as well. BSDs and Windows do not but I do believe them to be beyond the scope of btrfs-progs. Compiled sizes on Ubuntu 16.04: Before: 3916512 btrfs 233688 libbtrfs.so.0.1 4899 bcp 2367672 btrfs-convert 2208488 btrfs-corrupt-block 13302 btrfs-debugfs 2152160 btrfs-debug-tree 2136024 btrfs-find-root 2287592 btrfs-image 2144600 btrfs-map-logical 2130760 btrfs-select-super 2152608 btrfstune 2131760 btrfs-zero-log 2277752 mkfs.btrfs 9166 show-blocks After: 3908744 btrfs 233256 libbtrfs.so.0.1 4899 bcp 2366560 btrfs-convert 2207432 btrfs-corrupt-block 13302 btrfs-debugfs 2151104 btrfs-debug-tree 2134968 btrfs-find-root 2281864 btrfs-image 2143536 btrfs-map-logical 2129704 btrfs-select-super 2151552 btrfstune 2130696 btrfs-zero-log 2276272 mkfs.btrfs 9166 show-blocks Total savings: 23928 (24 kilo)bytes Signed-off-by: Rosen Penev Signed-off-by: David Sterba --- convert/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 89f92611..d31b3370 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1115,7 +1115,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, goto fail; fd = open(devname, O_RDWR); if (fd < 0) { - error("unable to open %s: %s", devname, strerror(errno)); + error("unable to open %s: %m", devname); goto fail; } btrfs_parse_features_to_string(features_buf, features); @@ -1526,7 +1526,7 @@ static int do_rollback(const char *devname) } fd = open(devname, O_RDWR); if (fd < 0) { - error("unable to open %s: %s", devname, strerror(errno)); + error("unable to open %s: %m", devname); ret = -EIO; goto free_mem; } -- cgit v1.2.3 From 1945854e012b9e8b720f826f90158f339101cf4b Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 24 Jan 2018 10:30:28 +0800 Subject: btrfs-progs: Remove unnecessary parameter for btrfs_add_block_group @chunk_objectid of btrfs_make_block_group() function is always fixed to BTRFS_FIRST_FREE_OBJECTID, so there is no need to pass it as parameter explicitly. Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- convert/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index d31b3370..b3ea31d7 100644 --- a/convert/main.c +++ b/convert/main.c @@ -916,9 +916,7 @@ static int make_convert_data_block_groups(struct btrfs_trans_handle *trans, if (ret < 0) break; ret = btrfs_make_block_group(trans, fs_info, 0, - BTRFS_BLOCK_GROUP_DATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - cur, len); + BTRFS_BLOCK_GROUP_DATA, cur, len); if (ret < 0) break; cur += len; -- cgit v1.2.3 From 26072f584d347ef2d471c02fbe27dbfedd606ac4 Mon Sep 17 00:00:00 2001 From: Gu Jinxiang Date: Fri, 26 Jan 2018 15:26:00 +0800 Subject: btrfs-progs: Use fs_info instead of root for BTRFS_LEAF_DATA_SIZE Do a cleanup. Also make it consistent with kernel. Use fs_info instead of root for BTRFS_LEAF_DATA_SIZE, since maybe in some situation we do not know root, but just know fs_info. Signed-off-by: Gu Jinxiang Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-ext2.c | 2 +- convert/source-reiserfs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index e9277213..e5c2a943 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -520,7 +520,7 @@ static int ext2_copy_single_xattr(struct btrfs_trans_handle *trans, } strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX); strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len); - if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) - + if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root->fs_info) - 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); diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index be79d8e2..e3582bda 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -676,7 +676,7 @@ static int reiserfs_xattr_indirect_fn(reiserfs_filsys_t fs, u64 position, size_t alloc = min(position + num_blocks * fs->fs_blocksize, size); char *body; - if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) - + if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root->fs_info) - sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n", xa_data->target_oid, (int)xa_data->namelen, @@ -714,7 +714,7 @@ static int reiserfs_xattr_direct_fn(reiserfs_filsys_t fs, __u64 position, struct reiserfs_xattr_data *xa_data = data; char *newbody; - if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root) - + if (size > BTRFS_LEAF_DATA_SIZE(xa_data->root->fs_info) - sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) { fprintf(stderr, "skip large xattr on objectid %llu name %.*s\n", xa_data->target_oid, (int)xa_data->namelen, -- cgit v1.2.3 From c9581107856ac932817c4bb832042c968af45394 Mon Sep 17 00:00:00 2001 From: Gu Jinxiang Date: Fri, 26 Jan 2018 15:26:02 +0800 Subject: btrfs-progs: Sync code with kernel for BTRFS_MAX_INLINE_DATA_SIZE Do a cleanup. Also make it consistent with kernel. Use fs_info instead of root for BTRFS_MAX_INLINE_DATA_SIZE, since maybe in some situation we do not know root, but just know fs_info. Change macro to inline function to be consistent with kernel. And change the function body to match kernel. Signed-off-by: Gu Jinxiang Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-ext2.c | 2 +- convert/source-reiserfs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index e5c2a943..f5ecd8cf 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -309,7 +309,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, goto fail; if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && data.first_block == 0 && data.num_blocks > 0 - && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root)) { + && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) { u64 num_bytes = data.num_blocks * sectorsize; u64 disk_bytenr = data.disk_block * sectorsize; u64 nbytes; diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index e3582bda..39d6f072 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -376,7 +376,7 @@ static int reiserfs_convert_tail(struct btrfs_trans_handle *trans, u64 isize; int ret; - if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root)) + if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) return convert_direct(trans, root, objectid, inode, body, length, offset, convert_flags); -- cgit v1.2.3 From de60d6f0479998f3b67a20e637268192f6611473 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Sat, 3 Feb 2018 01:06:21 +0100 Subject: btrfs-progs: convert: fix build on musl Voidlinux has a patch to fix build on musl. Signed-off-by: David Sterba --- convert/source-fs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'convert') diff --git a/convert/source-fs.h b/convert/source-fs.h index 4e5babef..f5314aff 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -21,6 +21,7 @@ #include #include #include +#include #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID -- cgit v1.2.3 From b8e884f1cda145c73115e2ff9dde096d39e199ca Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 14 Feb 2018 16:09:56 +0800 Subject: btrfs-progs: convert/ext2: Fix memory leak caused by handled ext2_filsys Exposed by convert-test with D=asan. Unlike btrfs, ext2fs_close() still leaves its ext2_filsys parameter filled with allocated pointers. It needs ext2fs_free() to free those pointers. Issue: #92 Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-ext2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index f5ecd8cf..b1492c78 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -95,6 +95,7 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name) return 0; fail: ext2fs_close(ext2_fs); + ext2fs_free(ext2_fs); return -1; } @@ -179,6 +180,7 @@ static void ext2_close_fs(struct btrfs_convert_context *cctx) cctx->volume_name = NULL; } ext2fs_close(cctx->fs_data); + ext2fs_free(cctx->fs_data); } static u8 ext2_filetype_conversion_table[EXT2_FT_MAX] = { -- cgit v1.2.3 From 92ffad47b5c78ca119e798505e20f1b5cf7b7dd8 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 14 Mar 2018 08:56:57 +0800 Subject: btrfs-progs: convert/ext2: Remove check for ext2_ext_attr_entry->e_value_block In latest e2fsprogs (1.44.0) definition of ext2_ext_attr_entry has removed member e_value_block, as currently ext* doesn't support it set anyway. So remove such check so that we can pass compile. Issue: #110 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=199071 Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-ext2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index b1492c78..070126ec 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -422,8 +422,7 @@ static int ext2_xattr_check_entry(struct ext2_ext_attr_entry *entry, { size_t value_size = entry->e_value_size; - if (entry->e_value_block != 0 || value_size > size || - entry->e_value_offs + value_size > size) + if (value_size > size || entry->e_value_offs + value_size > size) return -EIO; return 0; } -- cgit v1.2.3 From e02049d964e77d22348bf940a7027a1aff558f18 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 20 Mar 2018 14:42:24 +0800 Subject: btrfs-progs: convert: Fix inline file extent creation condition [Bug] On btrfs converted from ext*, one user reported the following kernel warning: ------------[ cut here ]------------ BTRFS: Transaction aborted (error -95) WARNING: CPU: 0 PID: 324 at fs/btrfs/inode.c:3042 btrfs_finish_ordered_io+0x7ab/0x850 [btrfs] Workqueue: btrfs-endio-write btrfs_endio_write_helper [btrfs] RIP: 0010:btrfs_finish_ordered_io+0x7ab/0x850 [btrfs] ... Call Trace: normal_work_helper+0x39/0x370 [btrfs] process_one_work+0x1ce/0x410 worker_thread+0x2b/0x3d0 ? process_one_work+0x410/0x410 kthread+0x113/0x130 ? kthread_create_on_node+0x70/0x70 ? do_syscall_64+0x74/0x190 ? SyS_exit_group+0x10/0x10 ret_from_fork+0x35/0x40 ---[ end trace c8ed62ff6a525901 ]--- BTRFS: error (device dm-2) in btrfs_finish_ordered_io:3042: errno=-95 unknown BTRFS info (device dm-2): forced readonly BTRFS error (device dm-2): pending csums is 6447104 [Cause] The call trace and the unique return value points to __btrfs_drop_extents(), when we tries to drop pages of an inline extent, we will trigger such -EOPNOTSUPP. However kernel has limitation on the size of inline file extent (sector size for ram size and sector size - 1 for on-disk size), btrfs-convert doesn't have the same limitation, resulting much larger file extent. The lack of correct inline extent size check dates back to 2008 when btrfs-convert is added into btrfs-progs. [Fix] Fix the inline extent creation condition, not only using BTRFS_MAX_INLINE_DATA_SIZE(), which is only the maximum size of inline data according to nodesize, but also limit it against sector size. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-ext2.c | 2 +- convert/source-reiserfs.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-ext2.c b/convert/source-ext2.c index 070126ec..a2af1212 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -310,7 +310,7 @@ static int ext2_create_file_extents(struct btrfs_trans_handle *trans, if (ret) goto fail; if ((convert_flags & CONVERT_FLAG_INLINE_DATA) && data.first_block == 0 - && data.num_blocks > 0 + && data.num_blocks > 0 && inode_size < sectorsize && inode_size <= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) { u64 num_bytes = data.num_blocks * sectorsize; u64 disk_bytenr = data.disk_block * sectorsize; diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 39d6f072..eeb68d96 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -376,7 +376,8 @@ static int reiserfs_convert_tail(struct btrfs_trans_handle *trans, u64 isize; int ret; - if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info)) + if (length >= BTRFS_MAX_INLINE_DATA_SIZE(root->fs_info) || + length >= root->fs_info->sectorsize) return convert_direct(trans, root, objectid, inode, body, length, offset, convert_flags); -- cgit v1.2.3 From c074434fb5f0874dcf8523191359976b668527f8 Mon Sep 17 00:00:00 2001 From: "Misono, Tomohiro" Date: Fri, 23 Mar 2018 17:20:07 +0900 Subject: btrfs-progs: remove BTRFS_CRC32_SIZE definition The kernel code no longer has BTRFS_CRC32_SIZE and only uses btrfs_csum_sizes[]. So, update the progs code as well. Suggested-by: Qu Wenruo Signed-off-by: Tomohiro Misono Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- convert/common.c | 2 +- convert/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 3860f3b9..2b944fd8 100644 --- a/convert/common.c +++ b/convert/common.c @@ -219,7 +219,7 @@ static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf, { int ret; - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0); /* Temporary extent buffer is always mapped 1:1 on disk */ ret = pwrite(fd, buf->data, buf->len, bytenr); diff --git a/convert/main.c b/convert/main.c index b3ea31d7..6bdfab40 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1026,7 +1026,7 @@ static int migrate_super_block(int fd, u64 old_bytenr) BUG_ON(btrfs_super_bytenr(super) != old_bytenr); btrfs_set_super_bytenr(super, BTRFS_SUPER_INFO_OFFSET); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + csum_tree_block_size(buf, btrfs_csum_sizes[BTRFS_CSUM_TYPE_CRC32], 0); ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, BTRFS_SUPER_INFO_OFFSET); if (ret != BTRFS_SUPER_INFO_SIZE) -- cgit v1.2.3 From f96ca97fb451801d899650a4d03f6a96fa8c59fb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 30 Mar 2018 13:48:55 +0800 Subject: btrfs-progs: extent_io: Refactor alloc_extent_buffer() to follow kernel parameters Instead of using the internal struct extent_io_tree, use struct fs_info. This does not only unify the interface between kernel and btrfs-progs, but also makes later btrfs_print_tree() use fewer parameters. Signed-off-by: Qu Wenruo Reviewed-by: Lu Fengqi Signed-off-by: David Sterba --- convert/source-reiserfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'convert') diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index eeb68d96..e0b3b685 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -350,8 +350,7 @@ static int convert_direct(struct btrfs_trans_handle *trans, if (ret) return ret; - eb = alloc_extent_buffer(&root->fs_info->extent_cache, key.objectid, - sectorsize); + eb = alloc_extent_buffer(root->fs_info, key.objectid, sectorsize); if (!eb) return -ENOMEM; -- cgit v1.2.3 From c57ed6ca6b1b0655bba4fff97ad10e32dba4a81d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 11 Apr 2018 15:29:35 +0800 Subject: btrfs-progs: Rename OPEN_CTREE_FS_PARTIAL to OPEN_CTREE_TEMPORARY_SUPER The old flag OPEN_CTREE_FS_PARTIAL is in fact quite easy to be confused with OPEN_CTREE_PARTIAL, which allow btrfs-progs to open damaged filesystem (like corrupted extent/csum tree). However OPEN_CTREE_FS_PARTIAL, unlike its name, is just allowing btrfs-progs to open fs with temporary superblocks (which only has 6 basic trees on SINGLE meta/sys chunks). The usage of FS_PARTIAL is really confusing here. So rename OPEN_CTREE_FS_PARTIAL to OPEN_CTREE_TEMPORARY_SUPER, and add extra comment for its behavior. Also rename BTRFS_MAGIC_PARTIAL to BTRFS_MAGIC_TEMPORARY to keep the naming consistent. And with above comment, the usage of FS_PARTIAL in dump-tree is obviously incorrect, fix it. Fixes: 8698a2b9ba89 ("btrfs-progs: Allow inspect dump-tree to show specified tree block even some tree roots are corrupted") Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/common.c | 2 +- convert/main.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 2b944fd8..6ddf4a46 100644 --- a/convert/common.c +++ b/convert/common.c @@ -116,7 +116,7 @@ static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg, btrfs_set_super_bytenr(super, cfg->super_bytenr); btrfs_set_super_num_devices(super, 1); - btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL); + btrfs_set_super_magic(super, BTRFS_MAGIC_TEMPORARY); btrfs_set_super_generation(super, 1); btrfs_set_super_root(super, root_bytenr); btrfs_set_super_chunk_root(super, chunk_bytenr); diff --git a/convert/main.c b/convert/main.c index 6bdfab40..80f3bed8 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1140,7 +1140,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, } root = open_ctree_fd(fd, devname, mkfs_cfg.super_bytenr, - OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL); + OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER); if (!root) { error("unable to open ctree"); goto fail; @@ -1230,7 +1230,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, } root = open_ctree_fd(fd, devname, 0, - OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL); + OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER); if (!root) { error("unable to open ctree for finalization"); goto fail; -- cgit v1.2.3 From 36da47b409535f947549dcf560a3b1c1bd38af4a Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Mon, 30 Apr 2018 10:37:06 -0400 Subject: btrfs-progs: convert: fix support for e2fsprogs < 1.42 Commit 324d4c1857a (btrfs-progs: convert: Add larger device support) introduced new dependencies on the 64-bit API provided by e2fsprogs. That API was introduced in v1.42 (along with bigalloc). This patch maps the following to their equivalents in e2fsprogs < 1.42. - ext2fs_get_block_bitmap_range2 - ext2fs_inode_data_blocks2 - ext2fs_read_ext_attr2 Since we need to detect and define EXT2_FLAG_64BITS for compatibilty anyway, it makes sense to use that to detect the older e2fsprogs instead of defining a new flag ourselves. Signed-off-by: Jeff Mahoney Signed-off-by: David Sterba --- convert/source-ext2.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-ext2.h b/convert/source-ext2.h index 80833b21..c3214125 100644 --- a/convert/source-ext2.h +++ b/convert/source-ext2.h @@ -33,8 +33,18 @@ * BIGALLOC. * Unlike normal RO compat flag, BIGALLOC affects how e2fsprogs check used * space, and btrfs-convert heavily relies on it. + * + * e2fsprogs 1.42 also introduced the 64-bit API. Any file system + * that requires it will have EXT4_FEATURE_INCOMPAT_64BIT set and + * will fail to open with earlier releases. We can map it to the + * older API without risk of corruption. */ -#ifdef HAVE_OLD_E2FSPROGS +#ifndef EXT2_FLAG_64BITS +#define EXT2_FLAG_64BITS (0) +#define ext2fs_get_block_bitmap_range2 ext2fs_get_block_bitmap_range +#define ext2fs_inode_data_blocks2 ext2fs_inode_data_blocks +#define ext2fs_read_ext_attr2 ext2fs_read_ext_attr +#define ext2fs_blocks_count(s) ((s)->s_blocks_count) #define EXT2FS_CLUSTER_RATIO(fs) (1) #define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_BLOCKS_PER_GROUP(s)) #define EXT2FS_B2C(fs, blk) (blk) -- cgit v1.2.3 From b4c4ff9c4a15eb0d97ab9dd6f12f7bc9774d0d7b Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 28 May 2018 09:36:41 +0300 Subject: btrfs-progs: check: Remove root parameter from btrfs_fix_block_accounting It's always set to extent_root and the function already takes a transaction handle where fs_info could be referenced and in turn the extent_tree. Signed-off-by: Nikolay Borisov Reviewed-by: Su Yue Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 80f3bed8..7077fcba 100644 --- a/convert/main.c +++ b/convert/main.c @@ -957,7 +957,7 @@ static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root, ret = PTR_ERR(trans); goto err; } - ret = btrfs_fix_block_accounting(trans, root); + ret = btrfs_fix_block_accounting(trans); if (ret) goto err; ret = make_convert_data_block_groups(trans, fs_info, cfg, cctx); -- cgit v1.2.3 From 078e9a1cc973e042ad8fde2d84e0db62fee9c45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Wed, 4 Jul 2018 21:20:14 +0200 Subject: btrfs-progs: check: enhanced progress indicator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We reuse the task_position enum and task_ctx struct of the original progress indicator, adding more values and fields for our needs. Then add hooks in all steps of the check to properly record progress. Here's how the output looks like on a 22 Tb 5-disk RAID1 FS: Opening filesystem to check... Checking filesystem on /dev/mapper/luks-ST10000VN0004-XXXXXXXX UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx [1/7] checking extents (0:20:21 elapsed, 950958 items checked) [2/7] checking root items (0:01:29 elapsed, 15121 items checked) [3/7] checking free space cache (0:00:11 elapsed, 4928 items checked) [4/7] checking fs roots (0:51:31 elapsed, 600892 items checked) [5/7] checking csums (0:14:35 elapsed, 754522 items checked) [6/7] checking root refs (0:00:00 elapsed, 232 items checked) [7/7] checking quota groups skipped (not enabled on this FS) found 5286458060800 bytes used, no error found Signed-off-by: Stéphane Lesimple Signed-off-by: David Sterba --- convert/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 7077fcba..3736a149 100644 --- a/convert/main.c +++ b/convert/main.c @@ -1182,7 +1182,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, if (progress) { ctx.info = task_init(print_copied_inodes, after_copied_inodes, &ctx); - task_start(ctx.info); + task_start(ctx.info, NULL, NULL); } ret = copy_inodes(&cctx, root, convert_flags, &ctx); if (ret) { -- cgit v1.2.3 From 6a659dbfde228f3e5287f445816b29969a482fe8 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 14 Sep 2018 15:25:04 +0800 Subject: btrfs-progs: convert: Make read_disk_extent return more -EIO instead of -1 When pread64() returns value smaller than expected, it normally means EIO, so just return -EIO to replace the intermediate number. So when IO fails, we should be able to get more meaningful error number of than EPERM. Reviewed-by: Nikolay Borisov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/source-fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/source-fs.c b/convert/source-fs.c index b6d08370..5660a22c 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -201,7 +201,7 @@ int read_disk_extent(struct btrfs_root *root, u64 bytenr, ret = 0; fail: if (ret > 0) - ret = -1; + ret = -EIO; return ret; } -- cgit v1.2.3 From 23eed7acfdc61f257da6744329dc5dbbb2da4493 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 14 Sep 2018 15:25:05 +0800 Subject: btrfs-progs: convert: Output meaningful error messages for create_image When convert failed, the error messsage would look like: create btrfs filesystem: blocksize: 4096 nodesize: 16384 features: extref, skinny-metadata (default) creating ext2 image file ERROR: failed to create ext2_saved/image: -1 WARNING: an error occurred during conversion, filesystem is partially created but not finalized and not mountable We can only know something wrong happened during "ext2_saved/image" file creation, but unable to know what exactly went wrong. This patch will add the following error messages for create_image() and its callee: 1) Sanity test error 2) Csum calculation error 3) Free ino number allocation error 4) Inode creation error 5) Inode mode change error 6) Inode link error With all these error messages, we should be pretty easy to locate the error without extra debugging. Reported-by: Serhat Sevki Dincer Reviewed-by: Nikolay Borisov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- convert/main.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 3736a149..52c6d4ee 100644 --- a/convert/main.c +++ b/convert/main.c @@ -290,10 +290,16 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, if (disk_bytenr) { /* Check if the range is in a data block group */ bg_cache = btrfs_lookup_block_group(root->fs_info, bytenr); - if (!bg_cache) + if (!bg_cache) { + error("missing data block for bytenr %llu", bytenr); return -ENOENT; - if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA)) + } + if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA)) { + error( + "data bytenr %llu is covered by non-data block group %llu flags 0x%llu", + bytenr, bg_cache->key.objectid, bg_cache->flags); return -EINVAL; + } /* The extent should never cross block group boundary */ len = min_t(u64, len, bg_cache->key.objectid + @@ -310,8 +316,13 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, if (ret < 0) return ret; - if (datacsum) + if (datacsum) { ret = csum_disk_extent(trans, root, bytenr, len); + if (ret < 0) + error( + "failed to calculate csum for bytenr %llu len %llu: %s", + bytenr, len, strerror(-ret)); + } *ret_len = len; return ret; } @@ -759,18 +770,30 @@ static int create_image(struct btrfs_root *root, ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID, &ino); - if (ret < 0) + if (ret < 0) { + error("failed to find free objectid for root %llu: %s", + root->root_key.objectid, strerror(-ret)); goto out; + } ret = btrfs_new_inode(trans, root, ino, 0400 | S_IFREG); - if (ret < 0) + if (ret < 0) { + error("failed to create new inode for root %llu: %s", + root->root_key.objectid, strerror(-ret)); goto out; + } ret = btrfs_change_inode_flags(trans, root, ino, flags); - if (ret < 0) + if (ret < 0) { + error("failed to change inode flag for ino %llu root %llu: %s", + ino, root->root_key.objectid, strerror(-ret)); goto out; + } ret = btrfs_add_link(trans, root, ino, BTRFS_FIRST_FREE_OBJECTID, name, strlen(name), BTRFS_FT_REG_FILE, NULL, 1, 0); - if (ret < 0) + if (ret < 0) { + error("failed to link ino %llu to '/%s' in root %llu: %s", + ino, name, root->root_key.objectid, strerror(-ret)); goto out; + } key.objectid = ino; key.type = BTRFS_INODE_ITEM_KEY; -- cgit v1.2.3 From e578b59bf612178d7727c303102049e62676c308 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 25 Oct 2018 14:10:54 +0200 Subject: btrfs-progs: convert strerror to implicit %m Similar to the changes where strerror(errno) was converted, continue with the remaining cases where the argument was stored in another variable. The savings in object size are about 4500 bytes: $ size btrfs.old btrfs.new text data bss dec hex filename 805055 24248 19748 849051 cf49b btrfs.old 804527 24248 19748 848523 cf28b btrfs.new Signed-off-by: David Sterba --- convert/main.c | 58 +++++++++++++++++++++++++++-------------------- convert/source-ext2.c | 4 ++-- convert/source-reiserfs.c | 6 ++--- 3 files changed, 39 insertions(+), 29 deletions(-) (limited to 'convert') diff --git a/convert/main.c b/convert/main.c index 52c6d4ee..5ebc4482 100644 --- a/convert/main.c +++ b/convert/main.c @@ -318,10 +318,12 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, if (datacsum) { ret = csum_disk_extent(trans, root, bytenr, len); - if (ret < 0) + if (ret < 0) { + errno = -ret; error( - "failed to calculate csum for bytenr %llu len %llu: %s", - bytenr, len, strerror(-ret)); + "failed to calculate csum for bytenr %llu len %llu: %m", + bytenr, len); + } } *ret_len = len; return ret; @@ -771,27 +773,31 @@ static int create_image(struct btrfs_root *root, ret = btrfs_find_free_objectid(trans, root, BTRFS_FIRST_FREE_OBJECTID, &ino); if (ret < 0) { - error("failed to find free objectid for root %llu: %s", - root->root_key.objectid, strerror(-ret)); + errno = -ret; + error("failed to find free objectid for root %llu: %m", + root->root_key.objectid); goto out; } ret = btrfs_new_inode(trans, root, ino, 0400 | S_IFREG); if (ret < 0) { - error("failed to create new inode for root %llu: %s", - root->root_key.objectid, strerror(-ret)); + errno = -ret; + error("failed to create new inode for root %llu: %m", + root->root_key.objectid); goto out; } ret = btrfs_change_inode_flags(trans, root, ino, flags); if (ret < 0) { - error("failed to change inode flag for ino %llu root %llu: %s", - ino, root->root_key.objectid, strerror(-ret)); + errno = -ret; + error("failed to change inode flag for ino %llu root %llu: %m", + ino, root->root_key.objectid); goto out; } ret = btrfs_add_link(trans, root, ino, BTRFS_FIRST_FREE_OBJECTID, name, strlen(name), BTRFS_FT_REG_FILE, NULL, 1, 0); if (ret < 0) { - error("failed to link ino %llu to '/%s' in root %llu: %s", - ino, name, root->root_key.objectid, strerror(-ret)); + errno = -ret; + error("failed to link ino %llu to '/%s' in root %llu: %m", + ino, name, root->root_key.objectid); goto out; } @@ -1158,7 +1164,8 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize, ret = make_convert_btrfs(fd, &mkfs_cfg, &cctx); if (ret) { - error("unable to create initial ctree: %s", strerror(-ret)); + errno = -ret; + error("unable to create initial ctree: %m"); goto fail; } @@ -1376,8 +1383,8 @@ static int check_convert_image(struct btrfs_root *image_root, u64 ino, * So we only need to check if ret < 0 */ if (ret < 0) { - error("failed to iterate file extents at offset 0: %s", - strerror(-ret)); + errno = -ret; + error("failed to iterate file extents at offset 0: %m"); btrfs_release_path(&path); return ret; } @@ -1581,8 +1588,8 @@ static int do_rollback(const char *devname) ret = -ENOENT; goto close_fs; } else if (ret < 0) { - error("failed to find source fs image subvolume: %s", - strerror(-ret)); + errno = -ret; + error("failed to find source fs image subvolume: %m"); goto close_fs; } @@ -1593,8 +1600,8 @@ static int do_rollback(const char *devname) image_root = btrfs_read_fs_root(fs_info, &key); if (IS_ERR(image_root)) { ret = PTR_ERR(image_root); - error("failed to open convert image subvolume: %s", - strerror(-ret)); + errno = -ret; + error("failed to open convert image subvolume: %m"); goto close_fs; } @@ -1609,8 +1616,8 @@ static int do_rollback(const char *devname) ret = PTR_ERR(dir); else ret = -ENOENT; - error("failed to locate file %s: %s", image_name, - strerror(-ret)); + errno = -ret; + error("failed to locate file %s: %m", image_name); goto close_fs; } btrfs_dir_item_key_to_cpu(path.nodes[0], dir, &key); @@ -1623,7 +1630,8 @@ static int do_rollback(const char *devname) if (ret < 0) { btrfs_release_path(&path); - error("unable to find inode %llu: %s", ino, strerror(-ret)); + errno = -ret; + error("unable to find inode %llu: %m", ino); goto close_fs; } inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0], @@ -1665,8 +1673,9 @@ close_fs: ret = -errno; else ret = -EIO; - error("failed to recover range [%llu, %llu): %s", - range->start, real_size, strerror(-ret)); + errno = -ret; + error("failed to recover range [%llu, %llu): %m", + range->start, real_size); goto free_mem; } ret = 0; @@ -1828,7 +1837,8 @@ int main(int argc, char *argv[]) file = argv[optind]; ret = check_mounted(file); if (ret < 0) { - error("could not check mount status: %s", strerror(-ret)); + errno = -ret; + error("could not check mount status: %m"); return 1; } else if (ret) { error("%s is mounted", file); diff --git a/convert/source-ext2.c b/convert/source-ext2.c index a2af1212..a136e565 100644 --- a/convert/source-ext2.c +++ b/convert/source-ext2.c @@ -162,8 +162,8 @@ static int ext2_read_used_space(struct btrfs_convert_context *cctx) } ret = __ext2_add_one_block(fs, block_bitmap, i, used_tree); if (ret < 0) { - error("fail to build used space tree, %s", - strerror(-ret)); + errno = -ret; + error("fail to build used space tree, %m"); break; } blk_itr += EXT2_CLUSTERS_PER_GROUP(fs->super); diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index e0b3b685..7a70cadd 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -493,10 +493,10 @@ static int reiserfs_copy_dirent(reiserfs_filsys_t fs, ret = reiserfs_copy_meta(fs, root, dirent_data->convert_flags, deh_dirid, deh_objectid, &type); if (ret) { + errno = -ret; error( - "an error occured while converting \"%.*s\", reiserfs key [%u %u]: %s", - (int)len, name, deh_dirid, deh_objectid, - strerror(-ret)); + "an error occured while converting \"%.*s\", reiserfs key [%u %u]: %m", + (int)len, name, deh_dirid, deh_objectid); return ret; } trans = btrfs_start_transaction(root, 1); -- cgit v1.2.3 From b1d39a42a4efd640df365447aadb719eadf7fa49 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Mon, 26 Nov 2018 18:01:42 +0100 Subject: btrfs-progs: fix typos in comments Generated by https://github.com/jsoref/spelling Issue: #154 Author: Josh Soref Signed-off-by: David Sterba --- convert/common.c | 2 +- convert/main.c | 12 ++++++------ convert/source-fs.h | 2 +- convert/source-reiserfs.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index 6ddf4a46..b7674297 100644 --- a/convert/common.c +++ b/convert/common.c @@ -76,7 +76,7 @@ static inline int write_temp_super(int fd, struct btrfs_super_block *sb, } /* - * Setup temporary superblock at cfg->super_bynter + * Setup temporary superblock at cfg->super_bytenr * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr * * For now sys chunk array will be empty and dev_item is empty too. diff --git a/convert/main.c b/convert/main.c index 5ebc4482..68f76f71 100644 --- a/convert/main.c +++ b/convert/main.c @@ -66,7 +66,7 @@ * c. Doesn't cover any data chunks in 1.1 * * 2) Create basic btrfs filesystem structure - * Initial metadata and sys chunks are inserted in the first availabe + * Initial metadata and sys chunks are inserted in the first available * space found in step 1.3 * Then insert all data chunks into the basic btrfs * @@ -682,7 +682,7 @@ static int calculate_available_space(struct btrfs_convert_context *cctx) cur_off = 0; /* * Calculate free space - * Always round up the start bytenr, to avoid metadata extent corss + * Always round up the start bytenr, to avoid metadata extent cross * stripe boundary, as later mkfs_convert() won't have all the extent * allocation check */ @@ -724,7 +724,7 @@ out: /* * Read used space, and since we have the used space, - * calcuate data_chunks and free for later mkfs + * calculate data_chunks and free for later mkfs */ static int convert_read_used_space(struct btrfs_convert_context *cctx) { @@ -975,7 +975,7 @@ static int init_btrfs(struct btrfs_mkfs_config *cfg, struct btrfs_root *root, /* * Don't alloc any metadata/system chunk, as we don't want - * any meta/sys chunk allcated before all data chunks are inserted. + * any meta/sys chunk allocated before all data chunks are inserted. * Or we screw up the chunk layout just like the old implement. */ fs_info->avoid_sys_chunk_alloc = 1; @@ -1344,7 +1344,7 @@ static bool is_chunk_direct_mapped(struct btrfs_fs_info *fs_info, u64 start) if (map->num_stripes != 1) goto out; - /* Chunk's logical doesn't match with phisical, not 1:1 mapped */ + /* Chunk's logical doesn't match with physical, not 1:1 mapped */ if (map->ce.start != map->stripes[0].physical) goto out; ret = true; @@ -1356,7 +1356,7 @@ out: * Iterate all file extents of the convert image. * * All file extents except ones in btrfs_reserved_ranges must be mapped 1:1 - * on disk. (Means thier file_offset must match their on disk bytenr) + * on disk. (Means their file_offset must match their on disk bytenr) * * File extents in reserved ranges can be relocated to other place, and in * that case we will read them out for later use. diff --git a/convert/source-fs.h b/convert/source-fs.h index f5314aff..7cf587d9 100644 --- a/convert/source-fs.h +++ b/convert/source-fs.h @@ -26,7 +26,7 @@ #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID /* - * Reresents a simple contiguous range. + * Represents a simple contiguous range. * * For multiple or non-contiguous ranges, use extent_cache_tree from * extent-cache.c diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 7a70cadd..cc592f19 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -301,7 +301,7 @@ static int reiserfs_record_indirect_extent(reiserfs_filsys_t fs, u64 position, /* * Unlike btrfs inline extents, reiserfs can have multiple inline extents. - * This handles concatanating multiple tails into one inline extent + * This handles concatenating multiple tails into one inline extent * for insertion. */ static int reiserfs_record_direct_extent(reiserfs_filsys_t fs, __u64 position, @@ -564,7 +564,7 @@ static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, }; /* The root directory's dirid in reiserfs points to an object - * that does't exist. In btrfs it's self-referential. + * that doens't exist. In btrfs it's self-referential. */ if (deh_dirid == REISERFS_ROOT_PARENT_OBJECTID) parent = objectid; -- cgit v1.2.3 From 2cd4a76ea9914c35b0fb614ad68aa9aefd7e5dd7 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Mon, 26 Nov 2018 18:08:21 +0100 Subject: btrfs-progs: fix typos in user-visible strings * error messages * help strings Generated by https://github.com/jsoref/spelling Issue: #154 Author: Josh Soref Signed-off-by: David Sterba --- convert/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'convert') diff --git a/convert/common.c b/convert/common.c index b7674297..a9b24043 100644 --- a/convert/common.c +++ b/convert/common.c @@ -98,7 +98,7 @@ static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg, if (*cfg->fs_uuid) { if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) { - error("cound not parse UUID: %s", cfg->fs_uuid); + error("could not parse UUID: %s", cfg->fs_uuid); ret = -EINVAL; goto out; } -- cgit v1.2.3