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