]> wimlib.net Git - wimlib/blobdiff - src/lookup_table.c
wimlib_join(): Fix buffer overrun when swm part 1 not specified
[wimlib] / src / lookup_table.c
index 2cca979c6996c3983ffe12f47f2532c77dfb64a1..ed0ab750c9b3746878a734c04c1c87a42787c080 100644 (file)
@@ -235,10 +235,30 @@ free_lookup_table_entry(struct wim_lookup_table_entry *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)
@@ -257,7 +277,8 @@ lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
                                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
@@ -266,7 +287,7 @@ lte_decrement_refcnt(struct wim_lookup_table_entry *lte,
 #ifdef WITH_FUSE
                if (lte->num_opened_fds == 0)
 #endif
-                       free_lookup_table_entry(lte);
+                       finalize_lte(lte);
        }
 }
 
@@ -277,7 +298,7 @@ lte_decrement_num_opened_fds(struct wim_lookup_table_entry *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
 
@@ -615,6 +636,7 @@ read_wim_lookup_table(WIMStruct *wim)
        struct wim_resource_spec *cur_rspec;
        void *buf;
        bool back_to_back_pack;
+       size_t num_duplicate_entries = 0;
 
        DEBUG("Reading lookup table.");
 
@@ -684,9 +706,27 @@ read_wim_lookup_table(WIMStruct *wim)
                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;
                        }
                }
 
@@ -873,8 +913,7 @@ read_wim_lookup_table(WIMStruct *wim)
                 * resource.  */
                duplicate_entry = lookup_stream(table, cur_entry->hash);
                if (duplicate_entry) {
-                       WARNING("The WIM lookup table contains two entries "
-                               "with the same SHA1 message digest!");
+                       num_duplicate_entries++;
                        free_lookup_table_entry(cur_entry);
                        continue;
                }
@@ -902,6 +941,12 @@ read_wim_lookup_table(WIMStruct *wim)
                        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;