]> wimlib.net Git - wimlib/blob - src/lookup_table.c
6370f03873440edf83e634bc7264a0a5b1d4ca67
[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         if (image == WIMLIB_ALL_IMAGES) {
502                 start_image = 1;
503                 end_image = w->hdr.image_count;
504         } else {
505                 start_image = image;
506                 end_image = image;
507         }
508         for (int i = start_image; i <= end_image; i++) {
509                 struct wim_lookup_table_entry *metadata_lte;
510
511                 metadata_lte = w->image_metadata[i - 1].metadata_lte;
512                 metadata_lte->out_refcnt = 1;
513                 metadata_lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA;
514                 ret = write_lookup_table_entry(metadata_lte, out);
515                 if (ret)
516                         return ret;
517         }
518
519         ret = for_lookup_table_entry(w->lookup_table, write_lookup_table_entry, out);
520         if (ret)
521                 return ret;
522
523         end_offset = ftello(out);
524         if (end_offset == -1)
525                 return WIMLIB_ERR_WRITE;
526
527         out_res_entry->offset        = start_offset;
528         out_res_entry->size          = end_offset - start_offset;
529         out_res_entry->original_size = end_offset - start_offset;
530         out_res_entry->flags         = WIM_RESHDR_FLAG_METADATA;
531         return 0;
532 }
533
534
535 int
536 lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
537 {
538         lte->real_refcnt = 0;
539         return 0;
540 }
541
542 int
543 lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore)
544 {
545         lte->out_refcnt = 0;
546         return 0;
547 }
548
549 int
550 lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore)
551 {
552         if (lte->extracted_file != NULL) {
553                 FREE(lte->extracted_file);
554                 lte->extracted_file = NULL;
555         }
556         return 0;
557 }
558
559 void
560 print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out)
561 {
562         if (!lte) {
563                 tputc(T('\n'), out);
564                 return;
565         }
566         tfprintf(out, T("Offset            = %"PRIu64" bytes\n"),
567                  lte->resource_entry.offset);
568
569         tfprintf(out, T("Size              = %"PRIu64" bytes\n"),
570                  (u64)lte->resource_entry.size);
571
572         tfprintf(out, T("Original size     = %"PRIu64" bytes\n"),
573                  lte->resource_entry.original_size);
574
575         tfprintf(out, T("Part Number       = %hu\n"), lte->part_number);
576         tfprintf(out, T("Reference Count   = %u\n"), lte->refcnt);
577
578         tfprintf(out, T("Hash              = 0x"));
579         print_hash(lte->hash, out);
580         tputc(T('\n'), out);
581
582         tfprintf(out, T("Flags             = "));
583         u8 flags = lte->resource_entry.flags;
584         if (flags & WIM_RESHDR_FLAG_COMPRESSED)
585                 tfputs(T("WIM_RESHDR_FLAG_COMPRESSED, "), out);
586         if (flags & WIM_RESHDR_FLAG_FREE)
587                 tfputs(T("WIM_RESHDR_FLAG_FREE, "), out);
588         if (flags & WIM_RESHDR_FLAG_METADATA)
589                 tfputs(T("WIM_RESHDR_FLAG_METADATA, "), out);
590         if (flags & WIM_RESHDR_FLAG_SPANNED)
591                 tfputs(T("WIM_RESHDR_FLAG_SPANNED, "), out);
592         tputc(T('\n'), out);
593         switch (lte->resource_location) {
594         case RESOURCE_IN_WIM:
595                 if (lte->wim->filename) {
596                         tfprintf(out, T("WIM file          = `%"TS"'\n"),
597                                  lte->wim->filename);
598                 }
599                 break;
600 #ifdef __WIN32__
601         case RESOURCE_WIN32:
602 #endif
603         case RESOURCE_IN_FILE_ON_DISK:
604                 tfprintf(out, T("File on Disk      = `%"TS"'\n"),
605                          lte->file_on_disk);
606                 break;
607         case RESOURCE_IN_STAGING_FILE:
608                 tfprintf(out, T("Staging File      = `%"TS"'\n"),
609                                 lte->staging_file_name);
610                 break;
611         default:
612                 break;
613         }
614         tputc(T('\n'), out);
615 }
616
617 static int
618 do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, void *fp)
619 {
620         print_lookup_table_entry(lte, (FILE*)fp);
621         return 0;
622 }
623
624 /*
625  * Prints the lookup table of a WIM file.
626  */
627 WIMLIBAPI void
628 wimlib_print_lookup_table(WIMStruct *w)
629 {
630         for_lookup_table_entry(w->lookup_table,
631                                do_print_lookup_table_entry,
632                                stdout);
633 }
634
635 /* Given a SHA1 message digest, return the corresponding entry in the WIM's
636  * lookup table, or NULL if there is none.  */
637 struct wim_lookup_table_entry *
638 __lookup_resource(const struct wim_lookup_table *table, const u8 hash[])
639 {
640         size_t i;
641         struct wim_lookup_table_entry *lte;
642         struct hlist_node *pos;
643
644         wimlib_assert(table != NULL);
645         wimlib_assert(hash != NULL);
646
647         i = *(size_t*)hash % table->capacity;
648         hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
649                 if (hashes_equal(hash, lte->hash))
650                         return lte;
651         return NULL;
652 }
653
654 #ifdef WITH_FUSE
655 /*
656  * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
657  * given a path name.
658  *
659  * This is only for pre-resolved inodes.
660  */
661 int
662 lookup_resource(WIMStruct *w,
663                 const tchar *path,
664                 int lookup_flags,
665                 struct wim_dentry **dentry_ret,
666                 struct wim_lookup_table_entry **lte_ret,
667                 u16 *stream_idx_ret)
668 {
669         struct wim_dentry *dentry;
670         struct wim_lookup_table_entry *lte;
671         u16 stream_idx;
672         const tchar *stream_name = NULL;
673         struct wim_inode *inode;
674         tchar *p = NULL;
675
676         if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
677                 stream_name = path_stream_name(path);
678                 if (stream_name) {
679                         p = (tchar*)stream_name - 1;
680                         *p = T('\0');
681                 }
682         }
683
684         dentry = get_dentry(w, path);
685         if (p)
686                 *p = T(':');
687         if (!dentry)
688                 return -errno;
689
690         inode = dentry->d_inode;
691
692         wimlib_assert(inode->i_resolved);
693
694         if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
695               && inode_is_directory(inode))
696                 return -EISDIR;
697
698         if (stream_name) {
699                 struct wim_ads_entry *ads_entry;
700                 u16 ads_idx;
701                 ads_entry = inode_get_ads_entry(inode, stream_name,
702                                                 &ads_idx);
703                 if (ads_entry) {
704                         stream_idx = ads_idx + 1;
705                         lte = ads_entry->lte;
706                         goto out;
707                 } else {
708                         return -ENOENT;
709                 }
710         } else {
711                 lte = inode->i_lte;
712                 stream_idx = 0;
713         }
714 out:
715         if (dentry_ret)
716                 *dentry_ret = dentry;
717         if (lte_ret)
718                 *lte_ret = lte;
719         if (stream_idx_ret)
720                 *stream_idx_ret = stream_idx;
721         return 0;
722 }
723 #endif
724
725 /* Resolve an inode's lookup table entries
726  *
727  * This replaces the SHA1 hash fields (which are used to lookup an entry in the
728  * lookup table) with pointers directly to the lookup table entries.  A circular
729  * linked list of streams sharing the same lookup table entry is created.
730  *
731  * This function always succeeds; unresolved lookup table entries are given a
732  * NULL pointer.
733  */
734 void
735 inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table)
736 {
737
738         if (!inode->i_resolved) {
739                 struct wim_lookup_table_entry *lte;
740                 /* Resolve the default file stream */
741                 lte = __lookup_resource(table, inode->i_hash);
742                 inode->i_lte = lte;
743                 inode->i_resolved = 1;
744
745                 /* Resolve the alternate data streams */
746                 for (u16 i = 0; i < inode->i_num_ads; i++) {
747                         struct wim_ads_entry *cur_entry = &inode->i_ads_entries[i];
748                         lte = __lookup_resource(table, cur_entry->hash);
749                         cur_entry->lte = lte;
750                 }
751         }
752 }
753
754 void
755 inode_unresolve_ltes(struct wim_inode *inode)
756 {
757         if (inode->i_resolved) {
758                 if (inode->i_lte)
759                         copy_hash(inode->i_hash, inode->i_lte->hash);
760                 else
761                         zero_out_hash(inode->i_hash);
762
763                 for (u16 i = 0; i < inode->i_num_ads; i++) {
764                         if (inode->i_ads_entries[i].lte)
765                                 copy_hash(inode->i_ads_entries[i].hash,
766                                           inode->i_ads_entries[i].lte->hash);
767                         else
768                                 zero_out_hash(inode->i_ads_entries[i].hash);
769                 }
770                 inode->i_resolved = 0;
771         }
772 }
773
774 /*
775  * Returns the lookup table entry for stream @stream_idx of the inode, where
776  * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1
777  * corresponds to an alternate data stream.
778  *
779  * This works for both resolved and un-resolved inodes.
780  */
781 struct wim_lookup_table_entry *
782 inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx,
783                  const struct wim_lookup_table *table)
784 {
785         if (inode->i_resolved)
786                 return inode_stream_lte_resolved(inode, stream_idx);
787         else
788                 return inode_stream_lte_unresolved(inode, stream_idx, table);
789 }
790
791
792 /* Return the lookup table entry for the unnamed data stream of an inode, or
793  * NULL if there is none.
794  *
795  * You'd think this would be easier than it actually is, since the unnamed data
796  * stream should be the one referenced from the inode itself.  Alas, if there
797  * are named data streams, Microsoft's "imagex.exe" program will put the unnamed
798  * data stream in one of the alternate data streams instead of inside the WIM
799  * dentry itself.  So we need to check the alternate data streams too.
800  *
801  * Also, note that a dentry may appear to have more than one unnamed stream, but
802  * if the SHA1 message digest is all 0's then the corresponding stream does not
803  * really "count" (this is the case for the inode's own file stream when the
804  * file stream that should be there is actually in one of the alternate stream
805  * entries.).  This is despite the fact that we may need to extract such a
806  * missing entry as an empty file or empty named data stream.
807  */
808 struct wim_lookup_table_entry *
809 inode_unnamed_lte(const struct wim_inode *inode,
810                   const struct wim_lookup_table *table)
811 {
812         if (inode->i_resolved)
813                 return inode_unnamed_lte_resolved(inode);
814         else
815                 return inode_unnamed_lte_unresolved(inode, table);
816 }
817
818 static int
819 lte_add_stream_size(struct wim_lookup_table_entry *lte, void *total_bytes_p)
820 {
821         *(u64*)total_bytes_p += lte->resource_entry.size;
822         return 0;
823 }
824
825 u64
826 lookup_table_total_stream_size(struct wim_lookup_table *table)
827 {
828         u64 total_size = 0;
829         for_lookup_table_entry(table, lte_add_stream_size, &total_size);
830         return total_size;
831 }