]> wimlib.net Git - wimlib/blobdiff - src/write.c
overwrite_wim_inplace(): cleanup
[wimlib] / src / write.c
index 43e81b677ea57848759cbd2a86b11127f69e98dd..6ae5599337b9552f556134effd691838feaa3318 100644 (file)
@@ -339,12 +339,6 @@ write_wim_resource(struct wim_lookup_table_entry *lte,
 
        flags &= ~WIMLIB_RESOURCE_FLAG_RECOMPRESS;
 
-       if (wim_resource_size(lte) == 0) {
-               /* Empty resource; nothing needs to be done, so just return
-                * success. */
-               return 0;
-       }
-
        /* Get current position in output WIM */
        offset = ftello(out_fp);
        if (offset == -1) {
@@ -621,11 +615,11 @@ do_write_stream_list(struct list_head *stream_list,
                     struct wim_lookup_table *lookup_table,
                     FILE *out_fp,
                     int out_ctype,
+                    int write_resource_flags,
                     wimlib_progress_func_t progress_func,
-                    union wimlib_progress_info *progress,
-                    int write_resource_flags)
+                    union wimlib_progress_info *progress)
 {
-       int ret;
+       int ret = 0;
        struct wim_lookup_table_entry *lte;
 
        /* For each stream in @stream_list ... */
@@ -645,7 +639,7 @@ do_write_stream_list(struct list_head *stream_list,
                                                   lookup_table,
                                                   &tmp);
                        if (ret)
-                               return ret;
+                               break;
                        if (tmp != lte) {
                                lte = tmp;
                                /* We found a duplicate stream. */
@@ -674,7 +668,7 @@ do_write_stream_list(struct list_head *stream_list,
                                         &lte->output_resource_entry,
                                         write_resource_flags);
                if (ret)
-                       return ret;
+                       break;
                if (lte->unhashed) {
                        list_del(&lte->unhashed_list);
                        lookup_table_insert(lookup_table, lte);
@@ -685,7 +679,7 @@ do_write_stream_list(struct list_head *stream_list,
                                          progress_func,
                                          wim_resource_size(lte));
        }
-       return 0;
+       return ret;
 }
 
 static int
@@ -707,8 +701,10 @@ write_stream_list_serial(struct list_head *stream_list,
        return do_write_stream_list(stream_list,
                                    lookup_table,
                                    out_fp,
-                                   out_ctype, progress_func,
-                                   progress, write_resource_flags);
+                                   out_ctype,
+                                   write_resource_flags,
+                                   progress_func,
+                                   progress);
 }
 
 #ifdef ENABLE_MULTITHREADED_COMPRESSION
@@ -1268,6 +1264,10 @@ write_stream_list(struct list_head *stream_list,
        if (list_empty(stream_list))
                return 0;
 
+       /* Calculate the total size of the streams to be written.  Note: this
+        * will be the uncompressed size, as we may not know the compressed size
+        * yet, and also this will assume that every unhashed stream will be
+        * written (which will not necessarily be the case). */
        list_for_each_entry(lte, stream_list, write_streams_list) {
                num_streams++;
                total_bytes += wim_resource_size(lte);
@@ -1336,15 +1336,15 @@ stream_size_table_insert(struct wim_lookup_table_entry *lte, void *_tab)
 {
        struct stream_size_table *tab = _tab;
        size_t pos;
-       struct wim_lookup_table_entry *hashed_lte;
+       struct wim_lookup_table_entry *same_size_lte;
        struct hlist_node *tmp;
 
        pos = hash_u64(wim_resource_size(lte)) % tab->capacity;
        lte->unique_size = 1;
-       hlist_for_each_entry(hashed_lte, tmp, &tab->array[pos], hash_list_2) {
-               if (wim_resource_size(hashed_lte) == wim_resource_size(lte)) {
+       hlist_for_each_entry(same_size_lte, tmp, &tab->array[pos], hash_list_2) {
+               if (wim_resource_size(same_size_lte) == wim_resource_size(lte)) {
                        lte->unique_size = 0;
-                       hashed_lte->unique_size = 0;
+                       same_size_lte->unique_size = 0;
                        break;
                }
        }
@@ -1362,14 +1362,34 @@ struct lte_overwrite_prepare_args {
        struct stream_size_table stream_size_tab;
 };
 
+/* First phase of preparing streams for an in-place overwrite.  This is called
+ * on all streams, both hashed and unhashed, except the metadata resources. */
 static int
-lte_overwrite_prepare(struct wim_lookup_table_entry *lte, void *arg)
+lte_overwrite_prepare(struct wim_lookup_table_entry *lte, void *_args)
 {
-       struct lte_overwrite_prepare_args *args = arg;
+       struct lte_overwrite_prepare_args *args = _args;
 
-       if (lte->resource_location == RESOURCE_IN_WIM &&
-           lte->wim == args->wim)
-       {
+       wimlib_assert(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA));
+       if (lte->resource_location != RESOURCE_IN_WIM || lte->wim != args->wim)
+               list_add_tail(&lte->write_streams_list, &args->stream_list);
+       lte->out_refcnt = lte->refcnt;
+       stream_size_table_insert(lte, &args->stream_size_tab);
+       return 0;
+}
+
+/* Second phase of preparing streams for an in-place overwrite.  This is called
+ * on existing metadata resources and hashed streams, but not unhashed streams.
+ *
+ * NOTE: lte->output_resource_entry is in union with lte->hash_list_2, so
+ * lte_overwrite_prepare_2() must be called after lte_overwrite_prepare(), as
+ * the latter uses lte->hash_list_2, while the former expects to set
+ * lte->output_resource_entry. */
+static int
+lte_overwrite_prepare_2(struct wim_lookup_table_entry *lte, void *_args)
+{
+       struct lte_overwrite_prepare_args *args = _args;
+
+       if (lte->resource_location == RESOURCE_IN_WIM && lte->wim == args->wim) {
                /* We can't do an in place overwrite on the WIM if there are
                 * streams after the XML data. */
                if (lte->resource_entry.offset +
@@ -1381,19 +1401,6 @@ lte_overwrite_prepare(struct wim_lookup_table_entry *lte, void *arg)
                #endif
                        return WIMLIB_ERR_RESOURCE_ORDER;
                }
-       } else {
-               wimlib_assert(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA));
-               list_add_tail(&lte->write_streams_list, &args->stream_list);
-       }
-       lte->out_refcnt = lte->refcnt;
-       stream_size_table_insert(lte, &args->stream_size_tab);
-       return 0;
-}
-
-static int
-lte_set_output_res_entry(struct wim_lookup_table_entry *lte, void *_wim)
-{
-       if (lte->resource_location == RESOURCE_IN_WIM && lte->wim == _wim) {
                copy_resource_entry(&lte->output_resource_entry,
                                    &lte->resource_entry);
        }
@@ -1419,6 +1426,7 @@ prepare_streams_for_overwrite(WIMStruct *wim, off_t end_offset,
 {
        int ret;
        struct lte_overwrite_prepare_args args;
+       unsigned i;
 
        args.wim = wim;
        args.end_offset = end_offset;
@@ -1428,28 +1436,25 @@ prepare_streams_for_overwrite(WIMStruct *wim, off_t end_offset,
                return ret;
 
        INIT_LIST_HEAD(&args.stream_list);
-       for (int i = 0; i < wim->hdr.image_count; i++) {
+       for (i = 0; i < wim->hdr.image_count; i++) {
                struct wim_image_metadata *imd;
                struct wim_lookup_table_entry *lte;
 
                imd = wim->image_metadata[i];
-               image_for_each_unhashed_stream(lte, imd) {
-                       ret = lte_overwrite_prepare(lte, &args);
-                       if (ret)
-                               goto out_destroy_stream_size_table;
-               }
+               image_for_each_unhashed_stream(lte, imd)
+                       lte_overwrite_prepare(lte, &args);
+       }
+       for_lookup_table_entry(wim->lookup_table, lte_overwrite_prepare, &args);
+       list_transfer(&args.stream_list, stream_list);
+
+       for (i = 0; i < wim->hdr.image_count; i++) {
+               ret = lte_overwrite_prepare_2(wim->image_metadata[i]->metadata_lte,
+                                             &args);
+               if (ret)
+                       goto out_destroy_stream_size_table;
        }
        ret = for_lookup_table_entry(wim->lookup_table,
-                                    lte_overwrite_prepare, &args);
-       if (ret)
-               goto out_destroy_stream_size_table;
-
-       for (int i = 0; i < wim->hdr.image_count; i++)
-               lte_set_output_res_entry(wim->image_metadata[i]->metadata_lte,
-                                        wim);
-       for_lookup_table_entry(wim->lookup_table, lte_set_output_res_entry, wim);
-       INIT_LIST_HEAD(stream_list);
-       list_splice(&args.stream_list, stream_list);
+                                    lte_overwrite_prepare_2, &args);
 out_destroy_stream_size_table:
        destroy_stream_size_table(&args.stream_size_tab);
        return ret;
@@ -1484,8 +1489,8 @@ inode_find_streams_to_write(struct wim_inode *inode,
 static int
 image_find_streams_to_write(WIMStruct *w)
 {
-       struct wim_image_metadata *imd;
        struct find_streams_ctx *ctx;
+       struct wim_image_metadata *imd;
        struct wim_inode *inode;
        struct wim_lookup_table_entry *lte;
 
@@ -1535,10 +1540,8 @@ prepare_stream_list(WIMStruct *wim, int image, struct list_head *stream_list)
        wim->private = &ctx;
        ret = for_image(wim, image, image_find_streams_to_write);
        destroy_stream_size_table(&ctx.stream_size_tab);
-       if (ret == 0) {
-               INIT_LIST_HEAD(stream_list);
-               list_splice(&ctx.stream_list, stream_list);
-       }
+       if (ret == 0)
+               list_transfer(&ctx.stream_list, stream_list);
        return ret;
 }
 
@@ -1622,13 +1625,11 @@ finish_write(WIMStruct *w, int image, int write_flags,
         * it should be a copy of the resource entry for the image that is
         * marked as bootable.  This is not well documented...  */
        if (hdr.boot_idx == 0) {
-               memset(&hdr.boot_metadata_res_entry, 0,
-                      sizeof(struct resource_entry));
+               zero_resource_entry(&hdr.boot_metadata_res_entry);
        } else {
-               memcpy(&hdr.boot_metadata_res_entry,
-                      &w->image_metadata[
-                         hdr.boot_idx - 1]->metadata_lte->output_resource_entry,
-                      sizeof(struct resource_entry));
+               copy_resource_entry(&hdr.boot_metadata_res_entry,
+                           &w->image_metadata[ hdr.boot_idx- 1
+                                       ]->metadata_lte->output_resource_entry);
        }
 
        if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
@@ -1648,7 +1649,7 @@ finish_write(WIMStruct *w, int image, int write_flags,
                if (write_flags & WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) {
                        struct wim_header checkpoint_hdr;
                        memcpy(&checkpoint_hdr, &hdr, sizeof(struct wim_header));
-                       memset(&checkpoint_hdr.integrity, 0, sizeof(struct resource_entry));
+                       zero_resource_entry(&checkpoint_hdr.integrity);
                        if (fseeko(out, 0, SEEK_SET)) {
                                ERROR_WITH_ERRNO("Failed to seek to beginning "
                                                 "of WIM being written");
@@ -1692,7 +1693,7 @@ finish_write(WIMStruct *w, int image, int write_flags,
                if (ret)
                        goto out_close_wim;
        } else {
-               memset(&hdr.integrity, 0, sizeof(struct resource_entry));
+               zero_resource_entry(&hdr.integrity);
        }
 
        if (fseeko(out, 0, SEEK_SET) != 0) {
@@ -1923,6 +1924,7 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags,
        int ret;
        struct list_head stream_list;
        off_t old_wim_end;
+       u64 old_lookup_table_end, old_xml_begin, old_xml_end;
 
        DEBUG("Overwriting `%"TS"' in-place", w->filename);
 
@@ -1930,33 +1932,45 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags,
         * data, and that there are no stream resources, metadata resources, or
         * lookup tables after the XML data.  Otherwise, these data would be
         * overwritten. */
-       if (w->hdr.integrity.offset != 0 &&
-           w->hdr.integrity.offset < w->hdr.xml_res_entry.offset) {
+       old_xml_begin = w->hdr.xml_res_entry.offset;
+       old_xml_end = old_xml_begin + w->hdr.xml_res_entry.size;
+       old_lookup_table_end = w->hdr.lookup_table_res_entry.offset +
+                              w->hdr.lookup_table_res_entry.size;
+       if (w->hdr.integrity.offset != 0 && w->hdr.integrity.offset < old_xml_end) {
                ERROR("Didn't expect the integrity table to be before the XML data");
                return WIMLIB_ERR_RESOURCE_ORDER;
        }
 
-       if (w->hdr.lookup_table_res_entry.offset > w->hdr.xml_res_entry.offset) {
+       if (old_lookup_table_end > old_xml_begin) {
                ERROR("Didn't expect the lookup table to be after the XML data");
                return WIMLIB_ERR_RESOURCE_ORDER;
        }
 
-
-       if (w->hdr.integrity.offset)
-               old_wim_end = w->hdr.integrity.offset + w->hdr.integrity.size;
-       else
-               old_wim_end = w->hdr.xml_res_entry.offset + w->hdr.xml_res_entry.size;
-
+       /* Set @old_wim_end, which indicates the point beyond which we don't
+        * allow any file and metadata resources to appear without returning
+        * WIMLIB_ERR_RESOURCE_ORDER (due to the fact that we would otherwise
+        * overwrite these resources). */
        if (!w->deletion_occurred && !any_images_modified(w)) {
                /* If no images have been modified and no images have been
-                * deleted, a new lookup table does not need to be written. */
+                * deleted, a new lookup table does not need to be written.  We
+                * shall write the new XML data and optional integrity table
+                * immediately after the lookup table.  Note that this may
+                * overwrite an existing integrity table. */
                DEBUG("Skipping writing lookup table "
                      "(no images modified or deleted)");
-               old_wim_end = w->hdr.lookup_table_res_entry.offset +
-                             w->hdr.lookup_table_res_entry.size;
+               old_wim_end = old_lookup_table_end;
                write_flags |= WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE |
                               WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML;
+       } else if (w->hdr.integrity.offset) {
+               /* Old WIM has an integrity table; begin writing new streams
+                * after it. */
+               old_wim_end = w->hdr.integrity.offset + w->hdr.integrity.size;
+       } else {
+               /* No existing integrity table; begin writing new streams after
+                * the old XML data. */
+               old_wim_end = old_xml_end;
        }
+
        ret = prepare_streams_for_overwrite(w, old_wim_end, &stream_list);
        if (ret)
                return ret;
@@ -1968,15 +1982,13 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags,
 
        ret = lock_wim(w, w->out_fp);
        if (ret) {
-               fclose(w->out_fp);
-               w->out_fp = NULL;
+               close_wim_writable(w);
                return ret;
        }
 
        if (fseeko(w->out_fp, old_wim_end, SEEK_SET) != 0) {
                ERROR_WITH_ERRNO("Can't seek to end of WIM");
-               fclose(w->out_fp);
-               w->out_fp = NULL;
+               close_wim_writable(w);
                w->wim_locked = 0;
                return WIMLIB_ERR_WRITE;
        }
@@ -1991,20 +2003,20 @@ overwrite_wim_inplace(WIMStruct *w, int write_flags,
                                num_threads,
                                progress_func);
        if (ret)
-               goto out_ftruncate;
+               goto out_truncate;
 
        for (int i = 0; i < w->hdr.image_count; i++) {
                if (w->image_metadata[i]->modified) {
                        select_wim_image(w, i + 1);
                        ret = write_metadata_resource(w);
                        if (ret)
-                               goto out_ftruncate;
+                               goto out_truncate;
                }
        }
        write_flags |= WIMLIB_WRITE_FLAG_REUSE_INTEGRITY_TABLE;
        ret = finish_write(w, WIMLIB_ALL_IMAGES, write_flags,
                           progress_func);
-out_ftruncate:
+out_truncate:
        close_wim_writable(w);
        if (ret != 0 && !(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
                WARNING("Truncating `%"TS"' to its original size (%"PRIu64" bytes)",
@@ -2038,9 +2050,9 @@ overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
        ret = wimlib_write(w, tmpfile, WIMLIB_ALL_IMAGES,
                           write_flags | WIMLIB_WRITE_FLAG_FSYNC,
                           num_threads, progress_func);
-       if (ret != 0) {
+       if (ret) {
                ERROR("Failed to write the WIM file `%"TS"'", tmpfile);
-               goto err;
+               goto out_unlink;
        }
 
        DEBUG("Renaming `%"TS"' to `%"TS"'", tmpfile, w->filename);
@@ -2061,7 +2073,7 @@ overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
                ERROR_WITH_ERRNO("Failed to rename `%"TS"' to `%"TS"'",
                                 tmpfile, w->filename);
                ret = WIMLIB_ERR_RENAME;
-               goto err;
+               goto out_unlink;
        }
 
        if (progress_func) {
@@ -2086,11 +2098,12 @@ overwrite_wim_via_tmpfile(WIMStruct *w, int write_flags,
                FREE(w->filename);
                w->filename = NULL;
        }
-       return ret;
-err:
+       goto out;
+out_unlink:
        /* Remove temporary file. */
        if (tunlink(tmpfile) != 0)
                WARNING_WITH_ERRNO("Failed to remove `%"TS"'", tmpfile);
+out:
        return ret;
 }