+ if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
+ return &ntfs_3g_apply_ops;
+#endif
+#ifdef __WIN32__
+ return &win32_apply_ops;
+#else
+ return &unix_apply_ops;
+#endif
+}
+
+#ifdef __WIN32__
+static const utf16lechar replacement_char = cpu_to_le16(0xfffd);
+#else
+static const utf16lechar replacement_char = cpu_to_le16('?');
+#endif
+
+static bool
+file_name_valid(utf16lechar *name, size_t num_chars, bool fix)
+{
+ size_t i;
+
+ if (num_chars == 0)
+ return true;
+ for (i = 0; i < num_chars; i++) {
+ switch (name[i]) {
+ #ifdef __WIN32__
+ case cpu_to_le16('\\'):
+ case cpu_to_le16(':'):
+ case cpu_to_le16('*'):
+ case cpu_to_le16('?'):
+ case cpu_to_le16('"'):
+ case cpu_to_le16('<'):
+ case cpu_to_le16('>'):
+ case cpu_to_le16('|'):
+ #endif
+ case cpu_to_le16('/'):
+ case cpu_to_le16('\0'):
+ if (fix)
+ name[i] = replacement_char;
+ else
+ return false;
+ }
+ }
+
+#ifdef __WIN32__
+ if (name[num_chars - 1] == cpu_to_le16(' ') ||
+ name[num_chars - 1] == cpu_to_le16('.'))
+ {
+ if (fix)
+ name[num_chars - 1] = replacement_char;
+ else
+ return false;
+ }
+#endif
+ return true;
+}
+
+static int
+dentry_calculate_extraction_name(struct wim_dentry *dentry,
+ struct apply_ctx *ctx)
+{
+ int ret;
+
+ if (dentry == ctx->target_dentry)
+ return 0;
+
+ if (!dentry_is_supported(dentry, &ctx->supported_features))
+ goto skip_dentry;
+
+ if (!ctx->ops->supports_case_sensitive_filenames)
+ {
+ struct wim_dentry *other;
+ list_for_each_entry(other, &dentry->case_insensitive_conflict_list,
+ case_insensitive_conflict_list)
+ {
+ if (dentry_in_list(other)) {
+ if (ctx->extract_flags &
+ WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS) {
+ WARNING("\"%"TS"\" has the same "
+ "case-insensitive name as "
+ "\"%"TS"\"; extracting "
+ "dummy name instead",
+ dentry_full_path(dentry),
+ dentry_full_path(other));
+ goto out_replace;
+ } else {
+ WARNING("Not extracting \"%"TS"\": "
+ "has same case-insensitive "
+ "name as \"%"TS"\"",
+ dentry_full_path(dentry),
+ dentry_full_path(other));
+ goto skip_dentry;
+ }
+ }
+ }
+ }
+
+ if (file_name_valid(dentry->file_name, dentry->file_name_nbytes / 2, false)) {
+#if TCHAR_IS_UTF16LE
+ dentry->extraction_name = dentry->file_name;
+ dentry->extraction_name_nchars = dentry->file_name_nbytes / 2;
+ return 0;
+#else
+ return utf16le_to_tstr(dentry->file_name,
+ dentry->file_name_nbytes,
+ &dentry->extraction_name,
+ &dentry->extraction_name_nchars);
+#endif
+ } else {
+ if (ctx->extract_flags & WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES)
+ {
+ WARNING("\"%"TS"\" has an invalid filename "
+ "that is not supported on this platform; "
+ "extracting dummy name instead",
+ dentry_full_path(dentry));
+ goto out_replace;
+ } else {
+ WARNING("Not extracting \"%"TS"\": has an invalid filename "
+ "that is not supported on this platform",
+ dentry_full_path(dentry));
+ goto skip_dentry;
+ }
+ }
+
+out_replace:
+ {
+ utf16lechar utf16_name_copy[dentry->file_name_nbytes / 2];
+
+ memcpy(utf16_name_copy, dentry->file_name, dentry->file_name_nbytes);
+ file_name_valid(utf16_name_copy, dentry->file_name_nbytes / 2, true);
+
+ tchar *tchar_name;
+ size_t tchar_nchars;
+ #if TCHAR_IS_UTF16LE
+ tchar_name = utf16_name_copy;
+ tchar_nchars = dentry->file_name_nbytes / 2;
+ #else
+ ret = utf16le_to_tstr(utf16_name_copy,
+ dentry->file_name_nbytes,
+ &tchar_name, &tchar_nchars);
+ if (ret)
+ return ret;
+ #endif
+ size_t fixed_name_num_chars = tchar_nchars;
+ tchar fixed_name[tchar_nchars + 50];
+
+ tmemcpy(fixed_name, tchar_name, tchar_nchars);
+ fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
+ T(" (invalid filename #%lu)"),
+ ++ctx->invalid_sequence);
+ #if !TCHAR_IS_UTF16LE
+ FREE(tchar_name);
+ #endif
+ dentry->extraction_name = memdup(fixed_name,
+ 2 * fixed_name_num_chars + 2);
+ if (!dentry->extraction_name)
+ return WIMLIB_ERR_NOMEM;
+ dentry->extraction_name_nchars = fixed_name_num_chars;
+ }
+ return 0;
+
+skip_dentry:
+ for_dentry_in_tree(dentry, dentry_delete_from_list, NULL);
+ return 0;
+}
+
+/*
+ * Calculate the actual filename component at which each WIM dentry will be
+ * extracted, with special handling for dentries that are unsupported by the
+ * extraction backend or have invalid names.
+ *
+ * Note: this has a dependency on start_extract() being called because
+ * ctx.supported_features must be filled in in order to determine whether each
+ * dentry is supported.
+ *
+ * Possible error codes: WIMLIB_ERR_NOMEM, WIMLIB_ERR_INVALID_UTF16_STRING
+ */
+static int
+dentry_list_calculate_extraction_names(struct list_head *dentry_list,
+ struct apply_ctx *ctx)
+{
+ struct list_head *prev, *cur;
+
+ /* Can't use list_for_each_entry() because a call to
+ * dentry_calculate_extraction_name() may delete the current dentry and
+ * its children from the list. */
+
+ prev = dentry_list;
+ for (;;) {
+ struct wim_dentry *dentry;
+ int ret;
+
+ cur = prev->next;
+ if (cur == dentry_list)
+ break;
+
+ dentry = list_entry(cur, struct wim_dentry, extraction_list);
+
+ ret = dentry_calculate_extraction_name(dentry, ctx);
+ if (ret)
+ return ret;
+
+ if (prev->next == cur)
+ prev = cur;
+ else
+ ; /* Current dentry and its children (which follow in
+ the list) were deleted. prev stays the same. */
+ }
+ return 0;
+}
+
+static int
+dentry_resolve_streams(struct wim_dentry *dentry, int extract_flags,
+ struct wim_lookup_table *lookup_table)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct wim_lookup_table_entry *lte;
+ int ret;
+ bool force = false;
+
+ /* Special case: when extracting from a pipe, the WIM lookup table is
+ * initially empty, so "resolving" an inode's streams is initially not
+ * possible. However, we still need to keep track of which streams,
+ * identified by SHA1 message digests, need to be extracted, so we
+ * "resolve" the inode's streams anyway by allocating new entries. */
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
+ force = true;
+ ret = inode_resolve_streams(inode, lookup_table, force);
+ if (ret)
+ return ret;
+ for (u32 i = 0; i <= inode->i_num_ads; i++) {
+ lte = inode_stream_lte_resolved(inode, i);
+ if (lte)
+ lte->out_refcnt = 0;
+ }
+ return 0;
+}
+
+/*
+ * For each dentry to be extracted, resolve all streams in the corresponding
+ * inode and set 'out_refcnt' in each to 0.
+ *
+ * Possible error codes: WIMLIB_ERR_RESOURCE_NOT_FOUND, WIMLIB_ERR_NOMEM.
+ */
+static int
+dentry_list_resolve_streams(struct list_head *dentry_list,
+ struct apply_ctx *ctx)
+{
+ struct wim_dentry *dentry;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, extraction_list) {
+ ret = dentry_resolve_streams(dentry,
+ ctx->extract_flags,
+ ctx->wim->lookup_table);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int
+ref_stream(struct wim_lookup_table_entry *lte,
+ struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ if (!lte)
+ return 0;
+
+ /* Tally the size only for each extraction of the stream (not hard
+ * links). */
+ if (!(dentry->d_inode->i_visited &&
+ ctx->supported_features.hard_links) &&
+ (!is_linked_extraction(ctx) || (lte->out_refcnt == 0 &&
+ lte->extracted_file == NULL)))
+ {
+ ctx->progress.extract.total_bytes += lte->size;
+ ctx->progress.extract.num_streams++;
+ }
+
+ /* Add stream to the dentry_list only one time, even if it's going
+ * to be extracted to multiple locations. */
+ if (lte->out_refcnt == 0) {
+ list_add_tail(<e->extraction_list, &ctx->stream_list);
+ ctx->num_streams_remaining++;
+ }
+
+ if (!(ctx->extract_flags & WIMLIB_EXTRACT_FLAG_FILE_ORDER)) {
+ struct wim_dentry **lte_dentries;
+
+ /* Append dentry to this stream's array of dentries referencing
+ * it. Use inline array to avoid memory allocation until the
+ * number of dentries becomes too large. */
+ if (lte->out_refcnt < ARRAY_LEN(lte->inline_lte_dentries)) {
+ lte_dentries = lte->inline_lte_dentries;
+ } else {
+ struct wim_dentry **prev_lte_dentries;
+ size_t alloc_lte_dentries;
+
+ if (lte->out_refcnt == ARRAY_LEN(lte->inline_lte_dentries)) {
+ prev_lte_dentries = NULL;
+ alloc_lte_dentries = ARRAY_LEN(lte->inline_lte_dentries);
+ } else {
+ prev_lte_dentries = lte->lte_dentries;
+ alloc_lte_dentries = lte->alloc_lte_dentries;
+ }
+
+ if (lte->out_refcnt == alloc_lte_dentries) {
+ alloc_lte_dentries *= 2;
+ lte_dentries = REALLOC(prev_lte_dentries,
+ alloc_lte_dentries *
+ sizeof(lte_dentries[0]));
+ if (lte_dentries == NULL)
+ return WIMLIB_ERR_NOMEM;
+ if (prev_lte_dentries == NULL) {
+ memcpy(lte_dentries,
+ lte->inline_lte_dentries,
+ sizeof(lte->inline_lte_dentries));
+ }
+ lte->lte_dentries = lte_dentries;
+ lte->alloc_lte_dentries = alloc_lte_dentries;
+ }
+ lte_dentries = lte->lte_dentries;
+ }
+ lte_dentries[lte->out_refcnt] = dentry;
+ }
+ lte->out_refcnt++;
+ return 0;
+}
+
+static int
+dentry_ref_streams(struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ int ret;
+
+ /* The unnamed data stream will always be extracted, except in an
+ * unlikely case. */
+ if (!inode_is_encrypted_directory(inode)) {
+ ret = ref_stream(inode_unnamed_lte_resolved(inode),
+ dentry, ctx);
+ if (ret)
+ return ret;
+ }
+
+ /* Named data streams will be extracted only if supported in the current
+ * extraction mode and volume, and to avoid complications, if not doing
+ * a linked extraction. */
+ if (can_extract_named_data_streams(ctx)) {
+ for (u16 i = 0; i < inode->i_num_ads; i++) {
+ if (!ads_entry_is_named_stream(&inode->i_ads_entries[i]))
+ continue;
+ ret = ref_stream(inode->i_ads_entries[i].lte,
+ dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ }
+ inode->i_visited = 1;
+ return 0;
+}
+
+/*
+ * For each dentry to be extracted, iterate through the data streams of the
+ * corresponding inode. For each such stream that is not to be ignored due to
+ * the supported features or extraction flags, add it to the list of streams to
+ * be extracted (ctx->stream_list) if not already done so.
+ *
+ * Also, if doing a sequential extraction, build a mapping from each stream to
+ * the dentries referencing it.
+ *
+ * This also initializes the extract progress info with byte and stream
+ * information.
+ *
+ * Note: This has a dependency on start_extract being called because
+ * ctx.supported_features must be filled in in order to determine whether named
+ * data streams are supported.
+ *
+ * Note: this uses the i_visited member of the inodes (assumed to be 0
+ * initially), but does not reset it.
+ *
+ * Possible error codes: WIMLIB_ERR_NOMEM.
+ */
+static int
+dentry_list_ref_streams(struct list_head *dentry_list, struct apply_ctx *ctx)
+{
+ struct wim_dentry *dentry;
+ int ret;
+
+ list_for_each_entry(dentry, dentry_list, extraction_list) {
+ ret = dentry_ref_streams(dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* Tally features necessary to extract a dentry and the corresponding inode. */
+static void
+dentry_tally_features(struct wim_dentry *dentry, struct wim_features *features)
+{
+ struct wim_inode *inode = dentry->d_inode;
+
+ if (inode->i_attributes & FILE_ATTRIBUTE_ARCHIVE)
+ features->archive_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_HIDDEN)
+ features->hidden_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_SYSTEM)
+ features->system_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED)
+ features->compressed_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ features->encrypted_directories++;
+ else
+ features->encrypted_files++;
+ }
+ if (inode->i_attributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ features->not_context_indexed_files++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE)
+ features->sparse_files++;
+ if (inode_has_named_stream(inode))
+ features->named_data_streams++;
+ if (inode->i_visited)
+ features->hard_links++;
+ if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ features->reparse_points++;
+ if (inode_is_symlink(inode))
+ features->symlink_reparse_points++;
+ else
+ features->other_reparse_points++;
+ }
+ if (inode->i_security_id != -1)
+ features->security_descriptors++;
+ if (dentry->short_name_nbytes)
+ features->short_names++;
+ if (inode_has_unix_data(inode))
+ features->unix_data++;
+ inode->i_visited = 1;
+}
+
+/* Tally the features necessary to extract the specified dentries. */
+static void
+dentry_list_get_features(struct list_head *dentry_list,
+ struct wim_features *features)
+{
+ struct wim_dentry *dentry;
+
+ memset(features, 0, sizeof(struct wim_features));
+
+ list_for_each_entry(dentry, dentry_list, extraction_list)
+ dentry_tally_features(dentry, features);
+
+ list_for_each_entry(dentry, dentry_list, extraction_list)
+ dentry->d_inode->i_visited = 0;
+}
+
+static u32
+compute_supported_attributes_mask(const struct wim_features *supported_features)
+{
+ u32 mask = (u32)~0UL;
+
+ if (!supported_features->archive_files)
+ mask &= ~FILE_ATTRIBUTE_ARCHIVE;
+
+ if (!supported_features->hidden_files)
+ mask &= ~FILE_ATTRIBUTE_HIDDEN;
+
+ if (!supported_features->system_files)
+ mask &= ~FILE_ATTRIBUTE_SYSTEM;
+
+ if (!supported_features->not_context_indexed_files)
+ mask &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+
+ if (!supported_features->compressed_files)
+ mask &= ~FILE_ATTRIBUTE_COMPRESSED;
+
+ if (!supported_features->sparse_files)
+ mask &= ~FILE_ATTRIBUTE_SPARSE_FILE;
+
+ if (!supported_features->reparse_points)
+ mask &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+ return mask;
+}
+
+static int
+do_feature_check(const struct wim_features *required_features,
+ const struct wim_features *supported_features,
+ int extract_flags, const struct apply_operations *ops)
+{
+ /* File attributes. */
+ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES)) {
+ /* Note: Don't bother the user about FILE_ATTRIBUTE_ARCHIVE.
+ * We're an archive program, so theoretically we can do what we
+ * want with it. */
+
+ if (required_features->hidden_files &&
+ !supported_features->hidden_files)
+ WARNING("Ignoring FILE_ATTRIBUTE_HIDDEN of %lu files",
+ required_features->hidden_files);
+
+ if (required_features->system_files &&
+ !supported_features->system_files)
+ WARNING("Ignoring FILE_ATTRIBUTE_SYSTEM of %lu files",
+ required_features->system_files);
+
+ if (required_features->compressed_files &&
+ !supported_features->compressed_files)
+ WARNING("Ignoring FILE_ATTRIBUTE_COMPRESSED of %lu files",
+ required_features->compressed_files);
+
+ if (required_features->not_context_indexed_files &&
+ !supported_features->not_context_indexed_files)
+ WARNING("Ignoring FILE_ATTRIBUTE_NOT_CONTENT_INDEXED of %lu files",
+ required_features->not_context_indexed_files);
+
+ if (required_features->sparse_files &&
+ !supported_features->sparse_files)
+ WARNING("Ignoring FILE_ATTRIBUTE_SPARSE_FILE of %lu files",
+ required_features->sparse_files);
+
+ if (required_features->encrypted_directories &&
+ !supported_features->encrypted_directories)
+ WARNING("Ignoring FILE_ATTRIBUTE_ENCRYPTED of %lu directories",
+ required_features->encrypted_directories);
+ }
+
+ /* Encrypted files. */
+ if (required_features->encrypted_files &&
+ !supported_features->encrypted_files)
+ WARNING("Ignoring %lu encrypted files",
+ required_features->encrypted_files);
+
+ /* Named data streams. */
+ if (required_features->named_data_streams &&
+ (!supported_features->named_data_streams ||
+ (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
+ WIMLIB_EXTRACT_FLAG_HARDLINK))))
+ WARNING("Ignoring named data streams of %lu files",
+ required_features->named_data_streams);
+
+ /* Hard links. */
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) &&
+ !supported_features->hard_links)
+ {
+ ERROR("Extraction backend does not support hard links!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if (required_features->hard_links && !supported_features->hard_links)
+ WARNING("Extracting %lu hard links as independent files",
+ required_features->hard_links);
+
+ /* Symbolic links and reparse points. */
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_SYMLINK) &&
+ !supported_features->symlink_reparse_points)
+ {
+ ERROR("Extraction backend does not support symbolic links!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SYMLINKS) &&
+ required_features->symlink_reparse_points &&
+ !supported_features->symlink_reparse_points &&
+ !supported_features->reparse_points)
+ {
+ ERROR("Extraction backend does not support symbolic links!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if (required_features->reparse_points &&
+ !supported_features->reparse_points)
+ {
+ if (supported_features->symlink_reparse_points) {
+ if (required_features->other_reparse_points) {
+ WARNING("Ignoring %lu non-symlink/junction "
+ "reparse point files",
+ required_features->other_reparse_points);
+ }
+ } else {
+ WARNING("Ignoring %lu reparse point files",
+ required_features->reparse_points);
+ }
+ }
+
+ /* Security descriptors. */
+ if (((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_ACLS |
+ WIMLIB_EXTRACT_FLAG_UNIX_DATA))
+ == WIMLIB_EXTRACT_FLAG_STRICT_ACLS) &&
+ required_features->security_descriptors &&
+ !supported_features->security_descriptors)
+ {
+ ERROR("Extraction backend does not support security descriptors!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS) &&
+ required_features->security_descriptors &&
+ !supported_features->security_descriptors)
+ WARNING("Ignoring Windows NT security descriptors of %lu files",
+ required_features->security_descriptors);
+
+ /* UNIX data. */
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) &&
+ required_features->unix_data && !supported_features->unix_data)
+ {
+ ERROR("Extraction backend does not support UNIX data!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ /* DOS Names. */
+ if (required_features->short_names &&
+ !supported_features->short_names)
+ {
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES) {
+ ERROR("Extraction backend does not support DOS names!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+ WARNING("Ignoring DOS names of %lu files",
+ required_features->short_names);
+ }
+
+ /* Timestamps. */
+ if ((extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS) &&
+ !ops->set_timestamps)
+ {
+ ERROR("Extraction backend does not support timestamps!");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static void
+do_extract_warnings(struct apply_ctx *ctx)
+{
+ if (ctx->partial_security_descriptors == 0 &&
+ ctx->no_security_descriptors == 0)
+ return;
+
+ WARNING("Extraction to \"%"TS"\" complete, but with one or more warnings:",
+ ctx->target);
+ if (ctx->partial_security_descriptors != 0) {
+ WARNING("- Could only partially set the security descriptor\n"
+ " on %lu files or directories.",
+ ctx->partial_security_descriptors);
+ }
+ if (ctx->no_security_descriptors != 0) {
+ WARNING("- Could not set security descriptor at all\n"
+ " on %lu files or directories.",
+ ctx->no_security_descriptors);
+ }
+#ifdef __WIN32__
+ WARNING("To fully restore all security descriptors, run the program\n"
+ " with Administrator rights.");
+#endif
+}
+
+static void
+destroy_dentry_list(struct list_head *dentry_list)
+{
+ struct wim_dentry *dentry, *tmp;
+ struct wim_inode *inode;
+
+ list_for_each_entry_safe(dentry, tmp, dentry_list, extraction_list) {
+ inode = dentry->d_inode;
+ dentry_reset_extraction_list_node(dentry);
+ dentry->was_linked = 0;
+ dentry->skeleton_extracted = 0;
+ inode->i_visited = 0;
+ FREE(inode->i_extracted_file);
+ inode->i_extracted_file = NULL;
+ inode->i_dos_name_extracted = 0;
+ if ((void*)dentry->extraction_name != (void*)dentry->file_name)
+ FREE(dentry->extraction_name);
+ dentry->extraction_name = NULL;
+ }
+}
+
+static void
+destroy_stream_list(struct list_head *stream_list)
+{
+ struct wim_lookup_table_entry *lte;
+
+ list_for_each_entry(lte, stream_list, extraction_list)
+ if (lte->out_refcnt > ARRAY_LEN(lte->inline_lte_dentries))
+ FREE(lte->lte_dentries);
+}
+
+static int
+extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
+ const tchar *target, int extract_flags,
+ wimlib_progress_func_t progress_func)
+{
+ int ret;
+ struct apply_ctx ctx;
+ struct list_head dentry_list;
+ struct wim_features required_features;
+
+ /* Handle stdout extraction as a separate case. */
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT)
+ return extract_dentries_to_stdout(trees, num_trees,
+ wim->lookup_table);
+
+ /* Start initializing the apply_ctx. */
+ memset(&ctx, 0, sizeof(struct apply_ctx));
+ ctx.wim = wim;
+ ctx.extract_flags = extract_flags;
+ ctx.target = target;
+ ctx.target_nchars = tstrlen(target);
+ ctx.progress_func = progress_func;
+ if (progress_func) {
+ ctx.progress.extract.wimfile_name = wim->filename;
+ ctx.progress.extract.image = wim->current_image;
+ ctx.progress.extract.extract_flags = (extract_flags &
+ WIMLIB_EXTRACT_MASK_PUBLIC);
+ ctx.progress.extract.image_name = wimlib_get_image_name(wim,
+ wim->current_image);
+ ctx.progress.extract.target = target;
+ }
+
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FILEMODE) {
+ /* Called from wimlib_extract_files(). There should be only 1
+ * tree, and directory structure should not be preserved. */
+ wimlib_assert(num_trees == 1);
+ wimlib_assert(extract_flags &
+ WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE);
+ ret = calculate_dentry_full_path(trees[0]);
+ if (ret)
+ return ret;
+ ctx.progress.extract.extract_root_wim_source_path = trees[0]->_full_path;
+ ctx.target_dentry = trees[0];
+ } else {
+ ctx.progress.extract.extract_root_wim_source_path = T("");
+ ctx.target_dentry = wim_root_dentry(wim);
+ }
+ /* Note: ctx.target_dentry represents the dentry that gets extracted to
+ * @target. There may be none, in which case it gets set to the image
+ * root and never matches any of the dentries actually being extracted.
+ */
+
+ num_trees = remove_duplicate_trees(trees, num_trees);
+
+ /* All trees are now distinct. */
+
+ num_trees = remove_contained_trees(trees, num_trees);
+
+ /* All trees are now distinct and non-overlapping. */
+
+ /* Build list of dentries to be extracted. */
+ build_dentry_list(&dentry_list, trees, num_trees,
+ !(extract_flags & WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE));
+
+ /* Select the appropriate apply_operations based on the platform and
+ * extract_flags. */
+ ctx.ops = select_apply_operations(extract_flags);
+
+ /* Figure out whether the root dentry is being extracted to the root of
+ * a volume and therefore needs to be treated "specially", for example
+ * not being explicitly created and not having attributes set. */
+ if (ctx.ops->target_is_root && ctx.ops->root_directory_is_special)
+ ctx.root_dentry_is_special = ctx.ops->target_is_root(target);
+
+ /* Call the start_extract() callback. This gives the apply_operations
+ * implementation a chance to do any setup needed to access the volume.
+ * Furthermore, start_extract() is expected to set the supported
+ * features of this extraction mode (ctx.supported_features), which are
+ * determined at runtime as they may vary depending on the actual
+ * volume. */
+ ret = ctx.ops->start_extract(target, &ctx);
+ if (ret)
+ goto out_destroy_dentry_list;
+
+ /* Get and check the features required to extract the dentries. */
+ dentry_list_get_features(&dentry_list, &required_features);
+ ret = do_feature_check(&required_features, &ctx.supported_features,
+ extract_flags, ctx.ops);
+ if (ret)
+ goto out_finish_or_abort_extract;
+
+ ctx.supported_attributes_mask =
+ compute_supported_attributes_mask(&ctx.supported_features);
+
+ /* Calculate extraction name for each dentry and remove subtrees that
+ * can't be extracted due to naming problems. */
+ ret = dentry_list_calculate_extraction_names(&dentry_list, &ctx);
+ if (ret)
+ goto out_finish_or_abort_extract;
+
+ /* Build list of streams to extract. */
+ ret = dentry_list_resolve_streams(&dentry_list, &ctx);
+ if (ret)
+ goto out_finish_or_abort_extract;
+ INIT_LIST_HEAD(&ctx.stream_list);
+ ret = dentry_list_ref_streams(&dentry_list, &ctx);
+ if (ret)
+ goto out_finish_or_abort_extract;
+
+ 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);