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.
8 * Copyright (C) 2012 Eric Biggers
10 * wimlib - Library for working with WIM files
12 * This library is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU Lesser General Public License as published by the Free
14 * Software Foundation; either version 2.1 of the License, or (at your option) any
17 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License along
22 * with this library; if not, write to the Free Software Foundation, Inc., 59
23 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "wimlib_internal.h"
30 /* Size, in bytes, of each SHA1-summed chunk, when wimlib writes integrity
32 #define INTEGRITY_CHUNK_SIZE 10485760
35 * Verifies the integrity of a WIM.
37 * @fp: FILE* of the WIM, currently positioned at the end of the header.
38 * @num_bytes: Number of bytes to verify the integrity of.
39 * @chunk_size: Chunk size per SHA1 message digest.
40 * @sha1sums: Array of SHA1 message digests; 20 bytes each, one per chunk.
41 * @show_progress: Nonzero if the percent complete is to be printed after every
43 * @status: On success, set to WIM_INTEGRITY_OK or WIM_INTEGRITY_NOT_OK
44 * based on whether the WIM is intact or not.
46 static int verify_integrity(FILE *fp, u64 num_bytes, u32 chunk_size,
47 const u8 *sha1sums, int show_progress,
51 u8 resblock[WIM_HASH_SIZE];
57 chunk_buf = MALLOC(chunk_size);
59 ERROR("Failed to allocate %u byte buffer for integrity "
60 "chunks\n", chunk_size);
61 return WIMLIB_ERR_NOMEM;
63 bytes_remaining = num_bytes;
64 while (bytes_remaining != 0) {
66 percent_done = (num_bytes - bytes_remaining) * 100 /
68 printf("Verifying integrity of WIM (%"PRIu64" bytes "
69 "remaining, %u%% done) \r",
70 bytes_remaining, percent_done);
73 bytes_to_read = min(chunk_size, bytes_remaining);
74 if (fread(chunk_buf, 1, bytes_to_read, fp) != bytes_to_read) {
76 ERROR("Unexpected EOF while verifying "
77 "integrity of WIM!\n");
79 ERROR("File stream error while verifying "
80 "integrity of WIM: %m\n");
82 ret = WIMLIB_ERR_READ;
83 goto verify_integrity_error;
85 sha1_buffer(chunk_buf, bytes_to_read, resblock);
86 if (memcmp(resblock, sha1sums, WIM_HASH_SIZE) != 0) {
87 *status = WIM_INTEGRITY_NOT_OK;
88 goto verify_integrity_done;
90 sha1sums += WIM_HASH_SIZE;
91 bytes_remaining -= bytes_to_read;
93 *status = WIM_INTEGRITY_OK;
94 verify_integrity_done:
96 verify_integrity_error:
104 * Verifies the integrity of the WIM.
106 * @show_progress: Nonzero if the percent complete is to be printed after every
108 * @status: On success, set to WIM_INTEGRITY_OK, WIM_INTEGRITY_NOT_OK,
109 * or WIM_INTEGRITY_NONEXISTENT.
111 * Returns: 0, WIMLIB_ERR_INVALID_INTEGRITY_TABLE, WIMLIB_ERR_NOMEM, or
112 * WIMLIB_ERR_READ. If nonzero, the boolean pointed to by @ok is not changed.
114 int check_wim_integrity(WIMStruct *w, int show_progress, int *status)
117 struct resource_entry *res_entry;
121 u32 integrity_table_size;
126 u64 end_lookup_table_offset;
128 u64 expected_num_entries;
130 res_entry = &w->hdr.integrity;
131 if (res_entry->size == 0) {
132 DEBUG("No integrity information.\n");
133 *status = WIM_INTEGRITY_NONEXISTENT;
136 ctype = wim_resource_compression_type(w, res_entry);
137 if (res_entry->original_size < 12) {
138 ERROR("Integrity table resource is too short!\n");
139 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
142 /* Read the integrity table into memory. */
143 buf = MALLOC(res_entry->original_size);
145 ERROR("Out of memory (needed %zu bytes for integrity table)!\n",
146 res_entry->original_size);
147 ret = WIMLIB_ERR_NOMEM;
148 goto check_integrity_error;
150 ret = read_full_resource(w->fp, res_entry->size, res_entry->original_size,
151 res_entry->offset, ctype, buf);
153 ERROR("Failed to read integrity table (size = %"PRIu64", "
154 "original_size = %"PRIu64", offset = "
155 "%"PRIu64", ctype = %d\n",
156 (u64)res_entry->size, res_entry->original_size,
157 res_entry->offset, ctype);
158 goto check_integrity_error;
161 p = get_u32(buf, &integrity_table_size);
162 p = get_u32(p, &num_entries);
163 p = get_u32(p, &chunk_size);
165 /* p now points to the array of SHA1 message digests for the WIM. */
167 /* Make sure the integrity table is the right size. */
168 if (integrity_table_size != res_entry->original_size) {
169 ERROR("Inconsistent integrity table sizes: header says %u "
170 "bytes but resource entry says "
171 "%"PRIu64" bytes\n", integrity_table_size,
172 res_entry->original_size);
174 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
175 goto check_integrity_error;
178 DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u\n",
179 integrity_table_size, num_entries, chunk_size);
182 expected_size = num_entries * WIM_HASH_SIZE + 12;
184 if (integrity_table_size != expected_size) {
185 ERROR("Integrity table is %u bytes, but expected %"PRIu64" "
186 "bytes to hold %u entries!\n",
187 integrity_table_size,
188 expected_size, num_entries);
189 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
190 goto check_integrity_error;
193 end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset +
194 w->hdr.lookup_table_res_entry.size;
196 bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE;
198 expected_num_entries = bytes_to_check / chunk_size +
199 (bytes_to_check % chunk_size != 0);
201 if (num_entries != expected_num_entries) {
202 ERROR("%"PRIu64 " entries would be required to checksum "
203 "the %"PRIu64" bytes from the end of the header to the\n"
204 "end of the lookup table with a chunk size of %u, but "
205 "there were only %u entries!\n",
206 expected_num_entries, bytes_to_check, chunk_size,
208 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
209 goto check_integrity_error;
212 /* The integrity checking starts after the header, so seek to the offset
213 * in the WIM after the header. */
215 if (fseeko(w->fp, WIM_HEADER_DISK_SIZE, SEEK_SET) != 0) {
216 ERROR("Failed to seek to byte %u of WIM to check "
217 "integrity: %m\n", WIM_HEADER_DISK_SIZE);
218 ret = WIMLIB_ERR_READ;
219 goto check_integrity_error;
221 /* call verify_integrity(), which does the actual checking of the SHA1
222 * message digests. */
223 ret = verify_integrity(w->fp, bytes_to_check, chunk_size, p,
224 show_progress, status);
225 check_integrity_error:
231 * Writes integrity information to the output stream for a WIM file being
234 * @end_header_offset is the offset of the byte after the header, which is the
235 * beginning of the region that is checksummed.
237 * @end_lookup_table_offset is the offset of the byte after the lookup table,
238 * which is the end of the region that is checksummed.
240 int write_integrity_table(FILE *out, u64 end_header_offset,
241 u64 end_lookup_table_offset, int show_progress)
249 u32 integrity_table_size;
252 DEBUG("Writing integrity table\n");
253 if (fseeko(out, end_header_offset, SEEK_SET) != 0) {
254 ERROR("Failed to seek to byte %"PRIu64" of WIM "
255 "to calculate integrity data: %m\n",
257 return WIMLIB_ERR_WRITE;
260 bytes_to_check = end_lookup_table_offset - end_header_offset;
261 num_entries = bytes_to_check / INTEGRITY_CHUNK_SIZE +
262 (bytes_to_check % INTEGRITY_CHUNK_SIZE != 0);
263 integrity_table_size = num_entries * WIM_HASH_SIZE + 3 * sizeof(u32);
265 DEBUG("integrity table size = %u\n", integrity_table_size);
268 buf = MALLOC(integrity_table_size);
270 ERROR("Failed to allocate %u bytes for integrity table!\n",
271 integrity_table_size);
272 return WIMLIB_ERR_NOMEM;
275 p = put_u32(buf, integrity_table_size);
276 p = put_u32(p, num_entries);
277 p = put_u32(p, INTEGRITY_CHUNK_SIZE);
279 chunk_buf = MALLOC(INTEGRITY_CHUNK_SIZE);
281 ERROR("Failed to allocate %u bytes for integrity chunk "
282 "buffer!\n", INTEGRITY_CHUNK_SIZE);
283 ret = WIMLIB_ERR_NOMEM;
287 bytes_remaining = bytes_to_check;
289 DEBUG("Bytes to check = %"PRIu64"\n", bytes_to_check);
291 while (bytes_remaining != 0) {
293 uint percent_done = (bytes_to_check - bytes_remaining) *
294 100 / bytes_to_check;
297 printf("Calculating integrity checksums for WIM "
298 "(%"PRIu64" bytes remaining, %u%% "
300 bytes_remaining, percent_done);
305 size_t bytes_to_read = min(INTEGRITY_CHUNK_SIZE, bytes_remaining);
306 size_t bytes_read = fread(chunk_buf, 1, bytes_to_read, out);
307 if (bytes_read != bytes_to_read) {
309 ERROR("Unexpected EOF while calculating "
310 "integrity checksums!\n");
312 ERROR("File stream error while calculating "
313 "integrity checksums: %m\n");
315 ret = WIMLIB_ERR_READ;
318 sha1_buffer(chunk_buf, bytes_read, p);
320 bytes_remaining -= bytes_read;
323 puts("Calculating integrity checksums for WIM "
324 "(0 bytes remaining, 100% done)"
327 if (fseeko(out, 0, SEEK_END) != 0) {
328 ERROR("Failed to seek to end of WIM to write integrity "
330 ret = WIMLIB_ERR_WRITE;
334 if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
335 ERROR("Failed to write integrity table to end of WIM: %m\n");
336 ret = WIMLIB_ERR_WRITE;