# define INVALID_HANDLE_VALUE ((HANDLE)(-1))
#endif
-static int
-fflush_and_ftruncate(FILE *fp, off_t size)
-{
- int ret;
-
- ret = fflush(fp);
- if (ret != 0) {
- ERROR_WITH_ERRNO("Failed to flush data to output WIM file");
- return WIMLIB_ERR_WRITE;
- }
- ret = ftruncate(fileno(fp), size);
- if (ret != 0) {
- ERROR_WITH_ERRNO("Failed to truncate output WIM file to "
- "%"PRIu64" bytes", size);
- return WIMLIB_ERR_WRITE;
- }
- return 0;
-}
-
/* Chunk table that's located at the beginning of each compressed resource in
* the WIM. (This is not the on-disk format; the on-disk format just has an
* array of offsets.) */
chunk_tab->table_disk_size) {
ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
"file resource");
+ FREE(chunk_tab);
ret = WIMLIB_ERR_WRITE;
goto out;
}
ret = 0;
-out:
*chunk_tab_ret = chunk_tab;
+out:
return ret;
}
return 0;
}
-static int
-write_uncompressed_resource_and_truncate(struct wim_lookup_table_entry *lte,
- FILE *out_fp,
- off_t file_offset,
- struct resource_entry *out_res_entry)
-{
- int ret;
- if (fseeko(out_fp, file_offset, SEEK_SET) != 0) {
- ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of "
- "output WIM file", file_offset);
- return WIMLIB_ERR_WRITE;
- }
- ret = write_wim_resource(lte, out_fp,
- WIMLIB_COMPRESSION_TYPE_NONE,
- out_res_entry,
- 0);
- if (ret)
- return ret;
-
- return fflush_and_ftruncate(out_fp,
- file_offset + wim_resource_size(lte));
-}
-
struct write_resource_ctx {
compress_func_t compress;
struct chunk_table *chunk_tab;
}
}
+/*
+ * Write a resource to an output WIM.
+ *
+ * @lte: Lookup table entry for the resource, which could be in another WIM,
+ * in an external file, or in another location.
+ *
+ * @out_fp: FILE * opened to the output WIM.
+ *
+ * @out_ctype: One of the WIMLIB_COMPRESSION_TYPE_* constants to indicate
+ * which compression algorithm to use.
+ *
+ * @out_res_entry: On success, this is filled in with the offset, flags,
+ * compressed size, and uncompressed size of the resource
+ * in the output WIM.
+ *
+ * @flags: WIMLIB_RESOURCE_FLAG_RECOMPRESS to force data to be recompressed
+ * even if it could otherwise be copied directly from the input.
+ *
+ * Additional notes: The SHA1 message digest of the uncompressed data is
+ * calculated (except when doing a raw copy --- see below). If the @unhashed
+ * flag is set on the lookup table entry, this message digest is simply copied
+ * to it; otherwise, the message digest is compared with the existing one, and
+ * the function will fail if they do not match.
+ */
int
write_wim_resource(struct wim_lookup_table_entry *lte,
FILE *out_fp, int out_ctype,
int flags)
{
struct write_resource_ctx write_ctx;
+ u64 read_size;
u64 new_size;
off_t offset;
int ret;
+ flags &= ~WIMLIB_RESOURCE_FLAG_RECOMPRESS;
+
if (wim_resource_size(lte) == 0) {
/* Empty resource; nothing needs to be done, so just return
* success. */
return 0;
}
+ /* Get current position in output WIM */
offset = ftello(out_fp);
if (offset == -1) {
ERROR_WITH_ERRNO("Can't get position in output WIM");
return WIMLIB_ERR_WRITE;
}
- /* Can we simply copy the compressed data without recompressing it? */
-
+ /* If we are not forcing the data to be recompressed, and the input
+ * resource is located in a WIM with the same compression type as that
+ * desired other than no compression, we can simply copy the compressed
+ * data without recompressing it. This also means we must skip
+ * calculating the SHA1, as we never will see the uncompressed data. */
if (!(flags & WIMLIB_RESOURCE_FLAG_RECOMPRESS) &&
lte->resource_location == RESOURCE_IN_WIM &&
+ out_ctype != WIMLIB_COMPRESSION_TYPE_NONE &&
wimlib_get_compression_type(lte->wim) == out_ctype)
{
flags |= WIMLIB_RESOURCE_FLAG_RAW;
write_ctx.doing_sha = false;
+ read_size = lte->resource_entry.size;
} else {
write_ctx.doing_sha = true;
sha1_init(&write_ctx.sha_ctx);
+ read_size = lte->resource_entry.original_size;
}
/* Initialize the chunk table and set the compression function if
return ret;
}
- /* Write the data */
+ /* Write the entire resource by reading the entire resource and feeding
+ * the data through the write_resource_cb function. */
write_ctx.out_fp = out_fp;
- ret = read_resource_prefix(lte, wim_resource_size(lte),
- write_resource_cb, &write_ctx, 0);
+try_write_again:
+ ret = read_resource_prefix(lte, read_size,
+ write_resource_cb, &write_ctx, flags);
+ if (ret)
+ goto out_free_chunk_tab;
/* Verify SHA1 message digest of the resource, or set the hash for the
* first time. */
if (new_size >= wim_resource_size(lte)) {
/* Oops! We compressed the resource to larger than the original
* size. Write the resource uncompressed instead. */
- ret = write_uncompressed_resource_and_truncate(lte,
- out_fp,
- offset,
- out_res_entry);
- goto out_free_chunk_tab;
+ if (fseeko(out_fp, offset, SEEK_SET) ||
+ fflush(out_fp) ||
+ ftruncate(fileno(out_fp),
+ offset + wim_resource_size(lte)))
+ {
+ ERROR_WITH_ERRNO("Failed to flush and/or truncate "
+ "output WIM file");
+ ret = WIMLIB_ERR_WRITE;
+ goto out_free_chunk_tab;
+ }
+ DEBUG("Compressed %"PRIu64" => %"PRIu64" bytes; "
+ "writing uncompressed instead",
+ wim_resource_size(lte), new_size);
+ write_ctx.compress = NULL;
+ write_ctx.doing_sha = false;
+ out_ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+ goto try_write_again;
}
out_res_entry->flags |= WIM_RESHDR_FLAG_COMPRESSED;
}
}
}
-static int
-sha1_chunk(const void *buf, size_t len, void *ctx)
-{
- sha1_update(ctx, buf, len);
- return 0;
-}
-
-static int
-sha1_resource(struct wim_lookup_table_entry *lte)
-{
- int ret;
- SHA_CTX sha_ctx;
-
- sha1_init(&sha_ctx);
- ret = read_resource_prefix(lte, wim_resource_size(lte),
- sha1_chunk, &sha_ctx, 0);
- if (ret == 0)
- sha1_final(lte->hash, &sha_ctx);
- return ret;
-}
-
enum {
STREAMS_MERGED = 0,
STREAMS_NOT_MERGED = 1,
};
static int
-do_write_stream_list(struct list_head *my_resources,
+do_write_stream_list(struct list_head *stream_list,
struct wim_lookup_table *lookup_table,
FILE *out_fp,
int out_ctype,
int ret;
struct wim_lookup_table_entry *lte;
- while (!list_empty(my_resources)) {
- lte = container_of(my_resources->next,
+ /* For each stream in @stream_list ... */
+ while (!list_empty(stream_list)) {
+ lte = container_of(stream_list->next,
struct wim_lookup_table_entry,
write_streams_list);
list_del(<e->write_streams_list);
if (lte->unhashed && !lte->unique_size) {
- struct wim_lookup_table_entry *duplicate_lte;
- struct wim_lookup_table_entry **my_ptr;
-
- my_ptr = lte->my_ptr;
- ret = sha1_resource(lte);
+ /* Unhashed stream that shares a size with some other
+ * stream in the WIM we are writing. The stream must be
+ * checksummed to know if we need to write it or not. */
+ struct wim_lookup_table_entry *tmp;
+ u32 orig_refcnt = lte->out_refcnt;
+
+ ret = hash_unhashed_stream(lte,
+ lookup_table,
+ &tmp);
if (ret)
return ret;
- duplicate_lte = __lookup_resource(lookup_table, lte->hash);
- if (duplicate_lte) {
- bool new_stream = (duplicate_lte->out_refcnt == 0);
- duplicate_lte->refcnt += lte->refcnt;
- duplicate_lte->out_refcnt += lte->refcnt;
- *my_ptr = duplicate_lte;
- free_lookup_table_entry(lte);
- lte = duplicate_lte;
- if (new_stream) {
- DEBUG("Stream of length %"PRIu64" is duplicate "
- "with one already in WIM",
- wim_resource_size(lte));
- } else {
+ if (tmp != lte) {
+ lte = tmp;
+ /* We found a duplicate stream. */
+ if (orig_refcnt != tmp->out_refcnt) {
+ /* We have already written, or are going
+ * to write, the duplicate stream. So
+ * just skip to the next stream. */
DEBUG("Discarding duplicate stream of length %"PRIu64,
wim_resource_size(lte));
goto skip_to_progress;
}
-
- } else {
- lookup_table_insert(lookup_table, lte);
- lte->out_refcnt = lte->refcnt;
- lte->unhashed = 0;
}
}
+ /* Here, @lte is either a hashed stream or an unhashed stream
+ * with a unique size. In either case we know that the stream
+ * has to be written. In either case the SHA1 message digest
+ * will be calculated over the stream while writing it; however,
+ * in the former case this is done merely to check the data,
+ * while in the latter case this is done because we do not have
+ * the SHA1 message digest yet. */
wimlib_assert(lte->out_refcnt != 0);
-
ret = write_wim_resource(lte,
out_fp,
out_ctype,
if (ret)
return ret;
if (lte->unhashed) {
+ list_del(<e->unhashed_list);
lookup_table_insert(lookup_table, lte);
lte->unhashed = 0;
}
wimlib_progress_func_t progress_func,
union wimlib_progress_info *progress)
{
- int write_resource_flags;
-
+ int write_resource_flags = 0;
if (write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
- write_resource_flags = WIMLIB_RESOURCE_FLAG_RECOMPRESS;
- else
- write_resource_flags = 0;
+ write_resource_flags |= WIMLIB_RESOURCE_FLAG_RECOMPRESS;
+
progress->write_streams.num_threads = 1;
if (progress_func)
progress_func(WIMLIB_PROGRESS_MSG_WRITE_STREAMS, progress);
}
next_lte = container_of(next_resource,
struct wim_lookup_table_entry,
- staging_list);
+ write_streams_list);
next_resource = next_resource->next;
if ((!(write_flags & WIMLIB_WRITE_FLAG_RECOMPRESS)
&& wim_resource_compression_type(next_lte) == out_ctype)
|| wim_resource_size(next_lte) == 0)
{
- list_add_tail(&next_lte->staging_list,
+ list_add_tail(&next_lte->write_streams_list,
&my_resources);
} else {
- list_add_tail(&next_lte->staging_list,
+ list_add_tail(&next_lte->write_streams_list,
&outstanding_resources);
next_chunk = 0;
next_num_chunks = wim_resource_chunks(next_lte);
FREE(cur_chunk_tab);
cur_chunk_tab = NULL;
- struct list_head *next = cur_lte->staging_list.next;
- list_del(&cur_lte->staging_list);
+ struct list_head *next = cur_lte->write_streams_list.next;
+ list_del(&cur_lte->write_streams_list);
if (next == &outstanding_resources)
cur_lte = NULL;
else
- cur_lte = container_of(cur_lte->staging_list.next,
+ cur_lte = container_of(cur_lte->write_streams_list.next,
struct wim_lookup_table_entry,
- staging_list);
+ write_streams_list);
// Since we just finished writing a stream,
// write any streams that have been added to the
struct lte_overwrite_prepare_args {
WIMStruct *wim;
off_t end_offset;
- struct list_head *stream_list;
- struct stream_size_table *stream_size_tab;
+ struct list_head stream_list;
+ struct stream_size_table stream_size_tab;
};
static int
return WIMLIB_ERR_RESOURCE_ORDER;
}
} else {
- if (!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA))
- list_add_tail(<e->write_streams_list, args->stream_list);
+ wimlib_assert(!(lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA));
+ list_add_tail(<e->write_streams_list, &args->stream_list);
}
lte->out_refcnt = lte->refcnt;
- stream_size_table_insert(lte, args->stream_size_tab);
+ stream_size_table_insert(lte, &args->stream_size_tab);
return 0;
}
static int
lte_set_output_res_entry(struct wim_lookup_table_entry *lte, void *_wim)
{
- if (lte->resource_location == RESOURCE_IN_WIM &&
- lte->wim == _wim)
- {
- memcpy(<e->output_resource_entry, <e->resource_entry,
- sizeof(struct resource_entry));
+ if (lte->resource_location == RESOURCE_IN_WIM && lte->wim == _wim) {
+ copy_resource_entry(<e->output_resource_entry,
+ <e->resource_entry);
}
return 0;
}
struct list_head *stream_list)
{
int ret;
- struct stream_size_table stream_size_tab;
- struct lte_overwrite_prepare_args args = {
- .wim = wim,
- .end_offset = end_offset,
- .stream_list = stream_list,
- .stream_size_tab = &stream_size_tab,
- };
-
- ret = init_stream_size_table(&stream_size_tab, 9001);
+ struct lte_overwrite_prepare_args args;
+
+ args.wim = wim;
+ args.end_offset = end_offset;
+ ret = init_stream_size_table(&args.stream_size_tab,
+ wim->lookup_table->capacity);
if (ret)
return ret;
- INIT_LIST_HEAD(stream_list);
+ INIT_LIST_HEAD(&args.stream_list);
for (int i = 0; i < wim->hdr.image_count; i++) {
struct wim_image_metadata *imd;
struct wim_lookup_table_entry *lte;
for (int i = 0; i < wim->hdr.image_count; i++)
lte_set_output_res_entry(wim->image_metadata[i]->metadata_lte,
wim);
- ret = for_lookup_table_entry(wim->lookup_table,
- lte_set_output_res_entry, wim);
+ for_lookup_table_entry(wim->lookup_table, lte_set_output_res_entry, wim);
+ INIT_LIST_HEAD(stream_list);
+ list_splice(&args.stream_list, stream_list);
out_destroy_stream_size_table:
- destroy_stream_size_table(&stream_size_tab);
+ destroy_stream_size_table(&args.stream_size_tab);
return ret;
}
struct stream_size_table stream_size_tab;
};
-static int
+static void
inode_find_streams_to_write(struct wim_inode *inode,
struct wim_lookup_table *table,
struct list_head *stream_list,
lte->out_refcnt += inode->i_nlink;
}
}
- return 0;
}
static int
ctx = w->private;
imd = wim_get_current_image_metadata(w);
- image_for_each_unhashed_stream(lte, imd) {
+ image_for_each_unhashed_stream(lte, imd)
lte->out_refcnt = 0;
- wimlib_assert(lte->unhashed);
- wimlib_assert(lte->my_ptr != NULL);
- }
/* Go through this image's inodes to find any streams that have not been
* found yet. */
struct find_streams_ctx ctx;
for_lookup_table_entry(wim->lookup_table, lte_zero_out_refcnt, NULL);
- ret = init_stream_size_table(&ctx.stream_size_tab, 9001);
+ ret = init_stream_size_table(&ctx.stream_size_tab,
+ wim->lookup_table->capacity);
if (ret)
return ret;
for_lookup_table_entry(wim->lookup_table, stream_size_table_insert,
&ctx.stream_size_tab);
INIT_LIST_HEAD(&ctx.stream_list);
wim->private = &ctx;
- for_image(wim, image, image_find_streams_to_write);
+ ret = for_image(wim, image, image_find_streams_to_write);
destroy_stream_size_table(&ctx.stream_size_tab);
-
- INIT_LIST_HEAD(stream_list);
- list_splice(&ctx.stream_list, stream_list);
- return 0;
+ if (ret == 0) {
+ INIT_LIST_HEAD(stream_list);
+ list_splice(&ctx.stream_list, stream_list);
+ }
+ return ret;
}
/* Writes the streams for the specified @image in @wim to @wim->out_fp.
if (!(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE)) {
ret = write_lookup_table(w, image, &hdr.lookup_table_res_entry);
- if (ret != 0)
- goto out;
+ if (ret)
+ goto out_close_wim;
}
ret = write_xml_data(w->wim_info, image, out,
(write_flags & WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE) ?
wim_info_get_total_bytes(w->wim_info) : 0,
&hdr.xml_res_entry);
- if (ret != 0)
- goto out;
+ if (ret)
+ goto out_close_wim;
if (write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) {
if (write_flags & WIMLIB_WRITE_FLAG_CHECKPOINT_AFTER_XML) {
struct wim_header checkpoint_hdr;
memcpy(&checkpoint_hdr, &hdr, sizeof(struct wim_header));
memset(&checkpoint_hdr.integrity, 0, sizeof(struct resource_entry));
- if (fseeko(out, 0, SEEK_SET) != 0) {
+ if (fseeko(out, 0, SEEK_SET)) {
ERROR_WITH_ERRNO("Failed to seek to beginning "
"of WIM being written");
ret = WIMLIB_ERR_WRITE;
- goto out;
+ goto out_close_wim;
}
ret = write_header(&checkpoint_hdr, out);
- if (ret != 0)
- goto out;
+ if (ret)
+ goto out_close_wim;
if (fflush(out) != 0) {
ERROR_WITH_ERRNO("Can't write data to WIM");
ret = WIMLIB_ERR_WRITE;
- goto out;
+ goto out_close_wim;
}
if (fseeko(out, 0, SEEK_END) != 0) {
ERROR_WITH_ERRNO("Failed to seek to end "
"of WIM being written");
ret = WIMLIB_ERR_WRITE;
- goto out;
+ goto out_close_wim;
}
}
new_lookup_table_end,
old_lookup_table_end,
progress_func);
- if (ret != 0)
- goto out;
+ if (ret)
+ goto out_close_wim;
} else {
memset(&hdr.integrity, 0, sizeof(struct resource_entry));
}
ERROR_WITH_ERRNO("Failed to seek to beginning of WIM "
"being written");
ret = WIMLIB_ERR_WRITE;
- goto out;
+ goto out_close_wim;
}
ret = write_header(&hdr, out);
if (ret)
- goto out;
+ goto out_close_wim;
if (write_flags & WIMLIB_WRITE_FLAG_FSYNC) {
if (fflush(out) != 0
ret = WIMLIB_ERR_WRITE;
}
}
-out:
+out_close_wim:
if (fclose(out) != 0) {
ERROR_WITH_ERRNO("Failed to close the WIM file");
if (ret == 0)
int ret;
ret = open_wim_writable(w, path, true,
(write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) != 0);
- if (ret != 0)
+ if (ret)
return ret;
/* Write dummy header. It will be overwritten later. */
return write_header(&w->hdr, w->out_fp);
if (!w->deletion_occurred && !any_images_modified(w)) {
/* If no images have been modified and no images have been
* deleted, a new lookup table does not need to be written. */
+ DEBUG("Skipping writing lookup table "
+ "(no images modified or deleted)");
old_wim_end = w->hdr.lookup_table_res_entry.offset +
w->hdr.lookup_table_res_entry.size;
write_flags |= WIMLIB_WRITE_FLAG_NO_LOOKUP_TABLE |
w->lookup_table,
w->out_fp,
wimlib_get_compression_type(w),
- write_flags, num_threads,
+ write_flags,
+ num_threads,
progress_func);
if (ret)
goto out_ftruncate;