}
void
-free_lookup_table_entry(struct wim_lookup_table_entry *lte)
+lte_put_resource(struct wim_lookup_table_entry *lte)
{
- if (lte) {
- switch (lte->resource_location) {
- case RESOURCE_IN_WIM:
- list_del(<e->rspec_node);
- if (list_empty(<e->rspec->stream_list))
- FREE(lte->rspec);
- break;
- case RESOURCE_IN_FILE_ON_DISK:
- #ifdef __WIN32__
- case RESOURCE_WIN32_ENCRYPTED:
- #endif
- #ifdef WITH_FUSE
- case RESOURCE_IN_STAGING_FILE:
- BUILD_BUG_ON((void*)<e->file_on_disk !=
- (void*)<e->staging_file_name);
- #endif
- case RESOURCE_IN_ATTACHED_BUFFER:
- BUILD_BUG_ON((void*)<e->file_on_disk !=
- (void*)<e->attached_buffer);
- FREE(lte->file_on_disk);
- break;
-#ifdef WITH_NTFS_3G
- case RESOURCE_IN_NTFS_VOLUME:
- if (lte->ntfs_loc) {
- FREE(lte->ntfs_loc->path);
- FREE(lte->ntfs_loc->stream_name);
- FREE(lte->ntfs_loc);
- }
- break;
+ switch (lte->resource_location) {
+ case RESOURCE_IN_WIM:
+ list_del(<e->rspec_node);
+ if (list_empty(<e->rspec->stream_list))
+ FREE(lte->rspec);
+ break;
+ case RESOURCE_IN_FILE_ON_DISK:
+#ifdef __WIN32__
+ case RESOURCE_WIN32_ENCRYPTED:
#endif
- default:
- break;
+#ifdef WITH_FUSE
+ case RESOURCE_IN_STAGING_FILE:
+ BUILD_BUG_ON((void*)<e->file_on_disk !=
+ (void*)<e->staging_file_name);
+#endif
+ case RESOURCE_IN_ATTACHED_BUFFER:
+ BUILD_BUG_ON((void*)<e->file_on_disk !=
+ (void*)<e->attached_buffer);
+ FREE(lte->file_on_disk);
+ break;
+#ifdef WITH_NTFS_3G
+ case RESOURCE_IN_NTFS_VOLUME:
+ if (lte->ntfs_loc) {
+ FREE(lte->ntfs_loc->path);
+ FREE(lte->ntfs_loc->stream_name);
+ FREE(lte->ntfs_loc);
}
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+void
+free_lookup_table_entry(struct wim_lookup_table_entry *lte)
+{
+ if (lte) {
+ lte_put_resource(lte);
FREE(lte);
}
}
-/* Decrements the reference count for the lookup table entry @lte. If its
- * 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. */
+/* Should this stream be retained even if it has no references? */
+static bool
+should_retain_lte(const struct wim_lookup_table_entry *lte)
+{
+ return lte->resource_location == RESOURCE_IN_WIM;
+}
+
+static void
+finalize_lte(struct wim_lookup_table_entry *lte)
+{
+ if (!should_retain_lte(lte))
+ free_lookup_table_entry(lte);
+}
+
+/*
+ * Decrements the reference count for the lookup table entry @lte, which must be
+ * inserted in the stream lookup table @table.
+ *
+ * If the reference count reaches 0, this may cause @lte to be destroyed.
+ * However, we may retain entries with 0 reference count. This does not affect
+ * correctness, but it prevents the entries for valid streams in a WIM archive,
+ * which will continue to be present after appending to the file, from being
+ * lost merely because we dropped all references to them.
+ */
void
lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
struct wim_lookup_table *table)
unlink(lte->staging_file_name);
#endif
} else {
- lookup_table_unlink(table, lte);
+ if (!should_retain_lte(lte))
+ lookup_table_unlink(table, lte);
}
/* If FUSE mounts are enabled, we don't actually free the entry
#ifdef WITH_FUSE
if (lte->num_opened_fds == 0)
#endif
- free_lookup_table_entry(lte);
+ finalize_lte(lte);
}
}
wimlib_assert(lte->num_opened_fds != 0);
if (--lte->num_opened_fds == 0 && lte->refcnt == 0)
- free_lookup_table_entry(lte);
+ finalize_lte(lte);
}
#endif
struct wim_resource_spec *cur_rspec;
void *buf;
bool back_to_back_pack;
+ size_t num_duplicate_entries = 0;
DEBUG("Reading lookup table.");
if (!(reshdr.flags & (WIM_RESHDR_FLAG_PACKED_STREAMS |
WIM_RESHDR_FLAG_COMPRESSED))) {
if (reshdr.uncompressed_size != reshdr.size_in_wim) {
- ERROR("Invalid resource entry!");
- ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
- goto err;
+ /* So ... This is an uncompressed resource, but
+ * its uncompressed size is NOT the same as its
+ * "compressed" size (size_in_wim). What to do
+ * with it?
+ *
+ * Based on a simple test, WIMGAPI seems to
+ * handle this as follows:
+ *
+ * if (size_in_wim > uncompressed_size) {
+ * Ignore uncompressed_size; use
+ * size_in_wim instead.
+ * } else {
+ * Honor uncompressed_size, but treat the
+ * part of the file data above size_in_wim
+ * as all zeros.
+ * }
+ *
+ * So we will do the same.
+ */
+ if (reshdr.size_in_wim > reshdr.uncompressed_size)
+ reshdr.uncompressed_size = reshdr.size_in_wim;
}
}
}
wim_res_hdr_to_spec(&reshdr, wim, cur_rspec);
+ /* If this is a packed run, the current stream entry may
+ * specify a stream within the resource, and not the
+ * resource itself. Zero possibly irrelevant data until
+ * it is read for certain. (Note that the computation
+ * of 'back_to_back_pack' tests if 'size_in_wim' is
+ * nonzero to see if the resource info has been read;
+ * hence we need to set it to 0 here.) */
+ if (reshdr.flags & WIM_RESHDR_FLAG_PACKED_STREAMS) {
+ cur_rspec->size_in_wim = 0;
+ cur_rspec->uncompressed_size = 0;
+ cur_rspec->offset_in_wim = 0;
+ }
+
if (prev_entry)
lte_bind_wim_resource_spec(prev_entry, cur_rspec);
}
/* Compression format numbers must be the same as in
* WIMGAPI to be compatible here. */
BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_NONE != 0);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 1);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 2);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 1);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 2);
BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
cur_rspec->compression_type = le32_to_cpu(hdr.compression_format);
}
if (cur_entry->refcnt != 1) {
- if (wimlib_print_errors) {
- ERROR("Found metadata resource with refcnt != 1:");
- print_lookup_table_entry(cur_entry, stderr);
- }
+ ERROR("Found metadata resource with refcnt != 1");
ret = WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
goto err;
}
* resource. */
duplicate_entry = lookup_stream(table, cur_entry->hash);
if (duplicate_entry) {
- if (wimlib_print_errors) {
- WARNING("The WIM lookup table contains two entries with the "
- "same SHA1 message digest!");
- WARNING("The first entry is:");
- print_lookup_table_entry(duplicate_entry, stderr);
- WARNING("The second entry is:");
- print_lookup_table_entry(cur_entry, stderr);
- }
+ num_duplicate_entries++;
free_lookup_table_entry(cur_entry);
continue;
}
put_image_metadata(wim->image_metadata[i], NULL);
wim->hdr.image_count = wim->current_image;
}
+
+ if (num_duplicate_entries > 0) {
+ WARNING("Ignoring %zu duplicate streams in the WIM lookup table",
+ num_duplicate_entries);
+ }
+
DEBUG("Done reading lookup table.");
wim->lookup_table = table;
ret = 0;
return 0;
}
-void
-print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out)
-{
- if (lte == NULL) {
- tputc(T('\n'), out);
- return;
- }
-
-
- tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
- lte->size);
- if (lte->flags & WIM_RESHDR_FLAG_PACKED_STREAMS) {
- tprintf(T("Offset = %"PRIu64" bytes\n"),
- lte->offset_in_res);
-
- tprintf(T("Raw uncompressed size = %"PRIu64" bytes\n"),
- lte->rspec->uncompressed_size);
-
- tprintf(T("Raw compressed size = %"PRIu64" bytes\n"),
- lte->rspec->size_in_wim);
-
- tprintf(T("Raw offset = %"PRIu64" bytes\n"),
- lte->rspec->offset_in_wim);
- } else if (lte->resource_location == RESOURCE_IN_WIM) {
- tprintf(T("Compressed size = %"PRIu64" bytes\n"),
- lte->rspec->size_in_wim);
-
- tprintf(T("Offset = %"PRIu64" bytes\n"),
- lte->rspec->offset_in_wim);
- }
-
- tfprintf(out, T("Reference Count = %u\n"), lte->refcnt);
-
- if (lte->unhashed) {
- tfprintf(out, T("(Unhashed: inode %p, stream_id = %u)\n"),
- lte->back_inode, lte->back_stream_id);
- } else {
- tfprintf(out, T("Hash = 0x"));
- print_hash(lte->hash, out);
- tputc(T('\n'), out);
- }
-
- tfprintf(out, T("Flags = "));
- u8 flags = lte->flags;
- if (flags & WIM_RESHDR_FLAG_COMPRESSED)
- tfputs(T("WIM_RESHDR_FLAG_COMPRESSED, "), out);
- if (flags & WIM_RESHDR_FLAG_FREE)
- tfputs(T("WIM_RESHDR_FLAG_FREE, "), out);
- if (flags & WIM_RESHDR_FLAG_METADATA)
- tfputs(T("WIM_RESHDR_FLAG_METADATA, "), out);
- if (flags & WIM_RESHDR_FLAG_SPANNED)
- tfputs(T("WIM_RESHDR_FLAG_SPANNED, "), out);
- if (flags & WIM_RESHDR_FLAG_PACKED_STREAMS)
- tfputs(T("WIM_RESHDR_FLAG_PACKED_STREAMS, "), out);
- tputc(T('\n'), out);
- switch (lte->resource_location) {
- case RESOURCE_IN_WIM:
- if (lte->rspec->wim->filename) {
- tfprintf(out, T("WIM file = `%"TS"'\n"),
- lte->rspec->wim->filename);
- }
- break;
-#ifdef __WIN32__
- case RESOURCE_WIN32_ENCRYPTED:
-#endif
- case RESOURCE_IN_FILE_ON_DISK:
- tfprintf(out, T("File on Disk = `%"TS"'\n"),
- lte->file_on_disk);
- break;
-#ifdef WITH_FUSE
- case RESOURCE_IN_STAGING_FILE:
- tfprintf(out, T("Staging File = `%"TS"'\n"),
- lte->staging_file_name);
- break;
-#endif
- default:
- break;
- }
- tputc(T('\n'), out);
-}
-
void
lte_to_wimlib_resource_entry(const struct wim_lookup_table_entry *lte,
struct wimlib_resource_entry *wentry)
wimlib_iterate_lookup_table_callback_t cb,
void *user_ctx)
{
+ if (flags != 0)
+ return WIMLIB_ERR_INVALID_PARAM;
+
struct iterate_lte_context ctx = {
.cb = cb,
.user_ctx = user_ctx,