+ wimlib_assert(p == lte_array + num_streams);
+
+ qsort(lte_array, num_streams, sizeof(lte_array[0]),
+ cmp_streams_by_sequential_order);
+ ret = 0;
+ for (size_t i = 0; i < num_streams; i++) {
+ ret = visitor(lte_array[i], arg);
+ if (ret)
+ break;
+ }
+ FREE(lte_array);
+ return ret;
+}
+
+/* On-disk format of a WIM lookup table entry (stream entry). */
+struct wim_lookup_table_entry_disk {
+ /* Size, offset, and flags of the stream. */
+ struct wim_reshdr_disk reshdr;
+
+ /* Which part of the split WIM this stream is in; indexed from 1. */
+ le16 part_number;
+
+ /* Reference count of this stream over all WIM images. */
+ le32 refcnt;
+
+ /* SHA1 message digest of the uncompressed data of this stream, or
+ * optionally all zeroes if this stream is of zero length. */
+ u8 hash[SHA1_HASH_SIZE];
+} _packed_attribute;
+
+#define WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE 50
+
+/* Validate the size and location of a WIM resource. */
+static int
+validate_resource(const struct wim_resource_spec *rspec)
+{
+ struct wim_lookup_table_entry *lte;
+ u64 cur_offset;
+
+ /* Verify that calculating the offset of the end of the resource doesn't
+ * overflow. */
+ if (rspec->offset_in_wim + rspec->size_in_wim < rspec->size_in_wim)
+ goto invalid;
+
+ /* Verify that each stream in the resource has a valid offset and size,
+ * and that no streams overlap, and that the streams were added in order
+ * of increasing offset. */
+ cur_offset = 0;
+ list_for_each_entry(lte, &rspec->stream_list, rspec_node) {
+ if (lte->offset_in_res + lte->size < lte->size ||
+ lte->offset_in_res + lte->size > rspec->uncompressed_size ||
+ lte->offset_in_res < cur_offset)
+ goto invalid;
+
+ cur_offset = lte->offset_in_res + lte->size;
+ }
+ return 0;
+
+invalid:
+
+ ERROR("Invalid resource entry!");
+ return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
+}
+
+/*
+ * Reads the lookup table from a WIM file. Each entry specifies a stream that
+ * the WIM file contains, along with its location and SHA1 message digest.
+ *
+ * Saves lookup table entries for non-metadata streams in a hash table, and
+ * saves the metadata entry for each image in a special per-image location (the
+ * image_metadata array).
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY
+ * WIMLIB_ERR_RESOURCE_NOT_FOUND
+ *
+ * Or an error code caused by failure to read the lookup table into memory.
+ */
+int
+read_wim_lookup_table(WIMStruct *wim)
+{
+ int ret;
+ size_t i;
+ size_t num_entries;
+ struct wim_lookup_table *table;
+ struct wim_lookup_table_entry *cur_entry, *duplicate_entry;
+ struct wim_resource_spec *cur_rspec;
+ void *buf;
+ bool back_to_back_pack;
+
+ DEBUG("Reading lookup table.");
+
+ /* Sanity check: lookup table entries are 50 bytes each. */
+ BUILD_BUG_ON(sizeof(struct wim_lookup_table_entry_disk) !=
+ WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE);
+
+ /* Calculate number of entries in the lookup table. */
+ num_entries = wim->hdr.lookup_table_reshdr.uncompressed_size /
+ sizeof(struct wim_lookup_table_entry_disk);
+
+ /* Read the lookup table into a buffer. */
+ ret = wim_reshdr_to_data(&wim->hdr.lookup_table_reshdr, wim, &buf);
+ if (ret)
+ goto out;
+
+ /* Allocate a hash table to map SHA1 message digests into stream
+ * specifications. This is the in-memory "lookup table". */
+ table = new_lookup_table(num_entries * 2 + 1);
+ if (table == NULL) {
+ ERROR("Not enough memory to read lookup table.");
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_buf;
+ }
+
+ /* Allocate and initalize stream entries from the raw lookup table
+ * buffer. */
+ wim->current_image = 0;
+ cur_rspec = NULL;
+ for (i = 0; i < num_entries; i++) {
+ const struct wim_lookup_table_entry_disk *disk_entry =
+ &((const struct wim_lookup_table_entry_disk*)buf)[i];
+ u16 part_number;
+ struct wim_reshdr reshdr;
+
+ get_wim_reshdr(&disk_entry->reshdr, &reshdr);
+
+ DEBUG("reshdr: size_in_wim=%"PRIu64", "
+ "uncompressed_size=%"PRIu64", "
+ "offset_in_wim=%"PRIu64", "
+ "flags=0x%02x",
+ reshdr.size_in_wim, reshdr.uncompressed_size,
+ reshdr.offset_in_wim, reshdr.flags);
+
+ if (wim->hdr.wim_version == WIM_VERSION_DEFAULT)
+ reshdr.flags &= ~WIM_RESHDR_FLAG_PACKED_STREAMS;