]> wimlib.net Git - wimlib/blob - src/lookup_table.c
Fix wimfs_getattr()
[wimlib] / src / lookup_table.c
1 /*
2  * lookup_table.c
3  *
4  * Lookup table, implemented as a hash table, that maps dentries to file
5  * resources.
6  */
7
8 /*
9  * Copyright (C) 2012 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 Lesser General Public License as published by the Free
15  * Software Foundation; either version 2.1 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 Lesser General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU Lesser 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 "io.h"
30 #include <errno.h>
31
32 struct lookup_table *new_lookup_table(size_t capacity)
33 {
34         struct lookup_table *table;
35         struct hlist_head *array;
36
37         table = MALLOC(sizeof(struct lookup_table));
38         if (!table)
39                 goto err;
40         array = CALLOC(capacity, sizeof(array[0]));
41         if (!array) {
42                 FREE(table);
43                 goto err;
44         }
45         table->num_entries = 0;
46         table->capacity = capacity;
47         table->array = array;
48         return table;
49 err:
50         ERROR("Failed to allocate memory for lookup table with capacity %zu",
51               capacity);
52         return NULL;
53 }
54
55 struct lookup_table_entry *new_lookup_table_entry(WIMStruct *wim)
56 {
57         struct lookup_table_entry *lte;
58         
59         lte = CALLOC(1, sizeof(struct lookup_table_entry));
60         if (!lte) {
61                 ERROR("Out of memory (tried to allocate %zu bytes for "
62                       "lookup table entry)",
63                       sizeof(struct lookup_table_entry));
64                 return NULL;
65         }
66
67         lte->part_number  = 1;
68         lte->refcnt       = 1;
69         lte->wim          = wim;
70         INIT_LIST_HEAD(&lte->lte_group_list);
71         return lte;
72 }
73
74 void free_lookup_table_entry(struct lookup_table_entry *lte)
75 {
76         if (lte) {
77                 if (lte->staging_list.next)
78                         list_del(&lte->staging_list);
79                 if (lte->resource_location != RESOURCE_IN_WIM &&
80                     lte->resource_location != RESOURCE_NONEXISTENT)
81                         FREE(lte->file_on_disk);
82                 FREE(lte);
83         }
84 }
85
86 static int do_free_lookup_table_entry(struct lookup_table_entry *entry,
87                                       void *ignore)
88 {
89         free_lookup_table_entry(entry);
90         return 0;
91 }
92
93
94 void free_lookup_table(struct lookup_table *table)
95 {
96         DEBUG("Freeing lookup table");
97         if (table) {
98                 if (table->array) {
99                         for_lookup_table_entry(table,
100                                                do_free_lookup_table_entry,
101                                                NULL);
102                         FREE(table->array);
103                 }
104                 FREE(table);
105         }
106 }
107
108 /*
109  * Inserts an entry into the lookup table.
110  *
111  * @table:      A pointer to the lookup table.
112  * @entry:      A pointer to the entry to insert.
113  */
114 void lookup_table_insert(struct lookup_table *table, 
115                          struct lookup_table_entry *lte)
116 {
117         size_t i = lte->hash_short % table->capacity;
118         hlist_add_head(&lte->hash_list, &table->array[i]);
119
120         /* XXX Make the table grow when too many entries have been inserted. */
121         table->num_entries++;
122 }
123
124
125
126 /* Decrements the reference count for the lookup table entry @lte.  If its
127  * reference count reaches 0, it is unlinked from the lookup table.  If,
128  * furthermore, the entry has no opened file descriptors associated with it, the
129  * entry is freed.  */
130 struct lookup_table_entry *
131 lte_decrement_refcnt(struct lookup_table_entry *lte, struct lookup_table *table)
132 {
133         if (lte) {
134                 wimlib_assert(lte->refcnt);
135                 if (--lte->refcnt == 0) {
136                         lookup_table_unlink(table, lte);
137                         if (lte->num_opened_fds == 0) {
138                                 free_lookup_table_entry(lte);
139                                 lte = NULL;
140                         }
141                 }
142         }
143         return lte;
144 }
145
146 /* 
147  * Calls a function on all the entries in the lookup table.  Stop early and
148  * return nonzero if any call to the function returns nonzero.
149  */
150 int for_lookup_table_entry(struct lookup_table *table, 
151                            int (*visitor)(struct lookup_table_entry *, void *),
152                            void *arg)
153 {
154         struct lookup_table_entry *lte;
155         struct hlist_node *pos, *tmp;
156         int ret;
157
158         for (size_t i = 0; i < table->capacity; i++) {
159                 hlist_for_each_entry_safe(lte, pos, tmp, &table->array[i],
160                                           hash_list)
161                 {
162                         ret = visitor(lte, arg);
163                         if (ret != 0)
164                                 return ret;
165                 }
166         }
167         return 0;
168 }
169
170
171 /*
172  * Reads the lookup table from a WIM file.
173  */
174 int read_lookup_table(WIMStruct *w)
175 {
176         u64    num_entries;
177         u8     buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
178         int    ret;
179         struct lookup_table *table;
180
181         DEBUG("Reading lookup table: offset %"PRIu64", size %"PRIu64"",
182               w->hdr.lookup_table_res_entry.offset,
183               w->hdr.lookup_table_res_entry.original_size);
184
185         if (fseeko(w->fp, w->hdr.lookup_table_res_entry.offset, SEEK_SET) != 0) {
186                 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" to read "
187                                  "lookup table",
188                                  w->hdr.lookup_table_res_entry.offset);
189                 return WIMLIB_ERR_READ;
190         }
191
192         num_entries = w->hdr.lookup_table_res_entry.original_size /
193                       WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE;
194         table = new_lookup_table(num_entries * 2 + 1);
195         if (!table)
196                 return WIMLIB_ERR_NOMEM;
197
198         while (num_entries--) {
199                 const u8 *p;
200                 struct lookup_table_entry *cur_entry, *duplicate_entry;
201
202                 if (fread(buf, 1, sizeof(buf), w->fp) != sizeof(buf)) {
203                         if (feof(w->fp)) {
204                                 ERROR("Unexpected EOF in WIM lookup table!");
205                         } else {
206                                 ERROR_WITH_ERRNO("Error reading WIM lookup "
207                                                  "table");
208                         }
209                         ret = WIMLIB_ERR_READ;
210                         goto out;
211                 }
212                 cur_entry = new_lookup_table_entry(w);
213                 if (!cur_entry) {
214                         ret = WIMLIB_ERR_NOMEM;
215                         goto out;
216                 }
217                 cur_entry->wim = w;
218                 cur_entry->resource_location = RESOURCE_IN_WIM;
219                          
220                 p = get_resource_entry(buf, &cur_entry->resource_entry);
221                 p = get_u16(p, &cur_entry->part_number);
222                 p = get_u32(p, &cur_entry->refcnt);
223                 p = get_bytes(p, SHA1_HASH_SIZE, cur_entry->hash);
224
225                 duplicate_entry = __lookup_resource(table, cur_entry->hash);
226                 if (duplicate_entry) {
227                         ERROR("The WIM lookup table contains two entries with the "
228                               "same SHA1 message digest!");
229                         ERROR("The first entry is:");
230                         print_lookup_table_entry(duplicate_entry);
231                         ERROR("The second entry is:");
232                         print_lookup_table_entry(cur_entry);
233                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
234                         goto out;
235                 }
236                 lookup_table_insert(table, cur_entry);
237
238                 if (!(cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED)
239                     && (cur_entry->resource_entry.size !=
240                       cur_entry->resource_entry.original_size))
241                 {
242                         ERROR("Found uncompressed resource with original size "
243                               "not the same as compressed size");
244                         ERROR("The lookup table entry for the resource is as follows:");
245                         print_lookup_table_entry(cur_entry);
246                         ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
247                         goto out;
248                 }
249         }
250         DEBUG("Done reading lookup table.");
251         w->lookup_table = table;
252         return 0;
253 out:
254         free_lookup_table(table);
255         return ret;
256 }
257
258
259 /* 
260  * Writes a lookup table entry to the output file.
261  */
262 int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out)
263 {
264         FILE *out;
265         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
266         u8 *p;
267
268         out = __out;
269
270         /* do not write lookup table entries for empty files */
271         if (lte->output_resource_entry.original_size == 0)
272                 return 0;
273
274         /* Don't write entries that have not had file resources or metadata
275          * resources written for them. */
276         if (lte->out_refcnt == 0)
277                 return 0;
278
279         if (lte->output_resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
280                 DEBUG("Writing metadata entry at %lu (orig size = %zu)",
281                       ftello(out), lte->output_resource_entry.original_size);
282
283         p = put_resource_entry(buf, &lte->output_resource_entry);
284         p = put_u16(p, lte->part_number);
285         p = put_u32(p, lte->out_refcnt);
286         p = put_bytes(p, SHA1_HASH_SIZE, lte->hash);
287         if (fwrite(buf, 1, sizeof(buf), out) != sizeof(buf)) {
288                 ERROR_WITH_ERRNO("Failed to write lookup table entry");
289                 return WIMLIB_ERR_WRITE;
290         }
291         return 0;
292 }
293
294
295
296 int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore)
297 {
298         entry->out_refcnt = 0;
299         return 0;
300 }
301
302 void print_lookup_table_entry(const struct lookup_table_entry *lte)
303 {
304         if (!lte) {
305                 putchar('\n');
306                 return;
307         }
308         printf("Offset            = %"PRIu64" bytes\n", 
309                lte->resource_entry.offset);
310         printf("Size              = %"PRIu64" bytes\n", 
311                (u64)lte->resource_entry.size);
312         printf("Original size     = %"PRIu64" bytes\n", 
313                lte->resource_entry.original_size);
314         printf("Part Number       = %hu\n", lte->part_number);
315         printf("Reference Count   = %u\n", lte->refcnt);
316         printf("Hash              = 0x");
317         print_hash(lte->hash);
318         putchar('\n');
319         printf("Flags             = ");
320         u8 flags = lte->resource_entry.flags;
321         if (flags & WIM_RESHDR_FLAG_COMPRESSED)
322                 fputs("WIM_RESHDR_FLAG_COMPRESSED, ", stdout);
323         if (flags & WIM_RESHDR_FLAG_FREE)
324                 fputs("WIM_RESHDR_FLAG_FREE, ", stdout);
325         if (flags & WIM_RESHDR_FLAG_METADATA)
326                 fputs("WIM_RESHDR_FLAG_METADATA, ", stdout);
327         if (flags & WIM_RESHDR_FLAG_SPANNED)
328                 fputs("WIM_RESHDR_FLAG_SPANNED, ", stdout);
329         putchar('\n');
330         switch (lte->resource_location) {
331         case RESOURCE_IN_WIM:
332                 if (lte->wim->filename) {
333                         printf("WIM file          = `%s'\n",
334                                lte->wim->filename);
335                 }
336                 break;
337         case RESOURCE_IN_FILE_ON_DISK:
338                 printf("File on Disk      = `%s'\n", lte->file_on_disk);
339                 break;
340         case RESOURCE_IN_STAGING_FILE:
341                 printf("Staging File      = `%s'\n", lte->staging_file_name);
342                 break;
343         }
344         putchar('\n');
345 }
346
347 static int do_print_lookup_table_entry(struct lookup_table_entry *lte,
348                                        void *ignore)
349 {
350         print_lookup_table_entry(lte);
351         return 0;
352 }
353
354 /*
355  * Prints the lookup table of a WIM file. 
356  */
357 WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w)
358 {
359         for_lookup_table_entry(w->lookup_table, 
360                                do_print_lookup_table_entry,
361                                NULL);
362 }
363
364 /* 
365  * Looks up an entry in the lookup table.
366  */
367 struct lookup_table_entry *
368 __lookup_resource(const struct lookup_table *table, const u8 hash[])
369 {
370         size_t i;
371         struct lookup_table_entry *lte;
372         struct hlist_node *pos;
373
374         i = *(size_t*)hash % table->capacity;
375         hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
376                 if (hashes_equal(hash, lte->hash))
377                         return lte;
378         return NULL;
379 }
380
381 /* 
382  * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
383  * given a path name.
384  *
385  * This is only for pre-resolved dentries.
386  */
387 int lookup_resource(WIMStruct *w, const char *path,
388                     int lookup_flags,
389                     struct dentry **dentry_ret,
390                     struct lookup_table_entry **lte_ret,
391                     unsigned *stream_idx_ret)
392 {
393         struct dentry *dentry;
394         struct lookup_table_entry *lte;
395         unsigned stream_idx;
396         const char *stream_name;
397         char *p = NULL;
398
399         if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
400                 stream_name = path_stream_name(path);
401                 if (stream_name) {
402                         p = (char*)stream_name - 1;
403                         *p = '\0';
404                 }
405         }
406
407         dentry = get_dentry(w, path);
408         if (p)
409                 *p = ':';
410         if (!dentry)
411                 return -ENOENT;
412
413         wimlib_assert(dentry->resolved);
414
415         lte = dentry->lte;
416         if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
417               && dentry_is_directory(dentry))
418                 return -EISDIR;
419         stream_idx = 0;
420         if (stream_name) {
421                 size_t stream_name_len = strlen(stream_name);
422                 for (u16 i = 0; i < dentry->num_ads; i++) {
423                         if (ads_entry_has_name(&dentry->ads_entries[i],
424                                                stream_name,
425                                                stream_name_len))
426                         {
427                                 stream_idx = i + 1;
428                                 lte = dentry->ads_entries[i].lte;
429                                 goto out;
430                         }
431                 }
432                 return -ENOENT;
433         }
434 out:
435         if (dentry_ret)
436                 *dentry_ret = dentry;
437         if (lte_ret)
438                 *lte_ret = lte;
439         if (stream_idx_ret)
440                 *stream_idx_ret = stream_idx;
441         return 0;
442 }
443
444 /* Resolve a dentry's lookup table entries 
445  *
446  * This replaces the SHA1 hash fields (which are used to lookup an entry in the
447  * lookup table) with pointers directly to the lookup table entries.  A circular
448  * linked list of streams sharing the same lookup table entry is created.
449  *
450  * This function always succeeds; unresolved lookup table entries are given a
451  * NULL pointer.
452  */
453 int dentry_resolve_ltes(struct dentry *dentry, void *__table)
454 {
455         struct lookup_table *table = __table;
456         struct lookup_table_entry *lte;
457
458         if (dentry->resolved)
459                 return 0;
460
461         /* Resolve the default file stream */
462         lte = __lookup_resource(table, dentry->hash);
463         if (lte)
464                 list_add(&dentry->lte_group_list.list, &lte->lte_group_list);
465         else
466                 INIT_LIST_HEAD(&dentry->lte_group_list.list);
467         dentry->lte = lte;
468         dentry->lte_group_list.type = STREAM_TYPE_NORMAL;
469         dentry->resolved = true;
470
471         /* Resolve the alternate data streams */
472         if (dentry->ads_entries_status != ADS_ENTRIES_USER) {
473                 for (u16 i = 0; i < dentry->num_ads; i++) {
474                         struct ads_entry *cur_entry = &dentry->ads_entries[i];
475
476                         lte = __lookup_resource(table, cur_entry->hash);
477                         if (lte)
478                                 list_add(&cur_entry->lte_group_list.list,
479                                          &lte->lte_group_list);
480                         else
481                                 INIT_LIST_HEAD(&cur_entry->lte_group_list.list);
482                         cur_entry->lte = lte;
483                         cur_entry->lte_group_list.type = STREAM_TYPE_ADS;
484                 }
485         }
486         return 0;
487 }
488
489 struct lookup_table_entry *
490 dentry_first_lte(const struct dentry *dentry, const struct lookup_table *table)
491 {
492         if (dentry->resolved)
493                 return dentry_first_lte_resolved(dentry);
494         else
495                 return dentry_first_lte_unresolved(dentry, table);
496 }
497