+static int
+end_chunk_table(struct write_streams_ctx *ctx, u64 res_actual_size,
+ u64 *res_start_offset_ret, u64 *res_store_size_ret)
+{
+ size_t actual_num_chunks;
+ size_t actual_num_chunk_entries;
+ size_t chunk_entry_size;
+ int ret;
+
+ actual_num_chunks = ctx->chunk_index;
+ actual_num_chunk_entries = actual_num_chunks;
+ if (!(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS))
+ actual_num_chunk_entries--;
+
+ chunk_entry_size = get_chunk_entry_size(res_actual_size,
+ 0 != (ctx->write_resource_flags &
+ WRITE_RESOURCE_FLAG_PACK_STREAMS));
+
+ typedef le64 __attribute__((may_alias)) aliased_le64_t;
+ typedef le32 __attribute__((may_alias)) aliased_le32_t;
+
+ if (chunk_entry_size == 4) {
+ aliased_le32_t *entries = (aliased_le32_t*)ctx->chunk_csizes;
+
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) {
+ for (size_t i = 0; i < actual_num_chunk_entries; i++)
+ entries[i] = cpu_to_le32(ctx->chunk_csizes[i]);
+ } else {
+ u32 offset = ctx->chunk_csizes[0];
+ for (size_t i = 0; i < actual_num_chunk_entries; i++) {
+ u32 next_size = ctx->chunk_csizes[i + 1];
+ entries[i] = cpu_to_le32(offset);
+ offset += next_size;
+ }
+ }
+ } else {
+ aliased_le64_t *entries = (aliased_le64_t*)ctx->chunk_csizes;
+
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) {
+ for (size_t i = 0; i < actual_num_chunk_entries; i++)
+ entries[i] = cpu_to_le64(ctx->chunk_csizes[i]);
+ } else {
+ u64 offset = ctx->chunk_csizes[0];
+ for (size_t i = 0; i < actual_num_chunk_entries; i++) {
+ u64 next_size = ctx->chunk_csizes[i + 1];
+ entries[i] = cpu_to_le64(offset);
+ offset += next_size;
+ }
+ }
+ }
+
+ size_t chunk_table_size = actual_num_chunk_entries * chunk_entry_size;
+ u64 res_start_offset;
+ u64 res_end_offset;
+
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) {
+ ret = full_write(ctx->out_fd, ctx->chunk_csizes, chunk_table_size);
+ if (ret)
+ goto error;
+ res_end_offset = ctx->out_fd->offset;
+ res_start_offset = ctx->chunks_start_offset;
+ } else {
+ res_end_offset = ctx->out_fd->offset;
+
+ u64 chunk_table_offset;
+
+ chunk_table_offset = ctx->chunks_start_offset - chunk_table_size;
+
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) {
+ struct alt_chunk_table_header_disk hdr;
+
+ hdr.res_usize = cpu_to_le64(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);
+
+ ret = full_pwrite(ctx->out_fd, &hdr, sizeof(hdr),
+ chunk_table_offset - sizeof(hdr));
+ if (ret)
+ goto error;
+ res_start_offset = chunk_table_offset - sizeof(hdr);
+ } else {
+ res_start_offset = chunk_table_offset;
+ }
+
+ ret = full_pwrite(ctx->out_fd, ctx->chunk_csizes,
+ chunk_table_size, chunk_table_offset);
+ if (ret)
+ goto error;
+ }
+
+ *res_start_offset_ret = res_start_offset;
+ *res_store_size_ret = res_end_offset - res_start_offset;
+
+ return 0;
+
+error:
+ ERROR_WITH_ERRNO("Write error");
+ return ret;
+}
+
+/* Finish writing a WIM resource by writing or updating the chunk table (if not
+ * writing the data uncompressed) and loading its metadata into @out_reshdr. */
+static int
+end_write_resource(struct write_streams_ctx *ctx, struct wim_reshdr *out_reshdr)
+{
+ int ret;
+ u64 res_size_in_wim;
+ u64 res_uncompressed_size;
+ u64 res_offset_in_wim;
+
+ wimlib_assert(ctx->cur_write_stream_offset == ctx->cur_write_res_size ||
+ (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS));
+ res_uncompressed_size = ctx->cur_write_res_size;
+
+ if (ctx->compressor) {
+ ret = end_chunk_table(ctx, res_uncompressed_size,
+ &res_offset_in_wim, &res_size_in_wim);
+ if (ret)
+ return ret;
+ } else {
+ res_offset_in_wim = ctx->chunks_start_offset;
+ res_size_in_wim = ctx->out_fd->offset - res_offset_in_wim;
+ }
+ 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;
+}
+
+/* No more data streams of the file at @path are needed. */
+static int
+done_with_file(const tchar *path, wimlib_progress_func_t progfunc, void *progctx)
+{
+ union wimlib_progress_info info;
+
+ info.done_with_file.path_to_file = path;
+
+ return call_progress(progfunc, WIMLIB_PROGRESS_MSG_DONE_WITH_FILE,
+ &info, progctx);
+}
+
+/* Handle WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES mode. */
+static int
+done_with_stream(struct wim_lookup_table_entry *stream,
+ wimlib_progress_func_t progfunc, void *progctx,
+ struct wim_inode *inode)
+{
+ if (stream->resource_location == RESOURCE_IN_FILE_ON_DISK
+#ifdef __WIN32__
+ || stream->resource_location == RESOURCE_IN_WINNT_FILE_ON_DISK
+ || stream->resource_location == RESOURCE_WIN32_ENCRYPTED
+#endif
+ )
+ {
+ int ret;
+
+ wimlib_assert(inode->num_unread_streams > 0);
+ if (--inode->num_unread_streams > 0)
+ return 0;
+
+ #ifdef __WIN32__
+ /* XXX: This logic really should be somewhere else. */
+
+ /* We want the path to the file, but stream->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(stream->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(stream->file_on_disk, L"\\??\\", 4)) {
+ p_question_mark = &stream->file_on_disk[1];
+ *p_question_mark = L'\\';
+ }
+ #endif
+
+ ret = done_with_file(stream->file_on_disk, progfunc, progctx);
+
+ #ifdef __WIN32__
+ if (p_colon)
+ *p_colon = L':';
+ if (p_question_mark)
+ *p_question_mark = L'?';
+ #endif
+ return ret;
+ }
+ return 0;
+}
+
+/* Begin processing a stream for writing. */
+static int
+write_stream_begin_read(struct wim_lookup_table_entry *lte, void *_ctx)
+{
+ struct write_streams_ctx *ctx = _ctx;
+ int ret;
+
+ wimlib_assert(lte->size > 0);
+
+ ctx->cur_read_stream_offset = 0;
+ ctx->cur_read_stream_size = lte->size;
+
+ if (lte->unhashed)
+ ctx->stream_inode = lte->back_inode;
+ else
+ ctx->stream_inode = NULL;
+
+ /* As an optimization, we allow some streams to be "unhashed", meaning
+ * their SHA1 message digests are unknown. This is the case with
+ * streams that are added by scanning a directry tree with
+ * wimlib_add_image(), for example. Since WIM uses single-instance
+ * streams, we don't know whether such each such stream really need to
+ * written until it is actually checksummed, unless it has a unique
+ * size. In such cases we read and checksum the stream in this
+ * function, thereby advancing ahead of read_stream_list(), which will
+ * still provide the data again to write_stream_process_chunk(). This
+ * is okay because an unhashed stream cannot be in a WIM resource, which
+ * might be costly to decompress. */
+ ctx->stream_was_duplicate = false;
+ if (ctx->lookup_table != NULL && lte->unhashed && !lte->unique_size) {
+
+ struct wim_lookup_table_entry *lte_new;
+
+ ret = hash_unhashed_stream(lte, ctx->lookup_table, <e_new);
+ if (ret)
+ return ret;
+ if (lte_new != lte) {
+ /* Duplicate stream detected. */
+
+ if (lte_new->will_be_in_output_wim ||
+ stream_filtered(lte_new, ctx->filter_ctx))
+ {
+ /* The duplicate stream is already being
+ * included in the output WIM, or it would be
+ * filtered out if it had been. Skip writing
+ * this stream (and reading it again) entirely,
+ * passing its output reference count to the
+ * duplicate stream in the former case. */
+ DEBUG("Discarding duplicate stream of "
+ "length %"PRIu64, lte->size);
+ ret = do_write_streams_progress(&ctx->progress_data,
+ lte, lte->size,
+ 1, true);
+ list_del(<e->write_streams_list);
+ list_del(<e->lookup_table_list);
+ if (lte_new->will_be_in_output_wim)
+ lte_new->out_refcnt += lte->out_refcnt;
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS)
+ ctx->cur_write_res_size -= lte->size;
+ if (!ret && unlikely(ctx->write_resource_flags &
+ WRITE_RESOURCE_FLAG_SEND_DONE_WITH_FILE))
+ {
+ ret = done_with_stream(lte,
+ ctx->progress_data.progfunc,
+ ctx->progress_data.progctx,
+ ctx->stream_inode);
+ }
+ free_lookup_table_entry(lte);