#include "wimlib/error.h"
#include "wimlib/file_io.h"
#include "wimlib/header.h"
+#include "wimlib/inode.h"
#include "wimlib/integrity.h"
#include "wimlib/lookup_table.h"
#include "wimlib/metadata.h"
if (rspec->is_pipable != !!(write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE))
return false;
-
if (rspec->flags & WIM_RESHDR_FLAG_COMPRESSED) {
/* Normal compressed resource: Must use same compression type
* and chunk size. */
- return (rspec->wim->compression_type == out_ctype &&
- rspec->wim->chunk_size == out_chunk_size);
+ return (rspec->compression_type == out_ctype &&
+ rspec->chunk_size == out_chunk_size);
}
- /* XXX: For compatibility, we can't allow multiple packed resources per
- * WIM. */
-#if 0
if ((rspec->flags & WIM_RESHDR_FLAG_PACKED_STREAMS) &&
(write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS))
{
/* Packed resource: Such resources may contain multiple streams,
* and in general only a subset of them need to be written. As
- * a heuristic, re-use the raw data if at least half the
+ * a heuristic, re-use the raw data if more than two-thirds the
* uncompressed size is being written. */
/* Note: packed resources contain a header that specifies the
if (res_stream->will_be_in_output_wim)
write_size += res_stream->size;
- return (write_size > rspec->uncompressed_size / 2);
+ return (write_size > rspec->uncompressed_size * 2 / 3);
}
-#endif
return false;
}
lte->out_res_offset_in_wim = rspec->offset_in_wim;
lte->out_res_size_in_wim = rspec->size_in_wim;
- /*lte->out_res_uncompressed_size = rspec->uncompressed_size;*/
+ lte->out_res_uncompressed_size = rspec->uncompressed_size;
} else {
wimlib_assert(!(lte->flags & WIM_RESHDR_FLAG_PACKED_STREAMS));
wimlib_progress_func_t progress_func;
union wimlib_progress_info progress;
uint64_t next_progress;
- WIMStruct *prev_wim_part;
};
static void
bool discarded)
{
union wimlib_progress_info *progress = &progress_data->progress;
- bool new_wim_part;
if (discarded) {
progress->write_streams.total_bytes -= complete_size;
progress->write_streams.completed_streams += complete_count;
}
- new_wim_part = false;
- if (cur_stream->resource_location == RESOURCE_IN_WIM &&
- cur_stream->rspec->wim != progress_data->prev_wim_part)
- {
- if (progress_data->prev_wim_part) {
- new_wim_part = true;
- progress->write_streams.completed_parts++;
- }
- progress_data->prev_wim_part = cur_stream->rspec->wim;
- }
-
if (progress_data->progress_func
- && (progress->write_streams.completed_bytes >= progress_data->next_progress
- || new_wim_part))
+ && (progress->write_streams.completed_bytes >= progress_data->next_progress))
{
progress_data->progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS,
progress);
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_LZX != 1);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 2);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 1);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 2);
BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
ret = full_pwrite(ctx->out_fd, &hdr, sizeof(hdr),
/* Begin processing a stream for writing. */
static int
write_stream_begin_read(struct wim_lookup_table_entry *lte,
- bool is_partial_res, void *_ctx)
+ u32 flags, void *_ctx)
{
struct write_streams_ctx *ctx = _ctx;
int ret;
ctx->stream_was_duplicate = false;
if (ctx->lookup_table != NULL && lte->unhashed && !lte->unique_size) {
- wimlib_assert(!is_partial_res);
+ wimlib_assert(!(flags & BEGIN_STREAM_FLAG_PARTIAL_RESOURCE));
struct wim_lookup_table_entry *lte_new;
* compressed chunk. */
while (!ctx->compressor->submit_chunk(ctx->compressor, chunk, size)) {
const void *cchunk;
- unsigned csize;
- unsigned usize;
+ u32 csize;
+ u32 usize;
bool bret;
int ret;
ctx->progress_data.progress.write_streams.total_parts = total_parts;
ctx->progress_data.progress.write_streams.completed_parts = 0;
ctx->progress_data.next_progress = 0;
- ctx->progress_data.prev_wim_part = NULL;
}
/* Find streams in @stream_list that can be copied to the output WIM in raw form
- * rather than compressed. Delete these streams from @stream_list, and move one
- * per resource to @raw_copy_resources. Return the total uncompressed size of
- * the streams that need to be compressed. */
+ * rather than compressed. Delete these streams from @stream_list and move them
+ * to @raw_copy_streams. Return the total uncompressed size of the streams that
+ * need to be compressed. */
static u64
-find_raw_copy_resources(struct list_head *stream_list,
- int write_resource_flags,
- int out_ctype,
- u32 out_chunk_size,
- struct list_head *raw_copy_resources)
+find_raw_copy_streams(struct list_head *stream_list,
+ int write_resource_flags,
+ int out_ctype,
+ u32 out_chunk_size,
+ struct list_head *raw_copy_streams)
{
struct wim_lookup_table_entry *lte, *tmp;
u64 num_bytes_to_compress = 0;
- INIT_LIST_HEAD(raw_copy_resources);
+ INIT_LIST_HEAD(raw_copy_streams);
/* Initialize temporary raw_copy_ok flag. */
list_for_each_entry(lte, stream_list, write_streams_list)
if (lte->resource_location == RESOURCE_IN_WIM &&
lte->rspec->raw_copy_ok)
{
- list_del(<e->write_streams_list);
+ list_move_tail(<e->write_streams_list,
+ raw_copy_streams);
} else if (can_raw_copy(lte, write_resource_flags,
out_ctype, out_chunk_size))
{
lte->rspec->raw_copy_ok = 1;
list_move_tail(<e->write_streams_list,
- raw_copy_resources);
+ raw_copy_streams);
} else {
num_bytes_to_compress += lte->size;
}
return 0;
}
-/* Copy a list of raw compressed resources located other WIM file(s) to the WIM
- * file being written. */
+/* Copy a list of raw compressed resources located in other WIM file(s) to the
+ * WIM file being written. */
static int
-write_raw_copy_resources(struct list_head *raw_copy_resources,
+write_raw_copy_resources(struct list_head *raw_copy_streams,
struct filedes *out_fd,
struct write_streams_progress_data *progress_data)
{
struct wim_lookup_table_entry *lte;
int ret;
- list_for_each_entry(lte, raw_copy_resources, write_streams_list) {
- ret = write_raw_copy_resource(lte->rspec, out_fd);
- if (ret)
- return ret;
+ list_for_each_entry(lte, raw_copy_streams, write_streams_list)
+ lte->rspec->raw_copy_ok = 1;
+
+ list_for_each_entry(lte, raw_copy_streams, write_streams_list) {
+ if (lte->rspec->raw_copy_ok) {
+ /* Write each packed resource only one time, no matter
+ * how many streams reference it. */
+ ret = write_raw_copy_resource(lte->rspec, out_fd);
+ if (ret)
+ return ret;
+ lte->rspec->raw_copy_ok = 0;
+ }
do_write_streams_progress(progress_data, lte, lte->size,
1, false);
}
finish_remaining_chunks(struct write_streams_ctx *ctx)
{
const void *cdata;
- unsigned csize;
- unsigned usize;
+ u32 csize;
+ u32 usize;
int ret;
if (ctx->compressor == NULL)
* Write a list of streams to the output WIM file.
*
* @stream_list
- * The list of streams to write, specifies a list of `struct
+ * The list of streams to write, specified by a list of `struct
* wim_lookup_table_entry's linked by the 'write_streams_list' member.
*
* @out_fd
*
* WRITE_RESOURCE_FLAG_RECOMPRESS:
* Force compression of all resources, even if they could otherwise
- * be re-used by caping the raw data, due to being located in a WIM
+ * be re-used by copying the raw data, due to being located in a WIM
* file with compatible compression parameters.
*
* WRITE_RESOURCE_FLAG_PIPABLE:
* Write the resources in the wimlib-specific pipable format, and
* furthermore do so in such a way that no seeking backwards in
- * @out_fd will be performed (so it may be a pipe, contrary to the
- * default behavior).
+ * @out_fd will be performed (so it may be a pipe).
*
* WRITE_RESOURCE_FLAG_PACK_STREAMS:
* Pack all the streams into a single resource rather than writing
- * them in separate resources. This format is only valid if the
- * WIM version number is WIM_VERSION_PACKED_STREAMS. This flag
- * currently may not be combined with WRITE_RESOURCE_FLAG_PIPABLE.
+ * them in separate resources. This flag is only valid if the WIM
+ * version number has been, or will be, set to
+ * WIM_VERSION_PACKED_STREAMS. This flag may not be combined with
+ * WRITE_RESOURCE_FLAG_PIPABLE.
*
* @out_ctype
* Compression format to use to write the output streams, specified as one
* In both cases, the @out_reshdr of the `struct wim_lookup_table_entry' for
* each stream written will be updated to specify its location, size, and flags
* in the output WIM. In the packed resource case,
- * WIM_RESHDR_FLAG_PACKED_STREAMS shall be set in the @flags field of the
- * @out_reshdr, and @out_res_offset_in_wim and @out_res_size_in_wim will also
- * be set to the offset and size, respectively, in the output WIM of the full
- * packed resource containing the corresponding stream.
+ * WIM_RESHDR_FLAG_PACKED_STREAMS will be set in the @flags field of each
+ * @out_reshdr, and furthermore @out_res_offset_in_wim and @out_res_size_in_wim
+ * of each @out_reshdr will be set to the offset and size, respectively, in the
+ * output WIM of the packed resource containing the corresponding stream.
*
* Each of the streams to write may be in any location supported by the
* resource-handling code (specifically, read_stream_list()), such as the
* contents of external file that has been logically added to the output WIM, or
- * a stream in another WIM file that has been imported, or even stream in the
+ * a stream in another WIM file that has been imported, or even a stream in the
* "same" WIM file of which a modified copy is being written. In the case that
* a stream is already in a WIM file and uses compatible compression parameters,
* by default this function will re-use the raw data instead of decompressing
* specified in @write_resource_flags, this is not done.
*
* As a further requirement, this function requires that the
- * @will_be_in_output_wim member be set on all streams in @stream_list as well
- * as any other streams not in @stream_list that will be in the output WIM file,
- * but not on any other streams in the output WIM's lookup table or sharing a
- * packed resource with a stream in @stream_list. Still furthermore, if
- * on-the-fly deduplication of streams is possible, then all streams in
+ * @will_be_in_output_wim member be set to 1 on all streams in @stream_list as
+ * well as any other streams not in @stream_list that will be in the output WIM
+ * file, but set to 0 on any other streams in the output WIM's lookup table or
+ * sharing a packed resource with a stream in @stream_list. Still furthermore,
+ * if on-the-fly deduplication of streams is possible, then all streams in
* @stream_list must also be linked by @lookup_table_list along with any other
* streams that have @will_be_in_output_wim set.
*
* This function handles on-the-fly deduplication of streams for which SHA1
- * message digests have not yet been calculated and it is therefore known
- * whether such streams are already in @stream_list or in the WIM's lookup table
- * at all. If @lookup_table is non-NULL, then each stream in @stream_list that
- * has @unhashed set but not @unique_size set is checksummed immediately before
- * it would otherwise be read for writing in order to determine if it is
- * identical to another stream already being written or one that would be
- * filtered out of the output WIM using stream_filtered() with the context
- * @filter_ctx. Each such duplicate stream will be removed from @stream_list, its
- * reference count transfered to the pre-existing duplicate stream, its memory
- * freed, and will not be written. Alternatively, if a stream in @stream_list
- * is a duplicate with any stream in @lookup_table that has not been marked for
- * writing or would not be hard-filtered, it is freed and the pre-existing
- * duplicate is written instead, taking ownership of the reference count and
- * slot in the @lookup_table_list.
- *
- * Returns 0 if all streams were written successfully (or did not need to be
- * written); otherwise a non-zero error code.
+ * message digests have not yet been calculated. Such streams may or may not
+ * need to be written. If @lookup_table is non-NULL, then each stream in
+ * @stream_list that has @unhashed set but not @unique_size set is checksummed
+ * immediately before it would otherwise be read for writing in order to
+ * determine if it is identical to another stream already being written or one
+ * that would be filtered out of the output WIM using stream_filtered() with the
+ * context @filter_ctx. Each such duplicate stream will be removed from
+ * @stream_list, its reference count transfered to the pre-existing duplicate
+ * stream, its memory freed, and will not be written. Alternatively, if a
+ * stream in @stream_list is a duplicate with any stream in @lookup_table that
+ * has not been marked for writing or would not be hard-filtered, it is freed
+ * and the pre-existing duplicate is written instead, taking ownership of the
+ * reference count and slot in the @lookup_table_list.
+ *
+ * Returns 0 if every stream was either written successfully or did not need to
+ * be written; otherwise returns a non-zero error code.
*/
static int
write_stream_list(struct list_head *stream_list,
{
int ret;
struct write_streams_ctx ctx;
- struct list_head raw_copy_resources;
+ struct list_head raw_copy_streams;
wimlib_assert((write_resource_flags &
(WRITE_RESOURCE_FLAG_PACK_STREAMS |
ctx.progress_data.progress_func = progress_func;
- ctx.num_bytes_to_compress = find_raw_copy_resources(stream_list,
- write_resource_flags,
- out_ctype,
- out_chunk_size,
- &raw_copy_resources);
+ ctx.num_bytes_to_compress = find_raw_copy_streams(stream_list,
+ write_resource_flags,
+ out_ctype,
+ out_chunk_size,
+ &raw_copy_streams);
DEBUG("Writing stream list "
"(offset = %"PRIu64", write_resource_flags=0x%08x, "
* to do compression. There are serial and parallel implementations of
* the chunk_compressor interface. We default to parallel using the
* specified number of threads, unless the upper bound on the number
- * bytes needing to be compressed is less 2000000 (heuristic value). */
+ * bytes needing to be compressed is less than a heuristic value. */
if (out_ctype != WIMLIB_COMPRESSION_TYPE_NONE) {
#ifdef ENABLE_MULTITHREADED_COMPRESSION
- if (ctx.num_bytes_to_compress >= 2000000) {
+ if (ctx.num_bytes_to_compress > max(2000000, out_chunk_size)) {
ret = new_parallel_chunk_compressor(out_ctype,
out_chunk_size,
num_threads, 0,
lte->out_reshdr.offset_in_wim = offset_in_res;
lte->out_res_offset_in_wim = reshdr.offset_in_wim;
lte->out_res_size_in_wim = reshdr.size_in_wim;
- /*lte->out_res_uncompressed_size = reshdr.uncompressed_size;*/
+ lte->out_res_uncompressed_size = reshdr.uncompressed_size;
offset_in_res += lte->size;
}
wimlib_assert(offset_in_res == reshdr.uncompressed_size);
out_write_raw_copy_resources:
/* Copy any compressed resources for which the raw data can be reused
* without decompression. */
- ret = write_raw_copy_resources(&raw_copy_resources, ctx.out_fd,
+ ret = write_raw_copy_resources(&raw_copy_streams, ctx.out_fd,
&ctx.progress_data);
out_destroy_context:
write_resource_flags = write_flags_to_resource_flags(write_flags);
+ /* wimlib v1.6.3: pack streams by default if the WIM version number is
+ * that usually used in solid archives. */
+ if (wim->hdr.wim_version == WIM_VERSION_PACKED_STREAMS)
+ write_resource_flags |= WRITE_RESOURCE_FLAG_PACK_STREAMS;
+
if (write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) {
out_chunk_size = wim->out_pack_chunk_size;
out_ctype = wim->out_pack_compression_type;
struct stream_size_table tab;
struct wim_lookup_table_entry *lte;
- ret = init_stream_size_table(&tab, lt->capacity);
+ ret = init_stream_size_table(&tab, 9001);
if (ret)
return ret;
int image, int write_flags, unsigned num_threads,
wimlib_progress_func_t progress_func)
{
- if (!path)
+ if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
return WIMLIB_ERR_INVALID_PARAM;
- write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
+ if (path == NULL || path[0] == T('\0'))
+ return WIMLIB_ERR_INVALID_PARAM;
return write_standalone_wim(wim, path, image, write_flags,
num_threads, progress_func);
int image, int write_flags, unsigned num_threads,
wimlib_progress_func_t progress_func)
{
+ if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
+ return WIMLIB_ERR_INVALID_PARAM;
+
if (fd < 0)
return WIMLIB_ERR_INVALID_PARAM;
- write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
write_flags |= WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR;
return write_standalone_wim(wim, &fd, image, write_flags,
return ret;
}
- close_wim(wim);
+ if (filedes_valid(&wim->in_fd)) {
+ filedes_close(&wim->in_fd);
+ filedes_invalidate(&wim->in_fd);
+ }
/* Rename the new WIM file to the original WIM file. Note: on Windows
* this actually calls win32_rename_replacement(), not _wrename(), so
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.
*/
int ret;
u32 orig_hdr_flags;
- write_flags &= WIMLIB_WRITE_MASK_PUBLIC;
-
- if (write_flags & WIMLIB_WRITE_FLAG_FILE_DESCRIPTOR)
+ if (write_flags & ~WIMLIB_WRITE_MASK_PUBLIC)
return WIMLIB_ERR_INVALID_PARAM;
if (!wim->filename)