]> wimlib.net Git - wimlib/blob - src/lookup_table.c
Partial fixes and comments
[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;
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                 lookup_table_insert(table, cur_entry);
225         }
226         DEBUG("Done reading lookup table.");
227         w->lookup_table = table;
228         return 0;
229 out:
230         free_lookup_table(table);
231         return ret;
232 }
233
234
235 /* 
236  * Writes a lookup table entry to the output file.
237  */
238 int write_lookup_table_entry(struct lookup_table_entry *lte, void *__out)
239 {
240         FILE *out;
241         u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE];
242         u8 *p;
243
244         out = __out;
245
246         /* do not write lookup table entries for empty files */
247         if (lte->output_resource_entry.original_size == 0)
248                 return 0;
249
250         /* Don't write entries that have not had file resources or metadata
251          * resources written for them. */
252         if (lte->out_refcnt == 0)
253                 return 0;
254
255         if (lte->output_resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
256                 DEBUG("Writing metadata entry at %lu (orig size = %zu)",
257                       ftello(out), lte->output_resource_entry.original_size);
258
259         p = put_resource_entry(buf, &lte->output_resource_entry);
260         p = put_u16(p, lte->part_number);
261         p = put_u32(p, lte->out_refcnt);
262         p = put_bytes(p, SHA1_HASH_SIZE, lte->hash);
263         if (fwrite(buf, 1, sizeof(buf), out) != sizeof(buf)) {
264                 ERROR_WITH_ERRNO("Failed to write lookup table entry");
265                 return WIMLIB_ERR_WRITE;
266         }
267         return 0;
268 }
269
270
271
272 int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore)
273 {
274         entry->out_refcnt = 0;
275         return 0;
276 }
277
278 void print_lookup_table_entry(const struct lookup_table_entry *lte)
279 {
280         if (!lte) {
281                 putchar('\n');
282                 return;
283         }
284         printf("Offset            = %"PRIu64" bytes\n", 
285                lte->resource_entry.offset);
286         printf("Size              = %"PRIu64" bytes\n", 
287                (u64)lte->resource_entry.size);
288         printf("Original size     = %"PRIu64" bytes\n", 
289                lte->resource_entry.original_size);
290         printf("Part Number       = %hu\n", lte->part_number);
291         printf("Reference Count   = %u\n", lte->refcnt);
292         printf("Hash              = 0x");
293         print_hash(lte->hash);
294         putchar('\n');
295         printf("Flags             = ");
296         u8 flags = lte->resource_entry.flags;
297         if (flags & WIM_RESHDR_FLAG_COMPRESSED)
298                 fputs("WIM_RESHDR_FLAG_COMPRESSED, ", stdout);
299         if (flags & WIM_RESHDR_FLAG_FREE)
300                 fputs("WIM_RESHDR_FLAG_FREE, ", stdout);
301         if (flags & WIM_RESHDR_FLAG_METADATA)
302                 fputs("WIM_RESHDR_FLAG_METADATA, ", stdout);
303         if (flags & WIM_RESHDR_FLAG_SPANNED)
304                 fputs("WIM_RESHDR_FLAG_SPANNED, ", stdout);
305         putchar('\n');
306         switch (lte->resource_location) {
307         case RESOURCE_IN_WIM:
308                 if (lte->wim->filename) {
309                         printf("WIM file          = `%s'\n",
310                                lte->wim->filename);
311                 }
312                 break;
313         case RESOURCE_IN_FILE_ON_DISK:
314                 printf("File on Disk      = `%s'\n", lte->file_on_disk);
315                 break;
316         case RESOURCE_IN_STAGING_FILE:
317                 printf("Staging File      = `%s'\n", lte->staging_file_name);
318                 break;
319         }
320         putchar('\n');
321 }
322
323 static int do_print_lookup_table_entry(struct lookup_table_entry *lte,
324                                        void *ignore)
325 {
326         print_lookup_table_entry(lte);
327         return 0;
328 }
329
330 /*
331  * Prints the lookup table of a WIM file. 
332  */
333 WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w)
334 {
335         for_lookup_table_entry(w->lookup_table, 
336                                do_print_lookup_table_entry,
337                                NULL);
338 }
339
340 /* 
341  * Looks up an entry in the lookup table.
342  */
343 struct lookup_table_entry *
344 __lookup_resource(const struct lookup_table *table, const u8 hash[])
345 {
346         size_t i;
347         struct lookup_table_entry *lte;
348         struct hlist_node *pos;
349
350         i = *(size_t*)hash % table->capacity;
351         hlist_for_each_entry(lte, pos, &table->array[i], hash_list)
352                 if (hashes_equal(hash, lte->hash))
353                         return lte;
354         return NULL;
355 }
356
357 /* 
358  * Finds the dentry, lookup table entry, and stream index for a WIM file stream,
359  * given a path name.
360  *
361  * This is only for pre-resolved dentries.
362  */
363 int lookup_resource(WIMStruct *w, const char *path,
364                     int lookup_flags,
365                     struct dentry **dentry_ret,
366                     struct lookup_table_entry **lte_ret,
367                     unsigned *stream_idx_ret)
368 {
369         struct dentry *dentry;
370         struct lookup_table_entry *lte;
371         unsigned stream_idx;
372         dentry = get_dentry(w, path);
373         if (!dentry)
374                 return -ENOENT;
375
376         wimlib_assert(dentry->resolved);
377
378         lte = dentry->lte;
379         if (!(lookup_flags & LOOKUP_FLAG_DIRECTORY_OK)
380               && dentry_is_directory(dentry))
381                 return -EISDIR;
382         stream_idx = 0;
383         if (lookup_flags & LOOKUP_FLAG_ADS_OK) {
384                 const char *stream_name = path_stream_name(path);
385                 if (stream_name) {
386                         size_t stream_name_len = strlen(stream_name);
387                         for (u16 i = 0; i < dentry->num_ads; i++) {
388                                 if (ads_entry_has_name(&dentry->ads_entries[i],
389                                                        stream_name,
390                                                        stream_name_len))
391                                 {
392                                         stream_idx = i + 1;
393                                         lte = dentry->ads_entries[i].lte;
394                                         goto out;
395                                 }
396                         }
397                         return -ENOENT;
398                 }
399         }
400 out:
401         if (dentry_ret)
402                 *dentry_ret = dentry;
403         if (lte_ret)
404                 *lte_ret = lte;
405         if (stream_idx_ret)
406                 *stream_idx_ret = stream_idx;
407         return 0;
408 }
409
410 /* Resolve a dentry's lookup table entries 
411  *
412  * This replaces the SHA1 hash fields (which are used to lookup an entry in the
413  * lookup table) with pointers directly to the lookup table entries.  A circular
414  * linked list of streams sharing the same lookup table entry is created.
415  *
416  * This function always succeeds; unresolved lookup table entries are given a
417  * NULL pointer.
418  */
419 int dentry_resolve_ltes(struct dentry *dentry, void *__table)
420 {
421         struct lookup_table *table = __table;
422         struct lookup_table_entry *lte;
423
424         if (dentry->resolved)
425                 return 0;
426
427         /* Resolve the default file stream */
428         lte = __lookup_resource(table, dentry->hash);
429         if (lte)
430                 list_add(&dentry->lte_group_list.list, &lte->lte_group_list);
431         else
432                 INIT_LIST_HEAD(&dentry->lte_group_list.list);
433         dentry->lte = lte;
434         dentry->lte_group_list.type = STREAM_TYPE_NORMAL;
435         dentry->resolved = true;
436
437         /* Resolve the alternate data streams */
438         if (dentry->ads_entries_status != ADS_ENTRIES_USER) {
439                 for (u16 i = 0; i < dentry->num_ads; i++) {
440                         struct ads_entry *cur_entry = &dentry->ads_entries[i];
441
442                         lte = __lookup_resource(table, cur_entry->hash);
443                         if (lte)
444                                 list_add(&cur_entry->lte_group_list.list,
445                                          &lte->lte_group_list);
446                         else
447                                 INIT_LIST_HEAD(&cur_entry->lte_group_list.list);
448                         cur_entry->lte = lte;
449                         cur_entry->lte_group_list.type = STREAM_TYPE_ADS;
450                 }
451         }
452         return 0;
453 }
454
455 struct lookup_table_entry *
456 dentry_first_lte(const struct dentry *dentry, const struct lookup_table *table)
457 {
458         if (dentry->resolved)
459                 return dentry_first_lte_resolved(dentry);
460         else
461                 return dentry_first_lte_unresolved(dentry, table);
462 }
463