+ if (out_ctype == WIMLIB_COMPRESSION_TYPE_LZX)
+ return lzx_compress;
+ else
+ return 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_fp: FILE * to write tho chunk to.
+ * @out_ctype: Compression type to use when writing the chunk (ignored if no
+ * chunk table provided)
+ * @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 u8 chunk[], unsigned chunk_size,
+ FILE *out_fp, compress_func_t compress,
+ struct chunk_table *chunk_tab)
+{
+ const u8 *out_chunk;
+ unsigned out_chunk_size;
+ if (chunk_tab) {
+ u8 *compressed_chunk = alloca(chunk_size);
+ int ret;
+
+ ret = compress(chunk, chunk_size, compressed_chunk,
+ &out_chunk_size);
+ 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;
+ } else {
+ out_chunk = chunk;
+ out_chunk_size = 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;
+}
+
+/*
+ * Finishes a WIM chunk tale 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,
+ 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_cpu_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] =
+ cpu_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;
+}
+
+/* Prepare for multiple reads to a resource by caching a FILE * or NTFS
+ * attribute pointer in the lookup table entry. */
+static int prepare_resource_for_read(struct lookup_table_entry *lte
+
+ #ifdef WITH_NTFS_3G
+ , ntfs_inode **ni_ret
+ #endif
+ )
+{
+ 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);
+ return WIMLIB_ERR_OPEN;
+ }
+ }
+#ifdef WITH_NTFS_3G
+ else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME
+ && !lte->attr)
+ {
+ struct ntfs_location *loc = lte->ntfs_loc;
+ ntfs_inode *ni;
+ wimlib_assert(loc);
+ ni = ntfs_pathname_to_inode(*loc->ntfs_vol_p, NULL, loc->path_utf8);
+ if (!ni) {
+ ERROR_WITH_ERRNO("Failed to open inode `%s' in NTFS "
+ "volume", loc->path_utf8);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ lte->attr = ntfs_attr_open(ni,
+ loc->is_reparse_point ? AT_REPARSE_POINT : AT_DATA,
+ (ntfschar*)loc->stream_name_utf16,
+ loc->stream_name_utf16_num_chars);
+ if (!lte->attr) {
+ ERROR_WITH_ERRNO("Failed to open attribute of `%s' in "
+ "NTFS volume", loc->path_utf8);
+ ntfs_inode_close(ni);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ *ni_ret = ni;
+ }
+#endif
+ return 0;
+}
+
+/* Undo prepare_resource_for_read() by closing the cached FILE * or NTFS
+ * attribute. */
+static void end_wim_resource_read(struct lookup_table_entry *lte
+ #ifdef WITH_NTFS_3G
+ , ntfs_inode *ni
+ #endif
+ )
+{
+ 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;
+ }
+#ifdef WITH_NTFS_3G
+ else if (lte->resource_location == RESOURCE_IN_NTFS_VOLUME) {
+ if (lte->attr) {
+ ntfs_attr_close(lte->attr);
+ lte->attr = NULL;
+ }
+ if (ni)
+ ntfs_inode_close(ni);
+ }
+#endif
+}
+
+/*
+ * Writes a WIM resource to a FILE * opened for writing. The resource may be
+ * written uncompressed or compressed depending on the @out_ctype parameter.
+ *
+ * If by chance the resource compresses to more than the original size (this may
+ * happen with random data or files than are pre-compressed), the resource is
+ * instead written uncompressed (and this is reflected in the @out_res_entry by
+ * removing the WIM_RESHDR_FLAG_COMPRESSED flag).
+ *
+ * @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.
+ */
+int write_wim_resource(struct lookup_table_entry *lte,
+ FILE *out_fp, int out_ctype,
+ struct resource_entry *out_res_entry,
+ int flags)
+{
+ u64 bytes_remaining;
+ u64 original_size;
+ u64 old_compressed_size;
+ u64 new_compressed_size;
+ u64 offset;