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 General Public License as published by the Free
16 * Software Foundation; either version 3 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 General Public License for more
24 * You should have received a copy of the GNU 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;
122 u32 integrity_table_size;
127 u64 end_lookup_table_offset;
129 u64 expected_num_entries;
131 res_entry = &w->hdr.integrity;
132 if (res_entry->size == 0) {
133 DEBUG("No integrity information.");
134 *status = WIM_INTEGRITY_NONEXISTENT;
137 if (res_entry->original_size < 12) {
138 ERROR("Integrity table is too short");
139 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
141 if (res_entry->flags & WIM_RESHDR_FLAG_COMPRESSED) {
142 ERROR("Didn't expect a compressed integrity table");
143 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
146 /* Read the integrity table into memory. */
147 buf = MALLOC(res_entry->original_size);
149 ERROR("Out of memory (needed %zu bytes for integrity table)",
150 res_entry->original_size);
151 ret = WIMLIB_ERR_NOMEM;
154 ret = read_uncompressed_resource(w->fp, res_entry->offset,
155 res_entry->original_size, buf);
157 ERROR("Failed to read integrity table (size = %"PRIu64", "
158 "original_size = %"PRIu64", offset = "
160 (u64)res_entry->size, res_entry->original_size,
165 p = get_u32(buf, &integrity_table_size);
166 p = get_u32(p, &num_entries);
167 p = get_u32(p, &chunk_size);
169 /* p now points to the array of SHA1 message digests for the WIM. */
171 /* Make sure the integrity table is the right size. */
172 if (integrity_table_size != res_entry->original_size) {
173 ERROR("Inconsistent integrity table sizes: header says %u "
174 "bytes but resource entry says "
176 integrity_table_size, res_entry->original_size);
177 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
181 DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u",
182 integrity_table_size, num_entries, chunk_size);
185 expected_size = num_entries * SHA1_HASH_SIZE + 12;
187 if (integrity_table_size != expected_size) {
188 ERROR("Integrity table is %u bytes, but expected %"PRIu64" "
189 "bytes to hold %u entries",
190 integrity_table_size, expected_size, num_entries);
191 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
195 if (chunk_size == 0) {
196 ERROR("Cannot use integrity chunk size of 0");
197 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
201 end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset +
202 w->hdr.lookup_table_res_entry.size;
204 bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE;
206 expected_num_entries = (bytes_to_check + chunk_size - 1) / chunk_size;
208 if (num_entries != expected_num_entries) {
209 ERROR("%"PRIu64" entries would be required to checksum "
210 "the %"PRIu64" bytes from the end of the header to the",
211 expected_num_entries, bytes_to_check);
212 ERROR("end of the lookup table with a chunk size of %u, but "
213 "there were only %u entries", chunk_size, num_entries);
214 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
218 /* The integrity checking starts after the header, so seek to the offset
219 * in the WIM after the header. */
221 if (fseeko(w->fp, WIM_HEADER_DISK_SIZE, SEEK_SET) != 0) {
222 ERROR_WITH_ERRNO("Failed to seek to byte %u of WIM to check "
223 "integrity", WIM_HEADER_DISK_SIZE);
224 ret = WIMLIB_ERR_READ;
227 /* call verify_integrity(), which does the actual checking of the SHA1
228 * message digests. */
229 ret = verify_integrity(w->fp, bytes_to_check, chunk_size, p,
230 show_progress, status);
237 * Writes integrity information to the output stream for a WIM file being
240 * @end_header_offset is the offset of the byte after the header, which is the
241 * beginning of the region that is checksummed.
243 * @end_lookup_table_offset is the offset of the byte after the lookup table,
244 * which is the end of the region that is checksummed.
246 int write_integrity_table(FILE *out, u64 end_header_offset,
247 u64 end_lookup_table_offset, int show_progress)
255 u32 integrity_table_size;
258 DEBUG("Writing integrity table");
259 if (fseeko(out, end_header_offset, SEEK_SET) != 0) {
260 ERROR_WITH_ERRNO("Failed to seek to byte %"PRIu64" of WIM to "
261 "calculate integrity data", end_header_offset);
262 return WIMLIB_ERR_WRITE;
265 bytes_to_check = end_lookup_table_offset - end_header_offset;
266 num_entries = bytes_to_check / INTEGRITY_CHUNK_SIZE +
267 (bytes_to_check % INTEGRITY_CHUNK_SIZE != 0);
268 integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32);
270 DEBUG("integrity table size = %u", integrity_table_size);
273 buf = MALLOC(integrity_table_size);
275 ERROR("Failed to allocate %u bytes for integrity table",
276 integrity_table_size);
277 return WIMLIB_ERR_NOMEM;
280 p = put_u32(buf, integrity_table_size);
281 p = put_u32(p, num_entries);
282 p = put_u32(p, INTEGRITY_CHUNK_SIZE);
284 chunk_buf = MALLOC(INTEGRITY_CHUNK_SIZE);
286 ERROR("Failed to allocate %u bytes for integrity chunk buffer",
287 INTEGRITY_CHUNK_SIZE);
288 ret = WIMLIB_ERR_NOMEM;
292 bytes_remaining = bytes_to_check;
294 DEBUG("Bytes to check = %"PRIu64, bytes_to_check);
296 while (bytes_remaining != 0) {
298 uint percent_done = (bytes_to_check - bytes_remaining) *
299 100 / bytes_to_check;
302 printf("Calculating integrity checksums for WIM "
303 "(%"PRIu64" bytes remaining, %u%% "
305 bytes_remaining, percent_done);
310 size_t bytes_to_read = min(INTEGRITY_CHUNK_SIZE, bytes_remaining);
311 size_t bytes_read = fread(chunk_buf, 1, bytes_to_read, out);
312 if (bytes_read != bytes_to_read) {
314 ERROR("Unexpected EOF while calculating "
315 "integrity checksums");
317 ERROR_WITH_ERRNO("File stream error while "
318 "calculating integrity "
321 ret = WIMLIB_ERR_READ;
324 sha1_buffer(chunk_buf, bytes_read, p);
326 bytes_remaining -= bytes_read;
329 puts("Calculating integrity checksums for WIM "
330 "(0 bytes remaining, 100% done)"
333 if (fseeko(out, 0, SEEK_END) != 0) {
334 ERROR_WITH_ERRNO("Failed to seek to end of WIM to write "
336 ret = WIMLIB_ERR_WRITE;
340 if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
341 ERROR_WITH_ERRNO("Failed to write integrity table to end of "
343 ret = WIMLIB_ERR_WRITE;