4 * WIM files can optionally contain an array of SHA1 message digests at the end,
5 * one digest for each 1 MB of the file. This file implements the checking of
6 * the digests, and the writing of the digests for new WIM files.
10 * Copyright (C) 2012 Eric Biggers
12 * This file is part of wimlib, a library for working with WIM files.
14 * wimlib is free software; you can redistribute it and/or modify it under the
15 * terms of the GNU Lesser General Public License as published by the Free
16 * Software Foundation; either version 2.1 of the License, or (at your option)
19 * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
20 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
21 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with wimlib; if not, see http://www.gnu.org/licenses/.
28 #include "wimlib_internal.h"
32 /* Size, in bytes, of each SHA1-summed chunk, when wimlib writes integrity
34 #define INTEGRITY_CHUNK_SIZE 10485760
37 * Verifies the integrity of a WIM.
39 * @fp: FILE* of the WIM, currently positioned at the end of the header.
40 * @num_bytes: Number of bytes to verify the integrity of.
41 * @chunk_size: Chunk size per SHA1 message digest.
42 * @sha1sums: Array of SHA1 message digests; 20 bytes each, one per chunk.
43 * @show_progress: Nonzero if the percent complete is to be printed after every
45 * @status: On success, set to WIM_INTEGRITY_OK or WIM_INTEGRITY_NOT_OK
46 * based on whether the WIM is intact or not.
48 static int verify_integrity(FILE *fp, u64 num_bytes, u32 chunk_size,
49 const u8 *sha1sums, int show_progress,
53 u8 resblock[SHA1_HASH_SIZE];
59 chunk_buf = MALLOC(chunk_size);
61 ERROR("Failed to allocate %u byte buffer for integrity chunks",
63 return WIMLIB_ERR_NOMEM;
65 bytes_remaining = num_bytes;
66 while (bytes_remaining != 0) {
68 percent_done = (num_bytes - bytes_remaining) * 100 /
70 printf("Verifying integrity of WIM (%"PRIu64" bytes "
71 "remaining, %u%% done) \r",
72 bytes_remaining, percent_done);
75 bytes_to_read = min(chunk_size, bytes_remaining);
76 if (fread(chunk_buf, 1, bytes_to_read, fp) != bytes_to_read) {
78 ERROR("Unexpected EOF while verifying "
81 ERROR_WITH_ERRNO("File stream error while "
82 "verifying integrity of WIM");
84 ret = WIMLIB_ERR_READ;
85 goto verify_integrity_error;
87 sha1_buffer(chunk_buf, bytes_to_read, resblock);
88 if (!hashes_equal(resblock, sha1sums)) {
89 *status = WIM_INTEGRITY_NOT_OK;
90 goto verify_integrity_done;
92 sha1sums += SHA1_HASH_SIZE;
93 bytes_remaining -= bytes_to_read;
95 *status = WIM_INTEGRITY_OK;
96 verify_integrity_done:
98 verify_integrity_error:
106 * Verifies the integrity of the WIM.
108 * @show_progress: Nonzero if the percent complete is to be printed after every
110 * @status: On success, set to WIM_INTEGRITY_OK, WIM_INTEGRITY_NOT_OK,
111 * or WIM_INTEGRITY_NONEXISTENT.
113 * Returns: 0, WIMLIB_ERR_INVALID_INTEGRITY_TABLE, WIMLIB_ERR_NOMEM, or
114 * WIMLIB_ERR_READ. If nonzero, the boolean pointed to by @ok is not changed.
116 int check_wim_integrity(WIMStruct *w, int show_progress, int *status)
119 struct resource_entry *res_entry;
123 u32 integrity_table_size;
128 u64 end_lookup_table_offset;
130 u64 expected_num_entries;
132 res_entry = &w->hdr.integrity;
133 if (res_entry->size == 0) {
134 DEBUG("No integrity information.");
135 *status = WIM_INTEGRITY_NONEXISTENT;
138 if (res_entry->original_size < 12) {
139 ERROR("Integrity table is too short");
140 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
142 if (res_entry->flags & WIM_RESHDR_FLAG_COMPRESSED) {
143 ERROR("Didn't expect a compressed integrity table");
144 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
147 /* Read the integrity table into memory. */
148 buf = MALLOC(res_entry->original_size);
150 ERROR("Out of memory (needed %zu bytes for integrity table)",
151 res_entry->original_size);
152 ret = WIMLIB_ERR_NOMEM;
155 ret = read_uncompressed_resource(w->fp, res_entry->offset,
156 res_entry->original_size, buf);
158 ERROR("Failed to read integrity table (size = %"PRIu64", "
159 "original_size = %"PRIu64", offset = "
161 (u64)res_entry->size, res_entry->original_size,
166 p = get_u32(buf, &integrity_table_size);
167 p = get_u32(p, &num_entries);
168 p = get_u32(p, &chunk_size);
170 /* p now points to the array of SHA1 message digests for the WIM. */
172 /* Make sure the integrity table is the right size. */
173 if (integrity_table_size != res_entry->original_size) {
174 ERROR("Inconsistent integrity table sizes: header says %u "
175 "bytes but resource entry says "
177 integrity_table_size, res_entry->original_size);
178 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
182 DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u",
183 integrity_table_size, num_entries, chunk_size);
186 expected_size = num_entries * SHA1_HASH_SIZE + 12;
188 if (integrity_table_size != expected_size) {
189 ERROR("Integrity table is %u bytes, but expected %"PRIu64" "
190 "bytes to hold %u entries",
191 integrity_table_size, expected_size, num_entries);
192 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
196 if (chunk_size == 0) {
197 ERROR("Cannot use integrity chunk size of 0");
198 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
202 end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset +
203 w->hdr.lookup_table_res_entry.size;
205 bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE;
207 expected_num_entries = (bytes_to_check + chunk_size - 1) / chunk_size;
209 if (num_entries != expected_num_entries) {
210 ERROR("%"PRIu64" entries would be required to checksum "
211 "the %"PRIu64" bytes from the end of the header to the",
212 expected_num_entries, bytes_to_check);
213 ERROR("end of the lookup table with a chunk size of %u, but "
214 "there were only %u entries", chunk_size, num_entries);
215 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
219 /* The integrity checking starts after the header, so seek to the offset
220 * in the WIM after the header. */
222 if (fseeko(w->fp, WIM_HEADER_DISK_SIZE, SEEK_SET) != 0) {
223 ERROR_WITH_ERRNO("Failed to seek to byte %u of WIM to check "
224 "integrity", WIM_HEADER_DISK_SIZE);
225 ret = WIMLIB_ERR_READ;
228 /* call verify_integrity(), which does the actual checking of the SHA1
229 * message digests. */
230 ret = verify_integrity(w->fp, bytes_to_check, chunk_size, p,
231 show_progress, status);
238 * Writes integrity information to the output stream for a WIM file being
241 * @end_header_offset is the offset of the byte after the header, which is the
242 * beginning of the region that is checksummed.
244 * @end_lookup_table_offset is the offset of the byte after the lookup table,
245 * which is the end of the region that is checksummed.
247 int write_integrity_table(FILE *out, u64 end_header_offset,
248 u64 end_lookup_table_offset, int show_progress)
256 u32 integrity_table_size;
259 DEBUG("Writing integrity table");
260 if (fseeko(out, end_header_offset, SEEK_SET) != 0) {
261 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to "
262 "calculate integrity data", end_header_offset);
263 return WIMLIB_ERR_WRITE;
266 bytes_to_check = end_lookup_table_offset - end_header_offset;
267 num_entries = bytes_to_check / INTEGRITY_CHUNK_SIZE +
268 (bytes_to_check % INTEGRITY_CHUNK_SIZE != 0);
269 integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32);
271 DEBUG("integrity table size = %u", integrity_table_size);
274 buf = MALLOC(integrity_table_size);
276 ERROR("Failed to allocate %u bytes for integrity table",
277 integrity_table_size);
278 return WIMLIB_ERR_NOMEM;
281 p = put_u32(buf, integrity_table_size);
282 p = put_u32(p, num_entries);
283 p = put_u32(p, INTEGRITY_CHUNK_SIZE);
285 chunk_buf = MALLOC(INTEGRITY_CHUNK_SIZE);
287 ERROR("Failed to allocate %u bytes for integrity chunk buffer",
288 INTEGRITY_CHUNK_SIZE);
289 ret = WIMLIB_ERR_NOMEM;
293 bytes_remaining = bytes_to_check;
295 DEBUG("Bytes to check = %"PRIu64, bytes_to_check);
297 while (bytes_remaining != 0) {
299 uint percent_done = (bytes_to_check - bytes_remaining) *
300 100 / bytes_to_check;
303 printf("Calculating integrity checksums for WIM "
304 "(%"PRIu64" bytes remaining, %u%% "
306 bytes_remaining, percent_done);
311 size_t bytes_to_read = min(INTEGRITY_CHUNK_SIZE, bytes_remaining);
312 size_t bytes_read = fread(chunk_buf, 1, bytes_to_read, out);
313 if (bytes_read != bytes_to_read) {
315 ERROR("Unexpected EOF while calculating "
316 "integrity checksums");
318 ERROR_WITH_ERRNO("File stream error while "
319 "calculating integrity "
322 ret = WIMLIB_ERR_READ;
325 sha1_buffer(chunk_buf, bytes_read, p);
327 bytes_remaining -= bytes_read;
330 puts("Calculating integrity checksums for WIM "
331 "(0 bytes remaining, 100% done)"
334 if (fseeko(out, 0, SEEK_END) != 0) {
335 ERROR_WITH_ERRNO("Failed to seek to end of WIM to write "
337 ret = WIMLIB_ERR_WRITE;
341 if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
342 ERROR_WITH_ERRNO("Failed to write integrity table to end of "
344 ret = WIMLIB_ERR_WRITE;