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