e1fb6adac77c3c46603468c9e1be058d450270f7
[wimlib] / src / blob_table.c
1 /*
2  * blob_table.c
3  *
4  * A blob table maps SHA-1 message digests to "blobs", which are nonempty
5  * sequences of binary data.  Within a WIM file, blobs are single-instanced.
6  *
7  * This file also contains code to read and write the corresponding on-disk
8  * representation of this table in the WIM file format.
9  */
10
11 /*
12  * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
13  *
14  * This file is free software; you can redistribute it and/or modify it under
15  * the terms of the GNU Lesser General Public License as published by the Free
16  * Software Foundation; either version 3 of the License, or (at your option) any
17  * later version.
18  *
19  * This file is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this file; if not, see http://www.gnu.org/licenses/.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h> /* for unlink()  */
35
36 #include "wimlib/assert.h"
37 #include "wimlib/bitops.h"
38 #include "wimlib/blob_table.h"
39 #include "wimlib/encoding.h"
40 #include "wimlib/endianness.h"
41 #include "wimlib/error.h"
42 #include "wimlib/metadata.h"
43 #include "wimlib/ntfs_3g.h"
44 #include "wimlib/resource.h"
45 #include "wimlib/unaligned.h"
46 #include "wimlib/util.h"
47 #include "wimlib/write.h"
48
49 /* A hash table mapping SHA-1 message digests to blob descriptors  */
50 struct blob_table {
51         struct hlist_head *array;
52         size_t num_blobs;
53         size_t mask; /* capacity - 1; capacity is a power of 2  */
54 };
55
56 static size_t
57 next_power_of_2(size_t n)
58 {
59         if (n <= 1)
60                 return 1;
61         return (size_t)1 << (1 + flsw(n - 1));
62 }
63
64 struct blob_table *
65 new_blob_table(size_t capacity)
66 {
67         struct blob_table *table;
68         struct hlist_head *array;
69
70         capacity = next_power_of_2(capacity);
71
72         table = MALLOC(sizeof(struct blob_table));
73         if (table == NULL)
74                 goto oom;
75
76         array = CALLOC(capacity, sizeof(array[0]));
77         if (array == NULL) {
78                 FREE(table);
79                 goto oom;
80         }
81
82         table->num_blobs = 0;
83         table->mask = capacity - 1;
84         table->array = array;
85         return table;
86
87 oom:
88         ERROR("Failed to allocate memory for blob table "
89               "with capacity %zu", capacity);
90         return NULL;
91 }
92
93 static int
94 do_free_blob_descriptor(struct blob_descriptor *blob, void *_ignore)
95 {
96         free_blob_descriptor(blob);
97         return 0;
98 }
99
100 void
101 free_blob_table(struct blob_table *table)
102 {
103         if (table) {
104                 for_blob_in_table(table, do_free_blob_descriptor, NULL);
105                 FREE(table->array);
106                 FREE(table);
107         }
108 }
109
110 struct blob_descriptor *
111 new_blob_descriptor(void)
112 {
113         STATIC_ASSERT(BLOB_NONEXISTENT == 0);
114         return CALLOC(1, sizeof(struct blob_descriptor));
115 }
116
117 struct blob_descriptor *
118 clone_blob_descriptor(const struct blob_descriptor *old)
119 {
120         struct blob_descriptor *new;
121
122         new = memdup(old, sizeof(struct blob_descriptor));
123         if (new == NULL)
124                 return NULL;
125
126         switch (new->blob_location) {
127         case BLOB_IN_WIM:
128                 list_add(&new->rdesc_node, &new->rdesc->blob_list);
129                 break;
130
131         case BLOB_IN_FILE_ON_DISK:
132 #ifdef __WIN32__
133         case BLOB_IN_WINNT_FILE_ON_DISK:
134         case BLOB_WIN32_ENCRYPTED:
135 #endif
136 #ifdef WITH_FUSE
137         case BLOB_IN_STAGING_FILE:
138                 STATIC_ASSERT((void*)&old->file_on_disk ==
139                               (void*)&old->staging_file_name);
140 #endif
141                 new->file_on_disk = TSTRDUP(old->file_on_disk);
142                 if (new->file_on_disk == NULL)
143                         goto out_free;
144                 break;
145         case BLOB_IN_ATTACHED_BUFFER:
146                 new->attached_buffer = memdup(old->attached_buffer, old->size);
147                 if (new->attached_buffer == NULL)
148                         goto out_free;
149                 break;
150 #ifdef WITH_NTFS_3G
151         case BLOB_IN_NTFS_VOLUME:
152                 new->ntfs_loc = clone_ntfs_location(old->ntfs_loc);
153                 if (!new->ntfs_loc)
154                         goto out_free;
155                 break;
156 #endif
157         }
158         return new;
159
160 out_free:
161         free_blob_descriptor(new);
162         return NULL;
163 }
164
165 /* Release a blob descriptor from its location, if any, and set its new location
166  * to BLOB_NONEXISTENT.  */
167 void
168 blob_release_location(struct blob_descriptor *blob)
169 {
170         switch (blob->blob_location) {
171         case BLOB_IN_WIM: {
172                 struct wim_resource_descriptor *rdesc = blob->rdesc;
173
174                 list_del(&blob->rdesc_node);
175                 if (list_empty(&rdesc->blob_list)) {
176                         wim_decrement_refcnt(rdesc->wim);
177                         FREE(rdesc);
178                 }
179                 break;
180         }
181         case BLOB_IN_FILE_ON_DISK:
182 #ifdef __WIN32__
183         case BLOB_IN_WINNT_FILE_ON_DISK:
184         case BLOB_WIN32_ENCRYPTED:
185 #endif
186 #ifdef WITH_FUSE
187         case BLOB_IN_STAGING_FILE:
188                 STATIC_ASSERT((void*)&blob->file_on_disk ==
189                               (void*)&blob->staging_file_name);
190 #endif
191         case BLOB_IN_ATTACHED_BUFFER:
192                 STATIC_ASSERT((void*)&blob->file_on_disk ==
193                               (void*)&blob->attached_buffer);
194                 FREE(blob->file_on_disk);
195                 break;
196 #ifdef WITH_NTFS_3G
197         case BLOB_IN_NTFS_VOLUME:
198                 if (blob->ntfs_loc)
199                         free_ntfs_location(blob->ntfs_loc);
200                 break;
201 #endif
202         }
203         blob->blob_location = BLOB_NONEXISTENT;
204 }
205
206 void
207 free_blob_descriptor(struct blob_descriptor *blob)
208 {
209         if (blob) {
210                 blob_release_location(blob);
211                 FREE(blob);
212         }
213 }
214
215 /* Should this blob be retained even if it has no references?  */
216 static bool
217 should_retain_blob(const struct blob_descriptor *blob)
218 {
219         return blob->blob_location == BLOB_IN_WIM;
220 }
221
222 static void
223 finalize_blob(struct blob_descriptor *blob)
224 {
225         if (!should_retain_blob(blob))
226                 free_blob_descriptor(blob);
227 }
228
229 /*
230  * Decrements the reference count of the specified blob, which must be either
231  * (a) unhashed, or (b) inserted in the specified blob table.
232  *
233  * If the blob's reference count reaches 0, we may unlink it from @table and
234  * free it.  However, we retain blobs with 0 reference count that originated
235  * from WIM files (BLOB_IN_WIM).  We do this for two reasons:
236  *
237  * 1. This prevents information about valid blobs in a WIM file --- blobs which
238  *    will continue to be present after appending to the WIM file --- from being
239  *    lost merely because we dropped all references to them.
240  *
241  * 2. Blob reference counts we read from WIM files can't be trusted.  It's
242  *    possible that a WIM has reference counts that are too low; WIMGAPI
243  *    sometimes creates WIMs where this is the case.  It's also possible that
244  *    blobs have been referenced from an external WIM; those blobs can
245  *    potentially have any reference count at all, either lower or higher than
246  *    would be expected for this WIM ("this WIM" meaning the owner of @table) if
247  *    it were a standalone WIM.
248  *
249  * So we can't take the reference counts too seriously.  But at least, we do
250  * recalculate by default when writing a new WIM file.
251  */
252 void
253 blob_decrement_refcnt(struct blob_descriptor *blob, struct blob_table *table)
254 {
255         blob_subtract_refcnt(blob, table, 1);
256 }
257
258 void
259 blob_subtract_refcnt(struct blob_descriptor *blob, struct blob_table *table,
260                      u32 count)
261 {
262         if (unlikely(blob->refcnt < count)) {
263                 blob->refcnt = 0; /* See comment above  */
264                 return;
265         }
266
267         blob->refcnt -= count;
268
269         if (blob->refcnt != 0)
270                 return;
271
272         if (blob->unhashed) {
273                 list_del(&blob->unhashed_list);
274         #ifdef WITH_FUSE
275                 /* If the blob has been extracted to a staging file for a FUSE
276                  * mount, unlink the staging file.  (Note that there still may
277                  * be open file descriptors to it.)  */
278                 if (blob->blob_location == BLOB_IN_STAGING_FILE)
279                         unlinkat(blob->staging_dir_fd,
280                                  blob->staging_file_name, 0);
281         #endif
282         } else {
283                 if (!should_retain_blob(blob))
284                         blob_table_unlink(table, blob);
285         }
286
287         /* If FUSE mounts are enabled, then don't actually free the blob
288          * descriptor until the last file descriptor to it has been closed.  */
289 #ifdef WITH_FUSE
290         if (blob->num_opened_fds == 0)
291 #endif
292                 finalize_blob(blob);
293 }
294
295 #ifdef WITH_FUSE
296 void
297 blob_decrement_num_opened_fds(struct blob_descriptor *blob)
298 {
299         wimlib_assert(blob->num_opened_fds != 0);
300
301         if (--blob->num_opened_fds == 0 && blob->refcnt == 0)
302                 finalize_blob(blob);
303 }
304 #endif
305
306 static void
307 blob_table_insert_raw(struct blob_table *table, struct blob_descriptor *blob)
308 {
309         size_t i = blob->hash_short & table->mask;
310
311         hlist_add_head(&blob->hash_list, &table->array[i]);
312 }
313
314 static void
315 enlarge_blob_table(struct blob_table *table)
316 {
317         size_t old_capacity, new_capacity;
318         struct hlist_head *old_array, *new_array;
319         struct blob_descriptor *blob;
320         struct hlist_node *tmp;
321         size_t i;
322
323         old_capacity = table->mask + 1;
324         new_capacity = old_capacity * 2;
325         new_array = CALLOC(new_capacity, sizeof(struct hlist_head));
326         if (new_array == NULL)
327                 return;
328         old_array = table->array;
329         table->array = new_array;
330         table->mask = new_capacity - 1;
331
332         for (i = 0; i < old_capacity; i++)
333                 hlist_for_each_entry_safe(blob, tmp, &old_array[i], hash_list)
334                         blob_table_insert_raw(table, blob);
335         FREE(old_array);
336 }
337
338 /* Insert a blob descriptor into the blob table.  */
339 void
340 blob_table_insert(struct blob_table *table, struct blob_descriptor *blob)
341 {
342         blob_table_insert_raw(table, blob);
343         if (table->num_blobs++ > table->mask)
344                 enlarge_blob_table(table);
345 }
346
347 /* Unlinks a blob descriptor from the blob table; does not free it.  */
348 void
349 blob_table_unlink(struct blob_table *table, struct blob_descriptor *blob)
350 {
351         wimlib_assert(!blob->unhashed);
352         wimlib_assert(table->num_blobs != 0);
353
354         hlist_del(&blob->hash_list);
355         table->num_blobs--;
356 }
357
358 /* Given a SHA-1 message digest, return the corresponding blob descriptor from
359  * the specified blob table, or NULL if there is none.  */
360 struct blob_descriptor *
361 lookup_blob(const struct blob_table *table, const u8 *hash)
362 {
363         size_t i;
364         struct blob_descriptor *blob;
365
366         i = load_size_t_unaligned(hash) & table->mask;
367         hlist_for_each_entry(blob, &table->array[i], hash_list)
368                 if (hashes_equal(hash, blob->hash))
369                         return blob;
370         return NULL;
371 }
372
373 /* Call a function on all blob descriptors in the specified blob table.  Stop
374  * early and return nonzero if any call to the function returns nonzero.  */
375 int
376 for_blob_in_table(struct blob_table *table,
377                   int (*visitor)(struct blob_descriptor *, void *), void *arg)
378 {
379         struct blob_descriptor *blob;
380         struct hlist_node *tmp;
381         int ret;
382
383         for (size_t i = 0; i <= table->mask; i++) {
384                 hlist_for_each_entry_safe(blob, tmp, &table->array[i],
385                                           hash_list)
386                 {
387                         ret = visitor(blob, arg);
388                         if (ret)
389                                 return ret;
390                 }
391         }
392         return 0;
393 }
394
395 /*
396  * This is a qsort() callback that sorts blobs into an order optimized for
397  * reading.  Sorting is done primarily by blob location, then secondarily by a
398  * location-dependent order.  For example, blobs in WIM resources are sorted
399  * such that the underlying WIM files will be read sequentially.  This is
400  * especially important for WIM files containing solid resources.
401  */
402 int
403 cmp_blobs_by_sequential_order(const void *p1, const void *p2)
404 {
405         const struct blob_descriptor *blob1, *blob2;
406         int v;
407         WIMStruct *wim1, *wim2;
408
409         blob1 = *(const struct blob_descriptor**)p1;
410         blob2 = *(const struct blob_descriptor**)p2;
411
412         v = (int)blob1->blob_location - (int)blob2->blob_location;
413
414         /* Different locations?  Note: "unsafe compaction mode" requires that
415          * blobs in WIMs sort before all others.  For the logic here to ensure
416          * this, BLOB_IN_WIM must have the lowest value among all defined
417          * blob_locations.  Statically verify that the enum values haven't
418          * changed.  */
419         STATIC_ASSERT(BLOB_NONEXISTENT == 0 && BLOB_IN_WIM == 1);
420         if (v)
421                 return v;
422
423         switch (blob1->blob_location) {
424         case BLOB_IN_WIM:
425                 wim1 = blob1->rdesc->wim;
426                 wim2 = blob2->rdesc->wim;
427
428                 /* Different WIM files?  */
429                 if (wim1 != wim2) {
430
431                         /* Resources from the WIM file currently being compacted
432                          * (if any) must always sort first.  */
433                         v = (int)wim2->being_compacted - (int)wim1->being_compacted;
434                         if (v)
435                                 return v;
436
437                         /* Different split WIMs?  */
438                         v = cmp_guids(wim1->hdr.guid, wim2->hdr.guid);
439                         if (v)
440                                 return v;
441
442                         /* Different part numbers in the same split WIM?  */
443                         v = (int)wim1->hdr.part_number - (int)wim2->hdr.part_number;
444                         if (v)
445                                 return v;
446
447                         /* Probably two WIMStructs for the same on-disk file.
448                          * Just sort by pointer.  */
449                         return wim1 < wim2 ? -1 : 1;
450                 }
451
452                 /* Same WIM file  */
453
454                 /* Sort by increasing resource offset  */
455                 if (blob1->rdesc->offset_in_wim != blob2->rdesc->offset_in_wim)
456                         return cmp_u64(blob1->rdesc->offset_in_wim,
457                                        blob2->rdesc->offset_in_wim);
458
459                 /* The blobs are in the same solid resource.  Sort by increasing
460                  * offset in the resource.  */
461                 return cmp_u64(blob1->offset_in_res, blob2->offset_in_res);
462
463         case BLOB_IN_FILE_ON_DISK:
464 #ifdef WITH_FUSE
465         case BLOB_IN_STAGING_FILE:
466 #endif
467 #ifdef __WIN32__
468         case BLOB_IN_WINNT_FILE_ON_DISK:
469         case BLOB_WIN32_ENCRYPTED:
470                 /* Windows: compare by starting LCN (logical cluster number)  */
471                 v = cmp_u64(blob1->sort_key, blob2->sort_key);
472                 if (v)
473                         return v;
474 #endif
475                 /* Compare files by path: just a heuristic that will place files
476                  * in the same directory next to each other.  */
477                 return tstrcmp(blob1->file_on_disk, blob2->file_on_disk);
478 #ifdef WITH_NTFS_3G
479         case BLOB_IN_NTFS_VOLUME:
480                 return cmp_ntfs_locations(blob1->ntfs_loc, blob2->ntfs_loc);
481 #endif
482         default:
483                 /* No additional sorting order defined for this resource
484                  * location (e.g. BLOB_IN_ATTACHED_BUFFER); simply compare
485                  * everything equal to each other.  */
486                 return 0;
487         }
488 }
489
490 int
491 sort_blob_list(struct list_head *blob_list, size_t list_head_offset,
492                int (*compar)(const void *, const void*))
493 {
494         struct list_head *cur;
495         struct blob_descriptor **array;
496         size_t i;
497         size_t array_size;
498         size_t num_blobs = 0;
499
500         list_for_each(cur, blob_list)
501                 num_blobs++;
502
503         if (num_blobs <= 1)
504                 return 0;
505
506         array_size = num_blobs * sizeof(array[0]);
507         array = MALLOC(array_size);
508         if (array == NULL)
509                 return WIMLIB_ERR_NOMEM;
510
511         cur = blob_list->next;
512         for (i = 0; i < num_blobs; i++) {
513                 array[i] = (struct blob_descriptor*)((u8*)cur - list_head_offset);
514                 cur = cur->next;
515         }
516
517         qsort(array, num_blobs, sizeof(array[0]), compar);
518
519         INIT_LIST_HEAD(blob_list);
520         for (i = 0; i < num_blobs; i++) {
521                 list_add_tail((struct list_head*)
522                                ((u8*)array[i] + list_head_offset), blob_list);
523         }
524         FREE(array);
525         return 0;
526 }
527
528 /* Sort the specified list of blobs in an order optimized for sequential
529  * reading.  */
530 int
531 sort_blob_list_by_sequential_order(struct list_head *blob_list,
532                                    size_t list_head_offset)
533 {
534         return sort_blob_list(blob_list, list_head_offset,
535                               cmp_blobs_by_sequential_order);
536 }
537
538 static int
539 add_blob_to_array(struct blob_descriptor *blob, void *_pp)
540 {
541         struct blob_descriptor ***pp = _pp;
542         *(*pp)++ = blob;
543         return 0;
544 }
545
546 /* Iterate through the blob descriptors in the specified blob table in an order
547  * optimized for sequential reading.  */
548 int
549 for_blob_in_table_sorted_by_sequential_order(struct blob_table *table,
550                                              int (*visitor)(struct blob_descriptor *, void *),
551                                              void *arg)
552 {
553         struct blob_descriptor **blob_array, **p;
554         size_t num_blobs = table->num_blobs;
555         int ret;
556
557         blob_array = MALLOC(num_blobs * sizeof(blob_array[0]));
558         if (!blob_array)
559                 return WIMLIB_ERR_NOMEM;
560         p = blob_array;
561         for_blob_in_table(table, add_blob_to_array, &p);
562
563         wimlib_assert(p == blob_array + num_blobs);
564
565         qsort(blob_array, num_blobs, sizeof(blob_array[0]),
566               cmp_blobs_by_sequential_order);
567         ret = 0;
568         for (size_t i = 0; i < num_blobs; i++) {
569                 ret = visitor(blob_array[i], arg);
570                 if (ret)
571                         break;
572         }
573         FREE(blob_array);
574         return ret;
575 }
576
577 /* On-disk format of a blob descriptor in a WIM file.
578  *
579  * Note: if the WIM file contains solid resource(s), then this structure is
580  * sometimes overloaded to describe a "resource" rather than a "blob".  See the
581  * code for details.  */
582 struct blob_descriptor_disk {
583
584         /* Size, offset, and flags of the blob.  */
585         struct wim_reshdr_disk reshdr;
586
587         /* Which part of the split WIM this blob is in; indexed from 1. */
588         le16 part_number;
589
590         /* Reference count of this blob over all WIM images.  (But see comment
591          * above blob_decrement_refcnt().)  */
592         le32 refcnt;
593
594         /* SHA-1 message digest of the uncompressed data of this blob, or all
595          * zeroes if this blob is of zero length.  */
596         u8 hash[SHA1_HASH_SIZE];
597 } _packed_attribute;
598
599 /* Given a nonempty run of consecutive blob descriptors with the SOLID flag set,
600  * count how many specify resources (as opposed to blobs within those
601  * resources).
602  *
603  * Returns the resulting count.  */
604 static size_t
605 count_solid_resources(const struct blob_descriptor_disk *entries, size_t max)
606 {
607         size_t count = 0;
608         do {
609                 struct wim_reshdr reshdr;
610
611                 get_wim_reshdr(&(entries++)->reshdr, &reshdr);
612
613                 if (!(reshdr.flags & WIM_RESHDR_FLAG_SOLID)) {
614                         /* Run was terminated by a stand-alone blob entry.  */
615                         break;
616                 }
617
618                 if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) {
619                         /* This is a resource entry.  */
620                         count++;
621                 }
622         } while (--max);
623         return count;
624 }
625
626 /*
627  * Given a run of consecutive blob descriptors with the SOLID flag set and
628  * having @num_rdescs resource entries, load resource information from them into
629  * the resource descriptors in the @rdescs array.
630  *
631  * Returns 0 on success, or a nonzero error code on failure.
632  */
633 static int
634 do_load_solid_info(WIMStruct *wim, struct wim_resource_descriptor **rdescs,
635                    size_t num_rdescs,
636                    const struct blob_descriptor_disk *entries)
637 {
638         for (size_t i = 0; i < num_rdescs; i++) {
639                 struct wim_reshdr reshdr;
640                 struct alt_chunk_table_header_disk hdr;
641                 struct wim_resource_descriptor *rdesc;
642                 int ret;
643
644                 /* Advance to next resource entry.  */
645
646                 do {
647                         get_wim_reshdr(&(entries++)->reshdr, &reshdr);
648                 } while (reshdr.uncompressed_size != SOLID_RESOURCE_MAGIC_NUMBER);
649
650                 rdesc = rdescs[i];
651
652                 wim_reshdr_to_desc(&reshdr, wim, rdesc);
653
654                 /* For solid resources, the uncompressed size, compression type,
655                  * and chunk size are stored in the resource itself, not in the
656                  * blob table.  */
657
658                 ret = full_pread(&wim->in_fd, &hdr,
659                                  sizeof(hdr), reshdr.offset_in_wim);
660                 if (ret) {
661                         ERROR("Failed to read header of solid resource "
662                               "(offset_in_wim=%"PRIu64")",
663                               reshdr.offset_in_wim);
664                         return ret;
665                 }
666
667                 rdesc->uncompressed_size = le64_to_cpu(hdr.res_usize);
668
669                 /* Compression format numbers must be the same as in
670                  * WIMGAPI to be compatible here.  */
671                 STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_NONE == 0);
672                 STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_XPRESS == 1);
673                 STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZX == 2);
674                 STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZMS == 3);
675                 rdesc->compression_type = le32_to_cpu(hdr.compression_format);
676                 rdesc->chunk_size = le32_to_cpu(hdr.chunk_size);
677         }
678         return 0;
679 }
680
681 /*
682  * Given a nonempty run of consecutive blob descriptors with the SOLID flag set,
683  * allocate a 'struct wim_resource_descriptor' for each resource within that
684  * run.
685  *
686  * Returns 0 on success, or a nonzero error code on failure.
687  * Returns the pointers and count in *rdescs_ret and *num_rdescs_ret.
688  */
689 static int
690 load_solid_info(WIMStruct *wim,
691                 const struct blob_descriptor_disk *entries,
692                 size_t num_remaining_entries,
693                 struct wim_resource_descriptor ***rdescs_ret,
694                 size_t *num_rdescs_ret)
695 {
696         size_t num_rdescs;
697         struct wim_resource_descriptor **rdescs;
698         size_t i;
699         int ret;
700
701         num_rdescs = count_solid_resources(entries, num_remaining_entries);
702         rdescs = CALLOC(num_rdescs, sizeof(rdescs[0]));
703         if (!rdescs)
704                 return WIMLIB_ERR_NOMEM;
705
706         for (i = 0; i < num_rdescs; i++) {
707                 rdescs[i] = MALLOC(sizeof(struct wim_resource_descriptor));
708                 if (!rdescs[i]) {
709                         ret = WIMLIB_ERR_NOMEM;
710                         goto out_free_rdescs;
711                 }
712         }
713
714         ret = do_load_solid_info(wim, rdescs, num_rdescs, entries);
715         if (ret)
716                 goto out_free_rdescs;
717
718         wim->refcnt += num_rdescs;
719
720         *rdescs_ret = rdescs;
721         *num_rdescs_ret = num_rdescs;
722         return 0;
723
724 out_free_rdescs:
725         for (i = 0; i < num_rdescs; i++)
726                 FREE(rdescs[i]);
727         FREE(rdescs);
728         return ret;
729 }
730
731 /* Given a 'struct blob_descriptor' allocated for an on-disk blob descriptor
732  * with the SOLID flag set, try to assign it to resource in the current solid
733  * run.  */
734 static int
735 assign_blob_to_solid_resource(const struct wim_reshdr *reshdr,
736                               struct blob_descriptor *blob,
737                               struct wim_resource_descriptor **rdescs,
738                               size_t num_rdescs)
739 {
740         u64 offset = reshdr->offset_in_wim;
741
742         /* XXX: This linear search will be slow in the degenerate case where the
743          * number of solid resources in the run is huge.  */
744         blob->size = reshdr->size_in_wim;
745         for (size_t i = 0; i < num_rdescs; i++) {
746                 if (offset + blob->size <= rdescs[i]->uncompressed_size) {
747                         blob_set_is_located_in_wim_resource(blob, rdescs[i], offset);
748                         return 0;
749                 }
750                 offset -= rdescs[i]->uncompressed_size;
751         }
752         ERROR("blob could not be assigned to a solid resource");
753         return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
754 }
755
756 static void
757 free_solid_rdescs(struct wim_resource_descriptor **rdescs, size_t num_rdescs)
758 {
759         if (rdescs) {
760                 for (size_t i = 0; i < num_rdescs; i++) {
761                         if (list_empty(&rdescs[i]->blob_list)) {
762                                 rdescs[i]->wim->refcnt--;
763                                 FREE(rdescs[i]);
764                         }
765                 }
766                 FREE(rdescs);
767         }
768 }
769
770 static int
771 cmp_blobs_by_offset_in_res(const void *p1, const void *p2)
772 {
773         const struct blob_descriptor *blob1, *blob2;
774
775         blob1 = *(const struct blob_descriptor**)p1;
776         blob2 = *(const struct blob_descriptor**)p2;
777
778         return cmp_u64(blob1->offset_in_res, blob2->offset_in_res);
779 }
780
781 /* Validate the size and location of a WIM resource.  */
782 static int
783 validate_resource(struct wim_resource_descriptor *rdesc)
784 {
785         struct blob_descriptor *blob;
786         bool out_of_order;
787         u64 expected_next_offset;
788         int ret;
789
790         /* Verify that the resource itself has a valid offset and size.  */
791         if (rdesc->offset_in_wim + rdesc->size_in_wim < rdesc->size_in_wim)
792                 goto invalid_due_to_overflow;
793
794         /* Verify that each blob in the resource has a valid offset and size.
795          */
796         expected_next_offset = 0;
797         out_of_order = false;
798         list_for_each_entry(blob, &rdesc->blob_list, rdesc_node) {
799                 if (blob->offset_in_res + blob->size < blob->size ||
800                     blob->offset_in_res + blob->size > rdesc->uncompressed_size)
801                         goto invalid_due_to_overflow;
802
803                 if (blob->offset_in_res >= expected_next_offset)
804                         expected_next_offset = blob->offset_in_res + blob->size;
805                 else
806                         out_of_order = true;
807         }
808
809         /* If the blobs were not located at strictly increasing positions (not
810          * allowing for overlap), sort them.  Then make sure that none overlap.
811          */
812         if (out_of_order) {
813                 ret = sort_blob_list(&rdesc->blob_list,
814                                      offsetof(struct blob_descriptor,
815                                               rdesc_node),
816                                      cmp_blobs_by_offset_in_res);
817                 if (ret)
818                         return ret;
819
820                 expected_next_offset = 0;
821                 list_for_each_entry(blob, &rdesc->blob_list, rdesc_node) {
822                         if (blob->offset_in_res >= expected_next_offset)
823                                 expected_next_offset = blob->offset_in_res + blob->size;
824                         else
825                                 goto invalid_due_to_overlap;
826                 }
827         }
828
829         return 0;
830
831 invalid_due_to_overflow:
832         ERROR("Invalid blob table (offset overflow)");
833         return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
834
835 invalid_due_to_overlap:
836         ERROR("Invalid blob table (blobs in solid resource overlap)");
837         return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
838 }
839
840 static int
841 finish_solid_rdescs(struct wim_resource_descriptor **rdescs, size_t num_rdescs)
842 {
843         int ret = 0;
844         for (size_t i = 0; i < num_rdescs; i++) {
845                 ret = validate_resource(rdescs[i]);
846                 if (ret)
847                         break;
848         }
849         free_solid_rdescs(rdescs, num_rdescs);
850         return ret;
851 }
852
853 /*
854  * read_blob_table() -
855  *
856  * Read the blob table from a WIM file.  Usually, each entry in this table
857  * describes a "blob", or equivalently a "resource", that the WIM file contains,
858  * along with its location and SHA-1 message digest.  Descriptors for
859  * non-metadata blobs will be saved in the in-memory blob table
860  * (wim->blob_table), whereas descriptors for metadata blobs will be saved in a
861  * special location per-image (the wim->image_metadata array).
862  *
863  * However, in WIM_VERSION_SOLID (3584) WIMs, a resource may contain multiple
864  * blobs that are compressed together.  Such a resource is called a "solid
865  * resource".  Solid resources are still described in the on-disk "blob table",
866  * although the format is not the most logical.  A consecutive sequence of
867  * entries that all have flag WIM_RESHDR_FLAG_SOLID (0x10) set is a "solid run".
868  * A solid run describes a set of solid resources, each of which contains a set
869  * of blobs.  In a solid run, a 'struct wim_reshdr_disk' with 'uncompressed_size
870  * = SOLID_RESOURCE_MAGIC_NUMBER (0x100000000)' specifies a solid resource,
871  * whereas any other 'struct wim_reshdr_disk' specifies a blob within a solid
872  * resource.  There are some oddities in how we need to determine which solid
873  * resource a blob is actually in; see the code for details.
874  *
875  * Possible return values:
876  *      WIMLIB_ERR_SUCCESS (0)
877  *      WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY
878  *      WIMLIB_ERR_NOMEM
879  *
880  *      Or an error code caused by failure to read the blob table from the WIM
881  *      file.
882  */
883 int
884 read_blob_table(WIMStruct *wim)
885 {
886         int ret;
887         size_t num_entries;
888         void *buf = NULL;
889         struct blob_table *table = NULL;
890         struct blob_descriptor *cur_blob = NULL;
891         size_t num_duplicate_blobs = 0;
892         size_t num_empty_blobs = 0;
893         size_t num_wrong_part_blobs = 0;
894         u32 image_index = 0;
895         struct wim_resource_descriptor **cur_solid_rdescs = NULL;
896         size_t cur_num_solid_rdescs = 0;
897
898         /* Calculate the number of entries in the blob table.  */
899         num_entries = wim->hdr.blob_table_reshdr.uncompressed_size /
900                       sizeof(struct blob_descriptor_disk);
901
902         /* Read the blob table into a buffer.  */
903         ret = wim_reshdr_to_data(&wim->hdr.blob_table_reshdr, wim, &buf);
904         if (ret)
905                 goto out;
906
907         /* Allocate a hash table to map SHA-1 message digests into blob
908          * descriptors.  This is the in-memory "blob table".  */
909         table = new_blob_table(num_entries);
910         if (!table)
911                 goto oom;
912
913         /* Allocate and initalize blob descriptors from the raw blob table
914          * buffer.  */
915         for (size_t i = 0; i < num_entries; i++) {
916                 const struct blob_descriptor_disk *disk_entry =
917                         &((const struct blob_descriptor_disk*)buf)[i];
918                 struct wim_reshdr reshdr;
919                 u16 part_number;
920
921                 /* Get the resource header  */
922                 get_wim_reshdr(&disk_entry->reshdr, &reshdr);
923
924                 /* Ignore SOLID flag if it isn't supposed to be used in this WIM
925                  * version.  */
926                 if (wim->hdr.wim_version == WIM_VERSION_DEFAULT)
927                         reshdr.flags &= ~WIM_RESHDR_FLAG_SOLID;
928
929                 /* Allocate a new 'struct blob_descriptor'.  */
930                 cur_blob = new_blob_descriptor();
931                 if (!cur_blob)
932                         goto oom;
933
934                 /* Get the part number, reference count, and hash.  */
935                 part_number = le16_to_cpu(disk_entry->part_number);
936                 cur_blob->refcnt = le32_to_cpu(disk_entry->refcnt);
937                 copy_hash(cur_blob->hash, disk_entry->hash);
938
939                 if (reshdr.flags & WIM_RESHDR_FLAG_SOLID) {
940
941                         /* SOLID entry  */
942
943                         if (!cur_solid_rdescs) {
944                                 /* Starting new run  */
945                                 ret = load_solid_info(wim, disk_entry,
946                                                       num_entries - i,
947                                                       &cur_solid_rdescs,
948                                                       &cur_num_solid_rdescs);
949                                 if (ret)
950                                         goto out;
951                         }
952
953                         if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) {
954                                 /* Resource entry, not blob entry  */
955                                 goto free_cur_blob_and_continue;
956                         }
957
958                         /* Blob entry  */
959
960                         ret = assign_blob_to_solid_resource(&reshdr,
961                                                             cur_blob,
962                                                             cur_solid_rdescs,
963                                                             cur_num_solid_rdescs);
964                         if (ret)
965                                 goto out;
966
967                 } else {
968                         /* Normal blob/resource entry; SOLID not set.  */
969
970                         struct wim_resource_descriptor *rdesc;
971
972                         if (unlikely(cur_solid_rdescs)) {
973                                 /* This entry terminated a solid run.  */
974                                 ret = finish_solid_rdescs(cur_solid_rdescs,
975                                                           cur_num_solid_rdescs);
976                                 cur_solid_rdescs = NULL;
977                                 if (ret)
978                                         goto out;
979                         }
980
981                         if (unlikely(!(reshdr.flags & WIM_RESHDR_FLAG_COMPRESSED) &&
982                                      (reshdr.size_in_wim != reshdr.uncompressed_size)))
983                         {
984                                 ERROR("Uncompressed resource has "
985                                       "size_in_wim != uncompressed_size");
986                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
987                                 goto out;
988                         }
989
990                         /* Set up a resource descriptor for this blob.  */
991
992                         rdesc = MALLOC(sizeof(struct wim_resource_descriptor));
993                         if (!rdesc)
994                                 goto oom;
995
996                         wim_reshdr_to_desc_and_blob(&reshdr, wim, rdesc, cur_blob);
997                         wim->refcnt++;
998                 }
999
1000                 /* cur_blob is now a blob bound to a resource.  */
1001
1002                 /* Ignore entries with all zeroes in the hash field.  */
1003                 if (unlikely(is_zero_hash(cur_blob->hash)))
1004                         goto free_cur_blob_and_continue;
1005
1006                 /* Verify that the blob has nonzero size.  */
1007                 if (unlikely(cur_blob->size == 0)) {
1008                         num_empty_blobs++;
1009                         goto free_cur_blob_and_continue;
1010                 }
1011
1012                 /* Verify that the part number matches that of the underlying
1013                  * WIM file.  */
1014                 if (unlikely(part_number != wim->hdr.part_number)) {
1015                         num_wrong_part_blobs++;
1016                         goto free_cur_blob_and_continue;
1017                 }
1018
1019                 if (reshdr.flags & WIM_RESHDR_FLAG_METADATA) {
1020
1021                         cur_blob->is_metadata = 1;
1022
1023                         /* Blob table entry for a metadata resource.  */
1024
1025                         /* Metadata entries with no references must be ignored.
1026                          * See, for example, the WinPE WIMs from the WAIK v2.1.
1027                          */
1028                         if (cur_blob->refcnt == 0)
1029                                 goto free_cur_blob_and_continue;
1030
1031                         if (cur_blob->refcnt != 1) {
1032                                 /* We don't currently support this case due to
1033                                  * the complications of multiple images sharing
1034                                  * the same metadata resource or a metadata
1035                                  * resource also being referenced by files.  */
1036                                 ERROR("Found metadata resource with refcnt != 1");
1037                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
1038                                 goto out;
1039                         }
1040
1041                         if (reshdr.flags & WIM_RESHDR_FLAG_SOLID) {
1042                                 ERROR("Image metadata in solid resources "
1043                                       "is unsupported.");
1044                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
1045                                 goto out;
1046                         }
1047
1048                         if (wim->hdr.part_number != 1) {
1049                                 WARNING("Ignoring metadata resource found in a "
1050                                         "non-first part of the split WIM");
1051                                 goto free_cur_blob_and_continue;
1052                         }
1053
1054                         /* The number of entries in the blob table with
1055                          * WIM_RESHDR_FLAG_METADATA set should be the same as
1056                          * the image_count field in the WIM header.  */
1057                         if (image_index == wim->hdr.image_count) {
1058                                 WARNING("Found more metadata resources than images");
1059                                 goto free_cur_blob_and_continue;
1060                         }
1061
1062                         /* Notice very carefully:  We are assigning the metadata
1063                          * resources to images in the same order in which their
1064                          * blob table entries occur on disk.  (This is also the
1065                          * behavior of Microsoft's software.)  In particular,
1066                          * this overrides the actual locations of the metadata
1067                          * resources themselves in the WIM file as well as any
1068                          * information written in the XML data.  */
1069                         wim->image_metadata[image_index++]->metadata_blob = cur_blob;
1070                 } else {
1071                         /* Blob table entry for a non-metadata blob.  */
1072
1073                         /* Ignore this blob if it's a duplicate.  */
1074                         if (lookup_blob(table, cur_blob->hash)) {
1075                                 num_duplicate_blobs++;
1076                                 goto free_cur_blob_and_continue;
1077                         }
1078
1079                         /* Insert the blob into the in-memory blob table, keyed
1080                          * by its SHA-1 message digest.  */
1081                         blob_table_insert(table, cur_blob);
1082                 }
1083
1084                 continue;
1085
1086         free_cur_blob_and_continue:
1087                 if (cur_solid_rdescs &&
1088                     cur_blob->blob_location == BLOB_IN_WIM)
1089                         blob_unset_is_located_in_wim_resource(cur_blob);
1090                 free_blob_descriptor(cur_blob);
1091         }
1092         cur_blob = NULL;
1093
1094         if (cur_solid_rdescs) {
1095                 /* End of blob table terminated a solid run.  */
1096                 ret = finish_solid_rdescs(cur_solid_rdescs, cur_num_solid_rdescs);
1097                 cur_solid_rdescs = NULL;
1098                 if (ret)
1099                         goto out;
1100         }
1101
1102         if (wim->hdr.part_number == 1 && image_index != wim->hdr.image_count) {
1103                 WARNING("Could not find metadata resources for all images");
1104                 for (u32 i = image_index; i < wim->hdr.image_count; i++)
1105                         put_image_metadata(wim->image_metadata[i], NULL);
1106                 wim->hdr.image_count = image_index;
1107         }
1108
1109         if (num_duplicate_blobs > 0)
1110                 WARNING("Ignoring %zu duplicate blobs", num_duplicate_blobs);
1111
1112         if (num_empty_blobs > 0)
1113                 WARNING("Ignoring %zu empty blobs", num_empty_blobs);
1114
1115         if (num_wrong_part_blobs > 0) {
1116                 WARNING("Ignoring %zu blobs with wrong part number",
1117                         num_wrong_part_blobs);
1118         }
1119
1120         wim->blob_table = table;
1121         ret = 0;
1122         goto out_free_buf;
1123
1124 oom:
1125         ERROR("Not enough memory to read blob table!");
1126         ret = WIMLIB_ERR_NOMEM;
1127 out:
1128         free_solid_rdescs(cur_solid_rdescs, cur_num_solid_rdescs);
1129         free_blob_descriptor(cur_blob);
1130         free_blob_table(table);
1131 out_free_buf:
1132         FREE(buf);
1133         return ret;
1134 }
1135
1136 static void
1137 write_blob_descriptor(struct blob_descriptor_disk *disk_entry,
1138                       const struct wim_reshdr *out_reshdr,
1139                       u16 part_number, u32 refcnt, const u8 *hash)
1140 {
1141         put_wim_reshdr(out_reshdr, &disk_entry->reshdr);
1142         disk_entry->part_number = cpu_to_le16(part_number);
1143         disk_entry->refcnt = cpu_to_le32(refcnt);
1144         copy_hash(disk_entry->hash, hash);
1145 }
1146
1147 /* Note: the list of blob descriptors must be sorted so that all entries for the
1148  * same solid resource are consecutive.  In addition, blob descriptors for
1149  * metadata resources must be in the same order as the indices of the underlying
1150  * images.  */
1151 int
1152 write_blob_table_from_blob_list(struct list_head *blob_list,
1153                                 struct filedes *out_fd,
1154                                 u16 part_number,
1155                                 struct wim_reshdr *out_reshdr,
1156                                 int write_resource_flags)
1157 {
1158         size_t table_size;
1159         struct blob_descriptor *blob;
1160         struct blob_descriptor_disk *table_buf;
1161         struct blob_descriptor_disk *table_buf_ptr;
1162         int ret;
1163         u64 prev_res_offset_in_wim = ~0ULL;
1164         u64 prev_uncompressed_size;
1165         u64 logical_offset;
1166
1167         table_size = 0;
1168         list_for_each_entry(blob, blob_list, blob_table_list) {
1169                 table_size += sizeof(struct blob_descriptor_disk);
1170
1171                 if (blob->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID &&
1172                     blob->out_res_offset_in_wim != prev_res_offset_in_wim)
1173                 {
1174                         table_size += sizeof(struct blob_descriptor_disk);
1175                         prev_res_offset_in_wim = blob->out_res_offset_in_wim;
1176                 }
1177         }
1178
1179         table_buf = MALLOC(table_size);
1180         if (table_buf == NULL) {
1181                 ERROR("Failed to allocate %zu bytes for temporary blob table",
1182                       table_size);
1183                 return WIMLIB_ERR_NOMEM;
1184         }
1185         table_buf_ptr = table_buf;
1186
1187         prev_res_offset_in_wim = ~0ULL;
1188         prev_uncompressed_size = 0;
1189         logical_offset = 0;
1190         list_for_each_entry(blob, blob_list, blob_table_list) {
1191                 if (blob->out_reshdr.flags & WIM_RESHDR_FLAG_SOLID) {
1192                         struct wim_reshdr tmp_reshdr;
1193
1194                         /* Eww.  When WIMGAPI sees multiple solid resources, it
1195                          * expects the offsets to be adjusted as if there were
1196                          * really only one solid resource.  */
1197
1198                         if (blob->out_res_offset_in_wim != prev_res_offset_in_wim) {
1199                                 /* Put the resource entry for solid resource  */
1200                                 tmp_reshdr.offset_in_wim = blob->out_res_offset_in_wim;
1201                                 tmp_reshdr.size_in_wim = blob->out_res_size_in_wim;
1202                                 tmp_reshdr.uncompressed_size = SOLID_RESOURCE_MAGIC_NUMBER;
1203                                 tmp_reshdr.flags = WIM_RESHDR_FLAG_SOLID;
1204
1205                                 write_blob_descriptor(table_buf_ptr++, &tmp_reshdr,
1206                                                       part_number, 1, zero_hash);
1207
1208                                 logical_offset += prev_uncompressed_size;
1209
1210                                 prev_res_offset_in_wim = blob->out_res_offset_in_wim;
1211                                 prev_uncompressed_size = blob->out_res_uncompressed_size;
1212                         }
1213                         tmp_reshdr = blob->out_reshdr;
1214                         tmp_reshdr.offset_in_wim += logical_offset;
1215                         write_blob_descriptor(table_buf_ptr++, &tmp_reshdr,
1216                                               part_number, blob->out_refcnt, blob->hash);
1217                 } else {
1218                         write_blob_descriptor(table_buf_ptr++, &blob->out_reshdr,
1219                                               part_number, blob->out_refcnt, blob->hash);
1220                 }
1221
1222         }
1223         wimlib_assert((u8*)table_buf_ptr - (u8*)table_buf == table_size);
1224
1225         /* Write the blob table uncompressed.  Although wimlib can handle a
1226          * compressed blob table, MS software cannot.  */
1227         ret = write_wim_resource_from_buffer(table_buf,
1228                                              table_size,
1229                                              true,
1230                                              out_fd,
1231                                              WIMLIB_COMPRESSION_TYPE_NONE,
1232                                              0,
1233                                              out_reshdr,
1234                                              NULL,
1235                                              write_resource_flags);
1236         FREE(table_buf);
1237         return ret;
1238 }
1239
1240 /* Allocate a blob descriptor for the contents of the buffer, or re-use an
1241  * existing descriptor in @blob_table for an identical blob.  */
1242 struct blob_descriptor *
1243 new_blob_from_data_buffer(const void *buffer, size_t size,
1244                           struct blob_table *blob_table)
1245 {
1246         u8 hash[SHA1_HASH_SIZE];
1247         struct blob_descriptor *blob;
1248         void *buffer_copy;
1249
1250         sha1_buffer(buffer, size, hash);
1251
1252         blob = lookup_blob(blob_table, hash);
1253         if (blob)
1254                 return blob;
1255
1256         blob = new_blob_descriptor();
1257         if (!blob)
1258                 return NULL;
1259
1260         buffer_copy = memdup(buffer, size);
1261         if (!buffer_copy) {
1262                 free_blob_descriptor(blob);
1263                 return NULL;
1264         }
1265         blob_set_is_located_in_attached_buffer(blob, buffer_copy, size);
1266         copy_hash(blob->hash, hash);
1267         blob_table_insert(blob_table, blob);
1268         return blob;
1269 }
1270
1271 struct blob_descriptor *
1272 after_blob_hashed(struct blob_descriptor *blob,
1273                   struct blob_descriptor **back_ptr,
1274                   struct blob_table *blob_table)
1275 {
1276         struct blob_descriptor *duplicate_blob;
1277
1278         list_del(&blob->unhashed_list);
1279         blob->unhashed = 0;
1280
1281         /* Look for a duplicate blob  */
1282         duplicate_blob = lookup_blob(blob_table, blob->hash);
1283         if (duplicate_blob) {
1284                 /* We have a duplicate blob.  Transfer the reference counts from
1285                  * this blob to the duplicate and update the reference to this
1286                  * blob (from a stream) to point to the duplicate.  The caller
1287                  * is responsible for freeing @blob if needed.  */
1288                 wimlib_assert(duplicate_blob->size == blob->size);
1289                 duplicate_blob->refcnt += blob->refcnt;
1290                 blob->refcnt = 0;
1291                 *back_ptr = duplicate_blob;
1292                 return duplicate_blob;
1293         } else {
1294                 /* No duplicate blob, so we need to insert this blob into the
1295                  * blob table and treat it as a hashed blob.  */
1296                 blob_table_insert(blob_table, blob);
1297                 return blob;
1298         }
1299 }
1300
1301 /*
1302  * Calculate the SHA-1 message digest of a blob and move its descriptor from the
1303  * list of unhashed blobs to the blob table, possibly joining it with an
1304  * identical blob.
1305  *
1306  * @blob:
1307  *      The blob to hash
1308  * @blob_table:
1309  *      The blob table in which the blob needs to be indexed
1310  * @blob_ret:
1311  *      On success, a pointer to the resulting blob descriptor is written to
1312  *      this location.  This will be the same as @blob if it was inserted into
1313  *      the blob table, or different if a duplicate blob was found.
1314  *
1315  * Returns 0 on success; nonzero if there is an error reading the blob data.
1316  */
1317 int
1318 hash_unhashed_blob(struct blob_descriptor *blob, struct blob_table *blob_table,
1319                    struct blob_descriptor **blob_ret)
1320 {
1321         struct blob_descriptor **back_ptr;
1322         int ret;
1323
1324         back_ptr = retrieve_pointer_to_unhashed_blob(blob);
1325
1326         ret = sha1_blob(blob);
1327         if (ret)
1328                 return ret;
1329
1330         *blob_ret = after_blob_hashed(blob, back_ptr, blob_table);
1331         return 0;
1332 }
1333
1334 void
1335 blob_to_wimlib_resource_entry(const struct blob_descriptor *blob,
1336                               struct wimlib_resource_entry *wentry)
1337 {
1338         memset(wentry, 0, sizeof(*wentry));
1339
1340         wentry->uncompressed_size = blob->size;
1341         if (blob->blob_location == BLOB_IN_WIM) {
1342                 unsigned res_flags = blob->rdesc->flags;
1343
1344                 wentry->part_number = blob->rdesc->wim->hdr.part_number;
1345                 if (res_flags & WIM_RESHDR_FLAG_SOLID) {
1346                         wentry->offset = blob->offset_in_res;
1347                 } else {
1348                         wentry->compressed_size = blob->rdesc->size_in_wim;
1349                         wentry->offset = blob->rdesc->offset_in_wim;
1350                 }
1351                 wentry->raw_resource_offset_in_wim = blob->rdesc->offset_in_wim;
1352                 wentry->raw_resource_compressed_size = blob->rdesc->size_in_wim;
1353                 wentry->raw_resource_uncompressed_size = blob->rdesc->uncompressed_size;
1354
1355                 wentry->is_compressed = (res_flags & WIM_RESHDR_FLAG_COMPRESSED) != 0;
1356                 wentry->is_free = (res_flags & WIM_RESHDR_FLAG_FREE) != 0;
1357                 wentry->is_spanned = (res_flags & WIM_RESHDR_FLAG_SPANNED) != 0;
1358                 wentry->packed = (res_flags & WIM_RESHDR_FLAG_SOLID) != 0;
1359         }
1360         if (!blob->unhashed)
1361                 copy_hash(wentry->sha1_hash, blob->hash);
1362         wentry->reference_count = blob->refcnt;
1363         wentry->is_metadata = blob->is_metadata;
1364 }
1365
1366 struct iterate_blob_context {
1367         wimlib_iterate_lookup_table_callback_t cb;
1368         void *user_ctx;
1369 };
1370
1371 static int
1372 do_iterate_blob(struct blob_descriptor *blob, void *_ctx)
1373 {
1374         struct iterate_blob_context *ctx = _ctx;
1375         struct wimlib_resource_entry entry;
1376
1377         blob_to_wimlib_resource_entry(blob, &entry);
1378         return (*ctx->cb)(&entry, ctx->user_ctx);
1379 }
1380
1381 /* API function documented in wimlib.h  */
1382 WIMLIBAPI int
1383 wimlib_iterate_lookup_table(WIMStruct *wim, int flags,
1384                             wimlib_iterate_lookup_table_callback_t cb,
1385                             void *user_ctx)
1386 {
1387         if (flags != 0)
1388                 return WIMLIB_ERR_INVALID_PARAM;
1389
1390         struct iterate_blob_context ctx = {
1391                 .cb = cb,
1392                 .user_ctx = user_ctx,
1393         };
1394         if (wim_has_metadata(wim)) {
1395                 int ret;
1396                 for (int i = 0; i < wim->hdr.image_count; i++) {
1397                         struct blob_descriptor *blob;
1398                         struct wim_image_metadata *imd = wim->image_metadata[i];
1399
1400                         ret = do_iterate_blob(imd->metadata_blob, &ctx);
1401                         if (ret)
1402                                 return ret;
1403                         image_for_each_unhashed_blob(blob, imd) {
1404                                 ret = do_iterate_blob(blob, &ctx);
1405                                 if (ret)
1406                                         return ret;
1407                         }
1408                 }
1409         }
1410         return for_blob_in_table(wim->blob_table, do_iterate_blob, &ctx);
1411 }