X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fresource.c;h=385f9f65ba6836aa594c5bb34e9b4823eabff79c;hp=9afa25ffc626842a55883d566a3474a1b83318d1;hb=8618172276fae088f311923a61bbf26c3d4d8941;hpb=97fb83608ac22580bbfeff1bdf3111f72d2c1634 diff --git a/src/resource.c b/src/resource.c index 9afa25ff..385f9f65 100644 --- a/src/resource.c +++ b/src/resource.c @@ -57,8 +57,8 @@ * "chunk table" provides the offset, in bytes relative to the end of the * chunk table, of the start of each compressed chunk, except for the first * chunk which is omitted as it always has an offset of 0. Chunk table - * entries are 32-bit for resources <= 4 GiB uncompressed and 64-bit for - * resources > 4 GiB uncompressed. + * entries are 32-bit for resources < 4 GiB uncompressed and 64-bit for + * resources >= 4 GiB uncompressed. * * - Solid resource format (distinguished by the use of WIM_RESHDR_FLAG_SOLID * instead of WIM_RESHDR_FLAG_COMPRESSED): similar to the original format, but @@ -188,7 +188,7 @@ read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc, } } - const u32 chunk_order = fls32(chunk_size); + const u32 chunk_order = bsr32(chunk_size); /* Calculate the total number of chunks the resource is divided into. */ const u64 num_chunks = (rdesc->uncompressed_size + chunk_size - 1) >> chunk_order; @@ -234,35 +234,34 @@ read_compressed_wim_resource(const struct wim_resource_descriptor * const rdesc, * to initialize the chunk_offsets array. */ u64 first_chunk_entry_to_read; - u64 last_chunk_entry_to_read; + u64 num_chunk_entries_to_read; if (alt_chunk_table) { /* The alternate chunk table contains chunk sizes, not * offsets, so we always must read all preceding entries * in order to determine offsets. */ first_chunk_entry_to_read = 0; - last_chunk_entry_to_read = last_needed_chunk; + num_chunk_entries_to_read = last_needed_chunk + 1; } else { - /* Here we must account for the fact that the first - * chunk has no explicit chunk table entry. */ - if (read_start_chunk == 0) + num_chunk_entries_to_read = last_needed_chunk - read_start_chunk + 1; + + /* The first chunk has no explicit chunk table entry. */ + if (read_start_chunk == 0) { + num_chunk_entries_to_read--; first_chunk_entry_to_read = 0; - else + } else { first_chunk_entry_to_read = read_start_chunk - 1; + } - if (last_needed_chunk == 0) - last_chunk_entry_to_read = 0; - else - last_chunk_entry_to_read = last_needed_chunk - 1; - + /* Unless we're reading the final chunk of the resource, + * we need the offset of the chunk following the last + * needed chunk so that the compressed size of the last + * needed chunk can be computed. */ if (last_needed_chunk < num_chunks - 1) - last_chunk_entry_to_read++; + num_chunk_entries_to_read++; } - const u64 num_chunk_entries_to_read = - last_chunk_entry_to_read - first_chunk_entry_to_read + 1; - const u64 chunk_offsets_alloc_size = max(num_chunk_entries_to_read, num_needed_chunk_offsets) * sizeof(chunk_offsets[0]); @@ -537,7 +536,8 @@ read_error: * data in nonempty chunks into the cbs->consume_chunk() function. */ static int read_raw_file_data(struct filedes *in_fd, u64 offset, u64 size, - const struct read_blob_callbacks *cbs) + const struct read_blob_callbacks *cbs, + const tchar *filename) { u8 buf[BUFFER_SIZE]; size_t bytes_to_read; @@ -546,10 +546,8 @@ read_raw_file_data(struct filedes *in_fd, u64 offset, u64 size, while (size) { bytes_to_read = min(sizeof(buf), size); ret = full_pread(in_fd, buf, bytes_to_read, offset); - if (unlikely(ret)) { - ERROR_WITH_ERRNO("Read error"); - return ret; - } + if (unlikely(ret)) + goto read_error; ret = call_consume_chunk(buf, bytes_to_read, cbs); if (unlikely(ret)) return ret; @@ -557,6 +555,17 @@ read_raw_file_data(struct filedes *in_fd, u64 offset, u64 size, offset += bytes_to_read; } return 0; + +read_error: + if (!filename) { + ERROR_WITH_ERRNO("Error reading data from WIM file"); + } else if (ret == WIMLIB_ERR_UNEXPECTED_END_OF_FILE) { + ERROR("\"%"TS"\": File was concurrently truncated", filename); + ret = WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; + } else { + ERROR_WITH_ERRNO("\"%"TS"\": Error reading data", filename); + } + return ret; } /* A consume_chunk() implementation that simply concatenates all chunks into an @@ -601,7 +610,7 @@ read_partial_wim_resource(const struct wim_resource_descriptor *rdesc, /* Uncompressed resource */ return read_raw_file_data(&rdesc->wim->in_fd, rdesc->offset_in_wim + offset, - size, cbs); + size, cbs, NULL); } /* Read the specified range of uncompressed data from the specified blob, which @@ -660,7 +669,7 @@ read_file_on_disk_prefix(const struct blob_descriptor *blob, u64 size, return WIMLIB_ERR_OPEN; } filedes_init(&fd, raw_fd); - ret = read_raw_file_data(&fd, 0, size, cbs); + ret = read_raw_file_data(&fd, 0, size, cbs, blob->file_on_disk); filedes_close(&fd); return ret; } @@ -682,7 +691,7 @@ read_staging_file_prefix(const struct blob_descriptor *blob, u64 size, return WIMLIB_ERR_OPEN; } filedes_init(&fd, raw_fd); - ret = read_raw_file_data(&fd, 0, size, cbs); + ret = read_raw_file_data(&fd, 0, size, cbs, blob->staging_file_name); filedes_close(&fd); return ret; } @@ -730,8 +739,7 @@ read_blob_prefix(const struct blob_descriptor *blob, u64 size, [BLOB_IN_NTFS_VOLUME] = read_ntfs_attribute_prefix, #endif #ifdef __WIN32__ - [BLOB_IN_WINNT_FILE_ON_DISK] = read_winnt_stream_prefix, - [BLOB_WIN32_ENCRYPTED] = read_win32_encrypted_file_prefix, + [BLOB_IN_WINDOWS_FILE] = read_windows_file_prefix, #endif }; wimlib_assert(blob->blob_location < ARRAY_LEN(handlers) @@ -932,6 +940,62 @@ hasher_consume_chunk(const void *chunk, size_t size, void *_ctx) return call_consume_chunk(chunk, size, &ctx->cbs); } +static int +report_sha1_mismatch_error(const struct blob_descriptor *blob, + const u8 actual_hash[SHA1_HASH_SIZE]) +{ + tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1]; + tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1]; + + wimlib_assert(blob->blob_location != BLOB_NONEXISTENT); + wimlib_assert(blob->blob_location != BLOB_IN_ATTACHED_BUFFER); + + sprint_hash(blob->hash, expected_hashstr); + sprint_hash(actual_hash, actual_hashstr); + + if (blob_is_in_file(blob)) { + ERROR("A file was concurrently modified!\n" + " Path: \"%"TS"\"\n" + " Expected SHA-1: %"TS"\n" + " Actual SHA-1: %"TS"\n", + blob_file_path(blob), expected_hashstr, actual_hashstr); + return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; + } else if (blob->blob_location == BLOB_IN_WIM) { + const struct wim_resource_descriptor *rdesc = blob->rdesc; + ERROR("A WIM resource is corrupted!\n" + " WIM file: \"%"TS"\"\n" + " Blob uncompressed size: %"PRIu64"\n" + " Resource offset in WIM: %"PRIu64"\n" + " Resource uncompressed size: %"PRIu64"\n" + " Resource size in WIM: %"PRIu64"\n" + " Resource flags: 0x%x%"TS"\n" + " Resource compression type: %"TS"\n" + " Resource compression chunk size: %"PRIu32"\n" + " Expected SHA-1: %"TS"\n" + " Actual SHA-1: %"TS"\n", + rdesc->wim->filename, + blob->size, + rdesc->offset_in_wim, + rdesc->uncompressed_size, + rdesc->size_in_wim, + (unsigned int)rdesc->flags, + (rdesc->is_pipable ? T(", pipable") : T("")), + wimlib_get_compression_type_string( + rdesc->compression_type), + rdesc->chunk_size, + expected_hashstr, actual_hashstr); + return WIMLIB_ERR_INVALID_RESOURCE_HASH; + } else { + ERROR("File data was concurrently modified!\n" + " Location ID: %d\n" + " Expected SHA-1: %"TS"\n" + " Actual SHA-1: %"TS"\n", + (int)blob->blob_location, + expected_hashstr, actual_hashstr); + return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED; + } +} + /* Callback for finishing reading a blob while calculating its SHA-1 message * digest. */ static int @@ -958,16 +1022,7 @@ hasher_end_blob(struct blob_descriptor *blob, int status, void *_ctx) } else if ((ctx->flags & VERIFY_BLOB_HASHES) && unlikely(!hashes_equal(hash, blob->hash))) { - if (wimlib_print_errors) { - tchar expected_hashstr[SHA1_HASH_SIZE * 2 + 1]; - tchar actual_hashstr[SHA1_HASH_SIZE * 2 + 1]; - sprint_hash(blob->hash, expected_hashstr); - sprint_hash(hash, actual_hashstr); - ERROR("The data is corrupted!\n" - " (Expected SHA-1=%"TS", got SHA-1=%"TS")", - expected_hashstr, actual_hashstr); - } - ret = WIMLIB_ERR_INVALID_RESOURCE_HASH; + ret = report_sha1_mismatch_error(blob, hash); goto out_next_cb; } ret = 0;