#endif
#include "wimlib.h"
+#include "wimlib/assert.h"
#include "wimlib/endianness.h"
#include "wimlib/error.h"
#include "wimlib/file_io.h"
#include "wimlib/lookup_table.h"
#include "wimlib/resource.h"
#include "wimlib/sha1.h"
+#include "wimlib/wim.h"
#ifdef __WIN32__
/* for read_win32_file_prefix(), read_win32_encrypted_file_prefix() */
/* Get the maximum size of uncompressed chunks in this resource, which
* we require be a power of 2. */
- u32 chunk_size;
u64 cur_read_offset = rspec->offset_in_wim;
- int ctype;
+ int ctype = rspec->compression_type;
+ u32 chunk_size = rspec->chunk_size;
if (alt_chunk_table) {
/* Alternate chunk table format. Its header specifies the chunk
- * size and compression format. */
- struct alt_chunk_table_header_disk hdr;
-
- ret = full_pread(in_fd, &hdr, sizeof(hdr), cur_read_offset);
- if (ret)
- goto read_error;
- cur_read_offset += sizeof(hdr);
-
- chunk_size = le32_to_cpu(hdr.chunk_size);
- ctype = le32_to_cpu(hdr.compression_format);
-
- /* Format numbers must be the same as in WIMGAPI to be
- * compatible. */
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_NONE != 0);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 1);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 2);
- BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
- } else {
- /* "Normal" format: the maximum uncompressed chunk size and the
- * compression format default to those of the WIM itself. */
- chunk_size = rspec->wim->chunk_size;
- ctype = rspec->wim->compression_type;
+ * size and compression format. Note: it could be read here;
+ * however, the relevant data was already loaded into @rspec by
+ * read_wim_lookup_table(). */
+ cur_read_offset += sizeof(struct alt_chunk_table_header_disk);
}
+
if (!is_power_of_2(chunk_size)) {
ERROR("Invalid compressed resource: "
- "expected power-of-2 chunk size (got %u)", chunk_size);
+ "expected power-of-2 chunk size (got %"PRIu32")",
+ chunk_size);
ret = WIMLIB_ERR_INVALID_CHUNK_SIZE;
goto out_free_memory;
}
const u64 num_chunk_entries = (alt_chunk_table ? num_chunks : num_chunks - 1);
/* Set the size of each chunk table entry based on the resource's
- * uncompressed size. XXX: Does the alternate chunk table really
- * always have 4-byte entries? */
- const u64 chunk_entry_size =
- (rspec->uncompressed_size > (1ULL << 32) && !alt_chunk_table)
- ? 8 : 4;
+ * uncompressed size. */
+ const u64 chunk_entry_size = get_chunk_entry_size(rspec->uncompressed_size,
+ alt_chunk_table);
/* Calculate the size of the chunk table in bytes. */
const u64 chunk_table_size = num_chunk_entries * chunk_entry_size;
if (ctx->cur_stream_offset == ctx->cur_stream->size) {
/* Finished reading all the data for a stream. */
+ ctx->cur_stream_offset = 0;
+
DEBUG("End stream (size=%"PRIu64").", ctx->cur_stream->size);
ret = (*ctx->cbs.end_stream)(ctx->cur_stream, 0,
ctx->cbs.end_stream_ctx);
else
ctx->next_stream = NULL;
}
- ctx->cur_stream_offset = 0;
}
return 0;
}
return read_full_stream_with_cbs(lte, &hasher_cbs);
}
+static int
+read_packed_streams(struct wim_lookup_table_entry *first_stream,
+ struct wim_lookup_table_entry *last_stream,
+ u64 stream_count,
+ size_t list_head_offset,
+ const struct read_stream_list_callbacks *sink_cbs)
+{
+ struct data_range *ranges;
+ bool ranges_malloced;
+ struct wim_lookup_table_entry *cur_stream;
+ size_t i;
+ int ret;
+ u64 ranges_alloc_size;
+
+ DEBUG("Reading %"PRIu64" streams combined in same WIM resource",
+ stream_count);
+
+ /* Setup data ranges array (one range per stream to read); this way
+ * read_compressed_wim_resource() does not need to be aware of streams.
+ */
+
+ ranges_alloc_size = stream_count * sizeof(ranges[0]);
+
+ if (unlikely((size_t)ranges_alloc_size != ranges_alloc_size)) {
+ ERROR("Too many streams in one resource!");
+ return WIMLIB_ERR_NOMEM;
+ }
+ if (likely(ranges_alloc_size <= STACK_MAX)) {
+ ranges = alloca(ranges_alloc_size);
+ ranges_malloced = false;
+ } else {
+ ranges = MALLOC(ranges_alloc_size);
+ if (ranges == NULL) {
+ ERROR("Too many streams in one resource!");
+ return WIMLIB_ERR_NOMEM;
+ }
+ ranges_malloced = true;
+ }
+
+ for (i = 0, cur_stream = first_stream;
+ i < stream_count;
+ i++, cur_stream = next_stream(cur_stream, list_head_offset))
+ {
+ ranges[i].offset = cur_stream->offset_in_res;
+ ranges[i].size = cur_stream->size;
+ }
+
+ struct streamifier_context streamifier_ctx = {
+ .cbs = *sink_cbs,
+ .cur_stream = first_stream,
+ .next_stream = next_stream(first_stream, list_head_offset),
+ .cur_stream_offset = 0,
+ .final_stream = last_stream,
+ .list_head_offset = list_head_offset,
+ };
+
+ ret = read_compressed_wim_resource(first_stream->rspec,
+ ranges,
+ stream_count,
+ streamifier_cb,
+ &streamifier_ctx);
+
+ if (ranges_malloced)
+ FREE(ranges);
+
+ if (ret) {
+ if (streamifier_ctx.cur_stream_offset != 0) {
+ ret = (*streamifier_ctx.cbs.end_stream)
+ (streamifier_ctx.cur_stream,
+ ret,
+ streamifier_ctx.cbs.end_stream_ctx);
+ }
+ }
+ return ret;
+}
+
/*
* Read a list of streams, each of which may be in any supported location (e.g.
* in a WIM or in an external file). Unlike read_stream_prefix() or the
struct wim_lookup_table_entry *lte_next, *lte_last;
struct list_head *next2;
- size_t stream_count;
+ u64 stream_count;
/* The next stream is a proper sub-sequence of a WIM
* resource. See if there are other streams in the same
* first stream in the resource that needs to be
* read and @lte_last specifies the last stream
* in the resource that needs to be read. */
-
- DEBUG("Reading %zu streams combined in same "
- "WIM resource", stream_count);
-
next = next2;
-
- struct data_range ranges[stream_count];
-
- {
- struct list_head *next3;
- size_t i;
- struct wim_lookup_table_entry *lte_cur;
-
- next3 = cur;
- for (i = 0; i < stream_count; i++) {
- lte_cur = (struct wim_lookup_table_entry*)
- ((u8*)next3 - list_head_offset);
- ranges[i].offset = lte_cur->offset_in_res;
- ranges[i].size = lte_cur->size;
- next3 = next3->next;
- }
- }
-
- 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,
- };
-
- ret = read_compressed_wim_resource(lte->rspec,
- ranges,
- stream_count,
- streamifier_cb,
- &streamifier_ctx);
-
- if (ret) {
- if (streamifier_ctx.cur_stream_offset != 0) {
- ret = (*streamifier_ctx.cbs.end_stream)
- (streamifier_ctx.cur_stream,
- ret,
- streamifier_ctx.cbs.end_stream_ctx);
- }
+ ret = read_packed_streams(lte, lte_last,
+ stream_count,
+ list_head_offset,
+ sink_cbs);
+ if (ret)
return ret;
- }
continue;
}
}
return extract_stream(lte, size, extract_chunk_to_fd, fd);
}
+/* Extract the full uncompressed contents of the specified stream to the
+ * specified file descriptor. */
+int
+extract_full_stream_to_fd(struct wim_lookup_table_entry *lte,
+ struct filedes *fd)
+{
+ return extract_stream_to_fd(lte, fd, lte->size);
+}
+
/* Calculate the SHA1 message digest of a stream and store it in @lte->hash. */
int
sha1_stream(struct wim_lookup_table_entry *lte)
}
/* Convert a short WIM resource header to a stand-alone WIM resource
- * specification. */
+ * specification.
+ *
+ * Note: for packed resources some fields still need to be overridden.
+ */
void
wim_res_hdr_to_spec(const struct wim_reshdr *reshdr, WIMStruct *wim,
struct wim_resource_spec *rspec)
INIT_LIST_HEAD(&rspec->stream_list);
rspec->flags = reshdr->flags;
rspec->is_pipable = wim_is_pipable(wim);
+ if (rspec->flags & WIM_RESHDR_FLAG_COMPRESSED) {
+ rspec->compression_type = wim->compression_type;
+ rspec->chunk_size = wim->chunk_size;
+ } else {
+ rspec->compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
+ rspec->chunk_size = 0;
+ }
}
/* Convert a stand-alone resource specification to a WIM resource header. */