- struct message *next_msg;
- u64 next_chunk_in_msg;
-
- /* Update SHA1 message digest for the stream currently being read by the
- * main thread. */
- sha1_update(&ctx->next_sha_ctx, chunk, chunk_size);
-
- /* We send chunks of data to the compressor chunks in batches which we
- * refer to as "messages". @next_msg is the message that is currently
- * being prepared to send off. If it is NULL, that indicates that we
- * need to start a new message. */
- next_msg = ctx->next_msg;
- if (!next_msg) {
- /* We need to start a new message. First check to see if there
- * is a message available in the list of available messages. If
- * so, we can just take one. If not, all the messages (there is
- * a fixed number of them, proportional to the number of
- * threads) have been sent off to the compressor threads, so we
- * receive messages from the compressor threads containing
- * compressed chunks of data.
- *
- * We may need to receive multiple messages before one is
- * actually available to use because messages received that are
- * *not* for the very next set of chunks to compress must be
- * buffered until it's time to write those chunks. */
- while (list_empty(&ctx->available_msgs)) {
- ret = receive_compressed_chunks(ctx);
+ u64 begin_offset = blob->out_reshdr.offset_in_wim;
+ u64 end_offset = out_fd->offset;
+
+ if (filedes_seek(out_fd, begin_offset) == -1)
+ return 0;
+
+ ret = extract_blob_to_fd(blob, out_fd);
+ if (ret) {
+ /* Error reading the uncompressed data. */
+ if (out_fd->offset == begin_offset &&
+ filedes_seek(out_fd, end_offset) != -1)
+ {
+ /* Nothing was actually written yet, and we successfully
+ * seeked to the end of the compressed resource, so
+ * don't issue a hard error; just keep the compressed
+ * resource instead. */
+ WARNING("Recovered compressed resource of "
+ "size %"PRIu64", continuing on.", blob->size);
+ return 0;
+ }
+ return ret;
+ }
+
+ wimlib_assert(out_fd->offset - begin_offset == blob->size);
+
+ /* We could ftruncate() the file to 'out_fd->offset' here, but there
+ * isn't much point. Usually we will only be truncating by a few bytes
+ * and will just overwrite the data immediately. */
+
+ blob->out_reshdr.size_in_wim = blob->size;
+ blob->out_reshdr.flags &= ~(WIM_RESHDR_FLAG_COMPRESSED |
+ WIM_RESHDR_FLAG_SOLID);
+ return 0;
+}
+
+/* Returns true if the specified blob, which was written as a non-solid
+ * resource, should be truncated from the WIM file and re-written uncompressed.
+ * blob->out_reshdr must be filled in from the initial write of the blob. */
+static bool
+should_rewrite_blob_uncompressed(const struct write_blobs_ctx *ctx,
+ const struct blob_descriptor *blob)
+{
+ /* If the compressed data is smaller than the uncompressed data, prefer
+ * the compressed data. */
+ if (blob->out_reshdr.size_in_wim < blob->out_reshdr.uncompressed_size)
+ return false;
+
+ /* If we're not actually writing compressed data, then there's no need
+ * for re-writing. */
+ if (!ctx->compressor)
+ return false;
+
+ /* If writing a pipable WIM, everything we write to the output is final
+ * (it might actually be a pipe!). */
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE)
+ return false;
+
+ /* If the blob that would need to be re-read is located in a solid
+ * resource in another WIM file, then re-reading it would be costly. So
+ * don't do it.
+ *
+ * Exception: if the compressed size happens to be *exactly* the same as
+ * the uncompressed size, then the blob *must* be written uncompressed
+ * in order to remain compatible with the Windows Overlay Filesystem
+ * Filter Driver (WOF).
+ *
+ * TODO: we are currently assuming that the optimization for
+ * single-chunk resources in maybe_rewrite_blob_uncompressed() prevents
+ * this case from being triggered too often. To fully prevent excessive
+ * decompressions in degenerate cases, we really should obtain the
+ * uncompressed data by decompressing the compressed data we wrote to
+ * the output file.
+ */
+ if (blob->blob_location == BLOB_IN_WIM &&
+ blob->size != blob->rdesc->uncompressed_size &&
+ blob->size != blob->out_reshdr.size_in_wim)
+ return false;
+
+ return true;
+}
+
+static int
+maybe_rewrite_blob_uncompressed(struct write_blobs_ctx *ctx,
+ struct blob_descriptor *blob)
+{
+ if (!should_rewrite_blob_uncompressed(ctx, blob))
+ return 0;
+
+ /* Regular (non-solid) WIM resources with exactly one chunk and
+ * compressed size equal to uncompressed size are exactly the same as
+ * the corresponding compressed data --- since there must be 0 entries
+ * in the chunk table and the only chunk must be stored uncompressed.
+ * In this case, there's no need to rewrite anything. */
+ if (ctx->chunk_index == 1 &&
+ blob->out_reshdr.size_in_wim == blob->out_reshdr.uncompressed_size)
+ {
+ blob->out_reshdr.flags &= ~WIM_RESHDR_FLAG_COMPRESSED;
+ return 0;
+ }
+
+ return write_blob_uncompressed(blob, ctx->out_fd);
+}
+
+/* Write the next chunk of (typically compressed) data to the output WIM,
+ * handling the writing of the chunk table. */
+static int
+write_chunk(struct write_blobs_ctx *ctx, const void *cchunk,
+ size_t csize, size_t usize)
+{
+ int ret;
+ struct blob_descriptor *blob;
+ u32 completed_blob_count;
+ u32 completed_size;
+
+ blob = list_entry(ctx->blobs_being_compressed.next,
+ struct blob_descriptor, write_blobs_list);
+
+ if (ctx->cur_write_blob_offset == 0 &&
+ !(ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID))
+ {
+ /* Starting to write a new blob in non-solid mode. */
+
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) {
+ ret = write_pwm_blob_header(blob, ctx->out_fd,
+ ctx->compressor != NULL);
+ if (ret)
+ return ret;
+ }
+
+ ret = begin_write_resource(ctx, blob->size);
+ if (ret)
+ return ret;
+ }
+
+ if (ctx->compressor != NULL) {
+ /* Record the compresed chunk size. */
+ wimlib_assert(ctx->chunk_index < ctx->num_alloc_chunks);
+ ctx->chunk_csizes[ctx->chunk_index++] = csize;
+
+ /* If writing a pipable WIM, before the chunk data write a chunk
+ * header that provides the compressed chunk size. */
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_PIPABLE) {
+ struct pwm_chunk_hdr chunk_hdr = {
+ .compressed_size = cpu_to_le32(csize),
+ };
+ ret = full_write(ctx->out_fd, &chunk_hdr,
+ sizeof(chunk_hdr));
+ if (ret)
+ goto write_error;
+ }
+ }
+
+ /* Write the chunk data. */
+ ret = full_write(ctx->out_fd, cchunk, csize);
+ if (ret)
+ goto write_error;
+
+ ctx->cur_write_blob_offset += usize;
+
+ completed_size = usize;
+ completed_blob_count = 0;
+ if (ctx->write_resource_flags & WRITE_RESOURCE_FLAG_SOLID) {
+ /* Wrote chunk in solid mode. It may have finished multiple
+ * blobs. */
+ struct blob_descriptor *next_blob;
+
+ while (blob && ctx->cur_write_blob_offset >= blob->size) {
+
+ ctx->cur_write_blob_offset -= blob->size;
+
+ if (ctx->cur_write_blob_offset)
+ next_blob = list_entry(blob->write_blobs_list.next,
+ struct blob_descriptor,
+ write_blobs_list);
+ else
+ next_blob = NULL;
+
+ ret = done_with_blob(blob, ctx);
+ if (ret)
+ return ret;
+ list_move_tail(&blob->write_blobs_list, &ctx->blobs_in_solid_resource);
+ completed_blob_count++;
+
+ blob = next_blob;
+ }
+ } else {
+ /* Wrote chunk in non-solid mode. It may have finished a
+ * blob. */
+ if (ctx->cur_write_blob_offset == blob->size) {
+
+ wimlib_assert(ctx->cur_write_blob_offset ==
+ ctx->cur_write_res_size);
+
+ ret = end_write_resource(ctx, &blob->out_reshdr);