+ int ret;
+ struct list_head *cur, *next;
+ struct wim_lookup_table_entry *lte;
+
+ ret = sort_stream_list_by_sequential_order(stream_list, list_head_offset);
+ if (ret)
+ return ret;
+
+ 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_is_partial(lte)) {
+
+ 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;
+ }
+ }
+
+ /* Set up a chain of callbacks.
+ *
+ * The first level is the
+ * streamifier_cb,
+ * which takes in chunks of data and divides
+ * them into the constituent streams.
+ *
+ * The second level are the SHA1 message digest
+ * callbacks, which checksum each stream.
+ *
+ * rechunkifier_cb handles dividing the read
+ * data into chunks of maximum size
+ * @cb_chunk_size. If @cb_chunk_size is 0, then
+ * this callback is not needed.
+ *
+ * Finally, the last level of callbacks are
+ * @cbs, passed as arguments to this function.
+ */
+
+ struct rechunkifier_context *rechunkifier_ctx = NULL;
+ consume_data_callback_t last_cb;
+ void *last_cb_ctx;
+
+ if (cb_chunk_size != 0) {
+ rechunkifier_ctx = alloca(sizeof(*rechunkifier_ctx));
+ *rechunkifier_ctx = (struct rechunkifier_context) {
+ .buffer = MALLOC(cb_chunk_size),
+ .buffer_filled = 0,
+ .cb_chunk_size = cb_chunk_size,
+ .ranges = ranges,
+ .num_ranges = stream_count,
+ .cur_range = 0,
+ .range_bytes_remaining = ranges[0].size,
+ .cb = cbs->consume_chunk,
+ .cb_ctx = cbs->consume_chunk_ctx,
+ };
+
+ if (rechunkifier_ctx->buffer == NULL)
+ return WIMLIB_ERR_NOMEM;
+ last_cb = rechunkifier_cb;
+ last_cb_ctx = rechunkifier_ctx;
+ } else {
+ rechunkifier_ctx = NULL;
+ last_cb = cbs->consume_chunk;
+ last_cb_ctx = cbs->consume_chunk_ctx;
+ }
+
+ struct hasher_context hasher_ctx = {
+ .cbs = {
+ .begin_stream = cbs->begin_stream,
+ .begin_stream_ctx = cbs->begin_stream_ctx,
+ .consume_chunk = last_cb,
+ .consume_chunk_ctx = last_cb_ctx,
+ .end_stream = cbs->end_stream,
+ .end_stream_ctx = cbs->end_stream_ctx,
+ },
+ };
+
+ struct streamifier_context streamifier_ctx = {
+ .cbs = {
+ .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,
+ },
+ .cur_stream = lte,
+ .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,
+ false);
+ if (rechunkifier_ctx != NULL)
+ FREE(rechunkifier_ctx->buffer);
+
+ 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_sha1(lte, cbs);
+ if (ret > 0)
+ return ret;
+ }
+ return 0;