]> wimlib.net Git - wimlib/blobdiff - src/write.c
write_stream_list(): Write streams uncompressed when advantageous
[wimlib] / src / write.c
index 54e6394093baa0a65ae1247d456b1f8a3aaf28ed..0aacdf13e04fc57e5abd3574c7321a929201c99c 100644 (file)
@@ -34,7 +34,7 @@
 #  include <sys/file.h>
 #endif
 
-#include "wimlib/compress_chunks.h"
+#include "wimlib/chunk_compressor.h"
 #include "wimlib/endianness.h"
 #include "wimlib/error.h"
 #include "wimlib/file_io.h"
@@ -268,20 +268,6 @@ write_pwm_stream_header(const struct wim_lookup_table_entry *lte,
        return ret;
 }
 
-#if 0
-static int
-seek_and_truncate(struct filedes *out_fd, off_t offset)
-{
-       if (filedes_seek(out_fd, offset) == -1 ||
-           ftruncate(out_fd->fd, offset))
-       {
-               ERROR_WITH_ERRNO("Failed to truncate output WIM file");
-               return WIMLIB_ERR_WRITE;
-       }
-       return 0;
-}
-#endif
-
 struct write_streams_progress_data {
        wimlib_progress_func_t progress_func;
        union wimlib_progress_info progress;
@@ -718,6 +704,33 @@ write_stream_begin_read(struct wim_lookup_table_entry *lte,
        return 0;
 }
 
+static int
+write_stream_uncompressed(struct wim_lookup_table_entry *lte,
+                         struct filedes *out_fd)
+{
+       int ret;
+
+       if (-1 == lseek(out_fd->fd, lte->out_reshdr.offset_in_wim, SEEK_SET) ||
+           0 != ftruncate(out_fd->fd, lte->out_reshdr.offset_in_wim))
+       {
+               ERROR_WITH_ERRNO("Can't truncate output file to "
+                                "offset %"PRIu64, lte->out_reshdr.offset_in_wim);
+               return WIMLIB_ERR_WRITE;
+       }
+
+       out_fd->offset = lte->out_reshdr.offset_in_wim;
+
+       ret = extract_stream_to_fd(lte, out_fd, lte->size);
+       if (ret)
+               return ret;
+
+       wimlib_assert(out_fd->offset - lte->out_reshdr.offset_in_wim == lte->size);
+       lte->out_reshdr.size_in_wim = lte->size;
+       lte->out_reshdr.flags &= ~(WIM_RESHDR_FLAG_COMPRESSED |
+                                  WIM_RESHDR_FLAG_PACKED_STREAMS);
+       return 0;
+}
+
 /* Write the next chunk of (typically compressed) data to the output WIM,
  * handling the writing of the chunk table.  */
 static int
@@ -798,12 +811,32 @@ write_chunk(struct write_streams_ctx *ctx, const void *cchunk,
                if (ret)
                        return ret;
 
-               wimlib_assert(lte->out_reshdr.uncompressed_size == lte->size);
-
                lte->out_reshdr.flags = filter_resource_flags(lte->flags);
                if (ctx->compressor != NULL)
                        lte->out_reshdr.flags |= WIM_RESHDR_FLAG_COMPRESSED;
 
+               if (ctx->compressor != NULL &&
+                   lte->out_reshdr.size_in_wim >= lte->out_reshdr.uncompressed_size &&
+                   !(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) &&
+                   !(lte->flags & WIM_RESHDR_FLAG_PACKED_STREAMS))
+               {
+                       /* Stream did not compress to less than its original
+                        * size.  If we're not writing a pipable WIM (which
+                        * could mean the output file descriptor is
+                        * non-seekable), and the stream isn't located in a
+                        * resource pack (which would make reading it again
+                        * costly), truncate the file to the start of the stream
+                        * and write it uncompressed instead.  */
+                       DEBUG("Stream of size %"PRIu64" did not compress to "
+                             "less than original size; writing uncompressed.",
+                             lte->size);
+                       ret = write_stream_uncompressed(lte, ctx->out_fd);
+                       if (ret)
+                               return ret;
+               }
+
+               wimlib_assert(lte->out_reshdr.uncompressed_size == lte->size);
+
                list_del(&lte->write_streams_list);
                ctx->cur_write_res_offset = 0;
        }
@@ -1199,11 +1232,6 @@ remove_zero_length_streams(struct list_head *stream_list)
  *     no streams are hard-filtered or no streams are unhashed, this parameter
  *     can be NULL.
  *
- * @comp_ctx
- *     A location in which to allocate the pointer to the default LZX
- *     compression context.  This will only be used if @out_ctype is
- *     WIMLIB_COMPRESSION_TYPE_LZX.
- *
  * @progress_func
  *     If non-NULL, a progress function that will be called periodically with
  *     WIMLIB_PROGRESS_MSG_WRITE_STREAMS messages.  Note that on-the-fly
@@ -1268,7 +1296,6 @@ write_stream_list(struct list_head *stream_list,
                  unsigned num_threads,
                  struct wim_lookup_table *lookup_table,
                  struct filter_context *filter_ctx,
-                 struct wimlib_lzx_context **comp_ctx,
                  wimlib_progress_func_t progress_func)
 {
        int ret;
@@ -1355,6 +1382,7 @@ write_stream_list(struct list_head *stream_list,
                                "actually be written uncompressed.");
                }
 
+       #ifdef ENABLE_MULTITHREADED_COMPRESSION
                if (ctx.num_bytes_to_compress >= 2000000) {
                        ret = new_parallel_chunk_compressor(out_ctype,
                                                            out_chunk_size,
@@ -1365,17 +1393,11 @@ write_stream_list(struct list_head *stream_list,
                                      "(status %d)", ret);
                        }
                }
+       #endif
 
                if (ctx.compressor == NULL) {
-                       if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX) {
-                               ret = wimlib_lzx_alloc_context(out_chunk_size,
-                                                              NULL,
-                                                              comp_ctx);
-                               if (ret)
-                                       goto out_destroy_context;
-                       }
                        ret = new_serial_chunk_compressor(out_ctype, out_chunk_size,
-                                                         *comp_ctx, &ctx.compressor);
+                                                         &ctx.compressor);
                        if (ret)
                                goto out_destroy_context;
                }
