X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Flookup_table.c;h=064215ec0cf75529de50fe677ea758dbdd2fff5b;hp=d1e5061232f06747b8e70238224cf8fdd99f011c;hb=a8972bde4b080d2dd33a7500705d431b0f1fe901;hpb=276f9f9f9658f4a8bafd6216db46760abe8c848d diff --git a/src/lookup_table.c b/src/lookup_table.c index d1e50612..064215ec 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -28,12 +28,14 @@ #include "lookup_table.h" #include "buffer_io.h" #include +#include #ifdef WITH_FUSE #include #endif -struct wim_lookup_table *new_lookup_table(size_t capacity) +struct wim_lookup_table * +new_lookup_table(size_t capacity) { struct wim_lookup_table *table; struct hlist_head *array; @@ -84,7 +86,9 @@ clone_lookup_table_entry(const struct wim_lookup_table_entry *old) memcpy(new, old, sizeof(*old)); new->extracted_file = NULL; switch (new->resource_location) { +#ifdef __WIN32__ case RESOURCE_WIN32: +#endif case RESOURCE_IN_STAGING_FILE: case RESOURCE_IN_FILE_ON_DISK: BUILD_BUG_ON((void*)&old->file_on_disk != @@ -132,7 +136,8 @@ out_free: return NULL; } -void free_lookup_table_entry(struct wim_lookup_table_entry *lte) +void +free_lookup_table_entry(struct wim_lookup_table_entry *lte) { if (lte) { switch (lte->resource_location) { @@ -164,15 +169,16 @@ void free_lookup_table_entry(struct wim_lookup_table_entry *lte) } } -static int do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, - void *ignore) +static int +do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, void *ignore) { free_lookup_table_entry(entry); return 0; } -void free_lookup_table(struct wim_lookup_table *table) +void +free_lookup_table(struct wim_lookup_table *table) { DEBUG2("Freeing lookup table"); if (table) { @@ -192,8 +198,9 @@ void free_lookup_table(struct wim_lookup_table *table) * @table: A pointer to the lookup table. * @lte: A pointer to the entry to insert. */ -void lookup_table_insert(struct wim_lookup_table *table, - struct wim_lookup_table_entry *lte) +void +lookup_table_insert(struct wim_lookup_table *table, + struct wim_lookup_table_entry *lte) { size_t i = lte->hash_short % table->capacity; hlist_add_head(<e->hash_list, &table->array[i]); @@ -202,7 +209,8 @@ void lookup_table_insert(struct wim_lookup_table *table, table->num_entries++; } -static void finalize_lte(struct wim_lookup_table_entry *lte) +static void +finalize_lte(struct wim_lookup_table_entry *lte) { #ifdef WITH_FUSE if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { @@ -217,8 +225,9 @@ static void finalize_lte(struct wim_lookup_table_entry *lte) * reference count reaches 0, it is unlinked from the lookup table. If, * furthermore, the entry has no opened file descriptors associated with it, the * entry is freed. */ -void lte_decrement_refcnt(struct wim_lookup_table_entry *lte, - struct wim_lookup_table *table) +void +lte_decrement_refcnt(struct wim_lookup_table_entry *lte, + struct wim_lookup_table *table) { wimlib_assert(lte != NULL); wimlib_assert(lte->refcnt != 0); @@ -232,7 +241,8 @@ void lte_decrement_refcnt(struct wim_lookup_table_entry *lte, } #ifdef WITH_FUSE -void lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) +void +lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) { if (lte->num_opened_fds != 0) if (--lte->num_opened_fds == 0 && lte->refcnt == 0) @@ -242,9 +252,10 @@ void lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) /* Calls a function on all the entries in the WIM lookup table. Stop early and * return nonzero if any call to the function returns nonzero. */ -int for_lookup_table_entry(struct wim_lookup_table *table, - int (*visitor)(struct wim_lookup_table_entry *, void *), - void *arg) +int +for_lookup_table_entry(struct wim_lookup_table *table, + int (*visitor)(struct wim_lookup_table_entry *, void *), + void *arg) { struct wim_lookup_table_entry *lte; struct hlist_node *pos, *tmp; @@ -254,6 +265,7 @@ int for_lookup_table_entry(struct wim_lookup_table *table, hlist_for_each_entry_safe(lte, pos, tmp, &table->array[i], hash_list) { + wimlib_assert2(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)); ret = visitor(lte, arg); if (ret != 0) return ret; @@ -262,17 +274,109 @@ int for_lookup_table_entry(struct wim_lookup_table *table, return 0; } +static int +cmp_streams_by_wim_position(const void *p1, const void *p2) +{ + const struct wim_lookup_table_entry *lte1, *lte2; + lte1 = *(const struct wim_lookup_table_entry**)p1; + lte2 = *(const struct wim_lookup_table_entry**)p2; + if (lte1->resource_entry.offset < lte2->resource_entry.offset) + return -1; + else if (lte1->resource_entry.offset > lte2->resource_entry.offset) + return 1; + else + return 0; +} + +int +sort_stream_list_by_wim_position(struct list_head *stream_list) +{ + struct list_head *cur; + size_t num_streams; + struct wim_lookup_table_entry **array; + size_t i; + size_t array_size; + + num_streams = 0; + list_for_each(cur, stream_list) + num_streams++; + array_size = num_streams * sizeof(array[0]); + array = MALLOC(array_size); + if (!array) { + ERROR("Failed to allocate %zu bytes to sort stream entries", + array_size); + return WIMLIB_ERR_NOMEM; + } + cur = stream_list->next; + for (i = 0; i < num_streams; i++) { + array[i] = container_of(cur, struct wim_lookup_table_entry, staging_list); + cur = cur->next; + } + + qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position); + + INIT_LIST_HEAD(stream_list); + for (i = 0; i < num_streams; i++) + list_add_tail(&array[i]->staging_list, stream_list); + FREE(array); + return 0; +} + + +static int +add_lte_to_array(struct wim_lookup_table_entry *lte, + void *_pp) +{ + struct wim_lookup_table_entry ***pp = _pp; + *(*pp)++ = lte; + return 0; +} + +/* Iterate through the lookup table entries, but first sort them by stream + * offset in the WIM. Caution: this is intended to be used when the stream + * offset field has actually been set. */ +int +for_lookup_table_entry_pos_sorted(struct wim_lookup_table *table, + int (*visitor)(struct wim_lookup_table_entry *, + void *), + void *arg) +{ + struct wim_lookup_table_entry **lte_array, **p; + size_t num_streams = table->num_entries; + int ret; + + lte_array = MALLOC(num_streams * sizeof(lte_array[0])); + if (!lte_array) + return WIMLIB_ERR_NOMEM; + p = lte_array; + for_lookup_table_entry(table, add_lte_to_array, &p); + + wimlib_assert(p == lte_array + num_streams); + + qsort(lte_array, num_streams, sizeof(lte_array[0]), + cmp_streams_by_wim_position); + ret = 0; + for (size_t i = 0; i < num_streams && ret == 0; i++) + ret = visitor(lte_array[i], arg); + FREE(lte_array); + return ret; +} /* * Reads the lookup table from a WIM file. + * + * Saves lookup table entries for non-metadata streams in a hash table, and + * saves the metadata entry for each image in a special per-image location (the + * image_metadata array). */ -int read_lookup_table(WIMStruct *w) +int +read_lookup_table(WIMStruct *w) { u64 num_entries; u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE]; int ret; struct wim_lookup_table *table; - struct wim_lookup_table_entry *cur_entry = NULL, *duplicate_entry; + struct wim_lookup_table_entry *cur_entry, *duplicate_entry; if (resource_is_compressed(&w->hdr.lookup_table_res_entry)) { ERROR("Didn't expect a compressed lookup table!"); @@ -298,6 +402,7 @@ int read_lookup_table(WIMStruct *w) if (!table) return WIMLIB_ERR_NOMEM; + w->current_image = 0; while (num_entries--) { const u8 *p; @@ -309,16 +414,16 @@ int read_lookup_table(WIMStruct *w) "table"); } ret = WIMLIB_ERR_READ; - goto out; + goto out_free_lookup_table; } cur_entry = new_lookup_table_entry(); if (!cur_entry) { ret = WIMLIB_ERR_NOMEM; - goto out; + goto out_free_lookup_table; } + cur_entry->wim = w; cur_entry->resource_location = RESOURCE_IN_WIM; - p = get_resource_entry(buf, &cur_entry->resource_entry); p = get_u16(p, &cur_entry->part_number); p = get_u32(p, &cur_entry->refcnt); @@ -339,27 +444,6 @@ int read_lookup_table(WIMStruct *w) goto out_free_cur_entry; } - /* Ordinarily, no two streams should share the same SHA1 message - * digest. However, this constraint can be broken for metadata - * resources--- two identical images will have the same metadata - * resource, but their lookup table entries are not shared. */ - duplicate_entry = __lookup_resource(table, cur_entry->hash); - if (duplicate_entry - && !((duplicate_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) - && cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)) - { - #ifdef ENABLE_ERROR_MESSAGES - ERROR("The WIM lookup table contains two entries with the " - "same SHA1 message digest!"); - ERROR("The first entry is:"); - print_lookup_table_entry(duplicate_entry, stderr); - ERROR("The second entry is:"); - print_lookup_table_entry(cur_entry, stderr); - #endif - ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; - goto out_free_cur_entry; - } - if (!(cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_COMPRESSED) && (cur_entry->resource_entry.size != cur_entry->resource_entry.original_size)) @@ -373,26 +457,84 @@ int read_lookup_table(WIMStruct *w) ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; goto out_free_cur_entry; } - if ((cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) - && cur_entry->refcnt != 1) - { - #ifdef ENABLE_ERROR_MESSAGES - ERROR("Found metadata resource with refcnt != 1:"); - print_lookup_table_entry(cur_entry, stderr); - #endif - ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; - goto out_free_cur_entry; + + if (cur_entry->resource_entry.flags & WIM_RESHDR_FLAG_METADATA) { + /* Lookup table entry for a metadata resource */ + if (cur_entry->refcnt != 1) { + #ifdef ENABLE_ERROR_MESSAGES + ERROR("Found metadata resource with refcnt != 1:"); + print_lookup_table_entry(cur_entry, stderr); + #endif + ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; + goto out_free_cur_entry; + } + + if (w->hdr.part_number != 1) { + ERROR("Found a metadata resource in a " + "non-first part of the split WIM!"); + ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; + goto out_free_cur_entry; + } + if (w->current_image == w->hdr.image_count) { + ERROR("The WIM header says there are %u images " + "in the WIM, but we found more metadata " + "resources than this", w->hdr.image_count); + ret = WIMLIB_ERR_IMAGE_COUNT; + goto out_free_cur_entry; + } + + /* Notice very carefully: We are assigning the metadata + * resources in the exact order mirrored by their lookup + * table entries on disk, which is the behavior of + * Microsoft's software. In particular, this overrides + * the actual locations of the metadata resources + * themselves in the WIM file as well as any information + * written in the XML data. */ + DEBUG("Found metadata resource for image %u at " + "offset %"PRIu64".", + w->current_image + 1, + cur_entry->resource_entry.offset); + w->image_metadata[ + w->current_image++].metadata_lte = cur_entry; + } else { + /* Lookup table entry for a stream that is not a + * metadata resource */ + duplicate_entry = __lookup_resource(table, cur_entry->hash); + if (duplicate_entry) { + #ifdef ENABLE_ERROR_MESSAGES + ERROR("The WIM lookup table contains two entries with the " + "same SHA1 message digest!"); + ERROR("The first entry is:"); + print_lookup_table_entry(duplicate_entry, stderr); + ERROR("The second entry is:"); + print_lookup_table_entry(cur_entry, stderr); + #endif + ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY; + goto out_free_cur_entry; + } + lookup_table_insert(table, cur_entry); } - lookup_table_insert(table, cur_entry); + } + if (w->hdr.part_number == 1 && + w->current_image != w->hdr.image_count) + { + ERROR("The WIM header says there are %u images " + "in the WIM, but we only found %d metadata " + "resources!", w->hdr.image_count, w->current_image); + ret = WIMLIB_ERR_IMAGE_COUNT; + goto out_free_lookup_table; } DEBUG("Done reading lookup table."); w->lookup_table = table; - return 0; + ret = 0; + goto out; out_free_cur_entry: FREE(cur_entry); -out: +out_free_lookup_table: free_lookup_table(table); +out: + w->current_image = 0; return ret; } @@ -400,13 +542,14 @@ out: /* * Writes a lookup table entry to the output file. */ -int write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *__out) +int +write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *_out) { FILE *out; u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE]; u8 *p; - out = __out; + out = _out; /* Don't write entries that have not had file resources or metadata * resources written for them. */ @@ -430,21 +573,44 @@ int write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *__out) return 0; } -/* Writes the lookup table to the output file. */ -int write_lookup_table(struct wim_lookup_table *table, FILE *out, - struct resource_entry *out_res_entry) +/* Writes the WIM lookup table to the output file. */ +int +write_lookup_table(WIMStruct *w, int image, struct resource_entry *out_res_entry) { + FILE *out = w->out_fp; off_t start_offset, end_offset; int ret; + int start_image, end_image; start_offset = ftello(out); if (start_offset == -1) return WIMLIB_ERR_WRITE; - ret = for_lookup_table_entry(table, write_lookup_table_entry, out); - if (ret != 0) + /* Write lookup table entries for metadata resources */ + if (image == WIMLIB_ALL_IMAGES) { + start_image = 1; + end_image = w->hdr.image_count; + } else { + start_image = image; + end_image = image; + } + for (int i = start_image; i <= end_image; i++) { + struct wim_lookup_table_entry *metadata_lte; + + metadata_lte = w->image_metadata[i - 1].metadata_lte; + metadata_lte->out_refcnt = 1; + metadata_lte->output_resource_entry.flags |= WIM_RESHDR_FLAG_METADATA; + ret = write_lookup_table_entry(metadata_lte, out); + if (ret) + return ret; + } + + /* Write lookup table entries for other resources */ + ret = for_lookup_table_entry(w->lookup_table, write_lookup_table_entry, out); + if (ret) return ret; + /* Fill in the resource entry for the lookup table itself */ end_offset = ftello(out); if (end_offset == -1) return WIMLIB_ERR_WRITE; @@ -453,24 +619,26 @@ int write_lookup_table(struct wim_lookup_table *table, FILE *out, out_res_entry->size = end_offset - start_offset; out_res_entry->original_size = end_offset - start_offset; out_res_entry->flags = WIM_RESHDR_FLAG_METADATA; - return 0; } -int lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore) { lte->real_refcnt = 0; return 0; } -int lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore) { lte->out_refcnt = 0; return 0; } -int lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) { if (lte->extracted_file != NULL) { FREE(lte->extracted_file); @@ -479,8 +647,8 @@ int lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) return 0; } -void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, - FILE *out) +void +print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out) { if (!lte) { tputc(T('\n'), out); @@ -499,7 +667,7 @@ void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, tfprintf(out, T("Reference Count = %u\n"), lte->refcnt); tfprintf(out, T("Hash = 0x")); - print_hash(lte->hash); + print_hash(lte->hash, out); tputc(T('\n'), out); tfprintf(out, T("Flags = ")); @@ -520,6 +688,9 @@ void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, lte->wim->filename); } break; +#ifdef __WIN32__ + case RESOURCE_WIN32: +#endif case RESOURCE_IN_FILE_ON_DISK: tfprintf(out, T("File on Disk = `%"TS"'\n"), lte->file_on_disk); @@ -534,8 +705,8 @@ void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, tputc(T('\n'), out); } -static int do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, - void *fp) +static int +do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, void *fp) { print_lookup_table_entry(lte, (FILE*)fp); return 0; @@ -544,7 +715,8 @@ static int do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, /* * Prints the lookup table of a WIM file. */ -WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w) +WIMLIBAPI void +wimlib_print_lookup_table(WIMStruct *w) { for_lookup_table_entry(w->lookup_table, do_print_lookup_table_entry, @@ -650,7 +822,8 @@ out: * This function always succeeds; unresolved lookup table entries are given a * NULL pointer. */ -void inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) +void +inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) { if (!inode->i_resolved) { @@ -669,7 +842,8 @@ void inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) } } -void inode_unresolve_ltes(struct wim_inode *inode) +void +inode_unresolve_ltes(struct wim_inode *inode) { if (inode->i_resolved) { if (inode->i_lte) @@ -693,7 +867,7 @@ void inode_unresolve_ltes(struct wim_inode *inode) * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1 * corresponds to an alternate data stream. * - * This works for both resolved and un-resolved dentries. + * This works for both resolved and un-resolved inodes. */ struct wim_lookup_table_entry * inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx, @@ -732,14 +906,15 @@ inode_unnamed_lte(const struct wim_inode *inode, return inode_unnamed_lte_unresolved(inode, table); } -static int lte_add_stream_size(struct wim_lookup_table_entry *lte, - void *total_bytes_p) +static int +lte_add_stream_size(struct wim_lookup_table_entry *lte, void *total_bytes_p) { *(u64*)total_bytes_p += lte->resource_entry.size; return 0; } -u64 lookup_table_total_stream_size(struct wim_lookup_table *table) +u64 +lookup_table_total_stream_size(struct wim_lookup_table *table) { u64 total_size = 0; for_lookup_table_entry(table, lte_add_stream_size, &total_size);