+}
+
+static int
+extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
+ const tchar *target, int extract_flags)
+{
+ const struct apply_operations *ops;
+ struct apply_ctx *ctx;
+ int ret;
+ LIST_HEAD(dentry_list);
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
+ ret = extract_dentries_to_stdout(trees, num_trees,
+ wim->lookup_table);
+ goto out;
+ }
+
+ num_trees = remove_duplicate_trees(trees, num_trees);
+ num_trees = remove_contained_trees(trees, num_trees);
+
+ ops = select_apply_operations(extract_flags);
+
+ if (num_trees > 1 && ops->single_tree_only) {
+ ERROR("Extracting multiple directory trees "
+ "at once is not supported in %s extraction mode!",
+ ops->name);
+ ret = WIMLIB_ERR_UNSUPPORTED;
+ goto out;
+ }
+
+ ctx = CALLOC(1, ops->context_size);
+ if (!ctx) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out;
+ }
+
+ ctx->wim = wim;
+ ctx->target = target;
+ ctx->target_nchars = tstrlen(target);
+ ctx->extract_flags = extract_flags;
+ if (ctx->wim->progfunc) {
+ ctx->progfunc = ctx->wim->progfunc;
+ ctx->progctx = ctx->wim->progctx;
+ ctx->progress.extract.image = wim->current_image;
+ ctx->progress.extract.extract_flags = (extract_flags &
+ WIMLIB_EXTRACT_MASK_PUBLIC);
+ ctx->progress.extract.wimfile_name = wim->filename;
+ ctx->progress.extract.image_name = wimlib_get_image_name(wim,
+ wim->current_image);
+ ctx->progress.extract.target = target;
+ }
+ INIT_LIST_HEAD(&ctx->stream_list);
+ filedes_invalidate(&ctx->tmpfile_fd);
+
+ ret = (*ops->get_supported_features)(target, &ctx->supported_features);
+ if (ret)
+ goto out_cleanup;
+
+ build_dentry_list(&dentry_list, trees, num_trees,
+ !(extract_flags &
+ WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE));
+
+ dentry_list_get_features(&dentry_list, &ctx->required_features);
+
+ ret = do_feature_check(&ctx->required_features, &ctx->supported_features,
+ ctx->extract_flags);
+ if (ret)
+ goto out_cleanup;
+
+ ret = dentry_list_calculate_extraction_names(&dentry_list, ctx);
+ if (ret)
+ goto out_cleanup;
+
+ ret = dentry_list_resolve_streams(&dentry_list, ctx);
+ if (ret)
+ goto out_cleanup;
+
+ ret = dentry_list_ref_streams(&dentry_list, ctx);
+ if (ret)
+ goto out_cleanup;
+
+ dentry_list_build_inode_alias_lists(&dentry_list);
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
+ /* When extracting from a pipe, the number of bytes of data to
+ * extract can't be determined in the normal way (examining the
+ * lookup table), since at this point all we have is a set of
+ * SHA1 message digests of streams that need to be extracted.
+ * However, we can get a reasonably accurate estimate by taking
+ * <TOTALBYTES> from the corresponding <IMAGE> in the WIM XML
+ * data. This does assume that a full image is being extracted,
+ * but currently there is no API for doing otherwise. (Also,
+ * subtract <HARDLINKBYTES> from this if hard links are
+ * supported by the extraction mode.) */
+ ctx->progress.extract.total_bytes =
+ wim_info_get_image_total_bytes(wim->wim_info,
+ wim->current_image);
+ if (ctx->supported_features.hard_links) {
+ ctx->progress.extract.total_bytes -=
+ wim_info_get_image_hard_link_bytes(wim->wim_info,
+ wim->current_image);
+ }
+ }
+
+ ret = extract_progress(ctx,
+ ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ?
+ WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN :
+ WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN));
+ if (ret)
+ goto out_cleanup;
+
+ ret = (*ops->extract)(&dentry_list, ctx);
+ if (ret)
+ goto out_cleanup;
+
+ if (ctx->progress.extract.completed_bytes <
+ ctx->progress.extract.total_bytes)
+ {
+ ctx->progress.extract.completed_bytes =
+ ctx->progress.extract.total_bytes;
+ ret = extract_progress(ctx, WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS);
+ if (ret)
+ goto out_cleanup;
+ }
+
+ ret = extract_progress(ctx,
+ ((extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE) ?
+ WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END :
+ WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END));
+out_cleanup:
+ destroy_stream_list(&ctx->stream_list);
+ destroy_dentry_list(&dentry_list);
+ FREE(ctx);