@@ -1478,8 +1500,7 @@ write_wim_resource(struct wim_lookup_table_entry *lte,
                   struct filedes *out_fd,
                   int out_ctype,
                   u32 out_chunk_size,
-                  int write_resource_flags,
-                  struct wimlib_lzx_context **comp_ctx)
+                  int write_resource_flags)
 {
        LIST_HEAD(stream_list);
        list_add(&lte->write_streams_list, &stream_list);
@@ -1492,7 +1513,6 @@ write_wim_resource(struct wim_lookup_table_entry *lte,
                                 1,
                                 NULL,
                                 NULL,
-                                comp_ctx,
                                 NULL);
 }
 
@@ -1503,8 +1523,7 @@ write_wim_resource_from_buffer(const void *buf, size_t buf_size,
                               u32 out_chunk_size,
                               struct wim_reshdr *out_reshdr,
                               u8 *hash,
-                              int write_resource_flags,
-                              struct wimlib_lzx_context **comp_ctx)
+                              int write_resource_flags)
 {
        int ret;
        struct wim_lookup_table_entry *lte;
@@ -1529,7 +1548,7 @@ write_wim_resource_from_buffer(const void *buf, size_t buf_size,
        }
 
        ret = write_wim_resource(lte, out_fd, out_ctype, out_chunk_size,
-                                write_resource_flags, comp_ctx);
+                                write_resource_flags);
        if (ret)
                goto out_free_lte;
 
@@ -1936,7 +1955,6 @@ write_wim_streams(WIMStruct *wim, int image, int write_flags,
                                 num_threads,
                                 wim->lookup_table,
                                 filter_ctx,
-                                &wim->lzx_context,
                                 progress_func);
 }
 
