diff options
author | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-10-18 13:15:59 +0100 |
---|---|---|
committer | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-10-18 13:15:59 +0100 |
commit | 74d288e05c2d0cb97186f51049813b3e5b5bb0cd (patch) | |
tree | 4fc213398dc89e053d53ff7d42102942470e9cb5 /convert | |
parent | 569a646293cd782de7665b6158514f3b48d229d3 (diff) |
New upstream release.
Diffstat (limited to 'convert')
-rw-r--r-- | convert/main.c | 163 | ||||
-rw-r--r-- | convert/source-ext2.c | 44 | ||||
-rw-r--r-- | convert/source-ext2.h | 42 | ||||
-rw-r--r-- | convert/source-fs.c | 50 | ||||
-rw-r--r-- | convert/source-fs.h | 58 | ||||
-rw-r--r-- | convert/source-reiserfs.c | 976 | ||||
-rw-r--r-- | convert/source-reiserfs.h | 73 |
7 files changed, 1174 insertions, 232 deletions
diff --git a/convert/main.c b/convert/main.c index 0deccd9c..882daf7c 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) @@ -343,10 +347,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); @@ -414,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, @@ -633,7 +639,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 */ @@ -745,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); @@ -798,7 +804,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; @@ -832,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 (!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) { @@ -1013,7 +896,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); @@ -1071,9 +954,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); @@ -1289,7 +1172,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); @@ -1313,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, true); if (!image_root) { error("unable to link subvolume %s", subvol_name); goto fail; @@ -1357,7 +1241,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); @@ -1595,7 +1479,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-------------------->| @@ -1664,11 +1548,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; } @@ -1786,6 +1670,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-ext2.c b/convert/source-ext2.c index 24744e22..e9277213 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; } /* @@ -154,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); @@ -424,27 +426,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) { @@ -659,21 +640,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) { @@ -713,7 +685,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)); @@ -833,8 +805,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)); @@ -860,7 +832,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-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.c b/convert/source-fs.c index 59e36095..b6d08370 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -28,18 +28,41 @@ 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) +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; + + 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; - 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 +87,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 +115,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); diff --git a/convert/source-fs.h b/convert/source-fs.h index 3a6fa46c..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 <linux/kdev_t.h> #include <pthread.h> - #define CONV_IMAGE_SUBVOL_OBJECTID BTRFS_FIRST_FREE_OBJECTID /* @@ -55,6 +55,62 @@ 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); +} + +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 new file mode 100644 index 00000000..be79d8e2 --- /dev/null +++ b/convert/source-reiserfs.c @@ -0,0 +1,976 @@ +/* + * 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 <linux/limits.h> +#include <linux/fs.h> +#include <limits.h> +#include <sys/stat.h> +#include <stdbool.h> +#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; +} + +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 = 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 = 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); +} + +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; +} + +static int reiserfs_copy_meta(reiserfs_filsys_t fs, struct btrfs_root *root, + u32 convert_flags, u32 deh_dirid, + u32 deh_objectid, u8 *type); + +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 (IS_ERR(trans)) + return PTR_ERR(trans); + + 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 (IS_ERR(trans)) { + ret = PTR_ERR(trans); + 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 (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto fail; + } + + ret = btrfs_insert_inode_ref(trans, root, "..", 2, parent, + objectid, 0); + break; + case S_IFLNK: + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + 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 (IS_ERR(trans)) { + ret = PTR_ERR(trans); + 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; +} + +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 (IS_ERR(xa_data->trans)) + return PTR_ERR(xa_data->trans); + + 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..a4e14392 --- /dev/null +++ b/convert/source-reiserfs.h @@ -0,0 +1,73 @@ +/* + * 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 <reiserfs/misc.h> +#include <reiserfs/io.h> +#include <reiserfs/reiserfs_lib.h> +#include <reiserfs/reiserfs_fs.h> +#include "convert/source-fs.h" + +#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 |