+/* qsort() callback that sorts streams (represented by `struct
+ * wim_lookup_table_entry's) into an order optimized for reading and writing.
+ *
+ * Sorting is done primarily by resource location, then secondarily by a
+ * per-resource location order. For example, resources in WIM files are sorted
+ * primarily by part number, then secondarily by offset, as to implement optimal
+ * reading of either a standalone or split WIM. */
+static int
+cmp_streams_by_sequential_order(const void *p1, const void *p2)
+{
+ const struct wim_lookup_table_entry *lte1, *lte2;
+ int v;
+ WIMStruct *wim1, *wim2;
+
+ lte1 = *(const struct wim_lookup_table_entry**)p1;
+ lte2 = *(const struct wim_lookup_table_entry**)p2;
+
+ v = (int)lte1->resource_location - (int)lte2->resource_location;
+
+ /* Different resource locations? */
+ if (v)
+ return v;
+
+ switch (lte1->resource_location) {
+ case RESOURCE_IN_WIM:
+ wim1 = lte1->rspec->wim;
+ wim2 = lte2->rspec->wim;
+
+ /* Different (possibly split) WIMs? */
+ if (wim1 != wim2) {
+ v = memcmp(wim1->hdr.guid, wim2->hdr.guid, WIM_GID_LEN);
+ if (v)
+ return v;
+ }
+
+ /* Different part numbers in the same WIM? */
+ v = (int)wim1->hdr.part_number - (int)wim2->hdr.part_number;
+ if (v)
+ return v;
+
+ /* Compare by offset. */
+ if (lte1->rspec->offset_in_wim < lte2->rspec->offset_in_wim)
+ return -1;
+ if (lte1->rspec->offset_in_wim > lte2->rspec->offset_in_wim)
+ return 1;
+ return 0;
+ case RESOURCE_IN_FILE_ON_DISK:
+#ifdef WITH_FUSE
+ case RESOURCE_IN_STAGING_FILE:
+#endif
+#ifdef __WIN32__
+ case RESOURCE_WIN32_ENCRYPTED:
+#endif
+ /* Compare files by path: just a heuristic that will place files
+ * in the same directory next to each other. */
+ return tstrcmp(lte1->file_on_disk, lte2->file_on_disk);
+#ifdef WITH_NTFS_3G
+ case RESOURCE_IN_NTFS_VOLUME:
+ return tstrcmp(lte1->ntfs_loc->path, lte2->ntfs_loc->path);
+#endif
+ default:
+ /* No additional sorting order defined for this resource
+ * location (e.g. RESOURCE_IN_ATTACHED_BUFFER); simply compare
+ * everything equal to each other. */
+ return 0;
+ }
+}
+
+int
+sort_stream_list_by_sequential_order(struct list_head *stream_list,
+ size_t list_head_offset)
+{
+ struct list_head *cur;
+ struct wim_lookup_table_entry **array;
+ size_t i;
+ size_t array_size;
+ size_t num_streams = 0;
+
+ list_for_each(cur, stream_list)
+ num_streams++;
+
+ array_size = num_streams * sizeof(array[0]);
+ array = MALLOC(array_size);
+ if (array == NULL)
+ return WIMLIB_ERR_NOMEM;
+ cur = stream_list->next;
+ for (i = 0; i < num_streams; i++) {
+ array[i] = (struct wim_lookup_table_entry*)((u8*)cur -
+ list_head_offset);
+ cur = cur->next;
+ }
+
+ qsort(array, num_streams, sizeof(array[0]),
+ cmp_streams_by_sequential_order);
+
+ INIT_LIST_HEAD(stream_list);
+ for (i = 0; i < num_streams; i++) {
+ list_add_tail((struct list_head*)
+ ((u8*)array[i] + list_head_offset),
+ stream_list);
+ }
+ FREE(array);
+ return 0;
+}
+
+
+static int
+add_lte_to_array(struct wim_lookup_table_entry *lte,
+ void *_pp)
+{
+ struct wim_lookup_table_entry ***pp = _pp;
+ *(*pp)++ = lte;
+ return 0;
+}
+
+/* Iterate through the lookup table entries, but first sort them by stream
+ * offset in the WIM. Caution: this is intended to be used when the stream
+ * offset field has actually been set. */
+int
+for_lookup_table_entry_pos_sorted(struct wim_lookup_table *table,
+ int (*visitor)(struct wim_lookup_table_entry *,
+ void *),
+ void *arg)
+{
+ struct wim_lookup_table_entry **lte_array, **p;
+ size_t num_streams = table->num_entries;
+ int ret;
+
+ lte_array = MALLOC(num_streams * sizeof(lte_array[0]));
+ if (!lte_array)
+ return WIMLIB_ERR_NOMEM;
+ p = lte_array;
+ for_lookup_table_entry(table, add_lte_to_array, &p);
+
+ 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
+
+static int
+validate_resource(const struct wim_resource_spec *rspec)
+{
+ struct wim_lookup_table_entry *lte;
+ if (!list_is_singular(&rspec->lte_list)) {
+ list_for_each_entry(lte, &rspec->lte_list, wim_resource_list) {
+ if (rspec->flags & WIM_RESHDR_FLAG_COMPRESSED)
+ lte->flags |= WIM_RESHDR_FLAG_COMPRESSED;
+ else
+ lte->flags &= ~WIM_RESHDR_FLAG_COMPRESSED;
+
+ if (lte->offset_in_res + lte->size < lte->size ||
+ lte->offset_in_res + lte->size > rspec->uncompressed_size)
+ {
+ return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
+ }
+ }
+ }
+ return 0;
+}