libaaruformat 1.0 Aaru Data Preservation Suite - Format Library

Loading...
Searching...
No Matches
verify.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2025 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <aaruformat.h>
20#include <inttypes.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24#include "internal.h"
25#include "log.h"
26#include "utarray.h"
27
28#define VERIFY_SIZE 1048576
29
30 static int32_t update_crc64_from_stream(FILE *stream, const uint64_t total_length, void *buffer, size_t buffer_size,
31 crc64_ctx *crc_ctx, const char *label)
32{
33 uint64_t processed = 0;
34
35 while(processed < total_length)
36 {
37 size_t chunk = buffer_size;
38 uint64_t remaining = total_length - processed;
39
40 if(remaining < chunk) chunk = (size_t)remaining;
41
42 if(chunk == 0) break;
43
44 size_t read_bytes = fread(buffer, 1, chunk, stream);
45 if(read_bytes != chunk)
46 {
47 FATAL("Could not read %s chunk (expected %zu bytes, got %zu)", label, chunk, read_bytes);
48 return AARUF_ERROR_CANNOT_READ_BLOCK;
49 }
50
51 aaruf_crc64_update(crc_ctx, buffer, read_bytes);
52 processed += read_bytes;
53 }
54
55 return AARUF_STATUS_OK;
56}
57
130 AARU_EXPORT int32_t AARU_CALL aaruf_verify_image(void *context)
131{
132 TRACE("Entering aaruf_verify_image(%p)", context);
133
134 aaruformat_context *ctx = NULL;
135 uint64_t crc64 = 0;
136 size_t read_bytes = 0;
137 void *buffer = NULL;
138 crc64_ctx *crc64_context = NULL;
139 BlockHeader block_header;
140 DdtHeader ddt_header;
141 DdtHeader2 ddt2_header;
142 TracksHeader tracks_header;
143 uint32_t signature = 0;
144 UT_array *index_entries = NULL;
145 int32_t status = AARUF_STATUS_OK;
146
147 if(context == NULL)
148 {
149 FATAL("Invalid context");
150 status = AARUF_ERROR_NOT_AARUFORMAT;
151 goto cleanup;
152 }
153
154 ctx = context;
155
156 if(ctx->magic != AARU_MAGIC)
157 {
158 FATAL("Invalid context");
159 status = AARUF_ERROR_NOT_AARUFORMAT;
160 goto cleanup;
161 }
162
163 if(ctx->imageStream == NULL)
164 {
165 FATAL("Image stream is not available");
166 status = AARUF_ERROR_NOT_AARUFORMAT;
167 goto cleanup;
168 }
169
170 if(fseek(ctx->imageStream, ctx->header.indexOffset, SEEK_SET) != 0)
171 {
172 FATAL("Could not seek to index offset %" PRIu64, ctx->header.indexOffset);
173 status = AARUF_ERROR_CANNOT_READ_HEADER;
174 goto cleanup;
175 }
176
177 TRACE("Reading index signature at position %" PRIu64, ctx->header.indexOffset);
178 read_bytes = fread(&signature, 1, sizeof(signature), ctx->imageStream);
179 if(read_bytes != sizeof(signature))
180 {
181 FATAL("Could not read index signature");
182 status = AARUF_ERROR_CANNOT_READ_HEADER;
183 goto cleanup;
184 }
185
186 if(signature != IndexBlock && signature != IndexBlock2 && signature != IndexBlock3)
187 {
188 FATAL("Incorrect index signature %4.4s", (char *)&signature);
189 status = AARUF_ERROR_CANNOT_READ_INDEX;
190 goto cleanup;
191 }
192
193 if(signature == IndexBlock)
194 status = verify_index_v1(ctx);
195 else if(signature == IndexBlock2)
196 status = verify_index_v2(ctx);
197 else
198 status = verify_index_v3(ctx);
199
200 if(status != AARUF_STATUS_OK)
201 {
202 FATAL("Index verification failed with error code %d", status);
203 goto cleanup;
204 }
205
206 if(signature == IndexBlock)
207 index_entries = process_index_v1(ctx);
208 else if(signature == IndexBlock2)
209 index_entries = process_index_v2(ctx);
210 else
211 index_entries = process_index_v3(ctx);
212
213 if(index_entries == NULL)
214 {
215 FATAL("Could not process index");
216 status = AARUF_ERROR_CANNOT_READ_INDEX;
217 goto cleanup;
218 }
219
220 buffer = malloc(VERIFY_SIZE);
221 if(buffer == NULL)
222 {
223 FATAL("Cannot allocate memory for verification buffer");
224 status = AARUF_ERROR_NOT_ENOUGH_MEMORY;
225 goto cleanup;
226 }
227
228 {
229 const unsigned int entry_count = utarray_len(index_entries);
230
231 for(unsigned int i = 0; i < entry_count; i++)
232 {
233 IndexEntry *entry = utarray_eltptr(index_entries, i);
234 TRACE("Checking block with type %4.4s at position %" PRIu64, (char *)&entry->blockType, entry->offset);
235
236 if(fseek(ctx->imageStream, entry->offset, SEEK_SET) != 0)
237 {
238 FATAL("Could not seek to block at offset %" PRIu64, entry->offset);
239 status = AARUF_ERROR_CANNOT_READ_BLOCK;
240 goto cleanup;
241 }
242
243 switch(entry->blockType)
244 {
245 case DataBlock:
246 read_bytes = fread(&block_header, 1, sizeof(BlockHeader), ctx->imageStream);
247 if(read_bytes != sizeof(BlockHeader))
248 {
249 FATAL("Could not read block header");
250 status = AARUF_ERROR_CANNOT_READ_BLOCK;
251 goto cleanup;
252 }
253
254 crc64_context = aaruf_crc64_init();
255 if(crc64_context == NULL)
256 {
257 FATAL("Could not initialize CRC64 context");
258 status = AARUF_ERROR_CANNOT_READ_BLOCK;
259 goto cleanup;
260 }
261
262 status = update_crc64_from_stream(ctx->imageStream, block_header.cmpLength, buffer, VERIFY_SIZE,
263 crc64_context, "data block");
264 if(status != AARUF_STATUS_OK) goto cleanup;
265
266 if(aaruf_crc64_final(crc64_context, &crc64) != 0)
267 {
268 FATAL("Could not finalize CRC64 for data block");
269 status = AARUF_ERROR_CANNOT_READ_BLOCK;
270 goto cleanup;
271 }
272
273 if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
274
275 if(crc64 != block_header.cmpCrc64)
276 {
277 FATAL("Expected block CRC 0x%16llX but got 0x%16llX", block_header.cmpCrc64, crc64);
278 status = AARUF_ERROR_INVALID_BLOCK_CRC;
279 goto cleanup;
280 }
281
282 aaruf_crc64_free(crc64_context);
283 crc64_context = NULL;
284 break;
285 case DeDuplicationTable:
286 read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader), ctx->imageStream);
287 if(read_bytes != sizeof(DdtHeader))
288 {
289 FATAL("Could not read DDT header");
290 status = AARUF_ERROR_CANNOT_READ_BLOCK;
291 goto cleanup;
292 }
293
294 crc64_context = aaruf_crc64_init();
295 if(crc64_context == NULL)
296 {
297 FATAL("Could not initialize CRC64 context");
298 status = AARUF_ERROR_CANNOT_READ_BLOCK;
299 goto cleanup;
300 }
301
302 status = update_crc64_from_stream(ctx->imageStream, ddt_header.cmpLength, buffer, VERIFY_SIZE,
303 crc64_context, "DDT block");
304 if(status != AARUF_STATUS_OK) goto cleanup;
305
306 if(aaruf_crc64_final(crc64_context, &crc64) != 0)
307 {
308 FATAL("Could not finalize CRC64 for DDT block");
309 status = AARUF_ERROR_CANNOT_READ_BLOCK;
310 goto cleanup;
311 }
312
313 if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
314
315 if(crc64 != ddt_header.cmpCrc64)
316 {
317 FATAL("Expected DDT CRC 0x%16llX but got 0x%16llX", ddt_header.cmpCrc64, crc64);
318 status = AARUF_ERROR_INVALID_BLOCK_CRC;
319 goto cleanup;
320 }
321
322 aaruf_crc64_free(crc64_context);
323 crc64_context = NULL;
324 break;
325 case DeDuplicationTable2:
326 read_bytes = fread(&ddt2_header, 1, sizeof(DdtHeader2), ctx->imageStream);
327 if(read_bytes != sizeof(DdtHeader2))
328 {
329 FATAL("Could not read DDT2 header");
330 status = AARUF_ERROR_CANNOT_READ_BLOCK;
331 goto cleanup;
332 }
333
334 crc64_context = aaruf_crc64_init();
335 if(crc64_context == NULL)
336 {
337 FATAL("Could not initialize CRC64 context");
338 status = AARUF_ERROR_CANNOT_READ_BLOCK;
339 goto cleanup;
340 }
341
342 status = update_crc64_from_stream(ctx->imageStream, ddt2_header.cmpLength, buffer, VERIFY_SIZE,
343 crc64_context, "DDT2 block");
344 if(status != AARUF_STATUS_OK) goto cleanup;
345
346 if(aaruf_crc64_final(crc64_context, &crc64) != 0)
347 {
348 FATAL("Could not finalize CRC64 for DDT2 block");
349 status = AARUF_ERROR_CANNOT_READ_BLOCK;
350 goto cleanup;
351 }
352
353 if(crc64 != ddt2_header.cmpCrc64)
354 {
355 FATAL("Expected DDT2 CRC 0x%16llX but got 0x%16llX", ddt2_header.cmpCrc64, crc64);
356 status = AARUF_ERROR_INVALID_BLOCK_CRC;
357 goto cleanup;
358 }
359
360 aaruf_crc64_free(crc64_context);
361 crc64_context = NULL;
362 break;
363 case TracksBlock:
364 {
365 read_bytes = fread(&tracks_header, 1, sizeof(TracksHeader), ctx->imageStream);
366 if(read_bytes != sizeof(TracksHeader))
367 {
368 FATAL("Could not read tracks header");
369 status = AARUF_ERROR_CANNOT_READ_BLOCK;
370 goto cleanup;
371 }
372
373 const uint64_t tracks_bytes = (uint64_t)tracks_header.entries * sizeof(TrackEntry);
374 if(tracks_header.entries != 0 && tracks_bytes / sizeof(TrackEntry) != tracks_header.entries)
375 {
376 FATAL("Tracks header length overflow (entries=%u)", tracks_header.entries);
377 status = AARUF_ERROR_CANNOT_READ_BLOCK;
378 goto cleanup;
379 }
380
381 crc64_context = aaruf_crc64_init();
382 if(crc64_context == NULL)
383 {
384 FATAL("Could not initialize CRC64 context");
385 status = AARUF_ERROR_CANNOT_READ_BLOCK;
386 goto cleanup;
387 }
388
389 status = update_crc64_from_stream(ctx->imageStream, tracks_bytes, buffer, VERIFY_SIZE,
390 crc64_context, "tracks block");
391 if(status != AARUF_STATUS_OK) goto cleanup;
392
393 if(aaruf_crc64_final(crc64_context, &crc64) != 0)
394 {
395 FATAL("Could not finalize CRC64 for tracks block");
396 status = AARUF_ERROR_CANNOT_READ_BLOCK;
397 goto cleanup;
398 }
399
400 if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
401
402 if(crc64 != tracks_header.crc64)
403 {
404 FATAL("Expected tracks CRC 0x%16llX but got 0x%16llX", tracks_header.crc64, crc64);
405 status = AARUF_ERROR_INVALID_BLOCK_CRC;
406 goto cleanup;
407 }
408
409 aaruf_crc64_free(crc64_context);
410 crc64_context = NULL;
411 break;
412 }
413 default:
414 TRACE("Ignoring block type %4.4s", (char *)&entry->blockType);
415 break;
416 }
417 }
418 }
419
420 status = AARUF_STATUS_OK;
421
422cleanup:
423 if(crc64_context != NULL)
424 {
425 aaruf_crc64_free(crc64_context);
426 crc64_context = NULL;
427 }
428
429 if(buffer != NULL)
430 {
431 free(buffer);
432 buffer = NULL;
433 }
434
435 if(index_entries != NULL)
436 {
437 utarray_free(index_entries);
438 index_entries = NULL;
439 }
440
441 TRACE("Exiting aaruf_verify_image() = %d", status);
442 return status;
443}
aaruformat.h
AARU_MAGIC
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
AARUF_VERSION_V1
#define AARUF_VERSION_V1
First on‑disk version (C# implementation).
Definition consts.h:71
AARU_CALL
#define AARU_CALL
Definition decls.h:45
aaruf_crc64_update
int aaruf_crc64_update(crc64_ctx *ctx, const uint8_t *data, uint32_t len)
Updates the CRC64 context with new data.
Definition crc64.c:55
aaruf_crc64_free
void aaruf_crc64_free(crc64_ctx *ctx)
Frees a CRC64 context.
Definition crc64.c:155
aaruf_crc64_init
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:54
aaruf_crc64_final
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
bswap_64
#define bswap_64(x)
Definition endian.h:81
IndexBlock3
@ IndexBlock3
Block containing the index v3.
Definition enums.h:147
DataBlock
@ DataBlock
Block containing data.
Definition enums.h:141
IndexBlock2
@ IndexBlock2
Block containing the index v2.
Definition enums.h:146
IndexBlock
@ IndexBlock
Block containing the index (v1).
Definition enums.h:145
DeDuplicationTable2
@ DeDuplicationTable2
Block containing a deduplication table v2.
Definition enums.h:143
DeDuplicationTable
@ DeDuplicationTable
Block containing a deduplication table (v1).
Definition enums.h:142
TracksBlock
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:150
AARUF_STATUS_OK
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:75
AARUF_ERROR_CANNOT_READ_HEADER
#define AARUF_ERROR_CANNOT_READ_HEADER
Failed to read container header.
Definition errors.h:45
AARUF_ERROR_NOT_ENOUGH_MEMORY
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
AARUF_ERROR_CANNOT_READ_BLOCK
#define AARUF_ERROR_CANNOT_READ_BLOCK
Generic block read failure (seek/read error).
Definition errors.h:46
AARUF_ERROR_INVALID_BLOCK_CRC
#define AARUF_ERROR_INVALID_BLOCK_CRC
CRC64 mismatch indicating corruption.
Definition errors.h:57
AARUF_ERROR_CANNOT_READ_INDEX
#define AARUF_ERROR_CANNOT_READ_INDEX
Index block unreadable / truncated / bad identifier.
Definition errors.h:43
AARUF_ERROR_NOT_AARUFORMAT
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
internal.h
process_index_v2
UT_array * process_index_v2(aaruformat_context *ctx)
Processes an index block (version 2) from the image stream.
Definition index_v2.c:81
process_index_v1
UT_array * process_index_v1(aaruformat_context *ctx)
Processes an index block (version 1) from the image stream.
Definition index_v1.c:79
verify_index_v1
int32_t verify_index_v1(aaruformat_context *ctx)
Verifies the integrity of an index block (version 1) in the image stream.
Definition index_v1.c:225
verify_index_v3
int32_t verify_index_v3(aaruformat_context *ctx)
Verifies the integrity of an index block (version 3) in the image stream.
Definition index_v3.c:408
verify_index_v2
int32_t verify_index_v2(aaruformat_context *ctx)
Verifies the integrity of an index block (version 2) in the image stream.
Definition index_v2.c:227
process_index_v3
UT_array * process_index_v3(aaruformat_context *ctx)
Processes an index block (version 3) from the image stream.
Definition index_v3.c:98
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
AaruHeaderV2::indexOffset
uint64_t indexOffset
Absolute byte offset to primary index block (MUST be > 0; 0 => corrupt/unreadable).
Definition header.h:115
AaruHeaderV2::imageMajorVersion
uint8_t imageMajorVersion
Container format major version.
Definition header.h:110
BlockHeader
Header preceding the compressed data payload of a data block (BlockType::DataBlock).
Definition data.h:71
BlockHeader::cmpLength
uint32_t cmpLength
Size in bytes of the compressed payload immediately following this header.
Definition data.h:76
BlockHeader::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload (cmpLength bytes).
Definition data.h:78
DdtHeader2
Header preceding a version 2 hierarchical deduplication table.
Definition ddt.h:142
DdtHeader2::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of compressed table payload.
Definition ddt.h:161
DdtHeader2::cmpLength
uint64_t cmpLength
Compressed payload size in bytes.
Definition ddt.h:159
DdtHeader
Header preceding a version 1 (flat) deduplication table body.
Definition ddt.h:66
DdtHeader::cmpLength
uint64_t cmpLength
Size in bytes of compressed entries payload.
Definition ddt.h:72
DdtHeader::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload.
Definition ddt.h:74
IndexEntry
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
IndexEntry::blockType
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
IndexEntry::offset
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
TrackEntry
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
TracksHeader
Header for an optical tracks block listing track entries.
Definition optical.h:62
TracksHeader::entries
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
TracksHeader::crc64
uint64_t crc64
CRC64-ECMA of the TrackEntry array (header excluded, legacy byte-swap for early versions).
Definition optical.h:65
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:172
aaruformat_context::header
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:175
aaruformat_context::magic
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:174
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:176
crc64_ctx
Minimal ECMA-182 CRC64 incremental state container (running value only).
Definition crc64.h:56
VERIFY_SIZE
#define VERIFY_SIZE
Definition verify.c:28
aaruf_verify_image
int32_t aaruf_verify_image(void *context)
Verifies the integrity of an AaruFormat image file.
Definition verify.c:130
update_crc64_from_stream
static int32_t update_crc64_from_stream(FILE *stream, const uint64_t total_length, void *buffer, size_t buffer_size, crc64_ctx *crc_ctx, const char *label)
Definition verify.c:30