+/*
+ * compress_func_t- Pointer to a function to compresses a chunk
+ * of a WIM resource. This may be either
+ * wimlib_xpress_compress() (xpress-compress.c) or
+ * wimlib_lzx_compress() (lzx-compress.c).
+ *
+ * @chunk: Uncompressed data of the chunk.
+ * @chunk_size: Size of the uncompressed chunk, in bytes.
+ * @out: Pointer to output buffer of size at least (@chunk_size - 1) bytes.
+ *
+ * Returns the size of the compressed data written to @out in bytes, or 0 if the
+ * data could not be compressed to (@chunk_size - 1) bytes or fewer.
+ *
+ * As a special requirement, the compression code is optimized for the WIM
+ * format and therefore requires (@chunk_size <= 32768).
+ *
+ * As another special requirement, the compression code will read up to 8 bytes
+ * off the end of the @chunk array for performance reasons. The values of these
+ * bytes will not affect the output of the compression, but the calling code
+ * must make sure that the buffer holding the uncompressed chunk is actually at
+ * least (@chunk_size + 8) bytes, or at least that these extra bytes are in
+ * mapped memory that will not cause a memory access violation if accessed.
+ */
+typedef unsigned (*compress_func_t)(const void *chunk, unsigned chunk_size,
+ void *out);
+
+static compress_func_t
+get_compress_func(int out_ctype)
+{
+ if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX)
+ return wimlib_lzx_compress;
+ else
+ return wimlib_xpress_compress;
+}
+
+/*
+ * Writes a chunk of a WIM resource to an output file.
+ *
+ * @chunk: Uncompressed data of the chunk.
+ * @chunk_size: Size of the chunk (<= WIM_CHUNK_SIZE)
+ * @out_fd: File descriptor to write the chunk to.
+ * @compress: Compression function to use (NULL if writing uncompressed
+ * data).
+ * @chunk_tab: Pointer to chunk table being created. It is updated with the
+ * offset of the chunk we write.
+ *
+ * Returns 0 on success; nonzero on failure.
+ */
+static int
+write_wim_resource_chunk(const void * restrict chunk,
+ unsigned chunk_size,
+ int out_fd,
+ compress_func_t compress,
+ struct chunk_table * restrict chunk_tab)
+{
+ const void *out_chunk;
+ unsigned out_chunk_size;
+ if (compress) {
+ void *compressed_chunk = alloca(chunk_size);
+
+ out_chunk_size = (*compress)(chunk, chunk_size, compressed_chunk);
+ if (out_chunk_size) {
+ /* Write compressed */
+ out_chunk = compressed_chunk;
+ } else {
+ /* Write uncompressed */
+ out_chunk = chunk;
+ out_chunk_size = chunk_size;
+ }
+ chunk_tab_record_chunk(chunk_tab, out_chunk_size);
+ } else {
+ /* Write uncompressed */
+ out_chunk = chunk;
+ out_chunk_size = chunk_size;
+ }
+ if (full_write(out_fd, out_chunk, out_chunk_size) != out_chunk_size) {
+ ERROR_WITH_ERRNO("Failed to write WIM resource chunk");
+ return WIMLIB_ERR_WRITE;
+ }
+ return 0;
+}
+
+/*
+ * Finishes a WIM chunk table and writes it to the output file at the correct
+ * offset.
+ *
+ * The final size of the full compressed resource is returned in the
+ * @compressed_size_p.
+ */
+static int
+finish_wim_resource_chunk_tab(struct chunk_table *chunk_tab,
+ int out_fd, u64 *compressed_size_p)
+{
+ size_t bytes_written;
+
+ bytes_written = full_pwrite(out_fd,
+ chunk_tab->offsets + chunk_tab->bytes_per_chunk_entry,
+ chunk_tab->table_disk_size,
+ chunk_tab->file_offset);
+ if (bytes_written != chunk_tab->table_disk_size) {
+ ERROR_WITH_ERRNO("Failed to write chunk table in compressed "
+ "file resource");
+ return WIMLIB_ERR_WRITE;
+ }
+ if (chunk_tab->bytes_per_chunk_entry == 4)
+ *compressed_size_p = chunk_tab->cur_offset_u32 + chunk_tab->table_disk_size;
+ else
+ *compressed_size_p = chunk_tab->cur_offset_u64 + chunk_tab->table_disk_size;
+ return 0;
+}
+
+static int
+seek_and_truncate(int out_fd, off_t offset)
+{
+ if (lseek(out_fd, offset, SEEK_SET) == -1 ||
+ ftruncate(out_fd, offset))
+ {
+ ERROR_WITH_ERRNO("Failed to truncate output WIM file");
+ return WIMLIB_ERR_WRITE;
+ } else {
+ return 0;
+ }
+}
+
+static int
+finalize_and_check_sha1(SHA_CTX * restrict sha_ctx,
+ struct wim_lookup_table_entry * restrict lte)
+{
+ u8 md[SHA1_HASH_SIZE];
+ sha1_final(md, sha_ctx);
+ if (lte->unhashed) {
+ copy_hash(lte->hash, md);
+ } else if (!hashes_equal(md, lte->hash)) {
+ ERROR("WIM resource has incorrect hash!");
+ if (lte_filename_valid(lte)) {
+ ERROR("We were reading it from \"%"TS"\"; maybe "
+ "it changed while we were reading it.",
+ lte->file_on_disk);
+ }
+ return WIMLIB_ERR_INVALID_RESOURCE_HASH;