*/
-/* Decompress the specified chunk that uses the specified compression type
- * @ctype, part of a WIM with default chunk size @wim_chunk_size. For LZX the
- * separate @wim_chunk_size is needed because it determines the window size used
- * for LZX compression. */
-static int
-decompress(const void *cchunk, unsigned clen, void *uchunk, unsigned ulen,
- int ctype, u32 wim_chunk_size)
-{
- switch (ctype) {
- case WIMLIB_COMPRESSION_TYPE_LZX:
- return wimlib_lzx_decompress2(cchunk, clen,
- uchunk, ulen, wim_chunk_size);
- case WIMLIB_COMPRESSION_TYPE_XPRESS:
- return wimlib_xpress_decompress(cchunk, clen,
- uchunk, ulen);
- case WIMLIB_COMPRESSION_TYPE_LZMS:
- return wimlib_lzms_decompress(cchunk, clen, uchunk, ulen);
- default:
- ERROR("Invalid compression format (%d)", ctype);
- return -1;
- }
-}
-
struct data_range {
u64 offset;
u64 size;
bool chunk_offsets_malloced = false;
bool ubuf_malloced = false;
bool cbuf_malloced = false;
+ struct wimlib_decompressor *decompressor = NULL;
/* Sanity checks */
wimlib_assert(rspec != NULL);
goto out_free_memory;
}
+ /* Get valid decompressor. */
+ if (ctype == rspec->wim->decompressor_ctype &&
+ chunk_size == rspec->wim->decompressor_max_block_size)
+ {
+ /* Cached decompressor. */
+ decompressor = rspec->wim->decompressor;
+ rspec->wim->decompressor_ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+ rspec->wim->decompressor = NULL;
+ } else {
+ ret = wimlib_create_decompressor(ctype, chunk_size, NULL,
+ &decompressor);
+ if (ret)
+ goto out_free_memory;
+ }
+
const u32 chunk_order = bsr32(chunk_size);
/* Calculate the total number of chunks the resource is divided into. */
chunk_offsets_alloc_size -
chunk_table_size_to_read;
- ret = full_pread(in_fd, chunk_table_data, chunk_table_size,
+ ret = full_pread(in_fd, chunk_table_data, chunk_table_size_to_read,
file_offset_of_needed_chunk_entries);
if (ret)
goto read_error;
DEBUG("Decompressing chunk %"PRIu64" "
"(csize=%"PRIu32" usize=%"PRIu32")",
i, chunk_csize, chunk_usize);
- ret = decompress(cbuf,
- chunk_csize,
- ubuf,
- chunk_usize,
- ctype,
- chunk_size);
+ ret = wimlib_decompress(cbuf,
+ chunk_csize,
+ ubuf,
+ chunk_usize,
+ decompressor);
if (ret) {
ERROR("Failed to decompress data!");
ret = WIMLIB_ERR_DECOMPRESSION;
goto read_error;
}
ret = 0;
+
out_free_memory:
errno_save = errno;
+ if (decompressor) {
+ wimlib_free_decompressor(rspec->wim->decompressor);
+ rspec->wim->decompressor = decompressor;
+ rspec->wim->decompressor_ctype = ctype;
+ rspec->wim->decompressor_max_block_size = chunk_size;
+ }
if (chunk_offsets_malloced)
FREE(chunk_offsets);
if (ubuf_malloced)
/* Read raw data from a file descriptor at the specified offset, feeding the
* data it in chunks into the specified callback function. */
static int
-read_raw_file_data(struct filedes *in_fd, u64 size,
- consume_data_callback_t cb, void *cb_ctx, u64 offset)
+read_raw_file_data(struct filedes *in_fd, u64 offset, u64 size,
+ consume_data_callback_t cb, void *cb_ctx)
{
u8 buf[BUFFER_SIZE];
size_t bytes_to_read;
cb, cb_ctx);
} else {
return read_raw_file_data(&rspec->wim->in_fd,
+ rspec->offset_in_wim + offset,
size,
cb,
- cb_ctx,
- rspec->offset_in_wim + offset);
+ cb_ctx);
}
}
return WIMLIB_ERR_OPEN;
}
filedes_init(&fd, raw_fd);
- ret = read_raw_file_data(&fd, size, cb, cb_ctx, 0);
+ ret = read_raw_file_data(&fd, 0, size, cb, cb_ctx);
filedes_close(&fd);
return ret;
}
* reasons, depending on the stream location), or if @cb returned nonzero in
* which case that error code will be returned.
*/
-int
+static int
read_stream_prefix(const struct wim_lookup_table_entry *lte, u64 size,
consume_data_callback_t cb, void *cb_ctx)
{
struct streamifier_context {
struct read_stream_list_callbacks cbs;
struct wim_lookup_table_entry *cur_stream;
+ struct wim_lookup_table_entry *next_stream;
u64 cur_stream_offset;
struct wim_lookup_table_entry *final_stream;
size_t list_head_offset;
};
+static struct wim_lookup_table_entry *
+next_stream(struct wim_lookup_table_entry *lte, size_t list_head_offset)
+{
+ struct list_head *cur;
+
+ cur = (struct list_head*)((u8*)lte + list_head_offset);
+
+ return (struct wim_lookup_table_entry*)((u8*)cur->next - list_head_offset);
+}
+
/* A consume_data_callback_t implementation that translates raw resource data
* into streams, calling the begin_stream, consume_chunk, and end_stream
* callback functions as appropriate. */
if (ctx->cur_stream_offset == ctx->cur_stream->size) {
/* Finished reading all the data for a stream. */
- struct list_head *cur, *next;
-
- cur = (struct list_head *)
- ((u8*)ctx->cur_stream + ctx->list_head_offset);
- next = cur->next;
+ ctx->cur_stream_offset = 0;
DEBUG("End stream (size=%"PRIu64").", ctx->cur_stream->size);
ret = (*ctx->cbs.end_stream)(ctx->cur_stream, 0,
if (ret)
return ret;
- if (ctx->cur_stream != ctx->final_stream) {
- /* Advance to next stream. */
- ctx->cur_stream = (struct wim_lookup_table_entry *)
- ((u8*)next - ctx->list_head_offset);
-
- ctx->cur_stream_offset = 0;
- } else {
- /* No more streams. */
- ctx->cur_stream = NULL;
+ /* Advance to next stream. */
+ ctx->cur_stream = ctx->next_stream;
+ if (ctx->cur_stream != NULL) {
+ if (ctx->cur_stream != ctx->final_stream)
+ ctx->next_stream = next_stream(ctx->cur_stream,
+ ctx->list_head_offset);
+ else
+ ctx->next_stream = NULL;
}
}
return 0;
* @cbs
* Callback functions to accept the stream data.
* @flags
+ * Bitwise OR of zero or more of the following flags:
+ *
+ * VERIFY_STREAM_HASHES:
+ * For all streams being read that have already had SHA1 message
+ * digests computed, calculate the SHA1 message digest of the read
+ * data and compare it with the previously computed value. If they
+ * do not match, return WIMLIB_ERR_INVALID_RESOURCE_HASH.
*
+ * COMPUTE_MISSING_STREAM_HASHES
+ * For all streams being read that have not yet had their SHA1
+ * message digests computed, calculate and save their SHA1 message
+ * digests.
+ *
+ * STREAM_LIST_ALREADY_SORTED
+ * @stream_list is already sorted in sequential order for reading.
+ *
+ * The callback functions are allowed to delete the current stream from the list
+ * if necessary.
*
* Returns 0 on success; a nonzero error code on failure. Failure can occur due
* to an error reading the data or due to an error status being returned by any
struct streamifier_context streamifier_ctx = {
.cbs = *sink_cbs,
.cur_stream = lte,
+ .next_stream = next_stream(lte, list_head_offset),
.cur_stream_offset = 0,
.final_stream = lte_last,
.list_head_offset = list_head_offset,
extract_stream(struct wim_lookup_table_entry *lte, u64 size,
consume_data_callback_t extract_chunk, void *extract_chunk_arg)
{
+ wimlib_assert(size <= lte->size);
if (size == lte->size) {
/* Do SHA1. */
struct read_stream_list_callbacks cbs = {
* specification. */
void
wim_res_hdr_to_spec(const struct wim_reshdr *reshdr, WIMStruct *wim,
- struct wim_resource_spec *spec)
+ struct wim_resource_spec *rspec)
{
- spec->wim = wim;
- spec->offset_in_wim = reshdr->offset_in_wim;
- spec->size_in_wim = reshdr->size_in_wim;
- spec->uncompressed_size = reshdr->uncompressed_size;
- INIT_LIST_HEAD(&spec->stream_list);
- spec->flags = reshdr->flags;
- spec->is_pipable = wim_is_pipable(wim);
+ rspec->wim = wim;
+ rspec->offset_in_wim = reshdr->offset_in_wim;
+ rspec->size_in_wim = reshdr->size_in_wim;
+ rspec->uncompressed_size = reshdr->uncompressed_size;
+ INIT_LIST_HEAD(&rspec->stream_list);
+ rspec->flags = reshdr->flags;
+ rspec->is_pipable = wim_is_pipable(wim);
}
/* Convert a stand-alone resource specification to a WIM resource header. */