+ list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
+ ret = dentry_resolve_streams(dentry,
+ ctx->extract_flags,
+ ctx->wim->blob_table);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int
+ref_stream(struct wim_inode_stream *strm, struct wim_dentry *dentry,
+ struct apply_ctx *ctx)
+{
+ struct wim_inode *inode = dentry->d_inode;
+ struct blob_descriptor *blob = stream_blob_resolved(strm);
+ struct blob_extraction_target *targets;
+
+ if (!blob)
+ return 0;
+
+ /* Tally the size only for each actual extraction of the stream (not
+ * additional hard links to the inode). */
+ if (inode->i_visited && ctx->supported_features.hard_links)
+ return 0;
+
+ ctx->progress.extract.total_bytes += blob->size;
+ ctx->progress.extract.total_streams++;
+
+ if (inode->i_visited)
+ return 0;
+
+ /* Add each blob to 'ctx->blob_list' only one time, regardless of how
+ * many extraction targets it will have. */
+ if (blob->out_refcnt == 0) {
+ list_add_tail(&blob->extraction_list, &ctx->blob_list);
+ ctx->num_blobs_remaining++;
+ }
+
+ /* Set this stream as an extraction target of 'blob'. */
+
+ if (blob->out_refcnt < ARRAY_LEN(blob->inline_blob_extraction_targets)) {
+ targets = blob->inline_blob_extraction_targets;
+ } else {
+ struct blob_extraction_target *prev_targets;
+ size_t alloc_blob_extraction_targets;
+
+ if (blob->out_refcnt == ARRAY_LEN(blob->inline_blob_extraction_targets)) {
+ prev_targets = NULL;
+ alloc_blob_extraction_targets = ARRAY_LEN(blob->inline_blob_extraction_targets);
+ } else {
+ prev_targets = blob->blob_extraction_targets;
+ alloc_blob_extraction_targets = blob->alloc_blob_extraction_targets;
+ }
+
+ if (blob->out_refcnt == alloc_blob_extraction_targets) {
+ alloc_blob_extraction_targets *= 2;
+ targets = REALLOC(prev_targets,
+ alloc_blob_extraction_targets *
+ sizeof(targets[0]));
+ if (!targets)
+ return WIMLIB_ERR_NOMEM;
+ if (!prev_targets) {
+ memcpy(targets,
+ blob->inline_blob_extraction_targets,
+ sizeof(blob->inline_blob_extraction_targets));
+ }
+ blob->blob_extraction_targets = targets;
+ blob->alloc_blob_extraction_targets = alloc_blob_extraction_targets;
+ }
+ targets = blob->blob_extraction_targets;
+ }
+ targets[blob->out_refcnt].inode = inode;
+ targets[blob->out_refcnt].stream = strm;
+ blob->out_refcnt++;
+ return 0;
+}
+
+static int
+ref_stream_if_needed(struct wim_dentry *dentry, struct wim_inode *inode,
+ struct wim_inode_stream *strm, struct apply_ctx *ctx)
+{
+ bool need_stream = false;
+ switch (strm->stream_type) {
+ case STREAM_TYPE_DATA:
+ if (stream_is_named(strm)) {
+ /* Named data stream */
+ if (ctx->supported_features.named_data_streams)
+ need_stream = true;
+ } else if (!(inode->i_attributes & (FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_ENCRYPTED))
+ && !(inode_is_symlink(inode)
+ && !ctx->supported_features.reparse_points
+ && ctx->supported_features.symlink_reparse_points))
+ {
+ /*
+ * Unnamed data stream. Skip if any of the following is true:
+ *
+ * - file is a directory
+ * - file is encrypted
+ * - backend needs to create the file as UNIX symlink
+ * - backend will extract the stream as externally
+ * backed from the WIM archive itself
+ */
+ if (ctx->apply_ops->will_back_from_wim) {
+ int ret = (*ctx->apply_ops->will_back_from_wim)(dentry, ctx);
+ if (ret > 0) /* Error? */
+ return ret;
+ if (ret < 0) /* Won't externally back? */
+ need_stream = true;
+ } else {
+ need_stream = true;
+ }
+ }
+ break;
+ case STREAM_TYPE_REPARSE_POINT:
+ wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT);
+ if (ctx->supported_features.reparse_points ||
+ (inode_is_symlink(inode) &&
+ ctx->supported_features.symlink_reparse_points))
+ need_stream = true;
+ break;
+ case STREAM_TYPE_EFSRPC_RAW_DATA:
+ wimlib_assert(inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED);
+ if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (ctx->supported_features.encrypted_directories)
+ need_stream = true;
+ } else {
+ if (ctx->supported_features.encrypted_files)
+ need_stream = true;
+ }
+ break;
+ }
+ if (need_stream)
+ return ref_stream(strm, dentry, ctx);