X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fintegrity.c;h=b5fac79764abd2d0ea74f808c11ccff6efa5776d;hp=5ad55a5bc62a30a4a2797b082ef2df8a6f1d9118;hb=e5eee14a96d61d608672652b4a2ac6da02a617c0;hpb=e6227e4978dd41e0726ef1b54713089d343b57de diff --git a/src/integrity.c b/src/integrity.c index 5ad55a5b..b5fac797 100644 --- a/src/integrity.c +++ b/src/integrity.c @@ -1,9 +1,9 @@ /* * integrity.c * - * WIM files can optionally contain an array of SHA1 message digests at the end, - * one digest for each 1 MB of the file. This file implements the checking of - * the digests, and the writing of the digests for new WIM files. + * WIM files can optionally contain a table of SHA1 message digests at the end, + * one digest for each chunk of the file of some specified size (often 10 MB). + * This file implements the checking and writing this table. */ /* @@ -33,323 +33,516 @@ * information. */ #define INTEGRITY_CHUNK_SIZE 10485760 -/* - * Verifies the integrity of a WIM. - * - * @fp: FILE* of the WIM, currently positioned at the end of the header. - * @num_bytes: Number of bytes to verify the integrity of. - * @chunk_size: Chunk size per SHA1 message digest. - * @sha1sums: Array of SHA1 message digests; 20 bytes each, one per chunk. - * @show_progress: Nonzero if the percent complete is to be printed after every - * chunk. - */ -static int verify_integrity(FILE *fp, u64 num_bytes, u32 chunk_size, - const u8 *sha1sums, int show_progress) +/* Only use a different chunk size for compatiblity with an existing integrity + * table if the chunk size is between these two numbers. */ +#define INTEGRITY_MIN_CHUNK_SIZE 4096 +#define INTEGRITY_MAX_CHUNK_SIZE 134217728 + +struct integrity_table { + u32 size; + u32 num_entries; + u32 chunk_size; + u8 sha1sums[0][20]; +}; + +static int calculate_chunk_sha1(FILE *fp, size_t this_chunk_size, + off_t offset, u8 sha1_md[]) { - u8 *chunk_buf; - u8 resblock[SHA1_HASH_SIZE]; - u64 bytes_remaining; + int ret; + u8 buf[BUFFER_SIZE]; + SHA_CTX ctx; + size_t bytes_remaining; size_t bytes_to_read; - uint percent_done; - int ret; + size_t bytes_read; - chunk_buf = MALLOC(chunk_size); - if (!chunk_buf) { - ERROR("Failed to allocate %u byte buffer for integrity chunks", - chunk_size); - return WIMLIB_ERR_NOMEM; + ret = fseeko(fp, offset, SEEK_SET); + if (ret != 0) { + ERROR_WITH_ERRNO("Can't seek to offset " + "%"PRIu64" in WIM", offset); + return WIMLIB_ERR_READ; } - bytes_remaining = num_bytes; - while (bytes_remaining != 0) { - if (show_progress) { - percent_done = (num_bytes - bytes_remaining) * 100 / - num_bytes; - printf("Verifying integrity of WIM (%"PRIu64" bytes " - "remaining, %u%% done) \r", - bytes_remaining, percent_done); - fflush(stdout); - } - bytes_to_read = min(chunk_size, bytes_remaining); - if (fread(chunk_buf, 1, bytes_to_read, fp) != bytes_to_read) { + bytes_remaining = this_chunk_size; + sha1_init(&ctx); + do { + bytes_to_read = min(bytes_remaining, sizeof(buf)); + bytes_read = fread(buf, 1, bytes_to_read, fp); + if (bytes_read != bytes_to_read) { if (feof(fp)) { - ERROR("Unexpected EOF while verifying " - "integrity of WIM"); + ERROR("Unexpected EOF while calculating " + "integrity checksums"); } else { ERROR_WITH_ERRNO("File stream error while " - "verifying integrity of WIM"); + "calculating integrity " + "checksums"); } - ret = WIMLIB_ERR_READ; - goto out; - } - sha1_buffer(chunk_buf, bytes_to_read, resblock); - if (!hashes_equal(resblock, sha1sums)) { - ret = WIM_INTEGRITY_NOT_OK; - goto out; + return WIMLIB_ERR_READ; } - sha1sums += SHA1_HASH_SIZE; - bytes_remaining -= bytes_to_read; - } - ret = WIM_INTEGRITY_OK; -out: - FREE(chunk_buf); - if (show_progress) - putchar('\n'); - return ret; + sha1_update(&ctx, buf, bytes_read); + bytes_remaining -= bytes_read; + } while (bytes_remaining); + sha1_final(sha1_md, &ctx); + return 0; } + /* - * Verifies the integrity of the WIM. + * Reads the integrity table from a WIM file. + * + * @res_entry: + * The resource entry that specifies the location of the integrity table. + * The integrity table must exist (i.e. res_entry->offset must not be 0). + * + * @fp: + * FILE * to the WIM file, opened for reading. + * + * @num_checked_bytes: + * Number of bytes of data that should be checked by the integrity table. + * + * @table ret: + * On success, a pointer to an in-memory structure containing the integrity + * information is written to this location. + * + * Returns 0 on success; nonzero on failure. The possible error codes are: + * + * * WIMLIB_ERR_INVALID_INTEGRITY_TABLE: The integrity table is invalid. + * * WIMLIB_ERR_NOMEM: Could not allocate memory to store the integrity + * data. + * * WIMLIB_ERR_READ: Could not read the integrity data from the WIM file. */ -int check_wim_integrity(WIMStruct *w, int show_progress) +static int read_integrity_table(const struct resource_entry *res_entry, + FILE *fp, + u64 num_checked_bytes, + struct integrity_table **table_ret) { - - struct resource_entry *res_entry; - u8 *buf = NULL; - int ret; - u32 integrity_table_size; - u32 num_entries; - u32 chunk_size; - const u8 *p; + struct integrity_table *table = NULL; + int ret = 0; u64 expected_size; - u64 end_lookup_table_offset; - u64 bytes_to_check; u64 expected_num_entries; - res_entry = &w->hdr.integrity; - if (res_entry->size == 0) { - DEBUG("No integrity information."); - return WIM_INTEGRITY_NONEXISTENT; - } - if (res_entry->original_size < 12) { - ERROR("Integrity table is too short"); + if (resource_is_compressed(res_entry)) { + ERROR("Didn't expect a compressed integrity table"); return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; } - if (res_entry->flags & WIM_RESHDR_FLAG_COMPRESSED) { - ERROR("Didn't expect a compressed integrity table"); + + if (res_entry->size < 8 || res_entry->size > 0xffffffff) { + ERROR("Integrity table resource header is invalid"); return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; } /* Read the integrity table into memory. */ - if ((sizeof(size_t) < sizeof(u64) - && res_entry->original_size > ~(size_t)0) - || ((buf = MALLOC(res_entry->original_size)) == NULL)) - { - ERROR("Out of memory (needed %zu bytes for integrity table)", - (size_t)res_entry->original_size); - ret = WIMLIB_ERR_NOMEM; - goto out; + if ((table = MALLOC(res_entry->size)) == NULL) { + ERROR("Can't allocate %"PRIu64" bytes for integrity table", + (u64)res_entry->size); + return WIMLIB_ERR_NOMEM; } - ret = read_uncompressed_resource(w->fp, res_entry->offset, - res_entry->original_size, buf); + + ret = read_uncompressed_resource(fp, res_entry->offset, + res_entry->size, (void*)table); + if (ret != 0) { - ERROR("Failed to read integrity table (size = %"PRIu64", " - "original_size = %"PRIu64", offset = " - "%"PRIu64")", - (u64)res_entry->size, res_entry->original_size, - res_entry->offset); + ERROR("Failed to read integrity table (size = %u, " + " offset = %"PRIu64")", + (unsigned)res_entry->size, res_entry->offset); goto out; } - p = get_u32(buf, &integrity_table_size); - p = get_u32(p, &num_entries); - p = get_u32(p, &chunk_size); + table->size = le32_to_cpu(table->size); + table->num_entries = le32_to_cpu(table->num_entries); + table->chunk_size = le32_to_cpu(table->chunk_size); - /* p now points to the array of SHA1 message digests for the WIM. */ - - /* Make sure the integrity table is the right size. */ - if (integrity_table_size != res_entry->original_size) { - ERROR("Inconsistent integrity table sizes: header says %u " - "bytes but resource entry says " - "%"PRIu64" bytes", - integrity_table_size, res_entry->original_size); + if (table->size != res_entry->size) { + ERROR("Inconsistent integrity table sizes: Table header says " + "%u bytes but resource entry says %u bytes", + table->size, (unsigned)res_entry->size); ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE; goto out; } - DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u", - integrity_table_size, num_entries, chunk_size); - + DEBUG("table->size = %u, table->num_entries = %u, " + "table->chunk_size = %u", + table->size, table->num_entries, table->chunk_size); - expected_size = num_entries * SHA1_HASH_SIZE + 12; + expected_size = (u64)table->num_entries * SHA1_HASH_SIZE + 12; - if (integrity_table_size != expected_size) { + if (table->size != expected_size) { ERROR("Integrity table is %u bytes, but expected %"PRIu64" " "bytes to hold %u entries", - integrity_table_size, expected_size, num_entries); + table->size, expected_size, table->num_entries); ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE; goto out; } - if (chunk_size == 0) { + if (table->chunk_size == 0) { ERROR("Cannot use integrity chunk size of 0"); ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE; goto out; } - end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset + - w->hdr.lookup_table_res_entry.size; - - if (end_lookup_table_offset < WIM_HEADER_DISK_SIZE) { - ERROR("WIM lookup table ends before WIM header ends???"); - ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE; - goto out; - } - - bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE; - - expected_num_entries = (bytes_to_check + chunk_size - 1) / chunk_size; + expected_num_entries = DIV_ROUND_UP(num_checked_bytes, table->chunk_size); - if (num_entries != expected_num_entries) { - ERROR("%"PRIu64" entries would be required to checksum " - "the %"PRIu64" bytes from the end of the header to the", - expected_num_entries, bytes_to_check); + if (table->num_entries != expected_num_entries) { + ERROR("%"PRIu64" integrity table entries would be required " + "to checksum the %"PRIu64" bytes from the end of the " + "header to the", + expected_num_entries, num_checked_bytes); ERROR("end of the lookup table with a chunk size of %u, but " - "there were only %u entries", chunk_size, num_entries); + "there were only %u entries", + table->chunk_size, table->num_entries); ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE; - goto out; } - - /* The integrity checking starts after the header, so seek to the offset - * in the WIM after the header. */ - - if (fseeko(w->fp, WIM_HEADER_DISK_SIZE, SEEK_SET) != 0) { - ERROR_WITH_ERRNO("Failed to seek to byte %u of WIM to check " - "integrity", WIM_HEADER_DISK_SIZE); - ret = WIMLIB_ERR_READ; - goto out; - } - /* call verify_integrity(), which does the actual checking of the SHA1 - * message digests. */ - ret = verify_integrity(w->fp, bytes_to_check, chunk_size, p, - show_progress); out: - FREE(buf); + if (ret == 0) + *table_ret = table; + else + FREE(table); return ret; } /* - * Writes integrity information to the output stream for a WIM file being - * written. + * Calculates an integrity table for the data in a file beginning at offset 208 + * (WIM_HEADER_DISK_SIZE). + * + * @fp: + * FILE * for the file to be checked, opened for reading. Does not need to + * be at any specific location in the file. + * + * @new_check_end: + * Offset of byte after the last byte to be checked. + * + * @old_table: + * If non-NULL, a pointer to the table containing previously contained + * integrity data for a prefix of this file. * - * @end_header_offset is the offset of the byte after the header, which is the - * beginning of the region that is checksummed. + * @old_check_end: + * If @old_table is non-NULL, the byte after the last byte that was checked + * in the old table. Must be less than or equal to new_check_end. * - * @end_lookup_table_offset is the offset of the byte after the lookup table, - * which is the end of the region that is checksummed. + * @progress_func: + * If non-NULL, a progress function that will be called after every + * calculated chunk. + * + * @integrity_table_ret: + * On success, a pointer to the calculated integrity table is written into + * this location. + * + * Returns 0 on success; nonzero on failure. */ -int write_integrity_table(FILE *out, u64 end_header_offset, - u64 end_lookup_table_offset, int show_progress, - struct resource_entry *out_res_entry) +static int calculate_integrity_table(FILE *fp, + off_t new_check_end, + const struct integrity_table *old_table, + off_t old_check_end, + wimlib_progress_func_t progress_func, + struct integrity_table **integrity_table_ret) { - u64 bytes_to_check; - u64 bytes_remaining; - u8 *buf; - u8 *p; - u8 *chunk_buf; - u32 num_entries; - u32 integrity_table_size; - int ret; - off_t start_offset; - - start_offset = ftello(out); - if (start_offset == -1) - return WIMLIB_ERR_WRITE; - - DEBUG("Calculating integrity table"); - if (fseeko(out, end_header_offset, SEEK_SET) != 0) { - ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to " - "calculate integrity data", end_header_offset); - return WIMLIB_ERR_WRITE; + int ret = 0; + size_t chunk_size = INTEGRITY_CHUNK_SIZE; + + /* If an old table is provided, set the chunk size to be compatible with + * the old chunk size, unless the old chunk size was weird. */ + if (old_table != NULL) { + if (old_table->num_entries == 0 || + old_table->chunk_size < INTEGRITY_MIN_CHUNK_SIZE || + old_table->chunk_size > INTEGRITY_MAX_CHUNK_SIZE) + old_table = NULL; + else + chunk_size = old_table->chunk_size; } - bytes_to_check = end_lookup_table_offset - end_header_offset; - num_entries = (bytes_to_check + INTEGRITY_CHUNK_SIZE - 1) / - INTEGRITY_CHUNK_SIZE; - integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32); - DEBUG("integrity_table_size = %u", integrity_table_size); + u64 old_check_bytes = old_check_end - WIM_HEADER_DISK_SIZE; + u64 new_check_bytes = new_check_end - WIM_HEADER_DISK_SIZE; - buf = MALLOC(integrity_table_size); - if (!buf) { - ERROR("Failed to allocate %u bytes for integrity table", - integrity_table_size); - return WIMLIB_ERR_NOMEM; - } + u32 old_num_chunks = DIV_ROUND_UP(old_check_bytes, chunk_size); + u32 new_num_chunks = DIV_ROUND_UP(new_check_bytes, chunk_size); + + size_t old_last_chunk_size = MODULO_NONZERO(old_check_bytes, chunk_size); + size_t new_last_chunk_size = MODULO_NONZERO(new_check_bytes, chunk_size); - p = put_u32(buf, integrity_table_size); - p = put_u32(p, num_entries); - p = put_u32(p, INTEGRITY_CHUNK_SIZE); + size_t new_table_size = 12 + new_num_chunks * SHA1_HASH_SIZE; - chunk_buf = MALLOC(INTEGRITY_CHUNK_SIZE); - if (!chunk_buf) { - ERROR("Failed to allocate %u bytes for integrity chunk buffer", - INTEGRITY_CHUNK_SIZE); - ret = WIMLIB_ERR_NOMEM; - goto out_free_buf; + struct integrity_table *new_table = MALLOC(new_table_size); + if (!new_table) + return WIMLIB_ERR_NOMEM; + new_table->num_entries = new_num_chunks; + new_table->size = new_table_size; + new_table->chunk_size = chunk_size; + + u64 offset = WIM_HEADER_DISK_SIZE; + union wimlib_progress_info progress; + + if (progress_func) { + progress.integrity.total_bytes = new_check_bytes; + progress.integrity.total_chunks = new_num_chunks; + progress.integrity.completed_chunks = 0; + progress.integrity.completed_bytes = 0; + progress.integrity.chunk_size = chunk_size; + progress.integrity.filename = NULL; + progress_func(WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, + &progress); } - bytes_remaining = bytes_to_check; + for (u32 i = 0; i < new_num_chunks; i++) { + size_t this_chunk_size; + if (i == new_num_chunks - 1) + this_chunk_size = new_last_chunk_size; + else + this_chunk_size = chunk_size; + if (old_table && + ((this_chunk_size == chunk_size && i < old_num_chunks - 1) || + (i == old_num_chunks - 1 && this_chunk_size == old_last_chunk_size))) + { + /* Can use SHA1 message digest from old integrity table + * */ + copy_hash(new_table->sha1sums[i], old_table->sha1sums[i]); + } else { + /* Calculate the SHA1 message digest of this chunk */ + ret = calculate_chunk_sha1(fp, this_chunk_size, + offset, new_table->sha1sums[i]); + if (ret != 0) + break; + } + offset += this_chunk_size; + if (progress_func) { + progress.integrity.completed_chunks++; + progress.integrity.completed_bytes += this_chunk_size; + progress_func(WIMLIB_PROGRESS_MSG_CALC_INTEGRITY, + &progress); + } + } + if (ret == 0) + *integrity_table_ret = new_table; + else + FREE(new_table); + return ret; +} - DEBUG("Bytes to check = %"PRIu64, bytes_to_check); +/* + * Writes a WIM integrity table (a list of SHA1 message digests of raw 10 MiB + * chunks of the file). + * + * This function can optionally re-use entries from an older integrity table. + * To do this, make @integrity_res_entry point to the resource entry for the + * older table (note: this is an input-output parameter), and set + * @old_lookup_table_end to the offset of the byte directly following the last + * byte checked by the old table. If the old integrity table is invalid or + * cannot be read, a warning is printed and the integrity information is + * re-calculated. + * + * @fp: + * FILE * to the WIM file, opened read-write, positioned at the location at + * which the integrity table is to be written. + * + * @integrity_res_entry: + * Resource entry which will be set to point to the integrity table on + * success. In addition, if @old_lookup_table_end != 0, this initially + * must point to the resource entry for the old integrity table for the + * WIM. + * + * @new_lookup_table_end: + * The offset of the byte directly following the lookup table in the WIM + * being written. + * + * @old_lookup_table_end: + * If nonzero, the offset of the byte directly following the old lookup + * table in the WIM. + * + * @progress_func + * If non-NULL, a progress function that will be called after every + * calculated chunk. + * + * Returns: + * 0 on success, nonzero on failure. The possible error codes are: + * * WIMLIB_ERR_WRITE: Could not write the integrity table. + * * WIMLIB_ERR_READ: Could not read a chunk of data that needed + * to be checked. + */ +int write_integrity_table(FILE *fp, + struct resource_entry *integrity_res_entry, + off_t new_lookup_table_end, + off_t old_lookup_table_end, + wimlib_progress_func_t progress_func) +{ + struct integrity_table *old_table; + struct integrity_table *new_table; + int ret; + off_t cur_offset; + u32 new_table_size; - while (bytes_remaining != 0) { + wimlib_assert(old_lookup_table_end <= new_lookup_table_end); - uint percent_done = (bytes_to_check - bytes_remaining) * - 100 / bytes_to_check; + cur_offset = ftello(fp); + if (cur_offset == -1) + return WIMLIB_ERR_WRITE; - if (show_progress) { - printf("Calculating integrity checksums for WIM " - "(%"PRIu64" bytes remaining, %u%% " - "done) \r", - bytes_remaining, percent_done); - fflush(stdout); + if (integrity_res_entry->offset == 0 || old_lookup_table_end == 0) { + old_table = NULL; + } else { + ret = read_integrity_table(integrity_res_entry, fp, + old_lookup_table_end - WIM_HEADER_DISK_SIZE, + &old_table); + if (ret == WIMLIB_ERR_INVALID_INTEGRITY_TABLE) { + WARNING("Old integrity table is invalid! " + "Ignoring it"); + } else if (ret != 0) { + WARNING("Can't read old integrity table! " + "Ignoring it"); } + } + ret = calculate_integrity_table(fp, new_lookup_table_end, + old_table, old_lookup_table_end, + progress_func, &new_table); + if (ret != 0) + goto out_free_old_table; - size_t bytes_to_read = min(INTEGRITY_CHUNK_SIZE, bytes_remaining); - size_t bytes_read = fread(chunk_buf, 1, bytes_to_read, out); - if (bytes_read != bytes_to_read) { - if (feof(out)) { - ERROR("Unexpected EOF while calculating " - "integrity checksums"); - } else { - ERROR_WITH_ERRNO("File stream error while " - "calculating integrity " - "checksums"); - } - ret = WIMLIB_ERR_READ; - goto out_free_chunk_buf; - } - sha1_buffer(chunk_buf, bytes_read, p); - p += SHA1_HASH_SIZE; - bytes_remaining -= bytes_read; - } - if (show_progress) - puts("Calculating integrity checksums for WIM " - "(0 bytes remaining, 100% done)" - " "); + new_table_size = new_table->size; - if (fseeko(out, start_offset, SEEK_SET) != 0) { - ERROR_WITH_ERRNO("Failed to seek to end of WIM"); + new_table->size = cpu_to_le32(new_table->size); + new_table->num_entries = cpu_to_le32(new_table->num_entries); + new_table->chunk_size = cpu_to_le32(new_table->chunk_size); + + if (fseeko(fp, cur_offset, SEEK_SET) != 0) { + ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to " + "write integrity table", cur_offset); ret = WIMLIB_ERR_WRITE; - goto out_free_chunk_buf; + goto out_free_new_table; } - if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) { - ERROR_WITH_ERRNO("Failed to write integrity table to end of " - "WIM"); + if (fwrite(new_table, 1, new_table_size, fp) != new_table_size) { + ERROR_WITH_ERRNO("Failed to write WIM integrity table"); ret = WIMLIB_ERR_WRITE; - goto out_free_chunk_buf; + } else { + integrity_res_entry->offset = cur_offset; + integrity_res_entry->size = new_table_size; + integrity_res_entry->original_size = new_table_size; + integrity_res_entry->flags = 0; + ret = 0; + } +out_free_new_table: + FREE(new_table); +out_free_old_table: + FREE(old_table); + return ret; +} + +/* + * Checks a WIM for consistency with the integrity table. + * + * @fp: + * FILE * to the WIM file, opened for reading. + * + * @table: + * The integrity table for the WIM, read into memory. + * + * @bytes_to_check: + * Number of bytes in the WIM that need to be checked (offset of end of the + * lookup table minus offset of end of the header). + * + * @progress_func + * If non-NULL, a progress function that will be called after every + * verified chunk. + * + * Returns: + * > 0 (WIMLIB_ERR_*) on error + * 0 (WIM_INTEGRITY_OK) if the integrity was checked successfully and there + * were no inconsistencies. + * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. + */ +static int verify_integrity(FILE *fp, const char *filename, + const struct integrity_table *table, + u64 bytes_to_check, + wimlib_progress_func_t progress_func) +{ + int ret; + u64 offset = WIM_HEADER_DISK_SIZE; + u8 sha1_md[SHA1_HASH_SIZE]; + union wimlib_progress_info progress; + + if (progress_func) { + progress.integrity.total_bytes = bytes_to_check; + progress.integrity.total_chunks = table->num_entries; + progress.integrity.completed_chunks = 0; + progress.integrity.completed_bytes = 0; + progress.integrity.chunk_size = table->chunk_size; + progress.integrity.filename = filename; + progress_func(WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, + &progress); + } + for (u32 i = 0; i < table->num_entries; i++) { + size_t this_chunk_size; + if (i == table->num_entries - 1) + this_chunk_size = MODULO_NONZERO(bytes_to_check, + table->chunk_size); + else + this_chunk_size = table->chunk_size; + + ret = calculate_chunk_sha1(fp, this_chunk_size, offset, sha1_md); + if (ret != 0) + return ret; + + if (!hashes_equal(sha1_md, table->sha1sums[i])) + return WIM_INTEGRITY_NOT_OK; + + offset += this_chunk_size; + if (progress_func) { + progress.integrity.completed_chunks++; + progress.integrity.completed_bytes += this_chunk_size; + progress_func(WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY, + &progress); + } + } + return WIM_INTEGRITY_OK; +} + + +/* + * Verifies the integrity of the WIM by making sure the SHA1 message digests of + * ~10 MiB chunks of the WIM match up with the values given in the integrity + * table. + * + * @w: + * The WIM, opened for reading, and with the header already read. + * + * @progress_func + * If non-NULL, a progress function that will be called after every + * verified chunk. + * + * Returns: + * > 0 (WIMLIB_ERR_*) on error + * 0 (WIM_INTEGRITY_OK) if the integrity was checked successfully and there + * were no inconsistencies. + * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. + * -2 (WIM_INTEGRITY_NONEXISTENT) if the WIM contains no integrity + * information. + */ +int check_wim_integrity(WIMStruct *w, wimlib_progress_func_t progress_func) +{ + int ret; + u64 bytes_to_check; + struct integrity_table *table; + u64 end_lookup_table_offset; + + if (w->hdr.integrity.offset == 0) { + DEBUG("No integrity information."); + return WIM_INTEGRITY_NONEXISTENT; } - out_res_entry->offset = start_offset; - out_res_entry->size = integrity_table_size; - out_res_entry->original_size = integrity_table_size; - out_res_entry->flags = 0; - ret = 0; -out_free_chunk_buf: - FREE(chunk_buf); -out_free_buf: - FREE(buf); + end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset + + w->hdr.lookup_table_res_entry.size; + + if (end_lookup_table_offset < WIM_HEADER_DISK_SIZE) { + ERROR("WIM lookup table ends before WIM header ends!"); + return WIMLIB_ERR_INVALID_INTEGRITY_TABLE; + } + + bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE; + + ret = read_integrity_table(&w->hdr.integrity, w->fp, + bytes_to_check, &table); + if (ret != 0) + return ret; + ret = verify_integrity(w->fp, w->filename, table, + bytes_to_check, progress_func); + FREE(table); return ret; }