+ tmemcpy(fixed_name, tchar_name, tchar_nchars);
+ fixed_name_num_chars += tsprintf(fixed_name + tchar_nchars,
+ T(" (invalid filename #%lu)"),
+ ++ctx->invalid_sequence);
+
+ utf16le_put_tstr(tchar_name);
+
+ dentry->d_extraction_name = memdup(fixed_name,
+ 2 * fixed_name_num_chars + 2);
+ if (!dentry->d_extraction_name)
+ return WIMLIB_ERR_NOMEM;
+ dentry->d_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.
+ *
+ * ctx->supported_features must be filled in.
+ *
+ * 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, d_extraction_list_node);
+
+ 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, d_extraction_list_node) {
+ 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, unsigned stream_idx,
+ struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct stream_owner *stream_owners;
+
+ if (!lte)
+ return 0;
+
+ /* Tally the size only for each extraction of the stream (not hard
+ * links). */
+ if (inode->i_visited && ctx->supported_features.hard_links)
+ return 0;
+
+ ctx->progress.extract.total_bytes += lte->size;
+ ctx->progress.extract.total_streams++;
+
+ if (inode->i_visited)
+ return 0;
+
+ /* Add stream to the dentry_list only one time, even if it's going
+ * to be extracted to multiple inodes. */
+ if (lte->out_refcnt == 0) {
+ list_add_tail(<e->extraction_list, &ctx->stream_list);
+ ctx->num_streams_remaining++;
+ }
+
+ /* If inode not yet been visited, append it to the stream_owners array. */
+ if (lte->out_refcnt < ARRAY_LEN(lte->inline_stream_owners)) {
+ stream_owners = lte->inline_stream_owners;
+ } else {
+ struct stream_owner *prev_stream_owners;
+ size_t alloc_stream_owners;
+
+ if (lte->out_refcnt == ARRAY_LEN(lte->inline_stream_owners)) {
+ prev_stream_owners = NULL;
+ alloc_stream_owners = ARRAY_LEN(lte->inline_stream_owners);
+ } else {
+ prev_stream_owners = lte->stream_owners;
+ alloc_stream_owners = lte->alloc_stream_owners;
+ }
+
+ if (lte->out_refcnt == alloc_stream_owners) {
+ alloc_stream_owners *= 2;
+ stream_owners = REALLOC(prev_stream_owners,
+ alloc_stream_owners *
+ sizeof(stream_owners[0]));
+ if (!stream_owners)
+ return WIMLIB_ERR_NOMEM;
+ if (!prev_stream_owners) {
+ memcpy(stream_owners,
+ lte->inline_stream_owners,
+ sizeof(lte->inline_stream_owners));
+ }
+ lte->stream_owners = stream_owners;
+ lte->alloc_stream_owners = alloc_stream_owners;
+ }
+ stream_owners = lte->stream_owners;
+ }
+ stream_owners[lte->out_refcnt].inode = inode;
+ if (stream_idx == 0) {
+ stream_owners[lte->out_refcnt].stream_name = NULL;
+ } else {
+ stream_owners[lte->out_refcnt].stream_name =
+ inode->i_ads_entries[stream_idx - 1].stream_name;
+ }
+ lte->out_refcnt++;
+ return 0;
+}
+
+static int
+ref_unnamed_stream(struct wim_dentry *dentry, struct apply_ctx *ctx)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ int ret;
+ unsigned stream_idx;
+ struct wim_lookup_table_entry *stream;
+
+ if (unlikely(inode_is_encrypted_directory(inode)))
+ return 0;
+
+ if (unlikely(ctx->apply_ops->will_externally_back)) {
+ ret = (*ctx->apply_ops->will_externally_back)(dentry, ctx);
+ if (ret >= 0) {
+ if (ret) /* Error */
+ return ret;
+ /* Will externally back */
+ return 0;
+ }
+ /* Won't externally back */
+ }
+
+ stream = inode_unnamed_stream_resolved(inode, &stream_idx);
+ return ref_stream(stream, stream_idx, dentry, ctx);
+}
+
+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 almost always be extracted, but there
+ * exist cases in which it won't be. */
+ ret = ref_unnamed_stream(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 (ctx->supported_features.named_data_streams) {
+ for (unsigned i = 0; i < inode->i_num_ads; i++) {
+ if (!inode->i_ads_entries[i].stream_name_nbytes)
+ continue;
+ ret = ref_stream(inode->i_ads_entries[i].lte, i + 1,
+ 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 builds a mapping from each stream to the inodes referencing it.
+ *
+ * This also initializes the extract progress info with byte and stream
+ * information.
+ *
+ * ctx->supported_features must be filled in.
+ *
+ * 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, d_extraction_list_node) {
+ ret = dentry_ref_streams(dentry, ctx);
+ if (ret)
+ return ret;
+ }
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node)
+ dentry->d_inode->i_visited = 0;
+ return 0;
+}
+
+static void
+dentry_list_build_inode_alias_lists(struct list_head *dentry_list)
+{
+ struct wim_dentry *dentry;
+ struct wim_inode *inode;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ inode = dentry->d_inode;
+ if (!inode->i_visited)
+ INIT_LIST_HEAD(&inode->i_extraction_aliases);
+ list_add_tail(&dentry->d_extraction_alias_node,
+ &inode->i_extraction_aliases);
+ inode->i_visited = 1;
+ }
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node)
+ dentry->d_inode->i_visited = 0;
+}
+
+static void
+inode_tally_features(const struct wim_inode *inode,
+ struct wim_features *features)
+{
+ 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_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 (inode_has_unix_data(inode))
+ features->unix_data++;
+}
+
+/* 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 (dentry_has_short_name(dentry))
+ features->short_names++;
+
+ if (inode->i_visited) {
+ features->hard_links++;
+ } else {
+ inode_tally_features(inode, features);
+ 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;
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node)
+ dentry_tally_features(dentry, features);
+
+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node)
+ dentry->d_inode->i_visited = 0;
+}
+
+static int
+do_feature_check(const struct wim_features *required_features,
+ const struct wim_features *supported_features,
+ int extract_flags)
+{
+ /* 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))
+ WARNING("Ignoring named data streams of %lu files",
+ required_features->named_data_streams);
+
+ /* Hard links. */
+ 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_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;
+ }
+
+ if (required_features->unix_data &&
+ !(extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA))
+ {
+ WARNING("Ignoring UNIX metadata of %lu files",
+ required_features->unix_data);