+ if (unlikely(filedes_valid(&ctx->tmpfile_fd))) {
+ filedes_close(&ctx->tmpfile_fd);
+ if (!status)
+ status = extract_from_tmpfile(ctx->tmpfile_name, ctx);
+ filedes_invalidate(&ctx->tmpfile_fd);
+ tunlink(ctx->tmpfile_name);
+ FREE(ctx->tmpfile_name);
+ return status;
+ } else {
+ return (*ctx->saved_cbs->end_stream)(stream, status,
+ ctx->saved_cbs->end_stream_ctx);
+ }
+}
+
+/*
+ * Read the list of single-instance streams to extract and feed their data into
+ * the specified callback functions.
+ *
+ * This handles checksumming each stream.
+ *
+ * This also handles sending WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS.
+ *
+ * This also works if the WIM is being read from a pipe, whereas attempting to
+ * read streams directly (e.g. with read_full_stream_into_buf()) will not.
+ *
+ * This also will split up streams that will need to be extracted to more than
+ * MAX_OPEN_STREAMS locations, as measured by the 'out_refcnt' of each stream.
+ * Therefore, the apply_operations implementation need not worry about running
+ * out of file descriptors, unless it might open more than one file descriptor
+ * per nominal destination (e.g. Win32 currently might because the destination
+ * file system might not support hard links).
+ */
+int
+extract_stream_list(struct apply_ctx *ctx,
+ const struct read_stream_list_callbacks *cbs)
+{
+ struct read_stream_list_callbacks wrapper_cbs = {
+ .begin_stream = begin_extract_stream_wrapper,
+ .begin_stream_ctx = ctx,
+ .consume_chunk = extract_chunk_wrapper,
+ .consume_chunk_ctx = ctx,
+ .end_stream = end_extract_stream_wrapper,
+ .end_stream_ctx = ctx,
+ };
+ ctx->saved_cbs = cbs;
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE) {
+ return load_streams_from_pipe(ctx, &wrapper_cbs);
+ } else {
+ return read_stream_list(&ctx->stream_list,
+ offsetof(struct wim_lookup_table_entry,
+ extraction_list),
+ &wrapper_cbs, VERIFY_STREAM_HASHES);
+ }
+}
+
+/* Extract a WIM dentry to standard output.
+ *
+ * This obviously doesn't make sense in all cases. We return an error if the
+ * dentry does not correspond to a regular file. Otherwise we extract the
+ * unnamed data stream only. */
+static int
+extract_dentry_to_stdout(struct wim_dentry *dentry,
+ const struct wim_lookup_table *lookup_table)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ struct filedes _stdout;
+
+ if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_DIRECTORY))
+ {
+ ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
+ "extracted to standard output", dentry_full_path(dentry));
+ return WIMLIB_ERR_NOT_A_REGULAR_FILE;
+ }
+
+ lte = inode_unnamed_lte(inode, lookup_table);
+ if (!lte) {
+ const u8 *hash = inode_unnamed_stream_hash(inode);
+ if (!is_zero_hash(hash))
+ return stream_not_found_error(inode, hash);
+ return 0;
+ }
+
+ filedes_init(&_stdout, STDOUT_FILENO);
+ return extract_full_stream_to_fd(lte, &_stdout);
+}
+
+static int
+extract_dentries_to_stdout(struct wim_dentry **dentries, size_t num_dentries,
+ const struct wim_lookup_table *lookup_table)
+{
+ for (size_t i = 0; i < num_dentries; i++) {
+ int ret = extract_dentry_to_stdout(dentries[i], lookup_table);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/**********************************************************************/
+
+/*
+ * Removes duplicate dentries from the array.
+ *
+ * Returns the new number of dentries, packed at the front of the array.
+ */
+static size_t
+remove_duplicate_trees(struct wim_dentry **trees, size_t num_trees)
+{
+ size_t i, j = 0;
+ for (i = 0; i < num_trees; i++) {
+ if (!trees[i]->tmp_flag) {
+ /* Found distinct dentry. */
+ trees[i]->tmp_flag = 1;
+ trees[j++] = trees[i];
+ }
+ }
+ for (i = 0; i < j; i++)
+ trees[i]->tmp_flag = 0;
+ return j;
+}
+
+/*
+ * Remove dentries that are descendants of other dentries in the array.
+ *
+ * Returns the new number of dentries, packed at the front of the array.
+ */
+static size_t
+remove_contained_trees(struct wim_dentry **trees, size_t num_trees)
+{
+ size_t i, j = 0;
+ for (i = 0; i < num_trees; i++)
+ trees[i]->tmp_flag = 1;
+ for (i = 0; i < num_trees; i++) {
+ struct wim_dentry *d = trees[i];
+ while (!dentry_is_root(d)) {
+ d = d->d_parent;
+ if (d->tmp_flag)
+ goto tree_contained;
+ }
+ trees[j++] = trees[i];
+ continue;
+
+ tree_contained:
+ trees[i]->tmp_flag = 0;
+ }
+
+ for (i = 0; i < j; i++)
+ trees[i]->tmp_flag = 0;
+ return j;
+}
+
+static int
+dentry_append_to_list(struct wim_dentry *dentry, void *_dentry_list)
+{
+ struct list_head *dentry_list = _dentry_list;
+ list_add_tail(&dentry->d_extraction_list_node, dentry_list);
+ return 0;
+}
+
+static void
+dentry_reset_extraction_list_node(struct wim_dentry *dentry)
+{
+ dentry->d_extraction_list_node = (struct list_head){NULL, NULL};
+}
+
+static int
+dentry_delete_from_list(struct wim_dentry *dentry, void *_ignore)
+{
+ list_del(&dentry->d_extraction_list_node);
+ dentry_reset_extraction_list_node(dentry);
+ return 0;
+}
+
+/*
+ * Build the preliminary list of dentries to be extracted.
+ *
+ * The list maintains the invariant that if d1 and d2 are in the list and d1 is
+ * an ancestor of d2, then d1 appears before d2 in the list.
+ */
+static void
+build_dentry_list(struct list_head *dentry_list, struct wim_dentry **trees,
+ size_t num_trees, bool add_ancestors)
+{
+ INIT_LIST_HEAD(dentry_list);
+
+ /* Add the trees recursively. */
+ for (size_t i = 0; i < num_trees; i++)
+ for_dentry_in_tree(trees[i], dentry_append_to_list, dentry_list);
+
+ /* If requested, add ancestors of the trees. */
+ if (add_ancestors) {
+ for (size_t i = 0; i < num_trees; i++) {
+ struct wim_dentry *dentry = trees[i];
+ struct wim_dentry *ancestor;
+ struct list_head *place_after;
+
+ if (dentry_is_root(dentry))
+ continue;
+
+ place_after = dentry_list;
+ ancestor = dentry;
+ do {
+ ancestor = ancestor->d_parent;
+ if (will_extract_dentry(ancestor)) {
+ place_after = &ancestor->d_extraction_list_node;
+ break;