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 ctype = wim_resource_compression_type(w, res_entry);
139 if (res_entry->original_size < 12) {
140 ERROR("Integrity table is too short");
141 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
144 /* Read the integrity table into memory. */
145 buf = MALLOC(res_entry->original_size);
147 ERROR("Out of memory (needed %zu bytes for integrity table)",
148 res_entry->original_size);
149 ret = WIMLIB_ERR_NOMEM;
152 ret = read_full_resource(w->fp, res_entry->size,
153 res_entry->original_size,
154 res_entry->offset, ctype, buf);
156 ERROR("Failed to read integrity table (size = %"PRIu64", "
157 "original_size = %"PRIu64", offset = "
158 "%"PRIu64", ctype = %d",
159 (u64)res_entry->size, res_entry->original_size,
160 res_entry->offset, ctype);
164 p = get_u32(buf, &integrity_table_size);
165 p = get_u32(p, &num_entries);
166 p = get_u32(p, &chunk_size);
168 /* p now points to the array of SHA1 message digests for the WIM. */
170 /* Make sure the integrity table is the right size. */
171 if (integrity_table_size != res_entry->original_size) {
172 ERROR("Inconsistent integrity table sizes: header says %u "
173 "bytes but resource entry says "
175 integrity_table_size, res_entry->original_size);
176 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
180 DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u",
181 integrity_table_size, num_entries, chunk_size);
184 expected_size = num_entries * SHA1_HASH_SIZE + 12;
186 if (integrity_table_size != expected_size) {
187 ERROR("Integrity table is %u bytes, but expected %"PRIu64" "
188 "bytes to hold %u entries",
189 integrity_table_size, expected_size, num_entries);
190 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
194 if (chunk_size == 0) {
195 ERROR("Cannot use integrity chunk size of 0");
196 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
200 end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset +
201 w->hdr.lookup_table_res_entry.size;
203 bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE;
205 expected_num_entries = (bytes_to_check + chunk_size - 1) / chunk_size;
207 if (num_entries != expected_num_entries) {
208 ERROR("%"PRIu64" entries would be required to checksum "
209 "the %"PRIu64" bytes from the end of the header to the",
210 expected_num_entries, bytes_to_check);
211 ERROR("end of the lookup table with a chunk size of %u, but "
212 "there were only %u entries", chunk_size, num_entries);
213 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
217 /* The integrity checking starts after the header, so seek to the offset
218 * in the WIM after the header. */
220 if (fseeko(w->fp, WIM_HEADER_DISK_SIZE, SEEK_SET) != 0) {
221 ERROR_WITH_ERRNO("Failed to seek to byte %u of WIM to check "
222 "integrity", WIM_HEADER_DISK_SIZE);
223 ret = WIMLIB_ERR_READ;
226 /* call verify_integrity(), which does the actual checking of the SHA1
227 * message digests. */
228 ret = verify_integrity(w->fp, bytes_to_check, chunk_size, p,
229 show_progress, status);
236 * Writes integrity information to the output stream for a WIM file being
239 * @end_header_offset is the offset of the byte after the header, which is the
240 * beginning of the region that is checksummed.
242 * @end_lookup_table_offset is the offset of the byte after the lookup table,
243 * which is the end of the region that is checksummed.
245 int write_integrity_table(FILE *out, u64 end_header_offset,
246 u64 end_lookup_table_offset, int show_progress)
254 u32 integrity_table_size;
257 DEBUG("Writing integrity table");
258 if (fseeko(out, end_header_offset, SEEK_SET) != 0) {
259 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to "
260 "calculate integrity data", end_header_offset);
261 return WIMLIB_ERR_WRITE;
264 bytes_to_check = end_lookup_table_offset - end_header_offset;
265 num_entries = bytes_to_check / INTEGRITY_CHUNK_SIZE +
266 (bytes_to_check % INTEGRITY_CHUNK_SIZE != 0);
267 integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32);
269 DEBUG("integrity table size = %u", integrity_table_size);
272 buf = MALLOC(integrity_table_size);
274 ERROR("Failed to allocate %u bytes for integrity table",
275 integrity_table_size);
276 return WIMLIB_ERR_NOMEM;
279 p = put_u32(buf, integrity_table_size);
280 p = put_u32(p, num_entries);
281 p = put_u32(p, INTEGRITY_CHUNK_SIZE);
283 chunk_buf = MALLOC(INTEGRITY_CHUNK_SIZE);
285 ERROR("Failed to allocate %u bytes for integrity chunk buffer",
286 INTEGRITY_CHUNK_SIZE);
287 ret = WIMLIB_ERR_NOMEM;
291 bytes_remaining = bytes_to_check;
293 DEBUG("Bytes to check = %"PRIu64, bytes_to_check);
295 while (bytes_remaining != 0) {
297 uint percent_done = (bytes_to_check - bytes_remaining) *
298 100 / bytes_to_check;
301 printf("Calculating integrity checksums for WIM "
302 "(%"PRIu64" bytes remaining, %u%% "
304 bytes_remaining, percent_done);
309 size_t bytes_to_read = min(INTEGRITY_CHUNK_SIZE, bytes_remaining);
310 size_t bytes_read = fread(chunk_buf, 1, bytes_to_read, out);
311 if (bytes_read != bytes_to_read) {
313 ERROR("Unexpected EOF while calculating "
314 "integrity checksums");
316 ERROR_WITH_ERRNO("File stream error while "
317 "calculating integrity "
320 ret = WIMLIB_ERR_READ;
323 sha1_buffer(chunk_buf, bytes_read, p);
325 bytes_remaining -= bytes_read;
328 puts("Calculating integrity checksums for WIM "
329 "(0 bytes remaining, 100% done)"
332 if (fseeko(out, 0, SEEK_END) != 0) {
333 ERROR_WITH_ERRNO("Failed to seek to end of WIM to write "
335 ret = WIMLIB_ERR_WRITE;
339 if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
340 ERROR_WITH_ERRNO("Failed to write integrity table to end of "
342 ret = WIMLIB_ERR_WRITE;