+/*
+ * 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);
+
+ 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->cur_offset_p++ = chunk_tab->cur_offset;
+ chunk_tab->cur_offset += out_chunk_size;
+ } else {
+ /* Write uncompressed */
+ 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 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,
+ 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 wim_lookup_table_entry *lte
+
+ #ifdef WITH_NTFS_3G
+ , ntfs_inode **ni_ret
+ #endif
+ )
+{
+ switch (lte->resource_location) {
+ case RESOURCE_IN_FILE_ON_DISK:
+ if (!lte->file_on_disk_fp) {
+ lte->file_on_disk_fp = tfopen(lte->file_on_disk, T("rb"));
+ if (!lte->file_on_disk_fp) {
+ ERROR_WITH_ERRNO("Failed to open the file "
+ "`%"TS"'", lte->file_on_disk);
+ return WIMLIB_ERR_OPEN;
+ }
+ }
+ break;
+#ifdef WITH_NTFS_3G
+ case RESOURCE_IN_NTFS_VOLUME:
+ if (!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);
+ if (!ni) {
+ ERROR_WITH_ERRNO("Failed to open inode `%"TS"' in NTFS "
+ "volume", loc->path);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ lte->attr = ntfs_attr_open(ni,
+ loc->is_reparse_point ? AT_REPARSE_POINT : AT_DATA,
+ loc->stream_name,
+ loc->stream_name_nchars);
+ if (!lte->attr) {
+ ERROR_WITH_ERRNO("Failed to open attribute of `%"TS"' in "
+ "NTFS volume", loc->path);
+ ntfs_inode_close(ni);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ *ni_ret = ni;
+ }
+ break;
+#endif
+#ifdef __WIN32__
+ case RESOURCE_WIN32:
+ if (lte->win32_file_on_disk_fp == INVALID_HANDLE_VALUE) {
+ lte->win32_file_on_disk_fp =
+ win32_open_file_data_only(lte->file_on_disk);
+ if (lte->win32_file_on_disk_fp == INVALID_HANDLE_VALUE) {
+ ERROR("Win32 API: Can't open %"TS, lte->file_on_disk);
+ win32_error_last();
+ return WIMLIB_ERR_OPEN;
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Undo prepare_resource_for_read() by closing the cached FILE * or NTFS
+ * attribute. */
+static void
+end_wim_resource_read(struct wim_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
+#ifdef __WIN32__
+ else if (lte->resource_location == RESOURCE_WIN32
+ && lte->win32_file_on_disk_fp != INVALID_HANDLE_VALUE)
+ {
+ win32_close_file(lte->win32_file_on_disk_fp);
+ lte->win32_file_on_disk_fp = INVALID_HANDLE_VALUE;
+ }
+#endif
+}
+
+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 != 0)