+}
+
+int btrfs_scan_fs_devices(int fd, const char *path,
+ struct btrfs_fs_devices **fs_devices,
+ u64 sb_bytenr, int super_recover,
+ int skip_devices)
+{
+ u64 total_devs;
+ u64 dev_size;
+ off_t seek_ret;
+ int ret;
+ if (!sb_bytenr)
+ sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
+
+ seek_ret = lseek(fd, 0, SEEK_END);
+ if (seek_ret < 0)
+ return -errno;
+
+ dev_size = seek_ret;
+ lseek(fd, 0, SEEK_SET);
+ if (sb_bytenr > dev_size) {
+ fprintf(stderr, "Superblock bytenr is larger than device size\n");
+ return -EINVAL;
+ }
+
+ ret = btrfs_scan_one_device(fd, path, fs_devices,
+ &total_devs, sb_bytenr, super_recover);
+ if (ret) {
+ fprintf(stderr, "No valid Btrfs found on %s\n", path);
+ return ret;
+ }
+
+ if (!skip_devices && total_devs != 1) {
+ ret = btrfs_scan_lblkid();
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int btrfs_setup_chunk_tree_and_device_map(struct btrfs_fs_info *fs_info,
+ u64 chunk_root_bytenr)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ u32 sectorsize;
+ u32 nodesize;
+ u32 leafsize;
+ u32 blocksize;
+ u32 stripesize;
+ u64 generation;
+ int ret;
+
+ nodesize = btrfs_super_nodesize(sb);
+ leafsize = btrfs_super_leafsize(sb);
+ sectorsize = btrfs_super_sectorsize(sb);
+ stripesize = btrfs_super_stripesize(sb);
+
+ __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ fs_info->chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
+
+ ret = btrfs_read_sys_array(fs_info->chunk_root);
+ if (ret)
+ return ret;
+
+ blocksize = fs_info->chunk_root->nodesize;
+ generation = btrfs_super_chunk_root_generation(sb);
+
+ if (chunk_root_bytenr && !IS_ALIGNED(chunk_root_bytenr,
+ btrfs_super_sectorsize(sb))) {
+ warning("chunk_root_bytenr %llu is unaligned to %u, ignore it",
+ chunk_root_bytenr, btrfs_super_sectorsize(sb));
+ chunk_root_bytenr = 0;
+ }
+
+ if (!chunk_root_bytenr)
+ chunk_root_bytenr = btrfs_super_chunk_root(sb);
+ else
+ generation = 0;
+
+ fs_info->chunk_root->node = read_tree_block(fs_info->chunk_root,
+ chunk_root_bytenr,
+ blocksize, generation);
+ if (!extent_buffer_uptodate(fs_info->chunk_root->node)) {
+ if (fs_info->ignore_chunk_tree_error) {
+ warning("cannot read chunk root, continue anyway");
+ fs_info->chunk_root = NULL;
+ return 0;
+ } else {
+ error("cannot read chunk root");
+ return -EIO;
+ }
+ }
+
+ if (!(btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_METADUMP)) {
+ ret = btrfs_read_chunk_tree(fs_info->chunk_root);
+ if (ret) {
+ fprintf(stderr, "Couldn't read chunk tree\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
+ u64 sb_bytenr,
+ u64 root_tree_bytenr,
+ u64 chunk_root_bytenr,
+ enum btrfs_open_ctree_flags flags)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_super_block *disk_super;
+ struct btrfs_fs_devices *fs_devices = NULL;
+ struct extent_buffer *eb;
+ int ret;
+ int oflags;
+
+ if (sb_bytenr == 0)
+ sb_bytenr = BTRFS_SUPER_INFO_OFFSET;
+
+ /* try to drop all the caches */
+ if (posix_fadvise(fp, 0, 0, POSIX_FADV_DONTNEED))
+ fprintf(stderr, "Warning, could not drop caches\n");
+
+ fs_info = btrfs_new_fs_info(flags & OPEN_CTREE_WRITES, sb_bytenr);
+ if (!fs_info) {
+ fprintf(stderr, "Failed to allocate memory for fs_info\n");
+ return NULL;
+ }
+ if (flags & OPEN_CTREE_RESTORE)
+ fs_info->on_restoring = 1;
+ if (flags & OPEN_CTREE_SUPPRESS_CHECK_BLOCK_ERRORS)
+ fs_info->suppress_check_block_errors = 1;
+ if (flags & OPEN_CTREE_IGNORE_FSID_MISMATCH)
+ fs_info->ignore_fsid_mismatch = 1;
+ if (flags & OPEN_CTREE_IGNORE_CHUNK_TREE_ERROR)
+ fs_info->ignore_chunk_tree_error = 1;
+
+ ret = btrfs_scan_fs_devices(fp, path, &fs_devices, sb_bytenr,
+ (flags & OPEN_CTREE_RECOVER_SUPER),
+ (flags & OPEN_CTREE_NO_DEVICES));
+ if (ret)
+ goto out;
+
+ fs_info->fs_devices = fs_devices;
+ if (flags & OPEN_CTREE_WRITES)
+ oflags = O_RDWR;
+ else
+ oflags = O_RDONLY;
+
+ if (flags & OPEN_CTREE_EXCLUSIVE)
+ oflags |= O_EXCL;
+
+ ret = btrfs_open_devices(fs_devices, oflags);
+ if (ret)
+ goto out;
+
+ disk_super = fs_info->super_copy;
+ if (flags & OPEN_CTREE_RECOVER_SUPER)
+ ret = btrfs_read_dev_super(fs_devices->latest_bdev,
+ disk_super, sb_bytenr, 1);
+ else
+ ret = btrfs_read_dev_super(fp, disk_super, sb_bytenr, 0);
+ if (ret) {
+ printk("No valid btrfs found\n");
+ goto out_devices;
+ }
+
+ if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_CHANGING_FSID &&
+ !fs_info->ignore_fsid_mismatch) {
+ fprintf(stderr, "ERROR: Filesystem UUID change in progress\n");
+ goto out_devices;
+ }
+
+ memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE);
+
+ ret = btrfs_check_fs_compatibility(fs_info->super_copy,
+ flags & OPEN_CTREE_WRITES);
+ if (ret)
+ goto out_devices;
+
+ ret = btrfs_setup_chunk_tree_and_device_map(fs_info, chunk_root_bytenr);
+ if (ret)
+ goto out_chunk;
+
+ /* Chunk tree root is unable to read, return directly */
+ if (!fs_info->chunk_root)
+ return fs_info;
+
+ eb = fs_info->chunk_root->node;
+ read_extent_buffer(eb, fs_info->chunk_tree_uuid,
+ btrfs_header_chunk_tree_uuid(eb),
+ BTRFS_UUID_SIZE);
+
+ ret = btrfs_setup_all_roots(fs_info, root_tree_bytenr, flags);
+ if (ret && !(flags & __OPEN_CTREE_RETURN_CHUNK_ROOT) &&
+ !fs_info->ignore_chunk_tree_error)
+ goto out_chunk;
+
+ return fs_info;
+
+out_chunk:
+ btrfs_release_all_roots(fs_info);
+ btrfs_cleanup_all_caches(fs_info);
+out_devices:
+ btrfs_close_devices(fs_devices);