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