@@ -1996,8 +2014,7 @@ write_wim_metadata_resources(WIMStruct *wim, int image, int write_flags,
                                                 &wim->out_fd,
                                                 wim->out_compression_type,
                                                 wim->out_chunk_size,
-                                                write_resource_flags,
-                                                &wim->lzx_context);
+                                                write_resource_flags);
                }
                if (ret)
                        return ret;
@@ -2117,8 +2134,7 @@ write_wim_lookup_table(WIMStruct *wim, int image, int write_flags,
                                                       &wim->out_fd,
                                                       wim->hdr.part_number,
                                                       out_reshdr,
-                                                      write_flags_to_resource_flags(write_flags),
-                                                      &wim->lzx_context);
+                                                      write_flags_to_resource_flags(write_flags));
 }
 
 /*
@@ -2583,7 +2599,10 @@ write_wim_part(WIMStruct *wim,
                            WIMLIB_WRITE_FLAG_PACK_STREAMS))
                                    == (WIMLIB_WRITE_FLAG_PIPABLE |
                                        WIMLIB_WRITE_FLAG_PACK_STREAMS))
+       {
+               ERROR("Cannot specify both PIPABLE and PACK_STREAMS!");
                return WIMLIB_ERR_INVALID_PARAM;
+       }
 
        /* Set appropriate magic number.  */
        if (write_flags & WIMLIB_WRITE_FLAG_PIPABLE)
@@ -2886,8 +2905,13 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags,
        if (!(write_flags & (WIMLIB_WRITE_FLAG_PACK_STREAMS |
                             WIMLIB_WRITE_FLAG_NO_PACK_STREAMS)))
        {
+       #if 0
                if (wim->hdr.wim_version == WIM_VERSION_PACKED_STREAMS)
                        write_flags |= WIMLIB_WRITE_FLAG_PACK_STREAMS;
+       #endif
+               /* wimlib allows multiple packs in a single WIM, but they don't
+                * seem to be compatible with WIMGAPI.  Write new streams
+                * unpacked.  */
        } else if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS) {
                wim->hdr.wim_version = WIM_VERSION_PACKED_STREAMS;
        }
@@ -2983,7 +3007,6 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags,
                                num_threads,
                                wim->lookup_table,
                                &filter_ctx,
-                               &wim->lzx_context,
                                progress_func);
        if (ret)
                goto out_truncate;
@@ -3075,18 +3098,35 @@ overwrite_wim_via_tmpfile(WIMStruct *wim, int write_flags,
        return 0;
 }
 
+/* Determine if the specified WIM file may be updated by appending in-place
+ * rather than writing and replacing it with an entirely new file.  */
 static bool
 can_overwrite_wim_inplace(const WIMStruct *wim, int write_flags)
 {
+       /* REBUILD flag forces full rebuild.  */
        if (write_flags & WIMLIB_WRITE_FLAG_REBUILD)
                return false;
 
+       /* Deletions cause full rebuild by default.  */
        if (wim->deletion_occurred && !(write_flags & WIMLIB_WRITE_FLAG_SOFT_DELETE))
                return false;
 
+       /* Pipable WIMs cannot be updated in place, nor can a non-pipable WIM be
+        * turned into a pipable WIM in-place.  */
        if (wim_is_pipable(wim) || (write_flags & WIMLIB_WRITE_FLAG_PIPABLE))
                return false;
 
+       /* wimlib allows multiple packs in a single WIM, but they don't seem to
+        * be compatible with WIMGAPI, so force all streams to be repacked if
+        * the WIM already may have contained a pack and PACK_STREAMS was
+        * requested.  */
+       if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS &&
+           wim->hdr.wim_version == WIM_VERSION_PACKED_STREAMS)
+               return false;
+
+       /* The default compression type and compression chunk size selected for
+        * the output WIM must be the same as those currently used for the WIM.
+        */
        if (wim->compression_type != wim->out_compression_type)
                return false;
        if (wim->chunk_size != wim->out_chunk_size)