+ if (raw_fd < 0) {
+ if (errno == EEXIST) {
+ FREE(name);
+ goto retry;
+ }
+ ERROR_WITH_ERRNO("Failed to open temporary file \"%"TS"\"", name);
+ FREE(name);
+ return WIMLIB_ERR_OPEN;
+ }
+
+ filedes_init(fd_ret, raw_fd);
+ *name_ret = name;
+ return 0;
+}
+
+/* Extract all instances of the stream @lte that are being extracted in this
+ * call of extract_tree(), but actually read the stream data from @lte_override.
+ */
+static int
+extract_stream_instances(struct wim_lookup_table_entry *lte,
+ struct wim_lookup_table_entry *lte_override,
+ struct apply_ctx *ctx)
+{
+ struct wim_dentry **lte_dentries;
+ tchar path[ctx->ops->path_max];
+ size_t i;
+ int ret;
+
+ if (lte->out_refcnt <= ARRAY_LEN(lte->inline_lte_dentries))
+ lte_dentries = lte->inline_lte_dentries;
+ else
+ lte_dentries = lte->lte_dentries;
+
+ for (i = 0; i < lte->out_refcnt; i++) {
+ struct wim_dentry *dentry = lte_dentries[i];
+
+ if (dentry->tmp_flag)
+ continue;
+ if (!build_extraction_path(path, dentry, ctx))
+ continue;
+ ret = extract_streams(path, ctx, dentry, lte, lte_override);
+ if (ret)
+ goto out_clear_tmp_flags;
+ dentry->tmp_flag = 1;
+ }
+ ret = 0;
+out_clear_tmp_flags:
+ for (i = 0; i < lte->out_refcnt; i++)
+ lte_dentries[i]->tmp_flag = 0;
+ return ret;
+}
+
+/* Determine whether the specified stream needs to be extracted to a temporary
+ * file or not.
+ *
+ * @lte->out_refcnt specifies the number of instances of this stream that must
+ * be extracted.
+ *
+ * @is_partial_res is %true if this stream is just one of multiple in a single
+ * WIM resource being extracted. */
+static bool
+need_tmpfile_to_extract(struct wim_lookup_table_entry *lte,
+ bool is_partial_res)
+{
+ /* Temporary file is always required when reading a partial resource,
+ * since in that case we retrieve all the contained streams in one pass.
+ * */
+ if (is_partial_res)
+ return true;
+
+ /* Otherwise we don't need a temporary file if only a single instance of
+ * the stream is needed. */
+ if (lte->out_refcnt == 1)
+ return false;
+
+ wimlib_assert(lte->out_refcnt >= 2);
+
+ /* We also don't need a temporary file if random access to the stream is
+ * allowed. */
+ if (lte->resource_location != RESOURCE_IN_WIM ||
+ filedes_is_seekable(<e->rspec->wim->in_fd))
+ return false;
+
+ return true;
+}
+
+static int
+begin_extract_stream(struct wim_lookup_table_entry *lte,
+ u32 flags, void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ int ret;
+
+ if (flags & BEGIN_STREAM_FLAG_WHOLE_STREAM) {
+ DEBUG("Whole stream (size=%"PRIu64") will be read into memory",
+ lte->size);
+ ctx->cur_stream = lte;
+ filedes_invalidate(&ctx->tmpfile_fd);
+ return 0;
+ }
+
+ if (!need_tmpfile_to_extract(lte,
+ (flags & BEGIN_STREAM_FLAG_PARTIAL_RESOURCE)))
+ {
+ DEBUG("Temporary file not needed "
+ "for stream (size=%"PRIu64")", lte->size);
+ ret = extract_stream_instances(lte, lte, ctx);
+ if (ret)
+ return ret;
+
+ return BEGIN_STREAM_STATUS_SKIP_STREAM;
+ }
+
+ DEBUG("Temporary file needed for stream (size=%"PRIu64")", lte->size);
+ return create_temporary_file(&ctx->tmpfile_fd, &ctx->tmpfile_name);
+}
+
+static int
+extract_chunk(const void *chunk, size_t size, void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ int ret;
+
+ if (filedes_valid(&ctx->tmpfile_fd)) {
+ ret = full_write(&ctx->tmpfile_fd, chunk, size);
+ if (ret)
+ ERROR_WITH_ERRNO("Error writing to file descriptor");
+ } else {
+ struct wim_lookup_table_entry lte_override;
+
+ memcpy(<e_override, ctx->cur_stream,
+ sizeof(struct wim_lookup_table_entry));
+
+ lte_override.resource_location = RESOURCE_IN_ATTACHED_BUFFER;
+ lte_override.size = size;
+ lte_override.attached_buffer = (void *)chunk;
+
+ ret = extract_stream_instances(ctx->cur_stream, <e_override, ctx);
+ }
+ return ret;
+}
+
+static int
+end_extract_stream(struct wim_lookup_table_entry *lte,
+ int status, void *_ctx)
+{
+ struct apply_ctx *ctx = _ctx;
+ struct wim_lookup_table_entry lte_override;
+ int ret;
+ int errno_save = errno;
+
+ if (!filedes_valid(&ctx->tmpfile_fd))
+ return status;
+
+ ret = filedes_close(&ctx->tmpfile_fd);
+
+ if (status) {
+ ret = status;
+ errno = errno_save;
+ goto out_delete_tmpfile;
+ }
+
+ if (ret) {
+ ERROR_WITH_ERRNO("Error writing temporary file %"TS, ctx->tmpfile_name);
+ ret = WIMLIB_ERR_WRITE;
+ goto out_delete_tmpfile;
+ }
+
+ /* Now that a full stream has been extracted to a temporary file,
+ * extract all instances of it to the actual target. */
+
+ memcpy(<e_override, lte, sizeof(struct wim_lookup_table_entry));
+ lte_override.resource_location = RESOURCE_IN_FILE_ON_DISK;
+ lte_override.file_on_disk = ctx->tmpfile_name;
+
+ ret = extract_stream_instances(lte, <e_override, ctx);
+
+out_delete_tmpfile:
+ errno_save = errno;
+ tunlink(ctx->tmpfile_name);
+ FREE(ctx->tmpfile_name);
+ errno = errno_save;
+ return ret;
+}
+
+/* Extracts a list of streams (ctx.stream_list), assuming that the directory
+ * structure and empty files were already created. This relies on the
+ * per-`struct wim_lookup_table_entry' list of dentries that reference each
+ * stream that was constructed earlier. */
+static int
+extract_stream_list(struct apply_ctx *ctx)
+{
+ struct read_stream_list_callbacks cbs = {
+ .begin_stream = begin_extract_stream,
+ .begin_stream_ctx = ctx,
+ .consume_chunk = extract_chunk,
+ .consume_chunk_ctx = ctx,
+ .end_stream = end_extract_stream,
+ .end_stream_ctx = ctx,
+ };
+ return read_stream_list(&ctx->stream_list,
+ offsetof(struct wim_lookup_table_entry, extraction_list),
+ &cbs, VERIFY_STREAM_HASHES);
+}
+
+#define PWM_ALLOW_WIM_HDR 0x00001
+#define PWM_SILENT_EOF 0x00002
+
+/* Read the header from a stream in a pipable WIM. */
+static int
+read_pwm_stream_header(WIMStruct *pwm, struct wim_lookup_table_entry *lte,
+ struct wim_resource_spec *rspec,
+ int flags, struct wim_header_disk *hdr_ret)
+{
+ union {
+ struct pwm_stream_hdr stream_hdr;
+ struct wim_header_disk pwm_hdr;
+ } buf;
+ struct wim_reshdr reshdr;
+ int ret;
+
+ ret = full_read(&pwm->in_fd, &buf.stream_hdr, sizeof(buf.stream_hdr));
+ if (ret)
+ goto read_error;
+
+ if ((flags & PWM_ALLOW_WIM_HDR) && buf.stream_hdr.magic == PWM_MAGIC) {
+ BUILD_BUG_ON(sizeof(buf.pwm_hdr) < sizeof(buf.stream_hdr));
+ ret = full_read(&pwm->in_fd, &buf.stream_hdr + 1,
+ sizeof(buf.pwm_hdr) - sizeof(buf.stream_hdr));