X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fresource.c;h=9d8fe921490a38775975f438f1cb2a5be46c0f81;hp=b7713977abbf7c5c03f46733b3b285e5f6ce4c06;hb=50407ddd59aa2d0906225a4526eaefbf6da8450f;hpb=6ec05488c1ca6d629a560ba8efadd30418ae1493 diff --git a/src/resource.c b/src/resource.c index b7713977..9d8fe921 100644 --- a/src/resource.c +++ b/src/resource.c @@ -27,12 +27,14 @@ #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() */ @@ -175,37 +177,21 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec, /* 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; } @@ -253,11 +239,9 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec, 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; @@ -578,6 +562,28 @@ read_error: 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 @@ -670,11 +676,37 @@ read_partial_wim_resource(const struct wim_resource_spec *rspec, 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); } } @@ -894,6 +926,34 @@ wim_reshdr_to_data(const struct wim_reshdr *reshdr, WIMStruct *wim, void **buf_r 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; @@ -928,10 +988,17 @@ streamifier_cb(const void *chunk, size_t size, void *_ctx) wimlib_assert(size <= ctx->cur_stream->size - ctx->cur_stream_offset); if (ctx->cur_stream_offset == 0) { + u32 flags; + /* Starting a new stream. */ DEBUG("Begin new stream (size=%"PRIu64").", ctx->cur_stream->size); - ret = (*ctx->cbs.begin_stream)(ctx->cur_stream, true, + + flags = BEGIN_STREAM_FLAG_PARTIAL_RESOURCE; + if (size == ctx->cur_stream->size) + flags |= BEGIN_STREAM_FLAG_WHOLE_STREAM; + ret = (*ctx->cbs.begin_stream)(ctx->cur_stream, + flags, ctx->cbs.begin_stream_ctx); if (ret) return ret; @@ -977,7 +1044,7 @@ struct hasher_context { /* Callback for starting to read a stream while calculating its SHA1 message * digest. */ static int -hasher_begin_stream(struct wim_lookup_table_entry *lte, bool is_partial_res, +hasher_begin_stream(struct wim_lookup_table_entry *lte, u32 flags, void *_ctx) { struct hasher_context *ctx = _ctx; @@ -987,7 +1054,7 @@ hasher_begin_stream(struct wim_lookup_table_entry *lte, bool is_partial_res, if (ctx->cbs.begin_stream == NULL) return 0; else - return (*ctx->cbs.begin_stream)(lte, is_partial_res, + return (*ctx->cbs.begin_stream)(lte, flags, ctx->cbs.begin_stream_ctx); } @@ -1007,6 +1074,13 @@ hasher_consume_chunk(const void *chunk, size_t size, void *_ctx) 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 @@ -1039,9 +1113,14 @@ hasher_end_stream(struct wim_lookup_table_entry *lte, int status, void *_ctx) * 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; @@ -1065,7 +1144,7 @@ read_full_stream_with_cbs(struct wim_lookup_table_entry *lte, { int ret; - ret = (*cbs->begin_stream)(lte, false, cbs->begin_stream_ctx); + ret = (*cbs->begin_stream)(lte, 0, cbs->begin_stream_ctx); if (ret) return ret; @@ -1093,7 +1172,6 @@ read_full_stream_with_sha1(struct wim_lookup_table_entry *lte, .consume_chunk_ctx = &hasher_ctx, .end_stream = hasher_end_stream, .end_stream_ctx = &hasher_ctx, - }; return read_full_stream_with_cbs(lte, &hasher_cbs); } @@ -1379,7 +1457,10 @@ 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) @@ -1391,6 +1472,13 @@ wim_res_hdr_to_spec(const struct wim_reshdr *reshdr, WIMStruct *wim, 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. */