+ if (flags & (VERIFY_STREAM_HASHES | COMPUTE_MISSING_STREAM_HASHES)) {
+ hasher_ctx = alloca(sizeof(*hasher_ctx));
+ *hasher_ctx = (struct hasher_context) {
+ .flags = flags,
+ .cbs = *cbs,
+ };
+ sink_cbs = alloca(sizeof(*sink_cbs));
+ *sink_cbs = (struct read_stream_list_callbacks) {
+ .begin_stream = hasher_begin_stream,
+ .begin_stream_ctx = hasher_ctx,
+ .consume_chunk = hasher_consume_chunk,
+ .consume_chunk_ctx = hasher_ctx,
+ .end_stream = hasher_end_stream,
+ .end_stream_ctx = hasher_ctx,
+ };
+ } else {
+ sink_cbs = (struct read_stream_list_callbacks*)cbs;
+ }
+
+ for (cur = stream_list->next, next = cur->next;
+ cur != stream_list;
+ cur = next, next = cur->next)
+ {
+ lte = (struct wim_lookup_table_entry*)((u8*)cur - list_head_offset);
+
+ if (lte->flags & WIM_RESHDR_FLAG_PACKED_STREAMS &&
+ lte->size != lte->rspec->uncompressed_size)
+ {
+
+ struct wim_lookup_table_entry *lte_next, *lte_last;
+ struct list_head *next2;
+ size_t stream_count;
+
+ /* The next stream is a proper sub-sequence of a WIM
+ * resource. See if there are other streams in the same
+ * resource that need to be read. Since
+ * sort_stream_list_by_sequential_order() sorted the
+ * streams by offset in the WIM, this can be determined
+ * by simply scanning forward in the list. */
+
+ lte_last = lte;
+ stream_count = 1;
+ for (next2 = next;
+ next2 != stream_list
+ && (lte_next = (struct wim_lookup_table_entry*)
+ ((u8*)next2 - list_head_offset),
+ lte_next->resource_location == RESOURCE_IN_WIM
+ && lte_next->rspec == lte->rspec);
+ next2 = next2->next)
+ {
+ lte_last = lte_next;
+ stream_count++;
+ }
+ if (stream_count > 1) {
+ /* Reading multiple streams combined into a
+ * single WIM resource. They are in the stream
+ * list, sorted by offset; @lte specifies the
+ * first stream in the resource that needs to be
+ * read and @lte_last specifies the last stream
+ * in the resource that needs to be read. */
+
+ DEBUG("Reading %zu streams combined in same "
+ "WIM resource", stream_count);
+
+ next = next2;
+
+ struct data_range ranges[stream_count];
+
+ {
+ struct list_head *next3;
+ size_t i;
+ struct wim_lookup_table_entry *lte_cur;
+
+ next3 = cur;
+ for (i = 0; i < stream_count; i++) {
+ lte_cur = (struct wim_lookup_table_entry*)
+ ((u8*)next3 - list_head_offset);
+ ranges[i].offset = lte_cur->offset_in_res;
+ ranges[i].size = lte_cur->size;
+ next3 = next3->next;
+ }
+ }
+
+ struct streamifier_context streamifier_ctx = {
+ .cbs = *sink_cbs,
+ .cur_stream = lte,
+ .next_stream = next_stream(lte, list_head_offset),
+ .cur_stream_offset = 0,
+ .final_stream = lte_last,
+ .list_head_offset = list_head_offset,
+ };
+
+ ret = read_compressed_wim_resource(lte->rspec,
+ ranges,
+ stream_count,
+ streamifier_cb,
+ &streamifier_ctx);
+
+ if (ret) {
+ if (streamifier_ctx.cur_stream_offset != 0) {
+ ret = (*streamifier_ctx.cbs.end_stream)
+ (streamifier_ctx.cur_stream,
+ ret,
+ streamifier_ctx.cbs.end_stream_ctx);
+ }
+ return ret;
+ }
+ continue;
+ }
+ }
+
+ ret = read_full_stream_with_cbs(lte, sink_cbs);
+ if (ret && ret != BEGIN_STREAM_STATUS_SKIP_STREAM)
+ return ret;
+ }
+ return 0;
+}
+
+/* Extract the first @size bytes of the specified stream.
+ *
+ * If @size specifies the full uncompressed size of the stream, then the SHA1
+ * message digest of the uncompressed stream is checked while being extracted.
+ *
+ * The uncompressed data of the resource is passed in chunks of unspecified size
+ * to the @extract_chunk function, passing it @extract_chunk_arg. */
+int
+extract_stream(struct wim_lookup_table_entry *lte, u64 size,
+ consume_data_callback_t extract_chunk, void *extract_chunk_arg)
+{
+ wimlib_assert(size <= lte->size);
+ if (size == lte->size) {
+ /* Do SHA1. */
+ struct read_stream_list_callbacks cbs = {
+ .consume_chunk = extract_chunk,
+ .consume_chunk_ctx = extract_chunk_arg,
+ };
+ return read_full_stream_with_sha1(lte, &cbs);
+ } else {
+ /* Don't do SHA1. */
+ return read_stream_prefix(lte, size, extract_chunk,
+ extract_chunk_arg);
+ }
+}
+
+/* A consume_data_callback_t implementation that writes the chunk of data to a
+ * file descriptor. */
+int
+extract_chunk_to_fd(const void *chunk, size_t size, void *_fd_p)
+{
+ struct filedes *fd = _fd_p;