- /* Read the root directory entry starts after security data, on an
- * 8-byte aligned address.
- *
- * The security data starts with a 4-byte integer giving its total
- * length. */
- get_u32(buf, &dentry_offset);
- dentry_offset += (8 - dentry_offset % 8) % 8;
-
- ret = read_dentry(buf, res_entry->original_size, dentry_offset, dentry);
- if (ret != 0)
- goto err1;
-
- /* This is the root dentry, so set its pointers correctly. */
- dentry->parent = dentry;
- dentry->next = dentry;
- dentry->prev = dentry;
-
- /* Now read the entire directory entry tree. */
- ret = read_dentry_tree(buf, res_entry->original_size, dentry);
- if (ret != 0)
- goto err2;
-
- /* Calculate the full paths in the dentry tree. */
- ret = for_dentry_in_tree(dentry, calculate_dentry_full_path, NULL);
- if (ret != 0)
- goto err2;
-
- *root_dentry_p = dentry;
- FREE(buf);
- return ret;
-err2:
- free_dentry_tree(dentry, NULL, false);
-err1:
- FREE(buf);
+ /* Retrieve the final SHA1 message digest. */
+ sha1_final(hash, &ctx->sha_ctx);
+
+ if (lte->unhashed) {
+ if (ctx->flags & COMPUTE_MISSING_STREAM_HASHES) {
+ /* No SHA1 message digest was previously present for the
+ * stream. Set it to the one just calculated. */
+ DEBUG("Set SHA1 message digest for stream "
+ "(size=%"PRIu64").", lte->size);
+ copy_hash(lte->hash, hash);
+ }
+ } else {
+ if (ctx->flags & VERIFY_STREAM_HASHES) {
+ /* The stream already had a SHA1 message digest present. Verify
+ * that it is the same as the calculated value. */
+ if (!hashes_equal(hash, lte->hash)) {
+ if (wimlib_print_errors) {
+ tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1];
+ tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1];
+ sprint_hash(lte->hash, expected_hashstr);
+ sprint_hash(hash, actual_hashstr);
+ ERROR("The stream is corrupted!\n"
+ " (Expected SHA1=%"TS",\n"
+ " got SHA1=%"TS")",
+ expected_hashstr, actual_hashstr);
+ }
+ ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
+ errno = EINVAL;
+ goto out_next_cb;
+ }
+ DEBUG("SHA1 message digest okay for "
+ "stream (size=%"PRIu64").", lte->size);
+ }
+ }
+ ret = 0;
+out_next_cb:
+ if (ctx->cbs.end_stream == NULL)
+ return ret;
+ else
+ return (*ctx->cbs.end_stream)(lte, ret, ctx->cbs.end_stream_ctx);
+}
+
+static int
+read_full_stream_with_cbs(struct wim_lookup_table_entry *lte,
+ const struct read_stream_list_callbacks *cbs)
+{
+ int ret;
+
+ ret = (*cbs->begin_stream)(lte, cbs->begin_stream_ctx);
+ if (ret)
+ return ret;
+
+ ret = read_stream_prefix(lte, lte->size, cbs->consume_chunk,
+ cbs->consume_chunk_ctx);
+
+ return (*cbs->end_stream)(lte, ret, cbs->end_stream_ctx);
+}
+
+/* Read the full data of the specified stream, passing the data into the
+ * specified callbacks (all of which are optional) and either checking or
+ * computing the SHA1 message digest of the stream. */
+static int
+read_full_stream_with_sha1(struct wim_lookup_table_entry *lte,
+ const struct read_stream_list_callbacks *cbs)
+{
+ struct hasher_context hasher_ctx = {
+ .flags = VERIFY_STREAM_HASHES | COMPUTE_MISSING_STREAM_HASHES,
+ .cbs = *cbs,
+ };
+ struct read_stream_list_callbacks hasher_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,
+ };
+ return read_full_stream_with_cbs(lte, &hasher_cbs);
+}
+
+static int
+read_packed_streams(struct wim_lookup_table_entry *first_stream,
+ struct wim_lookup_table_entry *last_stream,
+ u64 stream_count,
+ size_t list_head_offset,
+ const struct read_stream_list_callbacks *sink_cbs)
+{
+ struct data_range *ranges;
+ bool ranges_malloced;
+ struct wim_lookup_table_entry *cur_stream;
+ size_t i;
+ int ret;
+ u64 ranges_alloc_size;
+
+ DEBUG("Reading %"PRIu64" streams combined in same WIM resource",
+ stream_count);
+
+ /* Setup data ranges array (one range per stream to read); this way
+ * read_compressed_wim_resource() does not need to be aware of streams.
+ */
+
+ ranges_alloc_size = stream_count * sizeof(ranges[0]);
+
+ if (unlikely((size_t)ranges_alloc_size != ranges_alloc_size)) {
+ ERROR("Too many streams in one resource!");
+ return WIMLIB_ERR_NOMEM;
+ }
+ if (likely(ranges_alloc_size <= STACK_MAX)) {
+ ranges = alloca(ranges_alloc_size);
+ ranges_malloced = false;
+ } else {
+ ranges = MALLOC(ranges_alloc_size);
+ if (ranges == NULL) {
+ ERROR("Too many streams in one resource!");
+ return WIMLIB_ERR_NOMEM;
+ }
+ ranges_malloced = true;
+ }
+
+ for (i = 0, cur_stream = first_stream;
+ i < stream_count;
+ i++, cur_stream = next_stream(cur_stream, list_head_offset))
+ {
+ ranges[i].offset = cur_stream->offset_in_res;
+ ranges[i].size = cur_stream->size;
+ }
+
+ struct streamifier_context streamifier_ctx = {
+ .cbs = *sink_cbs,
+ .cur_stream = first_stream,
+ .next_stream = next_stream(first_stream, list_head_offset),
+ .cur_stream_offset = 0,
+ .final_stream = last_stream,
+ .list_head_offset = list_head_offset,
+ };
+
+ ret = read_compressed_wim_resource(first_stream->rspec,
+ ranges,
+ stream_count,
+ streamifier_cb,
+ &streamifier_ctx);
+
+ if (ranges_malloced)
+ FREE(ranges);
+
+ 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);
+ }
+ }