+ /* 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;
+ u16 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 (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, 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);