]> wimlib.net Git - wimlib/blob - src/lookup_table.c
Initial rewrite of resource code
[wimlib] / src / lookup_table.c
1 /*
2  * lookup_table.c
3  *
4  * Lookup table, implemented as a hash table, that maps SHA1 message digests to
5  * data streams.
6  */
7
8 /*
9  * Copyright (C) 2012, 2013 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27 #include "wimlib_internal.h"
28 #include "lookup_table.h"
29 #include "buffer_io.h"
30 #include <errno.h>
31 #include <stdlib.h>
32
33 #ifdef WITH_FUSE
34 #include <unistd.h>
35 #endif
36
37 struct wim_lookup_table *
38 new_lookup_table(size_t capacity)
39 {
40         struct wim_lookup_table *table;
41         struct hlist_head *array;
42
43         table = CALLOC(1, sizeof(struct wim_lookup_table));
44         if (table) {
45                 array = CALLOC(capacity, sizeof(array[0]));
46                 if (array) {
47                         table->num_entries = 0;
48                         table->capacity = capacity;
49                         table->array = array;
50                 } else {
51                         FREE(table);
52                         table = NULL;
53                         ERROR("Failed to allocate memory for lookup table with capacity %zu",
54                               capacity);
55                 }
56         }
57         return table;
58 }
59
60 struct wim_lookup_table_entry *
61 new_lookup_table_entry()
62 {
63         struct wim_lookup_table_entry *lte;
64
65         lte = CALLOC(1, sizeof(struct wim_lookup_table_entry));
66         if (lte) {
67                 lte->part_number  = 1;
68                 lte->refcnt       = 1;
69         } else {
70                 ERROR("Out of memory (tried to allocate %zu bytes for "
71                       "lookup table entry)",
72                       sizeof(struct wim_lookup_table_entry));
73         }
74         return lte;
75 }
76
77 struct wim_lookup_table_entry *
78 clone_lookup_table_entry(const struct wim_lookup_table_entry *old)
79 {
80         struct wim_lookup_table_entry *new;
81
82         new = MALLOC(sizeof(*new));
83         if (!new)
84                 return NULL;
85
86         memcpy(new, old, sizeof(*old));
87         new->extracted_file = NULL;
88         switch (new->resource_location) {
89 #ifdef __WIN32__
90         case RESOURCE_WIN32:
91 #endif
92         case RESOURCE_IN_STAGING_FILE:
93         case RESOURCE_IN_FILE_ON_DISK:
94                 BUILD_BUG_ON((void*)&old->file_on_disk !=
95                              (void*)&old->staging_file_name);
96                 new->staging_file_name = TSTRDUP(old->staging_file_name);
97                 if (!new->staging_file_name)
98                         goto out_free;
99                 break;
100         case RESOURCE_IN_ATTACHED_BUFFER:
101                 new->attached_buffer = MALLOC(wim_resource_size(old));
102                 if (!new->attached_buffer)
103                         goto out_free;
104                 memcpy(new->attached_buffer, old->attached_buffer,
105                        wim_resource_size(old));
106                 break;
107 #ifdef WITH_NTFS_3G
108         case RESOURCE_IN_NTFS_VOLUME:
109                 if (old->ntfs_loc) {
110                         struct ntfs_location *loc;
111                         loc = MALLOC(sizeof(*loc));
112                         if (!loc)
113                                 goto out_free;
114                         memcpy(loc, old->ntfs_loc, sizeof(*loc));
115                         loc->path = NULL;
116                         loc->stream_name = NULL;
117                         new->ntfs_loc = loc;
118                         loc->path = STRDUP(old->ntfs_loc->path);
119                         if (!loc->path)
120                                 goto out_free;
121                         loc->stream_name = MALLOC((loc->stream_name_nchars + 1) * 2);
122                         if (!loc->stream_name)
123                                 goto out_free;
124                         memcpy(loc->stream_name,
125                                old->ntfs_loc->stream_name,
126                                (loc->stream_name_nchars + 1) * 2);
127                 }
128                 break;
129 #endif
130         default:
131                 break;
132         }
133         return new;
134 out_free:
135         free_lookup_table_entry(new);
136         return NULL;
137 }
138
139 void
140 free_lookup_table_entry(struct wim_lookup_table_entry *lte)
141 {
142         if (lte) {
143                 switch (lte->resource_location) {
144                 case RESOURCE_IN_STAGING_FILE:
145                 case RESOURCE_IN_ATTACHED_BUFFER:
146                 case RESOURCE_IN_FILE_ON_DISK:
147 #ifdef __WIN32__
148                 case RESOURCE_WIN32:
149 #endif
150                         BUILD_BUG_ON((void*)&lte->file_on_disk !=
151                                      (void*)&lte->staging_file_name);
152                         BUILD_BUG_ON((void*)&lte->file_on_disk !=
153                                      (void*)&lte->attached_buffer);
154                         FREE(lte->file_on_disk);
155                         break;
156 #ifdef WITH_NTFS_3G
157                 case RESOURCE_IN_NTFS_VOLUME:
158                         if (lte->ntfs_loc) {
159                                 FREE(lte->ntfs_loc->path);
160                                 FREE(lte->ntfs_loc->stream_name);
161                                 FREE(lte->ntfs_loc);
162                         }
163                         break;
164 #endif
165                 default:
166                         break;
167                 }
168                 FREE(lte);
169         }
170 }
171
172 static int
173 do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, void *ignore)
174 {
175         free_lookup_table_entry(entry);
176         return 0;
177 }
178
179
180 void
181 free_lookup_table(struct wim_lookup_table *table)
182 {
183         DEBUG2("Freeing lookup table");
184         if (table) {
185                 if (table->array) {
186                         for_lookup_table_entry(table,
187                                                do_free_lookup_table_entry,
188                                                NULL);
189                         FREE(table->array);
190                 }
191                 FREE(table);
192         }
193 }
194
195 /*
196  * Inserts an entry into the lookup table.
197  *
198  * @table:      A pointer to the lookup table.
199  * @lte:        A pointer to the entry to insert.
200  */
201 void
202 lookup_table_insert(struct wim_lookup_table *table,
203                     struct wim_lookup_table_entry *lte)
204 {
205         size_t i = lte->hash_short % table->capacity;
206         hlist_add_head(&lte->hash_list, &table->array[i]);
207
208         /* XXX Make the table grow when too many entries have been inserted. */
209         table->num_entries++;
210 }
211
212 static void
213 finalize_lte(struct wim_lookup_table_entry *lte)
214 {
215         #ifdef WITH_FUSE
216         if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
217                 unlink(lte->staging_file_name);
218                 list_del(&lte->staging_list);
219         }
220         #endif
221         free_lookup_table_entry(lte);
222 }
223
224 /* Decrements the reference count for the lookup table entry @lte.  If its
225  * reference count reaches 0, it is unlinked from the lookup table.  If,
226  * furthermore, the entry has no opened file descriptors associated with it, the
227  * entry is freed.  */
228 void
229 lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
230                      struct wim_lookup_table *table)
231 {
232         wimlib_assert(lte != NULL);
233         wimlib_assert(lte->refcnt != 0);
234         if (--lte->refcnt == 0) {
235                 lookup_table_unlink(table, lte);
236         #ifdef WITH_FUSE
237                 if (lte->num_opened_fds == 0)
238         #endif
239                         finalize_lte(lte);
240         }
241 }
242
243 #ifdef WITH_FUSE
244 void
245 lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte)
246 {
247         if (lte->num_opened_fds != 0)
248                 if (--lte->num_opened_fds == 0 && lte->refcnt == 0)
249                         finalize_lte(lte);
250 }
251 #endif
252
253 /* Calls a function on all the entries in the WIM lookup table.  Stop early and
254  * return nonzero if any call to the function returns nonzero. */
255 int
256 for_lookup_table_entry(struct wim_lookup_table *table,
257                        int (*visitor)(struct wim_lookup_table_entry *, void *),
258                        void *arg)
259 {
260         struct wim_lookup_table_entry *lte;
261         struct hlist_node *pos, *tmp;
262         int ret;
263
264         for (size_t i = 0; i < table->capacity; i++) {
265                 hlist_for_each_entry_safe(lte, pos, tmp, &table->array[i],
266                                           hash_list)
267                 {
268                         wimlib_assert2(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA));
269                         ret = visitor(lte, arg);
270                         if (ret != 0)
271                                 return ret;
272                 }
273         }
274         return 0;
275 }
276
277 static int
278 cmp_streams_by_wim_position(const void *p1, const void *p2)
279 {
280         const struct wim_lookup_table_entry *lte1, *lte2;
281         lte1 = *(const struct wim_lookup_table_entry**)p1;
282         lte2 = *(const struct wim_lookup_table_entry**)p2;
283         if (lte1->resource_entry.offset < lte2->resource_entry.offset)
284                 return -1;
285         else if (lte1->resource_entry.offset > lte2->resource_entry.offset)
286                 return 1;
287         else
288                 return 0;
289 }
290
291 int
292 sort_stream_list_by_wim_position(struct list_head *stream_list)
293 {
294         struct list_head *cur;
295         size_t num_streams;
296         struct wim_lookup_table_entry **array;
297         size_t i;
298         size_t array_size;
299
300         num_streams = 0;
301         list_for_each(cur, stream_list)
302                 num_streams++;
303         array_size = num_streams * sizeof(array[0]);
304         array = MALLOC(array_size);
305         if (!array) {
306                 ERROR("Failed to allocate %zu bytes to sort stream entries",
307                       array_size);
308                 return WIMLIB_ERR_NOMEM;
309         }
310         cur = stream_list->next;
311         for (i = 0; i < num_streams; i++) {
312                 array[i] = container_of(cur, struct wim_lookup_table_entry, staging_list);
313                 cur = cur->next;
314         }
315
316         qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
317
318         INIT_LIST_HEAD(stream_list);
319         for (i = 0; i < num_streams; i++)
320                 list_add_tail(&array[i]->staging_list, stream_list);
321         FREE(array);
322         return 0;
323 }
324
325
326 static int
327 add_lte_to_array(struct wim_lookup_table_entry *lte,
328                  void *_pp)
329 {
330         struct wim_lookup_table_entry ***pp = _pp;
331         *(*pp)++ = lte;
332         return 0;
333 }
334
335 /* Iterate through the lookup table entries, but first sort them by stream
336  * offset in the WIM.  Caution: this is intended to be used when the stream
337  * offset field has actually been set. */
338 int
339 for_lookup_table_entry_pos_sorted(struct wim_lookup_table *table,
340                                   int (*visitor)(struct wim_lookup_table_entry *,
341                                                  void *),
342                                   void *arg)
343 {
344         struct wim_lookup_table_entry **lte_array, **p;
345         size_t num_streams = table->num_entries;
346         int ret;
347
348         lte_array = MALLOC(num_streams * sizeof(lte_array[0]));
349         if (!lte_array)
350                 return WIMLIB_ERR_NOMEM;
351         p = lte_array;
352         for_lookup_table_entry(table, add_lte_to_array, &p);
353
354         wimlib_assert(p == lte_array + num_streams);
355
356         qsort(lte_array, num_streams, sizeof(lte_array[0]),
357               cmp_streams_by_wim_position);
358         ret = 0;
359         for (size_t i = 0; i < num_streams && ret == 0; i++)
360                 ret = visitor(lte_array[i], arg);
361         FREE(lte_array);
362         return ret;
363 }
364
365 /*
366  * Reads the lookup table from a WIM file.
367  *
368  * Saves lookup table entries for non-metadata streams in a hash table, and
369  * saves the metadata entry for each image in a special per-image location (the
370  * image_metadata array).
371  */
372 int
373 read_lookup_table(WIMStruct *w)
374 {
375         u64 num_entries;
376         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
377         int ret;
378         struct wim_lookup_table *table;
379         struct wim_lookup_table_entry *cur_entry, *duplicate_entry;
380
381         if (resource_is_compressed(&w->hdr.lookup_table_res_entry)) {
382                 ERROR("Didn't expect a compressed lookup table!");
383                 ERROR("Ask the author to implement support for this.");
384                 return WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE;
385         }
386
387         DEBUG("Reading lookup table: offset %"PRIu64", size %"PRIu64"",
388               w->hdr.lookup_table_res_entry.offset,
389               w->hdr.lookup_table_res_entry.original_size);
390
391         if (fseeko(w->fp, w->hdr.lookup_table_res_entry.offset, SEEK_SET) != 0)
392         {
393                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" to read "
394                                  "lookup table",
395                                  w->hdr.lookup_table_res_entry.offset);
396                 return WIMLIB_ERR_READ;
397         }
398
399         num_entries = w->hdr.lookup_table_res_entry.original_size /
400                       WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE;
401         table = new_lookup_table(num_entries * 2 + 1);
402         if (!table)
403                 return WIMLIB_ERR_NOMEM;
404
405         w->current_image = 0;
406         while (num_entries--) {
407                 const u8 *p;
408
409                 if (fread(buf, 1, sizeof(buf), w->fp) != sizeof(buf)) {
410                         if (feof(w->fp)) {
411                                 ERROR("Unexpected EOF in WIM lookup table!");
412                         } else {
413                                 ERROR_WITH_ERRNO("Error reading WIM lookup "
414                                                  "table");
415                         }
416                         ret = WIMLIB_ERR_READ;
417                         goto out_free_lookup_table;
418                 }
419                 cur_entry = new_lookup_table_entry();
420                 if (!cur_entry) {
421                         ret = WIMLIB_ERR_NOMEM;
422                         goto out_free_lookup_table;
423                 }
424
425                 cur_entry->wim = w;
426                 cur_entry->resource_location = RESOURCE_IN_WIM;
427                 p = get_resource_entry(buf, &cur_entry->resource_entry);
428                 p = get_u16(p, &cur_entry->part_number);
429                 p = get_u32(p, &cur_entry->refcnt);
430                 p = get_bytes(p, SHA1_HASH_SIZE, cur_entry->hash);
431
432                 if (cur_entry->part_number != w->hdr.part_number) {
433                         ERROR("A lookup table entry in part %hu of the WIM "
434                               "points to part %hu",
435                               w->hdr.part_number, cur_entry->part_number);
436                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
437                         goto out_free_cur_entry;
438                 }
439
440                 if (is_zero_hash(cur_entry->hash)) {
441                         ERROR("The WIM lookup table contains an entry with a "
442                               "SHA1 message digest of all 0's");
443                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
444                         goto out_free_cur_entry;
445                 }
446
447                 if (!(cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED)
448                     && (cur_entry->resource_entry.size !=
449                         cur_entry->resource_entry.original_size))
450                 {
451                 #ifdef ENABLE_ERROR_MESSAGES
452                         ERROR("Found uncompressed resource with original size "
453                               "not the same as compressed size");
454                         ERROR("The lookup table entry for the resource is as follows:");
455                         print_lookup_table_entry(cur_entry, stderr);
456                 #endif
457                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
458                         goto out_free_cur_entry;
459                 }
460
461                 if (cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) {
462                         /* Lookup table entry for a metadata resource */
463                         if (cur_entry->refcnt != 1) {
464                         #ifdef ENABLE_ERROR_MESSAGES
465                                 ERROR("Found metadata resource with refcnt != 1:");
466                                 print_lookup_table_entry(cur_entry, stderr);
467                         #endif
468                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
469                                 goto out_free_cur_entry;
470                         }
471
472                         if (w->hdr.part_number != 1) {
473                                 ERROR("Found a metadata resource in a "
474                                       "non-first part of the split WIM!");
475                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
476                                 goto out_free_cur_entry;
477                         }
478                         if (w->current_image == w->hdr.image_count) {
479                                 ERROR("The WIM header says there are %u images "
480                                       "in the WIM, but we found more metadata "
481                                       "resources than this", w->hdr.image_count);
482                                 ret = WIMLIB_ERR_IMAGE_COUNT;
483                                 goto out_free_cur_entry;
484                         }
485
486                         /* Notice very carefully:  We are assigning the metadata
487                          * resources in the exact order mirrored by their lookup
488                          * table entries on disk, which is the behavior of
489                          * Microsoft's software.  In particular, this overrides
490                          * the actual locations of the metadata resources
491                          * themselves in the WIM file as well as any information
492                          * written in the XML data. */
493                         DEBUG("Found metadata resource for image %u at "
494                               "offset %"PRIu64".",
495                               w->current_image + 1,
496                               cur_entry->resource_entry.offset);
497                         w->image_metadata[
498                                 w->current_image++].metadata_lte = cur_entry;
499                 } else {
500                         /* Lookup table entry for a stream that is not a
501                          * metadata resource */
502                         duplicate_entry = __lookup_resource(table, cur_entry->hash);
503                         if (duplicate_entry) {
504                         #ifdef ENABLE_ERROR_MESSAGES
505                                 ERROR("The WIM lookup table contains two entries with the "
506                                       "same SHA1 message digest!");
507                                 ERROR("The first entry is:");
508                                 print_lookup_table_entry(duplicate_entry, stderr);
509                                 ERROR("The second entry is:");
510                                 print_lookup_table_entry(cur_entry, stderr);
511                         #endif
512                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
513                                 goto out_free_cur_entry;
514                         }
515                         lookup_table_insert(table, cur_entry);
516                 }
517         }
518
519         if (w->hdr.part_number == 1 &&
520             w->current_image != w->hdr.image_count)
521         {
522                 ERROR("The WIM header says there are %u images "
523                       "in the WIM, but we only found %d metadata "
524                       "resources!", w->hdr.image_count, w->current_image);
525                 ret = WIMLIB_ERR_IMAGE_COUNT;
526                 goto out_free_lookup_table;
527         }
528         DEBUG("Done reading lookup table.");
529         w->lookup_table = table;
530         ret = 0;
531         goto out;
532 out_free_cur_entry:
533         FREE(cur_entry);
534 out_free_lookup_table:
535         free_lookup_table(table);
536 out:
537         w->current_image = 0;
538         return ret;
539 }
540
541
542 /*
543  * Writes a lookup table entry to the output file.
544  */
545 int
546 write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *_out)
547 {
548         FILE *out;
549         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
550         u8 *p;
551
552         out = _out;
553
554         /* Don't write entries that have not had file resources or metadata
555          * resources written for them. */
556         if (lte->out_refcnt == 0)
557                 return 0;
558
559         if (lte->output_resource_entry.flags & WIM_RESHDR_FLAG_METADATA) {
560                 DEBUG("Writing metadata entry at %"PRIu64" "
561                       "(orig size = %"PRIu64")",
562                       ftello(out), lte->output_resource_entry.original_size);
563         }
564
565         p = put_resource_entry(buf, &lte->output_resource_entry);
566         p = put_u16(p, lte->part_number);
567         p = put_u32(p, lte->out_refcnt);
568         p = put_bytes(p, SHA1_HASH_SIZE, lte->hash);
569         if (fwrite(buf, 1, sizeof(buf), out) != sizeof(buf)) {
570                 ERROR_WITH_ERRNO("Failed to write lookup table entry");
571                 return WIMLIB_ERR_WRITE;
572         }
573         return 0;
574 }
575
576 /* Writes the WIM lookup table to the output file. */
577 int
578 write_lookup_table(WIMStruct *w, int image, struct resource_entry *out_res_entry)
579 {
580         FILE *out = w->out_fp;
581         off_t start_offset, end_offset;
582         int ret;
583         int start_image, end_image;
584
585         start_offset = ftello(out);
586         if (start_offset == -1)
587                 return WIMLIB_ERR_WRITE;
588
589         /* Write lookup table entries for metadata resources */
590         if (image == WIMLIB_ALL_IMAGES) {
591                 start_image = 1;
592                 end_image = w->hdr.image_count;
593         } else {
594                 start_image = image;
595                 end_image = image;
596         }
597         for (int i = start_image; i <= end_image; i++) {
598                 struct wim_lookup_table_entry *metadata_lte;
599
600                 metadata_lte = w->image_metadata[i - 1].metadata_lte;
601                 metadata_lte->out_refcnt = 1;
602                 metadata_lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA;
603                 ret = write_lookup_table_entry(metadata_lte, out);
604                 if (ret)
605                         return ret;
606         }
607
608         /* Write lookup table entries for other resources */
609         ret = for_lookup_table_entry(w->lookup_table, write_lookup_table_entry, out);
610         if (ret)
611                 return ret;
612
613         /* Fill in the resource entry for the lookup table itself */
614         end_offset = ftello(out);
615         if (end_offset == -1)
616                 return WIMLIB_ERR_WRITE;
617
618         out_res_entry->offset        = start_offset;
619         out_res_entry->size          = end_offset - start_offset;
620         out_res_entry->original_size = end_offset - start_offset;
621         out_res_entry->flags         = WIM_RESHDR_FLAG_METADATA;
622         return 0;
623 }
624
625
626 int
627 lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
628 {
629         lte->real_refcnt = 0;
630         return 0;
631 }
632
633 int
634 lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
635 {
636         lte->out_refcnt = 0;
637         return 0;
638 }
639
640 int
641 lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore)
642 {
643         if (lte->extracted_file != NULL) {
644                 FREE(lte->extracted_file);
645                 lte->extracted_file = NULL;
646         }
647         return 0;
648 }
649
650 void
651 print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out)
652 {
653         if (!lte) {
654                 tputc(T('\n'), out);
655                 return;
656         }
657         tfprintf(out, T("Offset            = %"PRIu64" bytes\n"),
658                  lte->resource_entry.offset);
659
660         tfprintf(out, T("Size              = %"PRIu64" bytes\n"),
661                  (u64)lte->resource_entry.size);
662
663         tfprintf(out, T("Original size     = %"PRIu64" bytes\n"),
664                  lte->resource_entry.original_size);
665
666         tfprintf(out, T("Part Number       = %hu\n"), lte->part_number);
667         tfprintf(out, T("Reference Count   = %u\n"), lte->refcnt);
668
669         tfprintf(out, T("Hash              = 0x"));
670         print_hash(lte->hash, out);
671         tputc(T('\n'), out);
672
673         tfprintf(out, T("Flags             = "));
674         u8 flags = lte->resource_entry.flags;
675         if (flags & WIM_RESHDR_FLAG_COMPRESSED)
676                 tfputs(T("WIM_RESHDR_FLAG_COMPRESSED, "), out);
677         if (flags & WIM_RESHDR_FLAG_FREE)
678                 tfputs(T("WIM_RESHDR_FLAG_FREE, "), out);
679         if (flags & WIM_RESHDR_FLAG_METADATA)
680                 tfputs(T("WIM_RESHDR_FLAG_METADATA, "), out);
681         if (flags & WIM_RESHDR_FLAG_SPANNED)
682                 tfputs(T("WIM_RESHDR_FLAG_SPANNED, "), out);
683         tputc(T('\n'), out);
684         switch (lte->resource_location) {
685         case RESOURCE_IN_WIM:
686                 if (lte->wim->filename) {
687                         tfprintf(out, T("WIM file          = `%"TS"'\n"),
688                                  lte->wim->filename);
689                 }
690                 break;
691 #ifdef __WIN32__
692         case RESOURCE_WIN32:
693 #endif
694         case RESOURCE_IN_FILE_ON_DISK:
695                 tfprintf(out, T("File on Disk      = `%"TS"'\n"),
696                          lte->file_on_disk);
697                 break;
698         case RESOURCE_IN_STAGING_FILE:
699                 tfprintf(out, T("Staging File      = `%"TS"'\n"),
700                                 lte->staging_file_name);
701                 break;
702         default:
703                 break;
704         }
705         tputc(T('\n'), out);
706 }
707
708 static int
709 do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, void *fp)
710 {
711         print_lookup_table_entry(lte, (FILE*)fp);
712         return 0;
713 }
714
715 /*
716  * Prints the lookup table of a WIM file.
717  */
718 WIMLIBAPI void
719 wimlib_print_lookup_table(WIMStruct *w)
720 {
721         for_lookup_table_entry(w->lookup_table,
722                                do_print_lookup_table_entry,
723                                stdout);
724 }
725
726 /* Given a SHA1 message digest, return the corresponding entry in the WIM's
727  * lookup table, or NULL if there is none.  */
728 struct wim_lookup_table_entry *
729 __lookup_resource(const struct wim_lookup_table *table, const u8 hash[])
730 {
731         size_t i;
732         struct wim_lookup_table_entry *lte;
733         struct hlist_node *pos;
734
735         wimlib_assert(table != NULL);
736         wimlib_assert(hash != NULL);
737
738         i = *(size_t*)hash % table->capacity;
739         hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
740                 if (hashes_equal(hash, lte->hash))
741                         return lte;
742         return NULL;
743 }
744
745 #ifdef WITH_FUSE
746 /*
747  * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
748  * given a path name.
749  *
750  * This is only for pre-resolved inodes.
751  */
752 int
753 lookup_resource(WIMStruct *w,
754                 const tchar *path,
755                 int lookup_flags,
756                 struct wim_dentry **dentry_ret,
757                 struct wim_lookup_table_entry **lte_ret,
758                 u16 *stream_idx_ret)
759 {
760         struct wim_dentry *dentry;
761         struct wim_lookup_table_entry *lte;
762         u16 stream_idx;
763         const tchar *stream_name = NULL;
764         struct wim_inode *inode;
765         tchar *p = NULL;
766
767         if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
768                 stream_name = path_stream_name(path);
769                 if (stream_name) {
770                         p = (tchar*)stream_name - 1;
771                         *p = T('\0');
772                 }
773         }
774
775         dentry = get_dentry(w, path);
776         if (p)
777                 *p = T(':');
778         if (!dentry)
779                 return -errno;
780
781         inode = dentry->d_inode;
782
783         wimlib_assert(inode->i_resolved);
784
785         if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
786               && inode_is_directory(inode))
787                 return -EISDIR;
788
789         if (stream_name) {
790                 struct wim_ads_entry *ads_entry;
791                 u16 ads_idx;
792                 ads_entry = inode_get_ads_entry(inode, stream_name,
793                                                 &ads_idx);
794                 if (ads_entry) {
795                         stream_idx = ads_idx + 1;
796                         lte = ads_entry->lte;
797                         goto out;
798                 } else {
799                         return -ENOENT;
800                 }
801         } else {
802                 lte = inode->i_lte;
803                 stream_idx = 0;
804         }
805 out:
806         if (dentry_ret)
807                 *dentry_ret = dentry;
808         if (lte_ret)
809                 *lte_ret = lte;
810         if (stream_idx_ret)
811                 *stream_idx_ret = stream_idx;
812         return 0;
813 }
814 #endif
815
816 /* Resolve an inode's lookup table entries
817  *
818  * This replaces the SHA1 hash fields (which are used to lookup an entry in the
819  * lookup table) with pointers directly to the lookup table entries.  A circular
820  * linked list of streams sharing the same lookup table entry is created.
821  *
822  * This function always succeeds; unresolved lookup table entries are given a
823  * NULL pointer.
824  */
825 void
826 inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table)
827 {
828
829         if (!inode->i_resolved) {
830                 struct wim_lookup_table_entry *lte;
831                 /* Resolve the default file stream */
832                 lte = __lookup_resource(table, inode->i_hash);
833                 inode->i_lte = lte;
834                 inode->i_resolved = 1;
835
836                 /* Resolve the alternate data streams */
837                 for (u16 i = 0; i < inode->i_num_ads; i++) {
838                         struct wim_ads_entry *cur_entry = &inode->i_ads_entries[i];
839                         lte = __lookup_resource(table, cur_entry->hash);
840                         cur_entry->lte = lte;
841                 }
842         }
843 }
844
845 void
846 inode_unresolve_ltes(struct wim_inode *inode)
847 {
848         if (inode->i_resolved) {
849                 if (inode->i_lte)
850                         copy_hash(inode->i_hash, inode->i_lte->hash);
851                 else
852                         zero_out_hash(inode->i_hash);
853
854                 for (u16 i = 0; i < inode->i_num_ads; i++) {
855                         if (inode->i_ads_entries[i].lte)
856                                 copy_hash(inode->i_ads_entries[i].hash,
857                                           inode->i_ads_entries[i].lte->hash);
858                         else
859                                 zero_out_hash(inode->i_ads_entries[i].hash);
860                 }
861                 inode->i_resolved = 0;
862         }
863 }
864
865 /*
866  * Returns the lookup table entry for stream @stream_idx of the inode, where
867  * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1
868  * corresponds to an alternate data stream.
869  *
870  * This works for both resolved and un-resolved inodes.
871  */
872 struct wim_lookup_table_entry *
873 inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
874                  const struct wim_lookup_table *table)
875 {
876         if (inode->i_resolved)
877                 return inode_stream_lte_resolved(inode, stream_idx);
878         else
879                 return inode_stream_lte_unresolved(inode, stream_idx, table);
880 }
881
882
883 /* Return the lookup table entry for the unnamed data stream of an inode, or
884  * NULL if there is none.
885  *
886  * You'd think this would be easier than it actually is, since the unnamed data
887  * stream should be the one referenced from the inode itself.  Alas, if there
888  * are named data streams, Microsoft's "imagex.exe" program will put the unnamed
889  * data stream in one of the alternate data streams instead of inside the WIM
890  * dentry itself.  So we need to check the alternate data streams too.
891  *
892  * Also, note that a dentry may appear to have more than one unnamed stream, but
893  * if the SHA1 message digest is all 0's then the corresponding stream does not
894  * really "count" (this is the case for the inode's own file stream when the
895  * file stream that should be there is actually in one of the alternate stream
896  * entries.).  This is despite the fact that we may need to extract such a
897  * missing entry as an empty file or empty named data stream.
898  */
899 struct wim_lookup_table_entry *
900 inode_unnamed_lte(const struct wim_inode *inode,
901                   const struct wim_lookup_table *table)
902 {
903         if (inode->i_resolved)
904                 return inode_unnamed_lte_resolved(inode);
905         else
906                 return inode_unnamed_lte_unresolved(inode, table);
907 }
908
909 static int
910 lte_add_stream_size(struct wim_lookup_table_entry *lte, void *total_bytes_p)
911 {
912         *(u64*)total_bytes_p += lte->resource_entry.size;
913         return 0;
914 }
915
916 u64
917 lookup_table_total_stream_size(struct wim_lookup_table *table)
918 {
919         u64 total_size = 0;
920         for_lookup_table_entry(table, lte_add_stream_size, &total_size);
921         return total_size;
922 }
923
924 void
925 lookup_table_free_unhashed_streams(struct wim_lookup_table *table)
926 {
927         struct wim_lookup_table_entry *lte, *tmp;
928
929         list_for_each_entry_safe(lte, tmp, table->unhashed_streams, staging_list)
930                 free_lookup_table_entry(lte);
931         INIT_LIST_HEAD(table->unhashed_streams);
932 }