]> wimlib.net Git - wimlib/blobdiff - src/write.c
write.c: cleanups and bug fixes
[wimlib] / src / write.c
index c19d6fcbfc174a4cab3eb548928f38268776b041..34f6283325dd47bb6eaabce49a32fdae9cc839c8 100644 (file)
@@ -52,9 +52,7 @@
 #include "wimlib/progress.h"
 #include "wimlib/resource.h"
 #include "wimlib/solid.h"
-#ifdef __WIN32__
-#  include "wimlib/win32.h" /* win32_rename_replacement() */
-#endif
+#include "wimlib/win32.h" /* win32_rename_replacement() */
 #include "wimlib/write.h"
 #include "wimlib/xml.h"
 
@@ -140,60 +138,72 @@ blob_hard_filtered(const struct blob_descriptor *blob,
        return blob_filtered(blob, ctx) < 0;
 }
 
-static inline int
+static inline bool
 may_soft_filter_blobs(const struct filter_context *ctx)
 {
-       if (ctx == NULL)
-               return 0;
-       return ctx->write_flags & WIMLIB_WRITE_FLAG_OVERWRITE;
+       return ctx && (ctx->write_flags & WIMLIB_WRITE_FLAG_OVERWRITE);
 }
 
-static inline int
+static inline bool
 may_hard_filter_blobs(const struct filter_context *ctx)
 {
-       if (ctx == NULL)
-               return 0;
-       return ctx->write_flags & WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
+       return ctx && (ctx->write_flags & WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS);
 }
 
-static inline int
+static inline bool
 may_filter_blobs(const struct filter_context *ctx)
 {
        return (may_soft_filter_blobs(ctx) || may_hard_filter_blobs(ctx));
 }
 
-/* Return true if the specified resource is compressed and the compressed data
- * can be reused with the specified output parameters.  */
+/* Return true if the specified blob is located in a WIM resource which can be
+ * reused in the output WIM file, without being recompressed.  */
 static bool
-can_raw_copy(const struct blob_descriptor *blob,
-            int write_resource_flags, int out_ctype, u32 out_chunk_size)
+can_raw_copy(const struct blob_descriptor *blob, int write_resource_flags,
+            int out_ctype, u32 out_chunk_size)
 {
        const struct wim_resource_descriptor *rdesc;
 
+       /* Recompress everything if requested.  */
        if (write_resource_flags & WRITE_RESOURCE_FLAG_RECOMPRESS)
                return false;
 
-       if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE)
-               return false;
-
+       /* A blob not located in a WIM resource cannot be reused.  */
        if (blob->blob_location != BLOB_IN_WIM)
                return false;
 
        rdesc = blob->rdesc;
 
-       if (rdesc->is_pipable != !!(write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE))
+       /* Only reuse compressed resources.  */
+       if (out_ctype == WIMLIB_COMPRESSION_TYPE_NONE ||
+           !(rdesc->flags & (WIM_RESHDR_FLAG_COMPRESSED |
+                             WIM_RESHDR_FLAG_SOLID)))
+               return false;
+
+       /* When writing a pipable WIM, we can only reuse pipable resources; and
+        * when writing a non-pipable WIM, we can only reuse non-pipable
+        * resources.  */
+       if (rdesc->is_pipable !=
+           !!(write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE))
+               return false;
+
+       /* When writing a solid WIM, we can only reuse solid resources; and when
+        * writing a non-solid WIM, we can only reuse non-solid resources.  */
+       if (!!(rdesc->flags & WIM_RESHDR_FLAG_SOLID) !=
+           !!(write_resource_flags & WRITE_RESOURCE_FLAG_SOLID))
                return false;
 
+       /* Note: it is theoretically possible to copy chunks of compressed data
+        * between non-solid, solid, and pipable resources.  However, we don't
+        * currently implement this optimization because it would be complex and
+        * would usually go unused.  */
+
        if (rdesc->flags & WIM_RESHDR_FLAG_COMPRESSED) {
-               /* Normal compressed resource: Must use same compression type
-                * and chunk size.  */
+               /* To re-use a non-solid resource, it must use the desired
+                * compression type and chunk size.  */
                return (rdesc->compression_type == out_ctype &&
                        rdesc->chunk_size == out_chunk_size);
-       }
-
-       if ((rdesc->flags & WIM_RESHDR_FLAG_SOLID) &&
-           (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID))
-       {
+       } else {
                /* Solid resource: Such resources may contain multiple blobs,
                 * and in general only a subset of them need to be written.  As
                 * a heuristic, re-use the raw data if more than two-thirds the
@@ -204,6 +214,10 @@ can_raw_copy(const struct blob_descriptor *blob,
                 * check if they are compatible with @out_ctype and
                 * @out_chunk_size.  */
 
+               /* Did we already decide to reuse the resource?  */
+               if (rdesc->raw_copy_ok)
+                       return true;
+
                struct blob_descriptor *res_blob;
                u64 write_size = 0;
 
@@ -213,8 +227,6 @@ can_raw_copy(const struct blob_descriptor *blob,
 
                return (write_size > rdesc->uncompressed_size * 2 / 3);
        }
-
-       return false;
 }
 
 static u32
@@ -279,7 +291,7 @@ struct write_blobs_progress_data {
        wimlib_progress_func_t progfunc;
        void *progctx;
        union wimlib_progress_info progress;
-       uint64_t next_progress;
+       u64 next_progress;
 };
 
 static int
