- unsigned compressed_chunk_sz;
- int (*compress)(const void *, unsigned, void *, unsigned *);
- switch (ctype) {
- case WIM_COMPRESSION_TYPE_LZX:
- compress = lzx_compress;
- break;
- case WIM_COMPRESSION_TYPE_XPRESS:
- compress = xpress_compress;
- break;
- default:
- wimlib_assert(0);
- break;
- }
- return (*compress)(chunk, chunk_size, compressed_chunk,
- compressed_chunk_len_ret);
-}
-
-static int write_wim_resource_chunk(const u8 chunk[], unsigned chunk_size,
- FILE *out_fp, int out_ctype,
- struct chunk_table *chunk_tab)
-{
- const u8 *out_chunk;
- unsigned out_chunk_size;
-
- if (!chunk_tab) {
- out_chunk = chunk;
- out_chunk_size = chunk_size;
- } else {
- u8 *compressed_chunk = alloca(chunk_size);
- int ret;
- unsigned compressed_chunk_len;
-
- ret = compress_chunk(chunk, chunk_size, compressed_chunk,
- &out_chunk_size, out_ctype);
- if (ret == 0) {
- out_chunk = compressed_chunk;
- } else {
- out_chunk = chunk;
- out_chunk_size = chunk_size;
- }
- *chunk_tab->cur_offset_p++ = chunk_tab->cur_offset;
- chunk_tab->cur_offset += out_chunk_size;
- }
-
- if (fwrite(out_chunk, 1, out_chunk_size, out_fp) != out_chunk_size) {
- ERROR_WITH_ERRNO("Failed to write WIM resource chunk");
- return WIMLIB_ERR_WRITE;
- }
- return 0;
-}
-
-static int
-finish_wim_resource_chunk_tab(struct chunk_table *chunk_tab,
- FILE *out_fp, u64 *compressed_size_p)
-{
- size_t bytes_written;
- if (fseeko(out_fp, chunk_tab->file_offset, SEEK_SET) != 0) {
- ERROR_WITH_ERRNO("Failed to seek to byte "PRIu64" of output "
- "WIM file", chunk_tab->file_offset);
- return WIMLIB_ERR_WRITE;
- }
-
- if (chunk_tab->bytes_per_chunk_entry == 8) {
- array_to_le64(chunk_tab->offsets, chunk_tab->num_chunks);
- } else {
- for (u64 i = 0; i < chunk_tab->num_chunks; i++)
- ((u32*)chunk_tab->offsets)[i] =
- to_le32(chunk_tab->offsets[i]);
- }
- bytes_written = fwrite((u8*)chunk_tab->offsets +
- chunk_tab->bytes_per_chunk_entry,
- 1, chunk_tab->table_disk_size, out_fp);
- 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 (fseeko(out_fp, 0, SEEK_END) != 0) {
- ERROR_WITH_ERRNO("Failed to seek to end of output WIM file");
- return WIMLIB_ERR_WRITE;
- }
- *compressed_size_p = chunk_tab->cur_offset + chunk_tab->table_disk_size;
- return 0;
-}
-
-/*
- * Writes a WIM resource to a FILE * opened for writing. The resource may be
- * written uncompressed or compressed depending on the @out_ctype parameter.
- *
- * @lte: The lookup table entry for the WIM resource.
- * @out_fp: The FILE * to write the resource to.
- * @out_ctype: The compression type of the resource to write. Note: if this is
- * the same as the compression type of the WIM resource we
- * need to read, we simply copy the data (i.e. we do not
- * uncompress it, then compress it again).
- * @out_res_entry: If non-NULL, a resource entry that is filled in with the
- * offset, original size, compressed size, and compression flag
- * of the output resource.
- *
- * Returns 0 on success; nonzero on failure.
- */
-static int write_wim_resource(struct lookup_table_entry *lte,
- FILE *out_fp, int out_ctype,
- struct resource_entry *out_res_entry)
-{
- u64 bytes_remaining;
- u64 original_size;
- u64 old_compressed_size;
- u64 new_compressed_size;
- u64 offset = 0;
- int ret = 0;
- struct chunk_table *chunk_tab = NULL;
- bool raw;
- off_t file_offset;
-
- original_size = wim_resource_size(lte);
- old_compressed_size = wim_resource_compressed_size(lte);
-
- file_offset = ftello(out_fp);
- if (file_offset == -1) {
- ERROR_WITH_ERRNO("Failed to get offset in output "
- "stream");
- return WIMLIB_ERR_WRITE;
- }
-
- raw = (wim_resource_compression_type(lte) == out_ctype
- && out_ctype != WIM_COMPRESSION_TYPE_NONE);
- if (raw)
- bytes_remaining = old_compressed_size;
- else
- bytes_remaining = original_size;
-
- if (bytes_remaining == 0)
- return 0;
-
- char buf[min(WIM_CHUNK_SIZE, bytes_remaining)];
-
- if (out_ctype != WIM_COMPRESSION_TYPE_NONE && !raw) {
- ret = begin_wim_resource_chunk_tab(lte, out_fp, file_offset,
- &chunk_tab);
- if (ret != 0)
- goto out;
- }
- if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
- && !lte->file_on_disk_fp)
- {
- wimlib_assert(lte->file_on_disk);
- lte->file_on_disk_fp = fopen(lte->file_on_disk, "rb");
- if (!lte->file_on_disk_fp) {
- ERROR_WITH_ERRNO("Failed to open the file `%s' for "
- "reading", lte->file_on_disk);
- ret = WIMLIB_ERR_OPEN;
- goto out;
- }
- }
- SHA_CTX ctx;
- if (!raw)
- sha1_init(&ctx);
-
- do {
- u64 to_read = min(bytes_remaining, WIM_CHUNK_SIZE);
- ret = __read_wim_resource(lte, buf, to_read, offset, raw);
- if (ret != 0)
- goto out_fclose;
- if (!raw)
- sha1_update(&ctx, buf, to_read);
- ret = write_wim_resource_chunk(buf, to_read, out_fp,
- out_ctype, chunk_tab);
- if (ret != 0)
- goto out_fclose;
- bytes_remaining -= to_read;
- offset += to_read;
- } while (bytes_remaining);
- if (out_ctype != WIM_COMPRESSION_TYPE_NONE && !raw) {
- ret = finish_wim_resource_chunk_tab(chunk_tab, out_fp,
- &new_compressed_size);
- if (ret != 0)
- goto out_fclose;
- } else {
- new_compressed_size = old_compressed_size;
- }
-
- if (!raw) {
- u8 md[SHA1_HASH_SIZE];
- sha1_final(md, &ctx);
- if (!hashes_equal(md, lte->hash)) {
- ERROR("WIM resource has incorrect hash!");
- if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK) {
- ERROR("We were reading it from `%s'; maybe it changed "
- "while we were reading it.",
- lte->file_on_disk);
- }
- ret = WIMLIB_ERR_INTEGRITY;
- goto out_fclose;
- }
- }
-
- if (new_compressed_size > original_size) {
- /* Oops! We compressed the resource to larger than the original
- * size. Write the resource uncompressed instead. */
- if (fseeko(out_fp, file_offset, SEEK_SET) != 0) {
- ERROR_WITH_ERRNO("Failed to seek to byte "PRIu64" "
- "of output WIM file", file_offset);
- ret = WIMLIB_ERR_WRITE;
- goto out_fclose;
- }
- ret = write_wim_resource(lte, out_fp, WIM_COMPRESSION_TYPE_NONE,
- out_res_entry);
- if (ret != 0)
- goto out_fclose;
- if (fflush(out_fp) != 0) {
- ERROR_WITH_ERRNO("Failed to flush output WIM file");
- ret = WIMLIB_ERR_WRITE;
- goto out_fclose;
- }
- if (ftruncate(fileno(out_fp), file_offset + out_res_entry->size) != 0) {
- ERROR_WITH_ERRNO("Failed to truncate output WIM file");
- ret = WIMLIB_ERR_WRITE;
- }
- goto out_fclose;
- }
- wimlib_assert(new_compressed_size <= original_size);
- if (out_res_entry) {
- out_res_entry->size = new_compressed_size;
- out_res_entry->original_size = original_size;
- out_res_entry->offset = file_offset;
- if (out_ctype == WIM_COMPRESSION_TYPE_NONE)
- out_res_entry->flags = 0;
- else
- out_res_entry->flags = WIM_RESHDR_FLAG_COMPRESSED;
- if (lte->resource_entry.flags & WIM_RESHDR_FLAG_METADATA)
- out_res_entry->flags |= WIM_RESHDR_FLAG_METADATA;
- }
-out_fclose:
- if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK
- && lte->file_on_disk_fp) {
- fclose(lte->file_on_disk_fp);
- lte->file_on_disk_fp = NULL;
- }
-out:
- FREE(chunk_tab);
- return ret;