Introduce new function, check_rollback(), to collect data and check if we can rollback the image.
The check part is quite straight forward: Ensure all the file extents, except the ones inside reserved ranges, are mapped 1:1 on disk. The ones inside reserved ranges, 0~1M, 1st sb +64K, 2nd sb +64K, can be mapped to anywhere, as btrfs needs to put super blocks in them. Such behavir can meet both old convert(one large chunk) and new convert(only old image file is 1:1 mapped). Also, the function will read out the data in btrfs reserved ranges for later rollback usage. Signed-off-by: Qu Wenruo <quwen...@cn.fujitsu.com> --- convert/main.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/convert/main.c b/convert/main.c index 87c52c1..5b9141b 100644 --- a/convert/main.c +++ b/convert/main.c @@ -2781,6 +2781,220 @@ static int record_reloc_data(struct btrfs_fs_info *fs_info, return 0; } +static int check_image_file_extents(struct btrfs_root *image_root, u64 ino, + u64 total_size, char *reloc_ranges[3]) +{ + 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 only needs to check ret < 0 case + */ + 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; + u64 extent_offset; + 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); + extent_offset = btrfs_file_extent_offset(leaf, fi); + + checked_bytes += ram_bytes; + /* Skip hole */ + if (disk_bytenr == 0) + goto next; + + if (file_offset != disk_bytenr) { + /* + * Only file extent in btrfs reserved ranges are allow + * non-1:1 mapped + */ + if (!is_range_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; + } + } + ret = record_reloc_data(fs_info, file_offset, + disk_bytenr + extent_offset, ram_bytes, + reloc_ranges); + if (ret < 0) { + error("ino %llu offset %llu failed to read extent data", + 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); + } + } + return ret; +} + +/* + * Check and record needed blocks for rollback. + * + * It will record data for superblock and reserved ranges to reloc_ranges[]. + * So we can rollback the fs after close_ctree(). + */ +static int check_rollback(struct btrfs_fs_info *fs_info, char *reloc_ranges[3]) +{ + struct btrfs_root *image_root; + struct btrfs_dir_item *dir; + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_inode_item *inode_item; + char *image_name = "image"; + u64 ino; + u64 root_dir; + u64 total_bytes; + int ret; + + btrfs_init_path(&path); + + /* + * Search for root backref, or after subvolume delete(orphan), + * we can still rollback if the subvolume is just orphan. + */ + key.objectid = CONV_IMAGE_SUBVOL_OBJECTID; + key.type = BTRFS_ROOT_BACKREF_KEY; + key.offset = BTRFS_FS_TREE_OBJECTID; + + 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?"); + return -ENOENT; + } else if (ret < 0) { + error("failed to find ext2 image subvolume: %s", + strerror(-ret)); + return ret; + } + + /* 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(fs_info, &key); + if (IS_ERR(image_root)) { + ret = PTR_ERR(image_root); + error("failed to open convert image subvolume: %s", + strerror(-ret)); + return ret; + } + + /* 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)) { + btrfs_release_path(&path); + if (dir) + ret = PTR_ERR(dir); + else + ret = -ENOENT; + error("failed to locate file %s: %s", image_name, + strerror(-ret)); + return ret; + } + btrfs_dir_item_key_to_cpu(path.nodes[0], dir, &key); + btrfs_release_path(&path); + + /* Get total size of the original image */ + ino = key.objectid; + + ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0); + if (ret < 0) { + btrfs_release_path(&path); + error("unable to find inode %llu: %s", ino, strerror(-ret)); + return ret; + } + 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); + + /* Main function to check every file extent of the image file */ + ret = check_image_file_extents(image_root, ino, total_bytes, + reloc_ranges); + return ret; +} + static int do_rollback(const char *devname) { int fd = -1; -- 2.10.2 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html