]> wimlib.net Git - wimlib/blob - src/lookup_table.c
5b57e3ac2967bcb260c1c9b893a091aaa5a44840
[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                 if (!lte->unhashed)
236                         lookup_table_unlink(table, lte);
237         #ifdef WITH_FUSE
238                 if (lte->num_opened_fds == 0)
239         #endif
240                         finalize_lte(lte);
241         }
242 }
243
244 #ifdef WITH_FUSE
245 void
246 lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte)
247 {
248         if (lte->num_opened_fds != 0)
249                 if (--lte->num_opened_fds == 0 && lte->refcnt == 0)
250                         finalize_lte(lte);
251 }
252 #endif
253
254 /* Calls a function on all the entries in the WIM lookup table.  Stop early and
255  * return nonzero if any call to the function returns nonzero. */
256 int
257 for_lookup_table_entry(struct wim_lookup_table *table,
258                        int (*visitor)(struct wim_lookup_table_entry *, void *),
259                        void *arg)
260 {
261         struct wim_lookup_table_entry *lte;
262         struct hlist_node *pos, *tmp;
263         int ret;
264
265         for (size_t i = 0; i < table->capacity; i++) {
266                 hlist_for_each_entry_safe(lte, pos, tmp, &table->array[i],
267                                           hash_list)
268                 {
269                         wimlib_assert2(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA));
270                         ret = visitor(lte, arg);
271                         if (ret)
272                                 return ret;
273                 }
274         }
275         return 0;
276 }
277
278 static int
279 cmp_streams_by_wim_position(const void *p1, const void *p2)
280 {
281         const struct wim_lookup_table_entry *lte1, *lte2;
282         lte1 = *(const struct wim_lookup_table_entry**)p1;
283         lte2 = *(const struct wim_lookup_table_entry**)p2;
284         if (lte1->resource_entry.offset < lte2->resource_entry.offset)
285                 return -1;
286         else if (lte1->resource_entry.offset > lte2->resource_entry.offset)
287                 return 1;
288         else
289                 return 0;
290 }
291
292 int
293 sort_stream_list_by_wim_position(struct list_head *stream_list)
294 {
295         struct list_head *cur;
296         size_t num_streams;
297         struct wim_lookup_table_entry **array;
298         size_t i;
299         size_t array_size;
300
301         num_streams = 0;
302         list_for_each(cur, stream_list)
303                 num_streams++;
304         array_size = num_streams * sizeof(array[0]);
305         array = MALLOC(array_size);
306         if (!array) {
307                 ERROR("Failed to allocate %zu bytes to sort stream entries",
308                       array_size);
309                 return WIMLIB_ERR_NOMEM;
310         }
311         cur = stream_list->next;
312         for (i = 0; i < num_streams; i++) {
313                 array[i] = container_of(cur, struct wim_lookup_table_entry, staging_list);
314                 cur = cur->next;
315         }
316
317         qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
318
319         INIT_LIST_HEAD(stream_list);
320         for (i = 0; i < num_streams; i++)
321                 list_add_tail(&array[i]->staging_list, stream_list);
322         FREE(array);
323         return 0;
324 }
325
326
327 static int
328 add_lte_to_array(struct wim_lookup_table_entry *lte,
329                  void *_pp)
330 {
331         struct wim_lookup_table_entry ***pp = _pp;
332         *(*pp)++ = lte;
333         return 0;
334 }
335
336 /* Iterate through the lookup table entries, but first sort them by stream
337  * offset in the WIM.  Caution: this is intended to be used when the stream
338  * offset field has actually been set. */
339 int
340 for_lookup_table_entry_pos_sorted(struct wim_lookup_table *table,
341                                   int (*visitor)(struct wim_lookup_table_entry *,
342                                                  void *),
343                                   void *arg)
344 {
345         struct wim_lookup_table_entry **lte_array, **p;
346         size_t num_streams = table->num_entries;
347         int ret;
348
349         lte_array = MALLOC(num_streams * sizeof(lte_array[0]));
350         if (!lte_array)
351                 return WIMLIB_ERR_NOMEM;
352         p = lte_array;
353         for_lookup_table_entry(table, add_lte_to_array, &p);
354
355         wimlib_assert(p == lte_array + num_streams);
356
357         qsort(lte_array, num_streams, sizeof(lte_array[0]),
358               cmp_streams_by_wim_position);
359         ret = 0;
360         for (size_t i = 0; i < num_streams && ret == 0; i++)
361                 ret = visitor(lte_array[i], arg);
362         FREE(lte_array);
363         return ret;
364 }
365
366 /*
367  * Reads the lookup table from a WIM file.
368  *
369  * Saves lookup table entries for non-metadata streams in a hash table, and
370  * saves the metadata entry for each image in a special per-image location (the
371  * image_metadata array).
372  */
373 int
374 read_lookup_table(WIMStruct *w)
375 {
376         u64 num_entries;
377         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
378         int ret;
379         struct wim_lookup_table *table;
380         struct wim_lookup_table_entry *cur_entry, *duplicate_entry;
381
382         if (resource_is_compressed(&w->hdr.lookup_table_res_entry)) {
383                 ERROR("Didn't expect a compressed lookup table!");
384                 ERROR("Ask the author to implement support for this.");
385                 return WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE;
386         }
387
388         DEBUG("Reading lookup table: offset %"PRIu64", size %"PRIu64"",
389               w->hdr.lookup_table_res_entry.offset,
390               w->hdr.lookup_table_res_entry.original_size);
391
392         if (fseeko(w->fp, w->hdr.lookup_table_res_entry.offset, SEEK_SET) != 0)
393         {
394                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" to read "
395                                  "lookup table",
396                                  w->hdr.lookup_table_res_entry.offset);
397                 return WIMLIB_ERR_READ;
398         }
399
400         num_entries = w->hdr.lookup_table_res_entry.original_size /
401                       WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE;
402         table = new_lookup_table(num_entries * 2 + 1);
403         if (!table)
404                 return WIMLIB_ERR_NOMEM;
405
406         w->current_image = 0;
407         while (num_entries--) {
408                 const u8 *p;
409
410                 if (fread(buf, 1, sizeof(buf), w->fp) != sizeof(buf)) {
411                         if (feof(w->fp)) {
412                                 ERROR("Unexpected EOF in WIM lookup table!");
413                         } else {
414                                 ERROR_WITH_ERRNO("Error reading WIM lookup "
415                                                  "table");
416                         }
417                         ret = WIMLIB_ERR_READ;
418                         goto out_free_lookup_table;
419                 }
420                 cur_entry = new_lookup_table_entry();
421                 if (!cur_entry) {
422                         ret = WIMLIB_ERR_NOMEM;
423                         goto out_free_lookup_table;
424                 }
425
426                 cur_entry->wim = w;
427                 cur_entry->resource_location = RESOURCE_IN_WIM;
428                 p = get_resource_entry(buf, &cur_entry->resource_entry);
429                 p = get_u16(p, &cur_entry->part_number);
430                 p = get_u32(p, &cur_entry->refcnt);
431                 p = get_bytes(p, SHA1_HASH_SIZE, cur_entry->hash);
432
433                 if (cur_entry->part_number != w->hdr.part_number) {
434                         ERROR("A lookup table entry in part %hu of the WIM "
435                               "points to part %hu",
436                               w->hdr.part_number, cur_entry->part_number);
437                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
438                         goto out_free_cur_entry;
439                 }
440
441                 if (is_zero_hash(cur_entry->hash)) {
442                         ERROR("The WIM lookup table contains an entry with a "
443                               "SHA1 message digest of all 0's");
444                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
445                         goto out_free_cur_entry;
446                 }
447
448                 if (!(cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED)
449                     && (cur_entry->resource_entry.size !=
450                         cur_entry->resource_entry.original_size))
451                 {
452                 #ifdef ENABLE_ERROR_MESSAGES
453                         ERROR("Found uncompressed resource with original size "
454                               "not the same as compressed size");
455                         ERROR("The lookup table entry for the resource is as follows:");
456                         print_lookup_table_entry(cur_entry, stderr);
457                 #endif
458                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
459                         goto out_free_cur_entry;
460                 }
461
462                 if (cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) {
463                         /* Lookup table entry for a metadata resource */
464                         if (cur_entry->refcnt != 1) {
465                         #ifdef ENABLE_ERROR_MESSAGES
466                                 ERROR("Found metadata resource with refcnt != 1:");
467                                 print_lookup_table_entry(cur_entry, stderr);
468                         #endif
469                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
470                                 goto out_free_cur_entry;
471                         }
472
473                         if (w->hdr.part_number != 1) {
474                                 ERROR("Found a metadata resource in a "
475                                       "non-first part of the split WIM!");
476                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
477                                 goto out_free_cur_entry;
478                         }
479                         if (w->current_image == w->hdr.image_count) {
480                                 ERROR("The WIM header says there are %u images "
481                                       "in the WIM, but we found more metadata "
482                                       "resources than this", w->hdr.image_count);
483                                 ret = WIMLIB_ERR_IMAGE_COUNT;
484                                 goto out_free_cur_entry;
485                         }
486
487                         /* Notice very carefully:  We are assigning the metadata
488                          * resources in the exact order mirrored by their lookup
489                          * table entries on disk, which is the behavior of
490                          * Microsoft's software.  In particular, this overrides
491                          * the actual locations of the metadata resources
492                          * themselves in the WIM file as well as any information
493                          * written in the XML data. */
494                         DEBUG("Found metadata resource for image %u at "
495                               "offset %"PRIu64".",
496                               w->current_image + 1,
497                               cur_entry->resource_entry.offset);
498                         w->image_metadata[
499                                 w->current_image++]->metadata_lte = cur_entry;
500                 } else {
501                         /* Lookup table entry for a stream that is not a
502                          * metadata resource */
503                         duplicate_entry = __lookup_resource(table, cur_entry->hash);
504                         if (duplicate_entry) {
505                         #ifdef ENABLE_ERROR_MESSAGES
506                                 ERROR("The WIM lookup table contains two entries with the "
507                                       "same SHA1 message digest!");
508                                 ERROR("The first entry is:");
509                                 print_lookup_table_entry(duplicate_entry, stderr);
510                                 ERROR("The second entry is:");
511                                 print_lookup_table_entry(cur_entry, stderr);
512                         #endif
513                                 ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
514                                 goto out_free_cur_entry;
515                         }
516                         lookup_table_insert(table, cur_entry);
517                 }
518         }
519
520         if (w->hdr.part_number == 1 &&
521             w->current_image != w->hdr.image_count)
522         {
523                 ERROR("The WIM header says there are %u images "
524                       "in the WIM, but we only found %d metadata "
525                       "resources!", w->hdr.image_count, w->current_image);
526                 ret = WIMLIB_ERR_IMAGE_COUNT;
527                 goto out_free_lookup_table;
528         }
529         DEBUG("Done reading lookup table.");
530         w->lookup_table = table;
531         ret = 0;
532         goto out;
533 out_free_cur_entry:
534         FREE(cur_entry);
535 out_free_lookup_table:
536         free_lookup_table(table);
537 out:
538         w->current_image = 0;
539         return ret;
540 }
541
542
543 /*
544  * Writes a lookup table entry to the output file.
545  */
546 int
547 write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *_out)
548 {
549         FILE *out;
550         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
551         u8 *p;
552
553         out = _out;
554
555         /* Don't write entries that have not had file resources or metadata
556          * resources written for them. */
557         if (lte->out_refcnt == 0)
558                 return 0;
559
560         if (lte->output_resource_entry.flags & WIM_RESHDR_FLAG_METADATA) {
561                 DEBUG("Writing metadata entry at %"PRIu64" "
562                       "(orig size = %"PRIu64")",
563                       ftello(out), lte->output_resource_entry.original_size);
564         }
565
566         p = put_resource_entry(buf, &lte->output_resource_entry);
567         p = put_u16(p, lte->part_number);
568         p = put_u32(p, lte->out_refcnt);
569         p = put_bytes(p, SHA1_HASH_SIZE, lte->hash);
570         if (fwrite(buf, 1, sizeof(buf), out) != sizeof(buf)) {
571                 ERROR_WITH_ERRNO("Failed to write lookup table entry");
572                 return WIMLIB_ERR_WRITE;
573         }
574         return 0;
575 }
576
577 /* Writes the WIM lookup table to the output file. */
578 int
579 write_lookup_table(WIMStruct *w, int image, struct resource_entry *out_res_entry)
580 {
581         FILE *out = w->out_fp;
582         off_t start_offset, end_offset;
583         int ret;
584         int start_image, end_image;
585
586         start_offset = ftello(out);
587         if (start_offset == -1)
588                 return WIMLIB_ERR_WRITE;
589
590         /* Write lookup table entries for metadata resources */
591         if (image == WIMLIB_ALL_IMAGES) {
592                 start_image = 1;
593                 end_image = w->hdr.image_count;
594         } else {
595                 start_image = image;
596                 end_image = image;
597         }
598         for (int i = start_image; i <= end_image; i++) {
599                 struct wim_lookup_table_entry *metadata_lte;
600
601                 metadata_lte = w->image_metadata[i - 1]->metadata_lte;
602                 metadata_lte->out_refcnt = 1;
603                 metadata_lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA;
604                 ret = write_lookup_table_entry(metadata_lte, out);
605                 if (ret)
606                         return ret;
607         }
608
609         /* Write lookup table entries for other resources */
610         ret = for_lookup_table_entry(w->lookup_table, write_lookup_table_entry, out);
611         if (ret)
612                 return ret;
613
614         /* Fill in the resource entry for the lookup table itself */
615         end_offset = ftello(out);
616         if (end_offset == -1)
617                 return WIMLIB_ERR_WRITE;
618
619         out_res_entry->offset        = start_offset;
620         out_res_entry->size          = end_offset - start_offset;
621         out_res_entry->original_size = end_offset - start_offset;
622         out_res_entry->flags         = WIM_RESHDR_FLAG_METADATA;
623         return 0;
624 }
625
626
627 int
628 lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
629 {
630         lte->real_refcnt = 0;
631         return 0;
632 }
633
634 int
635 lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
636 {
637         lte->out_refcnt = 0;
638         return 0;
639 }
640
641 int
642 lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore)
643 {
644         if (lte->extracted_file != NULL) {
645                 FREE(lte->extracted_file);
646                 lte->extracted_file = NULL;
647         }
648         return 0;
649 }
650
651 void
652 print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out)
653 {
654         if (!lte) {
655                 tputc(T('\n'), out);
656                 return;
657         }
658         tfprintf(out, T("Offset            = %"PRIu64" bytes\n"),
659                  lte->resource_entry.offset);
660
661         tfprintf(out, T("Size              = %"PRIu64" bytes\n"),
662                  (u64)lte->resource_entry.size);
663
664         tfprintf(out, T("Original size     = %"PRIu64" bytes\n"),
665                  lte->resource_entry.original_size);
666
667         tfprintf(out, T("Part Number       = %hu\n"), lte->part_number);
668         tfprintf(out, T("Reference Count   = %u\n"), lte->refcnt);
669
670         tfprintf(out, T("Hash              = 0x"));
671         print_hash(lte->hash, out);
672         tputc(T('\n'), out);
673
674         tfprintf(out, T("Flags             = "));
675         u8 flags = lte->resource_entry.flags;
676         if (flags & WIM_RESHDR_FLAG_COMPRESSED)
677                 tfputs(T("WIM_RESHDR_FLAG_COMPRESSED, "), out);
678         if (flags & WIM_RESHDR_FLAG_FREE)
679                 tfputs(T("WIM_RESHDR_FLAG_FREE, "), out);
680         if (flags & WIM_RESHDR_FLAG_METADATA)
681                 tfputs(T("WIM_RESHDR_FLAG_METADATA, "), out);
682         if (flags & WIM_RESHDR_FLAG_SPANNED)
683                 tfputs(T("WIM_RESHDR_FLAG_SPANNED, "), out);
684         tputc(T('\n'), out);
685         switch (lte->resource_location) {
686         case RESOURCE_IN_WIM:
687                 if (lte->wim->filename) {
688                         tfprintf(out, T("WIM file          = `%"TS"'\n"),
689                                  lte->wim->filename);
690                 }
691                 break;
692 #ifdef __WIN32__
693         case RESOURCE_WIN32:
694 #endif
695         case RESOURCE_IN_FILE_ON_DISK:
696                 tfprintf(out, T("File on Disk      = `%"TS"'\n"),
697                          lte->file_on_disk);
698                 break;
699         case RESOURCE_IN_STAGING_FILE:
700                 tfprintf(out, T("Staging File      = `%"TS"'\n"),
701                                 lte->staging_file_name);
702                 break;
703         default:
704                 break;
705         }
706         tputc(T('\n'), out);
707 }
708
709 static int
710 do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, void *fp)
711 {
712         print_lookup_table_entry(lte, (FILE*)fp);
713         return 0;
714 }
715
716 /*
717  * Prints the lookup table of a WIM file.
718  */
719 WIMLIBAPI void
720 wimlib_print_lookup_table(WIMStruct *w)
721 {
722         for_lookup_table_entry(w->lookup_table,
723                                do_print_lookup_table_entry,
724                                stdout);
725 }
726
727 /* Given a SHA1 message digest, return the corresponding entry in the WIM's
728  * lookup table, or NULL if there is none.  */
729 struct wim_lookup_table_entry *
730 __lookup_resource(const struct wim_lookup_table *table, const u8 hash[])
731 {
732         size_t i;
733         struct wim_lookup_table_entry *lte;
734         struct hlist_node *pos;
735
736         wimlib_assert(table != NULL);
737         wimlib_assert(hash != NULL);
738
739         i = *(size_t*)hash % table->capacity;
740         hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
741                 if (hashes_equal(hash, lte->hash))
742                         return lte;
743         return NULL;
744 }
745
746 #ifdef WITH_FUSE
747 /*
748  * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
749  * given a path name.
750  *
751  * This is only for pre-resolved inodes.
752  */
753 int
754 lookup_resource(WIMStruct *w,
755                 const tchar *path,
756                 int lookup_flags,
757                 struct wim_dentry **dentry_ret,
758                 struct wim_lookup_table_entry **lte_ret,
759                 u16 *stream_idx_ret)
760 {
761         struct wim_dentry *dentry;
762         struct wim_lookup_table_entry *lte;
763         u16 stream_idx;
764         const tchar *stream_name = NULL;
765         struct wim_inode *inode;
766         tchar *p = NULL;
767
768         if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
769                 stream_name = path_stream_name(path);
770                 if (stream_name) {
771                         p = (tchar*)stream_name - 1;
772                         *p = T('\0');
773                 }
774         }
775
776         dentry = get_dentry(w, path);
777         if (p)
778                 *p = T(':');
779         if (!dentry)
780                 return -errno;
781
782         inode = dentry->d_inode;
783
784         wimlib_assert(inode->i_resolved);
785
786         if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
787               && inode_is_directory(inode))
788                 return -EISDIR;
789
790         if (stream_name) {
791                 struct wim_ads_entry *ads_entry;
792                 u16 ads_idx;
793                 ads_entry = inode_get_ads_entry(inode, stream_name,
794                                                 &ads_idx);
795                 if (ads_entry) {
796                         stream_idx = ads_idx + 1;
797                         lte = ads_entry->lte;
798                         goto out;
799                 } else {
800                         return -ENOENT;
801                 }
802         } else {
803                 lte = inode->i_lte;
804                 stream_idx = 0;
805         }
806 out:
807         if (dentry_ret)
808                 *dentry_ret = dentry;
809         if (lte_ret)
810                 *lte_ret = lte;
811         if (stream_idx_ret)
812                 *stream_idx_ret = stream_idx;
813         return 0;
814 }
815 #endif
816
817 /* Resolve an inode's lookup table entries
818  *
819  * This replaces the SHA1 hash fields (which are used to lookup an entry in the
820  * lookup table) with pointers directly to the lookup table entries.  A circular
821  * linked list of streams sharing the same lookup table entry is created.
822  *
823  * This function always succeeds; unresolved lookup table entries are given a
824  * NULL pointer.
825  */
826 void
827 inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table)
828 {
829
830         if (!inode->i_resolved) {
831                 struct wim_lookup_table_entry *lte;
832                 /* Resolve the default file stream */
833                 lte = __lookup_resource(table, inode->i_hash);
834                 inode->i_lte = lte;
835                 inode->i_resolved = 1;
836
837                 /* Resolve the alternate data streams */
838                 for (u16 i = 0; i < inode->i_num_ads; i++) {
839                         struct wim_ads_entry *cur_entry = &inode->i_ads_entries[i];
840                         lte = __lookup_resource(table, cur_entry->hash);
841                         cur_entry->lte = lte;
842                 }
843         }
844 }
845
846 void
847 inode_unresolve_ltes(struct wim_inode *inode)
848 {
849         if (inode->i_resolved) {
850                 if (inode->i_lte)
851                         copy_hash(inode->i_hash, inode->i_lte->hash);
852                 else
853                         zero_out_hash(inode->i_hash);
854
855                 for (u16 i = 0; i < inode->i_num_ads; i++) {
856                         if (inode->i_ads_entries[i].lte)
857                                 copy_hash(inode->i_ads_entries[i].hash,
858                                           inode->i_ads_entries[i].lte->hash);
859                         else
860                                 zero_out_hash(inode->i_ads_entries[i].hash);
861                 }
862                 inode->i_resolved = 0;
863         }
864 }
865
866 /*
867  * Returns the lookup table entry for stream @stream_idx of the inode, where
868  * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1
869  * corresponds to an alternate data stream.
870  *
871  * This works for both resolved and un-resolved inodes.
872  */
873 struct wim_lookup_table_entry *
874 inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
875                  const struct wim_lookup_table *table)
876 {
877         if (inode->i_resolved)
878                 return inode_stream_lte_resolved(inode, stream_idx);
879         else
880                 return inode_stream_lte_unresolved(inode, stream_idx, table);
881 }
882
883
884 /* Return the lookup table entry for the unnamed data stream of an inode, or
885  * NULL if there is none.
886  *
887  * You'd think this would be easier than it actually is, since the unnamed data
888  * stream should be the one referenced from the inode itself.  Alas, if there
889  * are named data streams, Microsoft's "imagex.exe" program will put the unnamed
890  * data stream in one of the alternate data streams instead of inside the WIM
891  * dentry itself.  So we need to check the alternate data streams too.
892  *
893  * Also, note that a dentry may appear to have more than one unnamed stream, but
894  * if the SHA1 message digest is all 0's then the corresponding stream does not
895  * really "count" (this is the case for the inode's own file stream when the
896  * file stream that should be there is actually in one of the alternate stream
897  * entries.).  This is despite the fact that we may need to extract such a
898  * missing entry as an empty file or empty named data stream.
899  */
900 struct wim_lookup_table_entry *
901 inode_unnamed_lte(const struct wim_inode *inode,
902                   const struct wim_lookup_table *table)
903 {
904         if (inode->i_resolved)
905                 return inode_unnamed_lte_resolved(inode);
906         else
907                 return inode_unnamed_lte_unresolved(inode, table);
908 }
909
910 static int
911 lte_add_stream_size(struct wim_lookup_table_entry *lte, void *total_bytes_p)
912 {
913         *(u64*)total_bytes_p += lte->resource_entry.size;
914         return 0;
915 }
916
917 u64
918 lookup_table_total_stream_size(struct wim_lookup_table *table)
919 {
920         u64 total_size = 0;
921         for_lookup_table_entry(table, lte_add_stream_size, &total_size);
922         return total_size;
923 }
924
925 void
926 free_lte_list(struct list_head *list)
927 {
928         struct wim_lookup_table_entry *lte, *tmp;
929
930         list_for_each_entry_safe(lte, tmp, list, staging_list) {
931                 DEBUG("%p", lte);
932                 free_lookup_table_entry(lte);
933 }
934         INIT_LIST_HEAD(list);
935 }