- return ret;
- }
-
- /* Create empty named data streams. */
- if (can_extract_named_data_streams(ctx)) {
- for (u16 i = 0; i < inode->i_num_ads; i++) {
- file_spec_t file_spec;
- struct wim_ads_entry *entry = &inode->i_ads_entries[i];
-
- if (!ads_entry_is_named_stream(entry))
- continue;
- if (entry->lte)
- continue;
- if (ctx->ops->uses_cookies)
- file_spec.cookie = inode->extract_cookie;
- else
- file_spec.path = path;
- ret = ctx->ops->extract_named_stream(file_spec,
- entry->stream_name,
- entry->stream_name_nbytes / 2,
- entry->lte, ctx);
- if (ret) {
- ERROR_WITH_ERRNO("\"%"TS"\": failed to create "
- "empty named data stream",
- path);
- return ret;
- }
- }
- }
-
- /* Set file attributes (if supported). */
- ret = extract_file_attributes(path, ctx, dentry, 0);
- if (ret)
- return ret;
-
- /* Set or remove file short name (if supported). */
- ret = extract_short_name(path, ctx, dentry);
- if (ret)
- return ret;
-
- /* If inode has multiple links and hard links are supported in this
- * extraction mode and volume, save the path to the extracted file in
- * case it's needed to create a hard link. */
- if (unlikely(is_linked_extraction(ctx))) {
- struct wim_lookup_table_entry *unnamed_lte;
-
- unnamed_lte = inode_unnamed_lte_resolved(dentry->d_inode);
- if (unnamed_lte) {
- unnamed_lte->extracted_file = TSTRDUP(path);
- if (!unnamed_lte->extracted_file)
- return WIMLIB_ERR_NOMEM;
- }
- } else if (inode->i_nlink > 1 && ctx->supported_features.hard_links) {
- inode->i_extracted_file = TSTRDUP(path);
- if (!inode->i_extracted_file)
- return WIMLIB_ERR_NOMEM;
- }
- return 0;
-
-symlink:
- ret = extract_multiimage_symlink(oldpath, path, ctx, dentry);
- if (ret)
- return ret;
- dentry->was_hardlinked = 1;
- return 0;
-
-hardlink:
- ret = extract_hardlink(oldpath, path, ctx);
- if (ret)
- return ret;
- dentry->was_hardlinked = 1;
- return 0;
-}
-
-/* This is a wrapper around do_dentry_extract_skeleton() that handles building
- * the path, doing short name reordering. This is also idempotent; dentries
- * already processed have skeleton_extracted set and no action is taken. See
- * apply_operations.requires_short_name_reordering for more details about short
- * name reordering. */
-static int
-dentry_extract_skeleton(struct wim_dentry *dentry, void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- tchar path[ctx->ops->path_max];
- struct wim_dentry *orig_dentry;
- struct wim_dentry *other_dentry;
- int ret;
-
- if (dentry->skeleton_extracted)
- return 0;
-
- orig_dentry = NULL;
- if (ctx->supported_features.short_names
- && ctx->ops->requires_short_name_reordering
- && !dentry_has_short_name(dentry)
- && !dentry->d_inode->i_dos_name_extracted)
- {
- inode_for_each_dentry(other_dentry, dentry->d_inode) {
- if (dentry_has_short_name(other_dentry)
- && !other_dentry->skeleton_extracted
- && other_dentry->in_extraction_tree)
- {
- DEBUG("Creating %"TS" before %"TS" "
- "to guarantee correct DOS name extraction",
- dentry_full_path(other_dentry),
- dentry_full_path(dentry));
- orig_dentry = dentry;
- dentry = other_dentry;
- break;
- }
- }
- }
-again:
- if (!build_extraction_path(path, dentry, ctx))
- return 0;
- ret = do_dentry_extract_skeleton(path, dentry, ctx);
- if (ret)
- return ret;
-
- dentry->skeleton_extracted = 1;
-
- if (orig_dentry) {
- dentry = orig_dentry;
- orig_dentry = NULL;
- goto again;
- }
- dentry->d_inode->i_dos_name_extracted = 1;
- return 0;
-}
-
-static int
-dentry_extract_dir_skeleton(struct wim_dentry *dentry, void *_ctx)
-{
- if (dentry->d_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
- return dentry_extract_skeleton(dentry, _ctx);
- return 0;
-}
-
-/* Create a file or directory, then immediately extract all streams. The WIM
- * may not be read sequentially by this function. */
-static int
-dentry_extract(struct wim_dentry *dentry, void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- tchar path[ctx->ops->path_max];
- int ret;
-
- ret = dentry_extract_skeleton(dentry, ctx);
- if (ret)
- return ret;
-
- if (!build_extraction_path(path, dentry, ctx))
- return 0;
-
- return extract_streams(path, ctx, dentry, NULL, NULL);
-}
-
-/* Creates a temporary file opened for writing. The open file descriptor is
- * returned in @fd_ret and its name is returned in @name_ret (dynamically
- * allocated). */
-static int
-create_temporary_file(struct filedes *fd_ret, tchar **name_ret)
-{
- tchar *name;
- int raw_fd;
-
-retry:
- name = ttempnam(NULL, T("wimlib"));
- if (name == NULL) {
- ERROR_WITH_ERRNO("Failed to create temporary filename");
- return WIMLIB_ERR_NOMEM;
- }
-
- raw_fd = topen(name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600);
-
- 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_to_tmpfile(struct wim_lookup_table_entry *lte,
- bool is_partial_res,
- void *_ctx)
-{
- struct apply_ctx *ctx = _ctx;
- int ret;
-
- if (!need_tmpfile_to_extract(lte, is_partial_res)) {
- 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
-end_extract_stream_to_tmpfile(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;
-
- 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)
-{
- if (!(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER)) {
- /* Sequential extraction: read the streams in the order in which
- * they appear in the WIM file. */
- struct read_stream_list_callbacks cbs = {
- .begin_stream = begin_extract_stream_to_tmpfile,
- .begin_stream_ctx = ctx,
- .consume_chunk = extract_chunk_to_fd,
- .consume_chunk_ctx = &ctx->tmpfile_fd,
- .end_stream = end_extract_stream_to_tmpfile,
- .end_stream_ctx = ctx,
- };
- return read_stream_list(&ctx->stream_list,
- offsetof(struct wim_lookup_table_entry, extraction_list),
- &cbs, VERIFY_STREAM_HASHES);
- } else {
- /* Extract the streams in unsorted order. */
- struct wim_lookup_table_entry *lte;
- int ret;
-
- list_for_each_entry(lte, &ctx->stream_list, extraction_list) {
- ret = extract_stream_instances(lte, lte, ctx);
- if (ret)
- return ret;
- }
- return 0;
- }
-}
-
-#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));
-
- if (ret)
- goto read_error;
- lte->resource_location = RESOURCE_NONEXISTENT;
- memcpy(hdr_ret, &buf.pwm_hdr, sizeof(buf.pwm_hdr));
- return 0;
- }
-
- if (le64_to_cpu(buf.stream_hdr.magic) != PWM_STREAM_MAGIC) {
- ERROR("Data read on pipe is invalid (expected stream header).");
- return WIMLIB_ERR_INVALID_PIPABLE_WIM;
- }
-
- copy_hash(lte->hash, buf.stream_hdr.hash);
-
- reshdr.size_in_wim = 0;
- reshdr.flags = le32_to_cpu(buf.stream_hdr.flags);
- reshdr.offset_in_wim = pwm->in_fd.offset;
- reshdr.uncompressed_size = le64_to_cpu(buf.stream_hdr.uncompressed_size);
- wim_res_hdr_to_spec(&reshdr, pwm, rspec);
- lte_bind_wim_resource_spec(lte, rspec);
- lte->flags = rspec->flags;
- lte->size = rspec->uncompressed_size;
- lte->offset_in_res = 0;
- return 0;
-
-read_error:
- if (ret != WIMLIB_ERR_UNEXPECTED_END_OF_FILE || !(flags & PWM_SILENT_EOF))
- ERROR_WITH_ERRNO("Error reading pipable WIM from pipe");
- return ret;
-}
-
-static int
-extract_streams_from_pipe(struct apply_ctx *ctx)
-{
- struct wim_lookup_table_entry *found_lte;
- struct wim_resource_spec *rspec;
- struct wim_lookup_table_entry *needed_lte;
- struct wim_lookup_table *lookup_table;
- struct wim_header_disk pwm_hdr;
- int ret;
- int pwm_flags;
-
- ret = WIMLIB_ERR_NOMEM;
- found_lte = new_lookup_table_entry();
- if (found_lte == NULL)
- goto out;
-
- rspec = MALLOC(sizeof(struct wim_resource_spec));
- if (rspec == NULL)
- goto out_free_found_lte;
-
- lookup_table = ctx->wim->lookup_table;
- pwm_flags = PWM_ALLOW_WIM_HDR;
- if ((ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
- pwm_flags |= PWM_SILENT_EOF;
- memcpy(ctx->progress.extract.guid, ctx->wim->hdr.guid, WIM_GID_LEN);
- ctx->progress.extract.part_number = ctx->wim->hdr.part_number;
- ctx->progress.extract.total_parts = ctx->wim->hdr.total_parts;
- if (ctx->progress_func)
- ctx->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN,
- &ctx->progress);
- while (ctx->num_streams_remaining) {
- if (found_lte->resource_location != RESOURCE_NONEXISTENT)
- lte_unbind_wim_resource_spec(found_lte);
- ret = read_pwm_stream_header(ctx->wim, found_lte, rspec,
- pwm_flags, &pwm_hdr);
- if (ret) {
- if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE &&
- (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_RESUME))
- {
- goto resume_done;
- }
- goto out_free_found_lte;
- }
-
- if ((found_lte->resource_location != RESOURCE_NONEXISTENT)
- && !(found_lte->flags & WIM_RESHDR_FLAG_METADATA)
- && (needed_lte = lookup_resource(lookup_table, found_lte->hash))
- && (needed_lte->out_refcnt))
- {
- tchar *tmpfile_name = NULL;
- struct wim_lookup_table_entry *lte_override;
- struct wim_lookup_table_entry tmpfile_lte;
-
- needed_lte->offset_in_res = found_lte->offset_in_res;
- needed_lte->flags = found_lte->flags;
- needed_lte->size = found_lte->size;
-
- lte_unbind_wim_resource_spec(found_lte);
- lte_bind_wim_resource_spec(needed_lte, rspec);
-
- if (needed_lte->out_refcnt > 1) {
-
- struct filedes tmpfile_fd;
-
- /* Extract stream to temporary file. */
- ret = create_temporary_file(&tmpfile_fd, &tmpfile_name);
- if (ret)
- goto out_free_found_lte;
-
- ret = extract_stream_to_fd(needed_lte, &tmpfile_fd,
- needed_lte->size);
- if (ret) {
- filedes_close(&tmpfile_fd);
- goto delete_tmpfile;
- }
-
- if (filedes_close(&tmpfile_fd)) {
- ERROR_WITH_ERRNO("Error writing to temporary "
- "file \"%"TS"\"", tmpfile_name);
- ret = WIMLIB_ERR_WRITE;
- goto delete_tmpfile;
- }
- memcpy(&tmpfile_lte, needed_lte,
- sizeof(struct wim_lookup_table_entry));
- tmpfile_lte.resource_location = RESOURCE_IN_FILE_ON_DISK;
- tmpfile_lte.file_on_disk = tmpfile_name;
- lte_override = &tmpfile_lte;
- } else {
- lte_override = needed_lte;
- }
-
- ret = extract_stream_instances(needed_lte, lte_override, ctx);
- delete_tmpfile:
- lte_unbind_wim_resource_spec(needed_lte);
- if (tmpfile_name) {
- tunlink(tmpfile_name);
- FREE(tmpfile_name);
- }
- if (ret)
- goto out_free_found_lte;
- ctx->num_streams_remaining--;
- } else if (found_lte->resource_location != RESOURCE_NONEXISTENT) {
- ret = skip_wim_stream(found_lte);
- if (ret)
- goto out_free_found_lte;
- } else {
- u16 part_number = le16_to_cpu(pwm_hdr.part_number);
- u16 total_parts = le16_to_cpu(pwm_hdr.total_parts);