* "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
*
* WIMLIB_ERR_SUCCESS (0)
* WIMLIB_ERR_READ (errno set)
- * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to 0)
+ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to EINVAL)
* WIMLIB_ERR_NOMEM (errno set to ENOMEM)
* WIMLIB_ERR_DECOMPRESSION (errno set to EINVAL)
* WIMLIB_ERR_INVALID_CHUNK_SIZE (errno set to EINVAL)
* 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]);
* 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;
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;
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
/* 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
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;
}
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;
}
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_on_disk, 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
} 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;