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
46 static int verify_integrity(FILE *fp, u64 num_bytes, u32 chunk_size,
47 const u8 *sha1sums, int show_progress)
50 u8 resblock[SHA1_HASH_SIZE];
56 chunk_buf = MALLOC(chunk_size);
58 ERROR("Failed to allocate %u byte buffer for integrity chunks",
60 return WIMLIB_ERR_NOMEM;
62 bytes_remaining = num_bytes;
63 while (bytes_remaining != 0) {
65 percent_done = (num_bytes - bytes_remaining) * 100 /
67 printf("Verifying integrity of WIM (%"PRIu64" bytes "
68 "remaining, %u%% done) \r",
69 bytes_remaining, percent_done);
72 bytes_to_read = min(chunk_size, bytes_remaining);
73 if (fread(chunk_buf, 1, bytes_to_read, fp) != bytes_to_read) {
75 ERROR("Unexpected EOF while verifying "
78 ERROR_WITH_ERRNO("File stream error while "
79 "verifying integrity of WIM");
81 ret = WIMLIB_ERR_READ;
84 sha1_buffer(chunk_buf, bytes_to_read, resblock);
85 if (!hashes_equal(resblock, sha1sums)) {
86 ret = WIM_INTEGRITY_NOT_OK;
89 sha1sums += SHA1_HASH_SIZE;
90 bytes_remaining -= bytes_to_read;
92 ret = WIM_INTEGRITY_OK;
101 * Verifies the integrity of the WIM.
103 int check_wim_integrity(WIMStruct *w, int show_progress)
106 struct resource_entry *res_entry;
109 u32 integrity_table_size;
114 u64 end_lookup_table_offset;
116 u64 expected_num_entries;
118 res_entry = &w->hdr.integrity;
119 if (res_entry->size == 0) {
120 DEBUG("No integrity information.");
121 return WIM_INTEGRITY_NONEXISTENT;
123 if (res_entry->original_size < 12) {
124 ERROR("Integrity table is too short");
125 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
127 if (res_entry->flags & WIM_RESHDR_FLAG_COMPRESSED) {
128 ERROR("Didn't expect a compressed integrity table");
129 return WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
132 /* Read the integrity table into memory. */
133 if ((sizeof(size_t) < sizeof(u64)
134 && res_entry->original_size > ~(size_t)0)
135 || ((buf = MALLOC(res_entry->original_size)) == NULL))
137 ERROR("Out of memory (needed %zu bytes for integrity table)",
138 (size_t)res_entry->original_size);
139 ret = WIMLIB_ERR_NOMEM;
142 ret = read_uncompressed_resource(w->fp, res_entry->offset,
143 res_entry->original_size, buf);
145 ERROR("Failed to read integrity table (size = %"PRIu64", "
146 "original_size = %"PRIu64", offset = "
148 (u64)res_entry->size, res_entry->original_size,
153 p = get_u32(buf, &integrity_table_size);
154 p = get_u32(p, &num_entries);
155 p = get_u32(p, &chunk_size);
157 /* p now points to the array of SHA1 message digests for the WIM. */
159 /* Make sure the integrity table is the right size. */
160 if (integrity_table_size != res_entry->original_size) {
161 ERROR("Inconsistent integrity table sizes: header says %u "
162 "bytes but resource entry says "
164 integrity_table_size, res_entry->original_size);
165 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
169 DEBUG("integrity_table_size = %u, num_entries = %u, chunk_size = %u",
170 integrity_table_size, num_entries, chunk_size);
173 expected_size = num_entries * SHA1_HASH_SIZE + 12;
175 if (integrity_table_size != expected_size) {
176 ERROR("Integrity table is %u bytes, but expected %"PRIu64" "
177 "bytes to hold %u entries",
178 integrity_table_size, expected_size, num_entries);
179 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
183 if (chunk_size == 0) {
184 ERROR("Cannot use integrity chunk size of 0");
185 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
189 end_lookup_table_offset = w->hdr.lookup_table_res_entry.offset +
190 w->hdr.lookup_table_res_entry.size;
192 if (end_lookup_table_offset < WIM_HEADER_DISK_SIZE) {
193 ERROR("WIM lookup table ends before WIM header ends???");
194 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
198 bytes_to_check = end_lookup_table_offset - WIM_HEADER_DISK_SIZE;
200 expected_num_entries = (bytes_to_check + chunk_size - 1) / chunk_size;
202 if (num_entries != expected_num_entries) {
203 ERROR("%"PRIu64" entries would be required to checksum "
204 "the %"PRIu64" bytes from the end of the header to the",
205 expected_num_entries, bytes_to_check);
206 ERROR("end of the lookup table with a chunk size of %u, but "
207 "there were only %u entries", chunk_size, num_entries);
208 ret = WIMLIB_ERR_INVALID_INTEGRITY_TABLE;
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_WITH_ERRNO("Failed to seek to byte %u of WIM to check "
217 "integrity", WIM_HEADER_DISK_SIZE);
218 ret = WIMLIB_ERR_READ;
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,
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,
242 struct resource_entry *out_res_entry)
250 u32 integrity_table_size;
254 start_offset = ftello(out);
255 if (start_offset == -1)
256 return WIMLIB_ERR_WRITE;
258 DEBUG("Calculating 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 - 1) /
267 INTEGRITY_CHUNK_SIZE;
268 integrity_table_size = num_entries * SHA1_HASH_SIZE + 3 * sizeof(u32);
270 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;
321 goto out_free_chunk_buf;
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, start_offset, SEEK_SET) != 0) {
333 ERROR_WITH_ERRNO("Failed to seek to end of WIM");
334 ret = WIMLIB_ERR_WRITE;
335 goto out_free_chunk_buf;
338 if (fwrite(buf, 1, integrity_table_size, out) != integrity_table_size) {
339 ERROR_WITH_ERRNO("Failed to write integrity table to end of "
341 ret = WIMLIB_ERR_WRITE;
342 goto out_free_chunk_buf;
345 out_res_entry->offset = start_offset;
346 out_res_entry->size = integrity_table_size;
347 out_res_entry->original_size = integrity_table_size;
348 out_res_entry->flags = 0;