+ /* 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 the 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)
+ goto oom;
+
+ /* Allocate and initalize stream entries ('struct
+ * wim_lookup_table_entry's) from the raw lookup table buffer. Each of
+ * these entries will point to a 'struct wim_resource_spec' that
+ * describes the underlying resource. In WIMs with version number
+ * WIM_VERSION_SOLID, a resource may contain multiple streams.
+ */
+ for (size_t i = 0; i < num_entries; i++) {
+ const struct wim_lookup_table_entry_disk *disk_entry =
+ &((const struct wim_lookup_table_entry_disk*)buf)[i];
+ struct wim_reshdr reshdr;
+ u16 part_number;
+
+ /* Get the resource header */
+ 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);
+
+ /* Ignore SOLID flag if it isn't supposed to be used in this WIM
+ * version. */
+ if (wim->hdr.wim_version == WIM_VERSION_DEFAULT)
+ reshdr.flags &= ~WIM_RESHDR_FLAG_SOLID;
+
+ /* Allocate a new 'struct wim_lookup_table_entry'. */
+ cur_entry = new_lookup_table_entry();
+ if (!cur_entry)
+ goto oom;
+
+ /* Get the part number, reference count, and hash. */
+ part_number = le16_to_cpu(disk_entry->part_number);
+ cur_entry->refcnt = le32_to_cpu(disk_entry->refcnt);
+ copy_hash(cur_entry->hash, disk_entry->hash);
+
+ if (reshdr.flags & WIM_RESHDR_FLAG_SOLID) {
+
+ /* SOLID entry */
+
+ if (!cur_solid_rspecs) {
+ /* Starting new run */
+ ret = load_solid_info(wim, disk_entry,
+ num_entries - i,
+ &cur_solid_rspecs,
+ &cur_num_solid_rspecs);
+ if (ret)
+ goto out;
+ }
+
+ if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) {
+ /* Resource entry, not stream entry */
+ goto free_cur_entry_and_continue;
+ }
+
+ /* Stream entry */
+
+ ret = bind_stream_to_solid_resource(&reshdr,
+ cur_entry,
+ cur_solid_rspecs,
+ cur_num_solid_rspecs);
+ if (ret)
+ goto out;
+
+ } else {
+ /* Normal stream/resource entry; SOLID not set. */
+
+ struct wim_resource_spec *rspec;
+
+ if (unlikely(cur_solid_rspecs)) {
+ /* This entry terminated a solid run. */
+ ret = finish_solid_rspecs(cur_solid_rspecs,
+ cur_num_solid_rspecs);
+ cur_solid_rspecs = NULL;
+ if (ret)
+ goto out;
+ }
+
+ /* How to handle an uncompressed resource with its
+ * uncompressed size different from its compressed size?
+ *
+ * Based on a simple test, WIMGAPI seems to handle this
+ * as follows:
+ *
+ * if (size_in_wim > uncompressed_size) {
+ * Ignore uncompressed_size; use size_in_wim
+ * instead.
+ * } else {
+ * Honor uncompressed_size, but treat the part of
+ * the file data above size_in_wim as all zeros.
+ * }
+ *
+ * So we will do the same. */
+ if (unlikely(!(reshdr.flags &
+ WIM_RESHDR_FLAG_COMPRESSED) &&
+ (reshdr.size_in_wim >
+ reshdr.uncompressed_size)))
+ {
+ reshdr.uncompressed_size = reshdr.size_in_wim;
+ }
+
+ /* Set up a resource specification for this stream. */
+
+ rspec = MALLOC(sizeof(struct wim_resource_spec));
+ if (!rspec)
+ goto oom;
+
+ wim_res_hdr_to_spec(&reshdr, wim, rspec);
+
+ cur_entry->offset_in_res = 0;
+ cur_entry->size = reshdr.uncompressed_size;
+ cur_entry->flags = reshdr.flags;
+
+ lte_bind_wim_resource_spec(cur_entry, rspec);
+ }
+
+ /* cur_entry is now a stream bound to a resource. */
+
+ /* Ignore entries with all zeroes in the hash field. */
+ if (is_zero_hash(cur_entry->hash))
+ goto free_cur_entry_and_continue;
+
+ /* Verify that the part number matches that of the underlying
+ * WIM file. */
+ if (part_number != wim->hdr.part_number) {
+ num_wrong_part_entries++;
+ goto free_cur_entry_and_continue;
+ }
+
+ if (reshdr.flags & WIM_RESHDR_FLAG_METADATA) {
+
+ /* Lookup table entry for a metadata resource. */
+
+ /* Metadata entries with no references must be ignored.
+ * See, for example, the WinPE WIMs from the WAIK v2.1.
+ */
+ if (cur_entry->refcnt == 0)
+ goto free_cur_entry_and_continue;
+
+ if (cur_entry->refcnt != 1) {
+ /* We don't currently support this case due to
+ * the complications of multiple images sharing
+ * the same metadata resource or a metadata
+ * resource also being referenced by files. */
+ ERROR("Found metadata resource with refcnt != 1");
+ ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
+ goto out;
+ }
+
+ if (wim->hdr.part_number != 1) {
+ WARNING("Ignoring metadata resource found in a "
+ "non-first part of the split WIM");
+ goto free_cur_entry_and_continue;
+ }
+
+ /* The number of entries in the lookup table with
+ * WIM_RESHDR_FLAG_METADATA set should be the same as
+ * the image_count field in the WIM header. */
+ if (image_index == wim->hdr.image_count) {
+ WARNING("Found more metadata resources than images");
+ goto free_cur_entry_and_continue;
+ }
+
+ /* Notice very carefully: We are assigning the metadata
+ * resources to images in the same order in which their
+ * lookup table entries occur on disk. (This is also
+ * the behavior of Microsoft's software.) In
+ * particular, this overrides the actual locations of
+ * the metadata resources themselves in the WIM file as
+ * well as any information written in the XML data. */
+ DEBUG("Found metadata resource for image %"PRIu32" at "
+ "offset %"PRIu64".",
+ image_index + 1,
+ reshdr.offset_in_wim);
+
+ wim->image_metadata[image_index++]->metadata_lte = cur_entry;
+ } else {
+ /* Lookup table entry for a non-metadata stream. */
+
+ /* Ignore this stream if it's a duplicate. */
+ if (lookup_stream(table, cur_entry->hash)) {
+ num_duplicate_entries++;
+ goto free_cur_entry_and_continue;
+ }
+
+ /* Insert the stream into the in-memory lookup table,
+ * keyed by its SHA1 message digest. */
+ lookup_table_insert(table, cur_entry);