+ /* Special case: when extracting from a pipe, the WIM blob table is
+ * initially empty, so "resolving" an inode's streams is initially not
+ * possible. However, we still need to keep track of which blobs,
+ * identified by SHA-1 message digests, need to be extracted, so we
+ * "resolve" the inode's streams anyway by allocating a 'struct
+ * blob_descriptor' for each one. */
+ if (extract_flags & WIMLIB_EXTRACT_FLAG_FROM_PIPE)
+ force = true;
+ ret = inode_resolve_streams(inode, blob_table, force);
+ if (ret)
+ return ret;
+ for (unsigned i = 0; i < inode->i_num_streams; i++) {
+ blob = stream_blob_resolved(&inode->i_streams[i]);
+ if (blob)
+ blob->out_refcnt = 0;
+ }
+ return 0;
+}
+
+/*
+ * For each dentry to be extracted, resolve all streams in the corresponding
+ * inode and set 'out_refcnt' in all referenced blob_descriptors 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->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++;