@@ -292,7 +304,7 @@ do_write_blobs_progress(struct write_blobs_progress_data *progress_data,
        if (discarded) {
                progress->write_streams.total_bytes -= complete_size;
                progress->write_streams.total_streams -= complete_count;
-               if (progress_data->next_progress != ~(uint64_t)0 &&
+               if (progress_data->next_progress != ~(u64)0 &&
                    progress_data->next_progress > progress->write_streams.total_bytes)
                {
                        progress_data->next_progress = progress->write_streams.total_bytes;
@@ -302,8 +314,8 @@ do_write_blobs_progress(struct write_blobs_progress_data *progress_data,
                progress->write_streams.completed_streams += complete_count;
        }
 
-       if (progress->write_streams.completed_bytes >= progress_data->next_progress)
-       {
+       if (progress->write_streams.completed_bytes >= progress_data->next_progress) {
+
                ret = call_progress(progress_data->progfunc,
                                    WIMLIB_PROGRESS_MSG_WRITE_STREAMS,
                                    progress,
@@ -311,32 +323,9 @@ do_write_blobs_progress(struct write_blobs_progress_data *progress_data,
                if (ret)
                        return ret;
 
-               if (progress_data->next_progress == progress->write_streams.total_bytes) {
-                       progress_data->next_progress = ~(uint64_t)0;
-               } else {
-                       /* Handle rate-limiting of messages  */
-
-                       /* Send new message as soon as another 1/128 of the
-                        * total has been written.  (Arbitrary number.)  */
-                       progress_data->next_progress =
-                               progress->write_streams.completed_bytes +
-                                       progress->write_streams.total_bytes / 128;
-
-                       /* ... Unless that would be more than 5000000 bytes, in
-                        * which case send the next after the next 5000000
-                        * bytes.  (Another arbitrary number.)  */
-                       if (progress->write_streams.completed_bytes + 5000000 <
-                           progress_data->next_progress)
-                               progress_data->next_progress =
-                                       progress->write_streams.completed_bytes + 5000000;
-
-                       /* ... But always send a message as soon as we're
-                        * completely done.  */
-                       if (progress->write_streams.total_bytes <
-                           progress_data->next_progress)
-                               progress_data->next_progress =
-                                       progress->write_streams.total_bytes;
-               }
+               set_next_progress(progress->write_streams.completed_bytes,
+                                 progress->write_streams.total_bytes,
+                                 &progress_data->next_progress);
        }
        return 0;
 }
@@ -363,10 +352,6 @@ struct write_blobs_ctx {
 
        struct filter_context *filter_ctx;
 
-       /* Upper bound on the total number of bytes that need to be compressed.
-        * */
-       u64 num_bytes_to_compress;
-
        /* Pointer to the chunk_compressor implementation being used for
         * compressing chunks of data, or NULL if chunks are being written
         * uncompressed.  */
@@ -573,9 +558,9 @@ end_chunk_table(struct write_blobs_ctx *ctx, u64 res_actual_size,
                        hdr.chunk_size = cpu_to_le32(ctx->out_chunk_size);
                        hdr.compression_format = cpu_to_le32(ctx->out_ctype);
 
-                       BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 1);
-                       BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 2);
-                       BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
+                       STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_XPRESS == 1);
+                       STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZX == 2);
+                       STATIC_ASSERT(WIMLIB_COMPRESSION_TYPE_LZMS == 3);
 
                        ret = full_pwrite(ctx->out_fd, &hdr, sizeof(hdr),
                                          chunk_table_offset - sizeof(hdr));
@@ -628,8 +613,6 @@ end_write_resource(struct write_blobs_ctx *ctx, struct wim_reshdr *out_reshdr)
        out_reshdr->uncompressed_size = res_uncompressed_size;
        out_reshdr->size_in_wim = res_size_in_wim;
        out_reshdr->offset_in_wim = res_offset_in_wim;
-       DEBUG("Finished writing resource: %"PRIu64" => %"PRIu64" @ %"PRIu64"",
-             res_uncompressed_size, res_size_in_wim, res_offset_in_wim);
        return 0;
 }
 
@@ -651,6 +634,8 @@ do_done_with_blob(struct blob_descriptor *blob,
 {
        int ret;
        struct wim_inode *inode;
+       tchar *cookie1;
+       tchar *cookie2;
 
        if (!blob->may_send_done_with_file)
                return 0;
@@ -658,42 +643,18 @@ do_done_with_blob(struct blob_descriptor *blob,
        inode = blob->file_inode;
 
        wimlib_assert(inode != NULL);
-       wimlib_assert(inode->num_remaining_streams > 0);
-       if (--inode->num_remaining_streams > 0)
+       wimlib_assert(inode->i_num_remaining_streams > 0);
+       if (--inode->i_num_remaining_streams > 0)
                return 0;
 
-#ifdef __WIN32__
-       /* XXX: This logic really should be somewhere else.  */
-
-       /* We want the path to the file, but blob->file_on_disk might actually
-        * refer to a named data stream.  Temporarily strip the named data
-        * stream from the path.  */
-       wchar_t *p_colon = NULL;
-       wchar_t *p_question_mark = NULL;
-       const wchar_t *p_stream_name;
-
-       p_stream_name = path_stream_name(blob->file_on_disk);
-       if (unlikely(p_stream_name)) {
-               p_colon = (wchar_t *)(p_stream_name - 1);
-               wimlib_assert(*p_colon == L':');
-               *p_colon = L'\0';
-       }
-
-       /* We also should use a fake Win32 path instead of a NT path  */
-       if (!wcsncmp(blob->file_on_disk, L"\\??\\", 4)) {
-               p_question_mark = &blob->file_on_disk[1];
-               *p_question_mark = L'\\';
-       }
-#endif
+       cookie1 = progress_get_streamless_path(blob->file_on_disk);
+       cookie2 = progress_get_win32_path(blob->file_on_disk);
 
        ret = done_with_file(blob->file_on_disk, progfunc, progctx);
 
-#ifdef __WIN32__
-       if (p_colon)
-               *p_colon = L':';
-       if (p_question_mark)
-               *p_question_mark = L'?';
-#endif
+       progress_put_win32_path(cookie2);
+       progress_put_streamless_path(cookie1);
+
        return ret;
 }
 
@@ -749,8 +710,6 @@ write_blob_begin_read(struct blob_descriptor *blob, void *_ctx)
                                 * (and reading it again) entirely, passing its
                                 * output reference count to the duplicate blob
                                 * in the former case.  */
-                               DEBUG("Discarding duplicate blob of "
-                                     "length %"PRIu64, blob->size);
                                ret = do_write_blobs_progress(&ctx->progress_data,
                                                              blob->size, 1, true);
                                list_del(&blob->write_blobs_list);
@@ -773,8 +732,6 @@ write_blob_begin_read(struct blob_descriptor *blob, void *_ctx)
                                 * blob descriptor must wait until
                                 * read_blob_list() has finished reading its
                                 * data.  */
-                               DEBUG("Blob duplicate, but not already "
-                                     "selected for writing.");
                                list_replace(&blob->write_blobs_list,
                                             &new_blob->write_blobs_list);
                                list_replace(&blob->blob_table_list,
@@ -803,7 +760,7 @@ write_blob_uncompressed(struct blob_descriptor *blob, struct filedes *out_fd)
        if (filedes_seek(out_fd, begin_offset) == -1)
                return 0;
 
-       ret = extract_full_blob_to_fd(blob, out_fd);
+       ret = extract_blob_to_fd(blob, out_fd);
        if (ret) {
                /* Error reading the uncompressed data.  */
                if (out_fd->offset == begin_offset &&
@@ -822,13 +779,9 @@ write_blob_uncompressed(struct blob_descriptor *blob, struct filedes *out_fd)
 
        wimlib_assert(out_fd->offset - begin_offset == blob->size);
 
-       if (out_fd->offset < end_offset &&
-           0 != ftruncate(out_fd->fd, out_fd->offset))
-       {
-               ERROR_WITH_ERRNO("Can't truncate output file to "
-                                "offset %"PRIu64, out_fd->offset);
-               return WIMLIB_ERR_WRITE;
-       }
+       /* We could ftruncate() the file to 'out_fd->offset' here, but there
+        * isn't much point.  Usually we will only be truncating by a few bytes
+        * and will just overwrite the data immediately.  */
 
        blob->out_reshdr.size_in_wim = blob->size;
        blob->out_reshdr.flags &= ~(WIM_RESHDR_FLAG_COMPRESSED |
@@ -924,8 +877,6 @@ write_chunk(struct write_blobs_ctx *ctx, const void *cchunk,
                /* Starting to write a new blob in non-solid mode.  */
 
                if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) {
-                       DEBUG("Writing pipable WIM blob header "
-                             "(offset=%"PRIu64")", ctx->out_fd->offset);
                        ret = write_pwm_blob_header(blob, ctx->out_fd,
                                                    ctx->compressor != NULL);
                        if (ret)
@@ -1192,14 +1143,12 @@ compute_blob_list_stats(struct list_head *blob_list,
  * @raw_copy_blobs.  Return the total uncompressed size of the blobs that need
  * to be compressed.  */
 static u64
-find_raw_copy_blobs(struct list_head *blob_list,
-                   int write_resource_flags,
-                   int out_ctype,
-                   u32 out_chunk_size,
+find_raw_copy_blobs(struct list_head *blob_list, int write_resource_flags,
+                   int out_ctype, u32 out_chunk_size,
                    struct list_head *raw_copy_blobs)
 {
        struct blob_descriptor *blob, *tmp;
-       u64 num_bytes_to_compress = 0;
+       u64 num_nonraw_bytes = 0;
 
        INIT_LIST_HEAD(raw_copy_blobs);
 
@@ -1209,23 +1158,17 @@ find_raw_copy_blobs(struct list_head *blob_list,
                        blob->rdesc->raw_copy_ok = 0;
 
        list_for_each_entry_safe(blob, tmp, blob_list, write_blobs_list) {
-               if (blob->blob_location == BLOB_IN_WIM &&
-                   blob->rdesc->raw_copy_ok)
-               {
-                       list_move_tail(&blob->write_blobs_list,
-                                      raw_copy_blobs);
-               } else if (can_raw_copy(blob, write_resource_flags,
-                                       out_ctype, out_chunk_size))
+               if (can_raw_copy(blob, write_resource_flags,
+                                out_ctype, out_chunk_size))
                {
                        blob->rdesc->raw_copy_ok = 1;
-                       list_move_tail(&blob->write_blobs_list,
-                                      raw_copy_blobs);
+                       list_move_tail(&blob->write_blobs_list, raw_copy_blobs);
                } else {
-                       num_bytes_to_compress += blob->size;
+                       num_nonraw_bytes += blob->size;
                }
        }
 
-       return num_bytes_to_compress;
+       return num_nonraw_bytes;
 }
 
 /* Copy a raw compressed resource located in another WIM file to the WIM file
@@ -1243,10 +1186,6 @@ write_raw_copy_resource(struct wim_resource_descriptor *in_rdesc,
        struct blob_descriptor *blob;
        u64 out_offset_in_wim;
 
-       DEBUG("Copying raw compressed data (size_in_wim=%"PRIu64", "
-             "uncompressed_size=%"PRIu64")",
-             in_rdesc->size_in_wim, in_rdesc->uncompressed_size);
-
        /* Copy the raw data.  */
        cur_read_offset = in_rdesc->offset_in_wim;
        end_read_offset = cur_read_offset + in_rdesc->size_in_wim;
@@ -1347,19 +1286,13 @@ finish_remaining_chunks(struct write_blobs_ctx *ctx)
 }
 
 static void
-remove_empty_blobs(struct list_head *blob_list)
+validate_blob_list(struct list_head *blob_list)
 {
-       struct blob_descriptor *blob, *tmp;
+       struct blob_descriptor *blob;
 
-       list_for_each_entry_safe(blob, tmp, blob_list, write_blobs_list) {
+       list_for_each_entry(blob, blob_list, write_blobs_list) {
                wimlib_assert(blob->will_be_in_output_wim);
-               if (blob->size == 0) {
-                       list_del(&blob->write_blobs_list);
-                       blob->out_reshdr.offset_in_wim = 0;
-                       blob->out_reshdr.size_in_wim = 0;
-                       blob->out_reshdr.uncompressed_size = 0;
-                       blob->out_reshdr.flags = reshdr_flags_for_blob(blob);
-               }
+               wimlib_assert(blob->size != 0);
        }
 }
 
@@ -1381,7 +1314,7 @@ init_done_with_file_info(struct list_head *blob_list)
 
        list_for_each_entry(blob, blob_list, write_blobs_list) {
                if (blob_is_in_file(blob)) {
-                       blob->file_inode->num_remaining_streams = 0;
+                       blob->file_inode->i_num_remaining_streams = 0;
                        blob->may_send_done_with_file = 1;
                } else {
                        blob->may_send_done_with_file = 0;
@@ -1390,7 +1323,7 @@ init_done_with_file_info(struct list_head *blob_list)
 
        list_for_each_entry(blob, blob_list, write_blobs_list)
                if (blob->may_send_done_with_file)
-                       blob->file_inode->num_remaining_streams++;
+                       blob->file_inode->i_num_remaining_streams++;
 }
 
 /*
@@ -1511,6 +1444,7 @@ write_blob_list(struct list_head *blob_list,
        int ret;
        struct write_blobs_ctx ctx;
        struct list_head raw_copy_blobs;
+       u64 num_nonraw_bytes;
 
        wimlib_assert((write_resource_flags &
                       (WRITE_RESOURCE_FLAG_SOLID |
@@ -1518,12 +1452,10 @@ write_blob_list(struct list_head *blob_list,
                                (WRITE_RESOURCE_FLAG_SOLID |
                                 WRITE_RESOURCE_FLAG_PIPABLE));
 
-       remove_empty_blobs(blob_list);
+       validate_blob_list(blob_list);
 
-       if (list_empty(blob_list)) {
-               DEBUG("No blobs to write.");
+       if (list_empty(blob_list))
                return 0;
-       }
 
        /* If needed, set auxiliary information so that we can detect when the
         * library has finished using each external file.  */
@@ -1569,25 +1501,12 @@ write_blob_list(struct list_head *blob_list,
        ctx.progress_data.progfunc = progfunc;
        ctx.progress_data.progctx = progctx;
 
-       ctx.num_bytes_to_compress = find_raw_copy_blobs(blob_list,
-                                                       write_resource_flags,
-                                                       out_ctype,
-                                                       out_chunk_size,
-                                                       &raw_copy_blobs);
-
-       DEBUG("Writing blob list "
-             "(offset = %"PRIu64", write_resource_flags=0x%08x, "
-             "out_ctype=%d, out_chunk_size=%u, num_threads=%u, "
-             "total_bytes=%"PRIu64", num_bytes_to_compress=%"PRIu64")",
-             out_fd->offset, write_resource_flags,
-             out_ctype, out_chunk_size, num_threads,
-             ctx.progress_data.progress.write_streams.total_bytes,
-             ctx.num_bytes_to_compress);
-
-       if (ctx.num_bytes_to_compress == 0) {
-               DEBUG("No compression needed; skipping to raw copy!");
+       num_nonraw_bytes = find_raw_copy_blobs(blob_list, write_resource_flags,
+                                              out_ctype, out_chunk_size,
+                                              &raw_copy_blobs);
+
+       if (num_nonraw_bytes == 0)
                goto out_write_raw_copy_resources;
-       }
 
        /* Unless uncompressed output was required, allocate a chunk_compressor
         * to do compression.  There are serial and parallel implementations of
@@ -1597,7 +1516,7 @@ write_blob_list(struct list_head *blob_list,
        if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) {
 
        #ifdef ENABLE_MULTITHREADED_COMPRESSION
-               if (ctx.num_bytes_to_compress > max(2000000, out_chunk_size)) {
+               if (num_nonraw_bytes > max(2000000, out_chunk_size)) {
                        ret = new_parallel_chunk_compressor(out_ctype,
                                                            out_chunk_size,
                                                            num_threads, 0,
@@ -1623,9 +1542,6 @@ write_blob_list(struct list_head *blob_list,
        else
                ctx.progress_data.progress.write_streams.num_threads = 1;
 
-       DEBUG("Actually using %u threads",
-             ctx.progress_data.progress.write_streams.num_threads);
-
        INIT_LIST_HEAD(&ctx.blobs_being_compressed);
        INIT_LIST_HEAD(&ctx.blobs_in_solid_resource);
 
@@ -1637,7 +1553,7 @@ write_blob_list(struct list_head *blob_list,
                goto out_destroy_context;
 
        if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) {
-               ret = begin_write_resource(&ctx, ctx.num_bytes_to_compress);
+               ret = begin_write_resource(&ctx, num_nonraw_bytes);
                if (ret)
                        goto out_destroy_context;
        }
@@ -1645,13 +1561,11 @@ write_blob_list(struct list_head *blob_list,
        /* Read the list of blobs needing to be compressed, using the specified
         * callbacks to execute processing of the data.  */
 
-       struct read_blob_list_callbacks cbs = {
-               .begin_blob             = write_blob_begin_read,
-               .begin_blob_ctx         = &ctx,
-               .consume_chunk          = write_blob_process_chunk,
-               .consume_chunk_ctx      = &ctx,
-               .end_blob               = write_blob_end_read,
-               .end_blob_ctx           = &ctx,
+       struct read_blob_callbacks cbs = {
+               .begin_blob     = write_blob_begin_read,
+               .consume_chunk  = write_blob_process_chunk,
+               .end_blob       = write_blob_end_read,
+               .ctx            = &ctx,
        };
 
        ret = read_blob_list(blob_list,
@@ -1677,11 +1591,6 @@ write_blob_list(struct list_head *blob_list,
                if (ret)
                        goto out_destroy_context;
 
-               DEBUG("Ending solid resource: %lu %lu %lu.",
-                     reshdr.offset_in_wim,
-                     reshdr.size_in_wim,
-                     reshdr.uncompressed_size);
-
                offset_in_res = 0;
                list_for_each_entry(blob, &ctx.blobs_in_solid_resource, write_blobs_list) {
                        blob->out_reshdr.size_in_wim = blob->size;
@@ -1707,7 +1616,6 @@ out_destroy_context:
        FREE(ctx.chunk_csizes);
        if (ctx.compressor)
                ctx.compressor->destroy(ctx.compressor);
-       DEBUG("Done (ret=%d)", ret);
        return ret;
 }
 
@@ -1725,16 +1633,6 @@ write_file_data_blobs(WIMStruct *wim,
 
        write_resource_flags = write_flags_to_resource_flags(write_flags);
 
-       /* wimlib v1.7.0: create a solid WIM file by default if the WIM version
-        * has been set to WIM_VERSION_SOLID and at least one blob in the WIM's
-        * blob table is located in a solid resource (may be the same WIM, or a
-        * different one in the case of export).  */
-       if (wim->hdr.wim_version == WIM_VERSION_SOLID &&
-           wim_has_solid_resources(wim))
-       {
-               write_resource_flags |= WRITE_RESOURCE_FLAG_SOLID;
-       }
-
        if (write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) {
                out_chunk_size = wim->out_solid_chunk_size;
                out_ctype = wim->out_solid_compression_type;
@@ -1793,6 +1691,13 @@ write_wim_resource_from_buffer(const void *buf,
        int ret;
        struct blob_descriptor blob;
 
+       if (unlikely(buf_size == 0)) {
+               zero_reshdr(out_reshdr);
+               if (hash_ret)
+                       copy_hash(hash_ret, zero_hash);
+               return 0;
+       }
+
        blob_set_is_located_in_attached_buffer(&blob, (void *)buf, buf_size);
        sha1_buffer(buf, buf_size, blob.hash);
        blob.unhashed = 0;
@@ -2204,18 +2109,13 @@ write_metadata_resources(WIMStruct *wim, int image, int write_flags)
        int end_image;
        int write_resource_flags;
 
-       if (write_flags & WIMLIB_WRITE_FLAG_NO_METADATA) {
-               DEBUG("Not writing any metadata resources.");
+       if (write_flags & WIMLIB_WRITE_FLAG_NO_METADATA)
                return 0;
-       }
 
        write_resource_flags = write_flags_to_resource_flags(write_flags);
 
        write_resource_flags &= ~WRITE_RESOURCE_FLAG_SOLID;
 
-       DEBUG("Writing metadata resources (offset=%"PRIu64")",
-             wim->out_fd.offset);
-
        ret = call_progress(wim->progfunc,
                            WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN,
                            NULL, wim->progctx);
@@ -2238,18 +2138,12 @@ write_metadata_resources(WIMStruct *wim, int image, int write_flags)
                 * the original (or was newly added).  Otherwise just copy the
                 * existing one.  */
                if (imd->modified) {
-                       DEBUG("Image %u was modified; building and writing new "
-                             "metadata resource", i);
                        ret = write_metadata_resource(wim, i,
                                                      write_resource_flags);
                } else if (write_flags & WIMLIB_WRITE_FLAG_OVERWRITE) {
-                       DEBUG("Image %u was not modified; re-using existing "
-                             "metadata resource.", i);
                        blob_set_out_reshdr_for_reuse(imd->metadata_blob);
                        ret = 0;
                } else {
-                       DEBUG("Image %u was not modified; copying existing "
-                             "metadata resource.", i);
                        ret = write_wim_resource(imd->metadata_blob,
                                                 &wim->out_fd,
                                                 wim->out_compression_type,
@@ -2268,10 +2162,7 @@ write_metadata_resources(WIMStruct *wim, int image, int write_flags)
 static int
 open_wim_writable(WIMStruct *wim, const tchar *path, int open_flags)
 {
-       int raw_fd;
-       DEBUG("Opening \"%"TS"\" for writing.", path);
-
-       raw_fd = topen(path, open_flags | O_BINARY, 0644);
+       int raw_fd = topen(path, open_flags | O_BINARY, 0644);
        if (raw_fd < 0) {
                ERROR_WITH_ERRNO("Failed to open \"%"TS"\" for writing", path);
                return WIMLIB_ERR_OPEN;
@@ -2285,12 +2176,10 @@ close_wim_writable(WIMStruct *wim, int write_flags)
 {
        int ret = 0;
 
-       if (!(write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR)) {
-               DEBUG("Closing WIM file.");
+       if (!(write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR))
                if (filedes_valid(&wim->out_fd))
                        if (filedes_close(&wim->out_fd))
                                ret = WIMLIB_ERR_WRITE;
-       }
        filedes_invalidate(&wim->out_fd);
        return ret;
 }
@@ -2321,7 +2210,6 @@ cmp_blobs_by_out_rdesc(const void *p1, const void *p2)
 
 static int
 write_blob_table(WIMStruct *wim, int image, int write_flags,
-                struct wim_reshdr *out_reshdr,
                 struct list_head *blob_table_list)
 {
        int ret;
@@ -2372,68 +2260,28 @@ write_blob_table(WIMStruct *wim, int image, int write_flags,
 
        return write_blob_table_from_blob_list(blob_table_list,
                                               &wim->out_fd,
-                                              wim->hdr.part_number,
-                                              out_reshdr,
+                                              wim->out_hdr.part_number,
+                                              &wim->out_hdr.blob_table_reshdr,
                                               write_flags_to_resource_flags(write_flags));
 }
 
 /*
- * finish_write():
- *
  * Finish writing a WIM file: write the blob table, xml data, and integrity
- * table, then overwrite the WIM header.  By default, closes the WIM file
- * descriptor (@wim->out_fd) if successful.
- *
- * write_flags is a bitwise OR of the following:
- *
- *     (public) WIMLIB_WRITE_FLAG_CHECK_INTEGRITY:
- *             Include an integrity table.
- *
- *     (public) WIMLIB_WRITE_FLAG_FSYNC:
- *             fsync() the output file before closing it.
- *
- *     (public) WIMLIB_WRITE_FLAG_PIPABLE:
- *             Writing a pipable WIM, possibly to a pipe; include pipable WIM
- *             blob headers before the blob table and XML data, and also write
- *             the WIM header at the end instead of seeking to the beginning.
- *             Can't be combined with WIMLIB_WRITE_FLAG_CHECK_INTEGRITY.
- *
- *     (private) WIMLIB_WRITE_FLAG_NO_BLOB_TABLE:
- *             Don't write the blob table.
- *
- *     (private) WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML:
- *             After writing the XML data but before writing the integrity
- *             table, write a temporary WIM header and flush the file
- *             descriptor so that the WIM is less likely to become corrupted
- *             upon abrupt program termination.
- *     (private) WIMLIB_WRITE_FLAG_HEADER_AT_END:
- *             Instead of overwriting the WIM header at the beginning of the
- *             file, simply append it to the end of the file.  (Used when
- *             writing to pipe.)
- *     (private) WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR:
- *             Do not close the file descriptor @wim->out_fd on either success
- *             on failure.
- *     (private) WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES:
- *             Use the existing <TOTALBYTES> stored in the in-memory XML
- *             information, rather than setting it to the offset of the XML
- *             data being written.
- *     (private) WIMLIB_WRITE_FLAG_OVERWRITE
- *             The existing WIM file is being updated in-place.  The entries
- *             from its integrity table may be re-used.
+ * table, then overwrite the WIM header.
+ *
+ * The output file descriptor is closed on success, except when writing to a
+ * user-specified file descriptor (WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR set).
  */
 static int
 finish_write(WIMStruct *wim, int image, int write_flags,
             struct list_head *blob_table_list)
 {
-       int ret;
-       off_t hdr_offset;
        int write_resource_flags;
        off_t old_blob_table_end = 0;
+       struct integrity_table *old_integrity_table = NULL;
        off_t new_blob_table_end;
        u64 xml_totalbytes;
-       struct integrity_table *old_integrity_table = NULL;
-
-       DEBUG("image=%d, write_flags=%08x", image, write_flags);
+       int ret;
 
        write_resource_flags = write_flags_to_resource_flags(write_flags);
 
@@ -2441,13 +2289,13 @@ finish_write(WIMStruct *wim, int image, int write_flags,
         * metadata resource labeled as the "boot metadata".  This entry should
         * be zeroed out if there is no bootable image (boot_idx 0).  Otherwise,
         * it should be a copy of the resource entry for the image that is
-        * marked as bootable.  This is not well documented...  */
-       if (wim->hdr.boot_idx == 0) {
-               zero_reshdr(&wim->hdr.boot_metadata_reshdr);
+        * marked as bootable.  */
+       if (wim->out_hdr.boot_idx == 0) {
+               zero_reshdr(&wim->out_hdr.boot_metadata_reshdr);
        } else {
-               copy_reshdr(&wim->hdr.boot_metadata_reshdr,
+               copy_reshdr(&wim->out_hdr.boot_metadata_reshdr,
                            &wim->image_metadata[
-                               wim->hdr.boot_idx - 1]->metadata_blob->out_reshdr);
+                               wim->out_hdr.boot_idx - 1]->metadata_blob->out_reshdr);
        }
 
        /* If overwriting the WIM file containing an integrity table in-place,
@@ -2471,10 +2319,9 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                 * ignoring of the return value.  */
        }
 
-       /* Write blob table.  */
-       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_BLOB_TABLE)) {
+       /* Write blob table if needed.  */
+       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS)) {
                ret = write_blob_table(wim, image, write_flags,
-                                      &wim->hdr.blob_table_reshdr,
                                       blob_table_list);
                if (ret) {
                        free_integrity_table(old_integrity_table);
@@ -2487,30 +2334,33 @@ finish_write(WIMStruct *wim, int image, int write_flags,
        if (write_flags & WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES)
                xml_totalbytes = WIM_TOTALBYTES_USE_EXISTING;
        ret = write_wim_xml_data(wim, image, xml_totalbytes,
-                                &wim->hdr.xml_data_reshdr,
+                                &wim->out_hdr.xml_data_reshdr,
                                 write_resource_flags);
        if (ret) {
                free_integrity_table(old_integrity_table);
                return ret;
        }
 
-       /* Write integrity table (optional).  */
+       /* Write integrity table if needed.  */
        if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
-               if (write_flags & WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) {
+               if (write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS) {
+                       /* The XML data we wrote may have overwritten part of
+                        * the old integrity table, so while calculating the new
+                        * integrity table we should temporarily update the WIM
+                        * header to remove the integrity table reference.   */
                        struct wim_header checkpoint_hdr;
-                       memcpy(&checkpoint_hdr, &wim->hdr, sizeof(struct wim_header));
+                       memcpy(&checkpoint_hdr, &wim->out_hdr, sizeof(struct wim_header));
                        zero_reshdr(&checkpoint_hdr.integrity_table_reshdr);
                        checkpoint_hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS;
-                       ret = write_wim_header_at_offset(&checkpoint_hdr,
-                                                        &wim->out_fd, 0);
+                       ret = write_wim_header(&checkpoint_hdr, &wim->out_fd, 0);
                        if (ret) {
                                free_integrity_table(old_integrity_table);
                                return ret;
                        }
                }
 
-               new_blob_table_end = wim->hdr.blob_table_reshdr.offset_in_wim +
-                                    wim->hdr.blob_table_reshdr.size_in_wim;
+               new_blob_table_end = wim->out_hdr.blob_table_reshdr.offset_in_wim +
+                                    wim->out_hdr.blob_table_reshdr.size_in_wim;
 
                ret = write_integrity_table(wim,
                                            new_blob_table_end,
@@ -2521,19 +2371,18 @@ finish_write(WIMStruct *wim, int image, int write_flags,
                        return ret;
        } else {
                /* No integrity table.  */
-               zero_reshdr(&wim->hdr.integrity_table_reshdr);
+               zero_reshdr(&wim->out_hdr.integrity_table_reshdr);
        }
 
        /* Now that all information in the WIM header has been determined, the
         * preliminary header written earlier can be overwritten, the header of
         * the existing WIM file can be overwritten, or the final header can be
         * written to the end of the pipable WIM.  */
-       wim->hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
-       hdr_offset = 0;
-       if (write_flags & WIMLIB_WRITE_FLAG_HEADER_AT_END)
-               hdr_offset = wim->out_fd.offset;
-       DEBUG("Writing new header @ %"PRIu64".", hdr_offset);
-       ret = write_wim_header_at_offset(&wim->hdr, &wim->out_fd, hdr_offset);
+       wim->out_hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
+       if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)
+               ret = write_wim_header(&wim->out_hdr, &wim->out_fd, wim->out_fd.offset);
+       else
+               ret = write_wim_header(&wim->out_hdr, &wim->out_fd, 0);
        if (ret)
                return ret;
 
@@ -2544,7 +2393,6 @@ finish_write(WIMStruct *wim, int image, int write_flags,
         * operation has been written to disk, but the new file data has not.
         */
        if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) {
-               DEBUG("Syncing WIM file.");
                if (fsync(wim->out_fd.fd)) {
                        ERROR_WITH_ERRNO("Error syncing data to WIM file");
                        return WIMLIB_ERR_WRITE;
@@ -2718,6 +2566,15 @@ write_pipable_wim(WIMStruct *wim, int image, int write_flags,
         * finish_write().  */
 }
 
+static bool
+should_default_to_solid_compression(WIMStruct *wim, int write_flags)
+{
+       return wim->out_hdr.wim_version == WIM_VERSION_SOLID &&
+               !(write_flags & (WIMLIB_WRITE_FLAG_SOLID |
+                                WIMLIB_WRITE_FLAG_PIPABLE)) &&
+               wim_has_solid_resources(wim);
+}
+
 /* Write a standalone WIM or split WIM (SWM) part to a new file or to a file
  * descriptor.  */
 int
@@ -2732,87 +2589,8 @@ write_wim_part(WIMStruct *wim,
               const u8 *guid)
 {
        int ret;
-       struct wim_header hdr_save;
        struct list_head blob_table_list;
 
-       if (total_parts == 1)
-               DEBUG("Writing standalone WIM.");
-       else
-               DEBUG("Writing split WIM part %u/%u", part_number, total_parts);
-       if (image == WIMLIB_ALL_IMAGES)
-               DEBUG("Including all images.");
-       else
-               DEBUG("Including image %d only.", image);
-       if (write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR)
-               DEBUG("File descriptor: %d", *(const int*)path_or_fd);
-       else
-               DEBUG("Path: \"%"TS"\"", (const tchar*)path_or_fd);
-       DEBUG("Write flags: 0x%08x", write_flags);
-
-       if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY)
-               DEBUG("\tCHECK_INTEGRITY");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY)
-               DEBUG("\tNO_CHECK_INTEGRITY");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)
-               DEBUG("\tPIPABLE");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_NOT_PIPABLE)
-               DEBUG("\tNOT_PIPABLE");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
-               DEBUG("\tRECOMPRESS");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_FSYNC)
-               DEBUG("\tFSYNC");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_REBUILD)
-               DEBUG("\tREBUILD");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE)
-               DEBUG("\tSOFT_DELETE");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG)
-               DEBUG("\tIGNORE_READONLY_FLAG");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS)
-               DEBUG("\tSKIP_EXTERNAL_WIMS");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_STREAMS_OK)
-               DEBUG("\tSTREAMS_OK");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_RETAIN_GUID)
-               DEBUG("\tRETAIN_GUID");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
-               DEBUG("\tSOLID");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES)
-               DEBUG("\tSEND_DONE_WITH_FILE_MESSAGES");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_NO_SOLID_SORT)
-               DEBUG("\tNO_SOLID_SORT");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR)
-               DEBUG("\tFILE_DESCRIPTOR");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_NO_METADATA)
-               DEBUG("\tNO_METADATA");
-
-       if (write_flags & WIMLIB_WRITE_FLAG_USE_EXISTING_TOTALBYTES)
-               DEBUG("\tUSE_EXISTING_TOTALBYTES");
-
-       if (num_threads == 0)
-               DEBUG("Number of threads: autodetect");
-       else
-               DEBUG("Number of threads: %u", num_threads);
-       DEBUG("Progress function: %s", (wim->progfunc ? "yes" : "no"));
-       DEBUG("Blob list:         %s", (blob_list_override ? "specified" : "autodetect"));
-       DEBUG("GUID:              %s", (write_flags &
-                                       WIMLIB_WRITE_FLAG_RETAIN_GUID) ? "retain"
-                                               : guid ? "explicit" : "generate new");
-
        /* Internally, this is always called with a valid part number and total
         * parts.  */
        wimlib_assert(total_parts >= 1);
@@ -2843,93 +2621,104 @@ write_wim_part(WIMStruct *wim,
                                    WIMLIB_WRITE_FLAG_NOT_PIPABLE))
                return WIMLIB_ERR_INVALID_PARAM;
 
-       /* Save previous header, then start initializing the new one.  */
-       memcpy(&hdr_save, &wim->hdr, sizeof(struct wim_header));
+       /* Include an integrity table by default if no preference was given and
+        * the WIM already had an integrity table.  */
+       if (!(write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY |
+                            WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY))) {
+               if (wim_has_integrity_table(wim))
+                       write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
+       }
 
-       /* Set default integrity, pipable, and solid flags.  */
+       /* Write a pipable WIM by default if no preference was given and the WIM
+        * was already pipable.  */
        if (!(write_flags & (WIMLIB_WRITE_FLAG_PIPABLE |
-                            WIMLIB_WRITE_FLAG_NOT_PIPABLE)))
-               if (wim_is_pipable(wim)) {
-                       DEBUG("WIM is pipable; default to PIPABLE.");
+                            WIMLIB_WRITE_FLAG_NOT_PIPABLE))) {
+               if (wim_is_pipable(wim))
                        write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
-               }
-
-       if (!(write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY |
-                            WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY)))
-               if (wim_has_integrity_table(wim)) {
-                       DEBUG("Integrity table present; default to CHECK_INTEGRITY.");
-                       write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
-               }
+       }
 
        if ((write_flags & (WIMLIB_WRITE_FLAG_PIPABLE |
                            WIMLIB_WRITE_FLAG_SOLID))
                                    == (WIMLIB_WRITE_FLAG_PIPABLE |
                                        WIMLIB_WRITE_FLAG_SOLID))
        {
-               ERROR("Cannot specify both PIPABLE and SOLID!");
+               ERROR("Solid compression is unsupported in pipable WIMs");
                return WIMLIB_ERR_INVALID_PARAM;
        }
 
-       /* Set appropriate magic number.  */
+       /* Start initializing the new file header.  */
+       memset(&wim->out_hdr, 0, sizeof(wim->out_hdr));
+
+       /* Set the magic number.  */
        if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)
-               wim->hdr.magic = PWM_MAGIC;
+               wim->out_hdr.magic = PWM_MAGIC;
        else
-               wim->hdr.magic = WIM_MAGIC;
+               wim->out_hdr.magic = WIM_MAGIC;
 
-       /* Set appropriate version number.  */
+       /* Set the version number.  */
        if ((write_flags & WIMLIB_WRITE_FLAG_SOLID) ||
            wim->out_compression_type == WIMLIB_COMPRESSION_TYPE_LZMS)
-               wim->hdr.wim_version = WIM_VERSION_SOLID;
+               wim->out_hdr.wim_version = WIM_VERSION_SOLID;
        else
-               wim->hdr.wim_version = WIM_VERSION_DEFAULT;
+               wim->out_hdr.wim_version = WIM_VERSION_DEFAULT;
 
-       /* Clear header flags that will be set automatically.  */
-       wim->hdr.flags &= ~(WIM_HDR_FLAG_METADATA_ONLY          |
-                           WIM_HDR_FLAG_RESOURCE_ONLY          |
-                           WIM_HDR_FLAG_SPANNED                |
-                           WIM_HDR_FLAG_WRITE_IN_PROGRESS);
+       /* Default to solid compression if it is valid in the chosen WIM file
+        * format and the WIMStruct references any solid resources.  This is
+        * useful when exporting an image from a solid WIM.  */
+       if (should_default_to_solid_compression(wim, write_flags))
+               write_flags |= WIMLIB_WRITE_FLAG_SOLID;
 
-       /* Set SPANNED header flag if writing part of a split WIM.  */
+       /* Set the header flags.  */
+       wim->out_hdr.flags = (wim->hdr.flags & (WIM_HDR_FLAG_RP_FIX |
+                                               WIM_HDR_FLAG_READONLY));
        if (total_parts != 1)
-               wim->hdr.flags |= WIM_HDR_FLAG_SPANNED;
+               wim->out_hdr.flags |= WIM_HDR_FLAG_SPANNED;
+       if (wim->out_compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
+               wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESSION;
+               switch (wim->out_compression_type) {
+               case WIMLIB_COMPRESSION_TYPE_XPRESS:
+                       wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_XPRESS;
+                       break;
+               case WIMLIB_COMPRESSION_TYPE_LZX:
+                       wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_LZX;
+                       break;
+               case WIMLIB_COMPRESSION_TYPE_LZMS:
+                       wim->out_hdr.flags |= WIM_HDR_FLAG_COMPRESS_LZMS;
+                       break;
+               }
+       }
 
-       /* Set part number and total parts of split WIM.  This will be 1 and 1
-        * if the WIM is standalone.  */
-       wim->hdr.part_number = part_number;
-       wim->hdr.total_parts = total_parts;
+       /* Set the chunk size.  */
+       wim->out_hdr.chunk_size = wim->out_chunk_size;
 
-       /* Set the compression type and chunk size.  */
-       set_wim_hdr_cflags(wim->out_compression_type, &wim->hdr);
-       wim->hdr.chunk_size = wim->out_chunk_size;
+       /* Set the GUID.  */
+       if (write_flags & WIMLIB_WRITE_FLAG_RETAIN_GUID)
+               guid = wim->hdr.guid;
+       if (guid)
+               copy_guid(wim->out_hdr.guid, guid);
+       else
+               generate_guid(wim->out_hdr.guid);
 
-       /* Set GUID.  */
-       if (!(write_flags & WIMLIB_WRITE_FLAG_RETAIN_GUID)) {
-               if (guid)
-                       memcpy(wim->hdr.guid, guid, WIMLIB_GUID_LEN);
-               else
-                       randomize_byte_array(wim->hdr.guid, WIMLIB_GUID_LEN);
-       }
+       /* Set the part number and total parts.  */
+       wim->out_hdr.part_number = part_number;
+       wim->out_hdr.total_parts = total_parts;
 
-       /* Clear references to resources that have not been written yet.  */
-       zero_reshdr(&wim->hdr.blob_table_reshdr);
-       zero_reshdr(&wim->hdr.xml_data_reshdr);
-       zero_reshdr(&wim->hdr.boot_metadata_reshdr);
-       zero_reshdr(&wim->hdr.integrity_table_reshdr);
+       /* Set the image count.  */
+       if (image == WIMLIB_ALL_IMAGES)
+               wim->out_hdr.image_count = wim->hdr.image_count;
+       else
+               wim->out_hdr.image_count = 1;
 
-       /* Set image count and boot index correctly for single image writes.  */
-       if (image != WIMLIB_ALL_IMAGES) {
-               wim->hdr.image_count = 1;
-               if (wim->hdr.boot_idx == image)
-                       wim->hdr.boot_idx = 1;
-               else
-                       wim->hdr.boot_idx = 0;
+       /* Set the boot index.  */
+       wim->out_hdr.boot_idx = 0;
+       if (total_parts == 1) {
+               if (image == WIMLIB_ALL_IMAGES)
+                       wim->out_hdr.boot_idx = wim->hdr.boot_idx;
+               else if (image == wim->hdr.boot_idx)
+                       wim->out_hdr.boot_idx = 1;
        }
 
-       /* Split WIMs can't be bootable.  */
-       if (total_parts != 1)
-               wim->hdr.boot_idx = 0;
-
-       /* Set up output file descriptor.  */
+       /* Set up the output file descriptor.  */
        if (write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR) {
                /* File descriptor was explicitly provided.  */
                filedes_init(&wim->out_fd, *(const int *)path_or_fd);
@@ -2937,32 +2726,31 @@ write_wim_part(WIMStruct *wim,
                        /* The file descriptor is a pipe.  */
                        ret = WIMLIB_ERR_INVALID_PARAM;
                        if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE))
-                               goto out_restore_hdr;
+                               goto out_cleanup;
                        if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
                                ERROR("Can't include integrity check when "
                                      "writing pipable WIM to pipe!");
-                               goto out_restore_hdr;
+                               goto out_cleanup;
                        }
                }
-
        } else {
                /* Filename of WIM to write was provided; open file descriptor
                 * to it.  */
                ret = open_wim_writable(wim, (const tchar*)path_or_fd,
                                        O_TRUNC | O_CREAT | O_RDWR);
                if (ret)
-                       goto out_restore_hdr;
+                       goto out_cleanup;
        }
 
        /* Write initial header.  This is merely a "dummy" header since it
-        * doesn't have all the information yet, so it will be overwritten later
-        * (unless writing a pipable WIM).  */
+        * doesn't have resource entries filled in yet, so it will be
+        * overwritten later (unless writing a pipable WIM).  */
        if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE))
-               wim->hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS;
-       ret = write_wim_header(&wim->hdr, &wim->out_fd);
-       wim->hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
+               wim->out_hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS;
+       ret = write_wim_header(&wim->out_hdr, &wim->out_fd, wim->out_fd.offset);
+       wim->out_hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
        if (ret)
-               goto out_restore_hdr;
+               goto out_cleanup;
 
        /* Write file data and metadata resources.  */
        if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
@@ -2972,28 +2760,24 @@ write_wim_part(WIMStruct *wim,
                                      blob_list_override,
                                      &blob_table_list);
                if (ret)
-                       goto out_restore_hdr;
+                       goto out_cleanup;
 
                ret = write_metadata_resources(wim, image, write_flags);
                if (ret)
-                       goto out_restore_hdr;
+                       goto out_cleanup;
        } else {
                /* Non-default case: create pipable WIM.  */
                ret = write_pipable_wim(wim, image, write_flags, num_threads,
                                        blob_list_override,
                                        &blob_table_list);
                if (ret)
-                       goto out_restore_hdr;
-               write_flags |= WIMLIB_WRITE_FLAG_HEADER_AT_END;
+                       goto out_cleanup;
        }
 
-
        /* Write blob table, XML data, and (optional) integrity table.  */
        ret = finish_write(wim, image, write_flags, &blob_table_list);
-out_restore_hdr:
-       memcpy(&wim->hdr, &hdr_save, sizeof(struct wim_header));
+out_cleanup:
        (void)close_wim_writable(wim, write_flags);
-       DEBUG("ret=%d", ret);
        return ret;
 }
 
@@ -3096,11 +2880,10 @@ check_resource_offsets(WIMStruct *wim, off_t end_offset)
  * header.  This operation is potentially unsafe if the program is abruptly
  * terminated while the XML data or integrity table are being overwritten, but
  * before the new header has been written.  To partially alleviate this problem,
- * a special flag (WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) is passed to
- * finish_write() to cause a temporary WIM header to be written after the XML
- * data has been written.  This may prevent the WIM from becoming corrupted if
- * the program is terminated while the integrity table is being calculated (but
- * no guarantees, due to write re-ordering...).
+ * we write a temporary header after the XML data has been written.  This may
+ * prevent the WIM from becoming corrupted if the program is terminated while
+ * the integrity table is being calculated (but no guarantees, due to write
+ * re-ordering...).
  *
  * If we are adding new blobs, including new file data as well as any metadata
  * for any new images, then the blob table needs to be changed, and those blobs
@@ -3143,25 +2926,30 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        int ret;
        off_t old_wim_end;
        u64 old_blob_table_end, old_xml_begin, old_xml_end;
-       struct wim_header hdr_save;
        struct list_head blob_list;
        struct list_head blob_table_list;
        struct filter_context filter_ctx;
 
-       DEBUG("Overwriting `%"TS"' in-place", wim->filename);
-
-       /* Save original header so it can be restored in case of error  */
-       memcpy(&hdr_save, &wim->hdr, sizeof(struct wim_header));
-
-       /* Set default integrity flag.  */
+       /* Include an integrity table by default if no preference was given and
+        * the WIM already had an integrity table.  */
        if (!(write_flags & (WIMLIB_WRITE_FLAG_CHECK_INTEGRITY |
                             WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY)))
                if (wim_has_integrity_table(wim))
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
 
-       /* Set WIM version if writing solid resources.  */
+       /* Start preparing the updated file header.  */
+       memcpy(&wim->out_hdr, &wim->hdr, sizeof(wim->out_hdr));
+
+       /* If using solid compression, the version number must be set to
+        * WIM_VERSION_SOLID.  */
        if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
-               wim->hdr.wim_version = WIM_VERSION_SOLID;
+               wim->out_hdr.wim_version = WIM_VERSION_SOLID;
+
+       /* Default to solid compression if it is valid in the chosen WIM file
+        * format and the WIMStruct references any solid resources.  This is
+        * useful when updating a solid WIM.  */
+       if (should_default_to_solid_compression(wim, write_flags))
+               write_flags |= WIMLIB_WRITE_FLAG_SOLID;
 
        /* Set additional flags for overwrite.  */
        write_flags |= WIMLIB_WRITE_FLAG_OVERWRITE |
@@ -3174,17 +2962,17 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        old_xml_end = old_xml_begin + wim->hdr.xml_data_reshdr.size_in_wim;
        old_blob_table_end = wim->hdr.blob_table_reshdr.offset_in_wim +
                             wim->hdr.blob_table_reshdr.size_in_wim;
-       if (wim->hdr.integrity_table_reshdr.offset_in_wim != 0 &&
+       if (wim_has_integrity_table(wim) &&
            wim->hdr.integrity_table_reshdr.offset_in_wim < old_xml_end) {
                WARNING("Didn't expect the integrity table to be before the XML data");
                ret = WIMLIB_ERR_RESOURCE_ORDER;
-               goto out_restore_memory_hdr;
+               goto out;
        }
 
        if (old_blob_table_end > old_xml_begin) {
                WARNING("Didn't expect the blob table to be after the XML data");
                ret = WIMLIB_ERR_RESOURCE_ORDER;
-               goto out_restore_memory_hdr;
+               goto out;
        }
 
        /* Set @old_wim_end, which indicates the point beyond which we don't
@@ -3197,12 +2985,9 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
                 * shall write the new XML data and optional integrity table
                 * immediately after the blob table.  Note that this may
                 * overwrite an existing integrity table. */
-               DEBUG("Skipping writing blob table "
-                     "(no images modified or deleted)");
                old_wim_end = old_blob_table_end;
-               write_flags |= WIMLIB_WRITE_FLAG_NO_BLOB_TABLE |
-                              WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML;
-       } else if (wim->hdr.integrity_table_reshdr.offset_in_wim != 0) {
+               write_flags |= WIMLIB_WRITE_FLAG_NO_NEW_BLOBS;
+       } else if (wim_has_integrity_table(wim)) {
                /* Old WIM has an integrity table; begin writing new blobs after
                 * it. */
                old_wim_end = wim->hdr.integrity_table_reshdr.offset_in_wim +
@@ -3215,17 +3000,20 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
 
        ret = check_resource_offsets(wim, old_wim_end);
        if (ret)
-               goto out_restore_memory_hdr;
+               goto out;
 
        ret = prepare_blob_list_for_write(wim, WIMLIB_ALL_IMAGES, write_flags,
                                          &blob_list, &blob_table_list,
                                          &filter_ctx);
        if (ret)
-               goto out_restore_memory_hdr;
+               goto out;
+
+       if (write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS)
+               wimlib_assert(list_empty(&blob_list));
 
        ret = open_wim_writable(wim, wim->filename, O_RDWR);
        if (ret)
-               goto out_restore_memory_hdr;
+               goto out;
 
        ret = lock_wim_for_append(wim);
        if (ret)
@@ -3234,6 +3022,7 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        /* Set WIM_HDR_FLAG_WRITE_IN_PROGRESS flag in header. */
        wim->hdr.flags |= WIM_HDR_FLAG_WRITE_IN_PROGRESS;
        ret = write_wim_header_flags(wim->hdr.flags, &wim->out_fd);
+       wim->hdr.flags &= ~WIM_HDR_FLAG_WRITE_IN_PROGRESS;
        if (ret) {
                ERROR_WITH_ERRNO("Error updating WIM header flags");
                goto out_unlock_wim;
@@ -3242,7 +3031,7 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        if (filedes_seek(&wim->out_fd, old_wim_end) == -1) {
                ERROR_WITH_ERRNO("Can't seek to end of WIM");
                ret = WIMLIB_ERR_WRITE;
-               goto out_restore_physical_hdr;
+               goto out_restore_hdr;
        }
 
        ret = write_file_data_blobs(wim, &blob_list, write_flags,
@@ -3263,21 +3052,20 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, unsigned num_threads)
        return 0;
 
 out_truncate:
-       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_BLOB_TABLE)) {
-               WARNING("Truncating `%"TS"' to its original size (%"PRIu64" bytes)",
-                       wim->filename, old_wim_end);
+       if (!(write_flags & WIMLIB_WRITE_FLAG_NO_NEW_BLOBS)) {
+               WARNING("Truncating \"%"TS"\" to its original size "
+                       "(%"PRIu64" bytes)", wim->filename, old_wim_end);
                /* Return value of ftruncate() is ignored because this is
                 * already an error path.  */
                (void)ftruncate(wim->out_fd.fd, old_wim_end);
        }
-out_restore_physical_hdr:
-       (void)write_wim_header_flags(hdr_save.flags, &wim->out_fd);
+out_restore_hdr:
+       (void)write_wim_header_flags(wim->hdr.flags, &wim->out_fd);
 out_unlock_wim:
        unlock_wim_for_append(wim);
 out_close_wim:
        (void)close_wim_writable(wim, write_flags);
-out_restore_memory_hdr:
-       memcpy(&wim->hdr, &hdr_save, sizeof(struct wim_header));
+out:
        return ret;
 }
 
@@ -3287,8 +3075,6 @@ overwrite_wim_via_tmpfile(WIMStruct *wim, int write_flags, unsigned num_threads)
        size_t wim_name_len;
        int ret;
 
-       DEBUG("Overwriting `%"TS"' via a temporary file", wim->filename);
-
        /* Write the WIM to a temporary file in the same directory as the
         * original WIM. */
        wim_name_len = tstrlen(wim->filename);
@@ -3315,7 +3101,6 @@ overwrite_wim_via_tmpfile(WIMStruct *wim, int write_flags, unsigned num_threads)
        /* Rename the new WIM file to the original WIM file.  Note: on Windows
         * this actually calls win32_rename_replacement(), not _wrename(), so
         * that removing the existing destination file can be handled.  */
-       DEBUG("Renaming `%"TS"' to `%"TS"'", tmpfile, wim->filename);
        ret = trename(tmpfile, wim->filename);
        if (ret) {
                ERROR_WITH_ERRNO("Failed to rename `%"TS"' to `%"TS"'",