#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 = 0;
u64 cur_read_offset = rspec->offset_in_wim;
- int ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+ 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;
}
goto out_free_memory;
}
+static int
+fill_zeroes(u64 size, consume_data_callback_t cb, void *cb_ctx)
+{
+ if (unlikely(size)) {
+ u8 buf[min(size, BUFFER_SIZE)];
+
+ memset(buf, 0, sizeof(buf));
+
+ do {
+ size_t len;
+ int ret;
+
+ len = min(size, BUFFER_SIZE);
+ ret = cb(buf, len, cb_ctx);
+ if (ret)
+ return ret;
+ size -= len;
+ } while (size);
+ }
+ return 0;
+}
+
/* Read raw data from a file descriptor at the specified offset, feeding the
* data it in chunks into the specified callback function. */
static int
return read_compressed_wim_resource(rspec, &range, 1,
cb, cb_ctx);
} else {
- return read_raw_file_data(&rspec->wim->in_fd,
- rspec->offset_in_wim + offset,
- size,
- cb,
- cb_ctx);
+ /* Reading uncompressed resource. For completeness, handle the
+ * weird case where size_in_wim < uncompressed_size. */
+
+ u64 read_size;
+ u64 zeroes_size;
+ int ret;
+
+ if (likely(offset + size <= rspec->size_in_wim) ||
+ rspec->is_pipable)
+ {
+ read_size = size;
+ zeroes_size = 0;
+ } else {
+ if (offset >= rspec->size_in_wim) {
+ read_size = 0;
+ zeroes_size = size;
+ } else {
+ read_size = rspec->size_in_wim - offset;
+ zeroes_size = offset + size - rspec->size_in_wim;
+ }
+ }
+
+ ret = read_raw_file_data(&rspec->wim->in_fd,
+ rspec->offset_in_wim + offset,
+ read_size,
+ cb,
+ cb_ctx);
+ if (ret)
+ return ret;
+
+ return fill_zeroes(zeroes_size, cb, cb_ctx);
}
}
return wim_resource_spec_to_data(&rspec, buf_ret);
}
+int
+wim_reshdr_to_hash(const struct wim_reshdr *reshdr, WIMStruct *wim,
+ u8 hash[SHA1_HASH_SIZE])
+{
+ struct wim_resource_spec rspec;
+ int ret;
+ struct wim_lookup_table_entry *lte;
+
+ wim_res_hdr_to_spec(reshdr, wim, &rspec);
+
+ lte = new_lookup_table_entry();
+ if (lte == NULL)
+ return WIMLIB_ERR_NOMEM;
+
+ lte_bind_wim_resource_spec(lte, &rspec);
+ lte->flags = rspec.flags;
+ lte->size = rspec.uncompressed_size;
+ lte->offset_in_res = 0;
+ lte->unhashed = 1;
+
+ ret = sha1_stream(lte);
+
+ lte_unbind_wim_resource_spec(lte);
+ copy_hash(hash, lte->hash);
+ free_lookup_table_entry(lte);
+ return ret;
+}
+
struct streamifier_context {
struct read_stream_list_callbacks cbs;
struct wim_lookup_table_entry *cur_stream;
return (*ctx->cbs.consume_chunk)(chunk, size, ctx->cbs.consume_chunk_ctx);
}
+static void
+get_sha1_string(const u8 md[SHA1_HASH_SIZE], tchar *str)
+{
+ for (size_t i = 0; i < SHA1_HASH_SIZE; i++)
+ str += tsprintf(str, T("%02x"), md[i]);
+}
+
/* Callback for finishing reading a stream while calculating its SHA1 message
* digest. */
static int
* that it is the same as the calculated value. */
if (!hashes_equal(hash, lte->hash)) {
if (wimlib_print_errors) {
- ERROR("Invalid SHA1 message digest "
- "on the following WIM stream:");
- print_lookup_table_entry(lte, stderr);
+ tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1];
+ tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1];
+ get_sha1_string(lte->hash, expected_hashstr);
+ get_sha1_string(hash, actual_hashstr);
+ ERROR("The stream is corrupted!\n"
+ " (Expected SHA1=%"TS",\n"
+ " got SHA1=%"TS")",
+ expected_hashstr, actual_hashstr);
}
ret = WIMLIB_ERR_INVALID_RESOURCE_HASH;
errno = EINVAL;
}
/* 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. */