diff --git a/CMakeLists.txt b/CMakeLists.txt index e3d08f9..edee0e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,7 +115,8 @@ add_library(aaruformat SHARED include/aaruformat/consts.h include/aaruformat/enu src/blocks/metadata.c src/blocks/optical.c src/blocks/dump.c - src/blocks/checksum.c) + src/blocks/checksum.c + src/index/index_v3.c) include_directories(include include/aaruformat) diff --git a/include/internal.h b/include/internal.h index 0cc9c54..5d9ba78 100644 --- a/include/internal.h +++ b/include/internal.h @@ -25,6 +25,8 @@ UT_array *process_index_v1(aaruformatContext *ctx); int32_t verify_index_v1(aaruformatContext *ctx); UT_array *process_index_v2(aaruformatContext *ctx); int32_t verify_index_v2(aaruformatContext *ctx); +UT_array *process_index_v3(aaruformatContext *ctx); +int32_t verify_index_v3(aaruformatContext *ctx); int32_t process_data_block(aaruformatContext *ctx, IndexEntry *entry); int32_t process_ddt_v1(aaruformatContext *ctx, IndexEntry *entry, bool *foundUserDataDdt); void process_metadata_block(aaruformatContext *ctx, const IndexEntry *entry); @@ -33,5 +35,6 @@ void process_tracks_block(aaruformatContext *ctx, const IndexEntry *entry); void process_cicm_block(aaruformatContext *ctx, const IndexEntry *entry); void process_dumphw_block(aaruformatContext *ctx, const IndexEntry *entry); void process_checksum_block(aaruformatContext *ctx, const IndexEntry *entry); +void add_subindex_entries(aaruformatContext *ctx, UT_array *index_entries, IndexEntry *subindex_entry); #endif // LIBAARUFORMAT_INTERNAL_H diff --git a/src/index/index_v3.c b/src/index/index_v3.c new file mode 100644 index 0000000..6f5cfa9 --- /dev/null +++ b/src/index/index_v3.c @@ -0,0 +1,162 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "aaruformat.h" +#include "internal.h" +#include "utarray.h" + +UT_array *process_index_v3(aaruformatContext *ctx) +{ + UT_array *index_entries = NULL; + IndexEntry entry; + + if(ctx == NULL || ctx->imageStream == NULL) return NULL; + + // Initialize the index entries array + UT_icd index_entry_icd = {sizeof(IndexEntry), NULL, NULL, NULL}; + + utarray_new(index_entries, &index_entry_icd); + + // Read the index header + fseek(ctx->imageStream, ctx->header.indexOffset, SEEK_SET); + IndexHeader3 idx_header; + fread(&idx_header, sizeof(IndexHeader3), 1, ctx->imageStream); + + // Check if the index header is valid + if(idx_header.identifier != IndexBlock3) + { + fprintf(stderr, "Incorrect index identifier.\n"); + utarray_free(index_entries); + return NULL; + } + + for(int i = 0; i < idx_header.entries; i++) + { + fread(&entry, sizeof(IndexEntry), 1, ctx->imageStream); + if(entry.blockType == IndexBlock3) + { + // If the entry is a subindex, we need to read its entries + add_subindex_entries(ctx, index_entries, &entry); + continue; + } + utarray_push_back(index_entries, &entry); + } + + return index_entries; +} + +// Add entries from a subindex to the array of index entries +void add_subindex_entries(aaruformatContext *ctx, UT_array *index_entries, IndexEntry *subindex_entry) +{ + IndexHeader3 subindex_header; + IndexEntry entry; + + if(ctx == NULL || ctx->imageStream == NULL || index_entries == NULL || subindex_entry == NULL) return; + + // Seek to the subindex + fseek(ctx->imageStream, subindex_entry->offset, SEEK_SET); + + // Read the subindex header + fread(&subindex_header, sizeof(IndexHeader3), 1, ctx->imageStream); + + // Check if the subindex header is valid + if(subindex_header.identifier != IndexBlock3) + { + fprintf(stderr, "Incorrect subindex identifier.\n"); + return; + } + + // Read each entry in the subindex and add it to the main index entries array + for(int i = 0; i < subindex_header.entries; i++) + { + fread(&entry, sizeof(IndexEntry), 1, ctx->imageStream); + if(entry.blockType == IndexBlock3) + { + // If the entry is a subindex, we need to read its entries + add_subindex_entries(ctx, index_entries, &entry); + continue; + } + utarray_push_back(index_entries, &entry); + } +} + +int32_t verify_index_v3(aaruformatContext *ctx) +{ + size_t read_bytes = 0; + IndexHeader2 index_header; + uint64_t crc64 = 0; + IndexEntry *index_entries = NULL; + + if(ctx == NULL || ctx->imageStream == NULL) return AARUF_ERROR_NOT_AARUFORMAT; + + // This will traverse all blocks and check their CRC64 without uncompressing them + fprintf(stderr, "Checking index integrity at %llu.\n", ctx->header.indexOffset); + fseek(ctx->imageStream, ctx->header.indexOffset, SEEK_SET); + + // Read the index header + read_bytes = fread(&index_header, 1, sizeof(IndexHeader3), ctx->imageStream); + + if(read_bytes != sizeof(IndexHeader3)) + { + fprintf(stderr, "Could not read index header.\n"); + return AARUF_ERROR_CANNOT_READ_HEADER; + } + + if(index_header.identifier != IndexBlock3) + { + fprintf(stderr, "Incorrect index identifier.\n"); + return AARUF_ERROR_CANNOT_READ_INDEX; + } + + fprintf(stderr, "Index at %llu contains %d entries.\n", ctx->header.indexOffset, index_header.entries); + + index_entries = malloc(sizeof(IndexEntry) * index_header.entries); + + if(index_entries == NULL) + { + fprintf(stderr, "Cannot allocate memory for index entries.\n"); + return AARUF_ERROR_NOT_ENOUGH_MEMORY; + } + + read_bytes = fread(index_entries, 1, sizeof(IndexEntry) * index_header.entries, ctx->imageStream); + + if(read_bytes != sizeof(IndexEntry) * index_header.entries) + { + fprintf(stderr, "Could not read index entries.\n"); + free(index_entries); + return AARUF_ERROR_CANNOT_READ_INDEX; + } + + crc64 = aaruf_crc64_data((const uint8_t *)index_entries, sizeof(IndexEntry) * index_header.entries); + + // Due to how C# wrote it, it is effectively reversed + if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64); + + if(crc64 != index_header.crc64) + { + fprintf(stderr, "Expected index CRC 0x%16llX but got 0x%16llX.\n", index_header.crc64, crc64); + free(index_entries); + return AARUF_ERROR_INVALID_BLOCK_CRC; + } + + return AARUF_STATUS_OK; +} \ No newline at end of file diff --git a/src/open.c b/src/open.c index 61cbd92..2272928 100644 --- a/src/open.c +++ b/src/open.c @@ -151,7 +151,8 @@ void *aaruf_open(const char *filepath) readBytes = fread(&signature, 1, sizeof(uint32_t), ctx->imageStream); - if(readBytes != sizeof(uint32_t) || (signature != IndexBlock && signature != IndexBlock2)) + if(readBytes != sizeof(uint32_t) || + (signature != IndexBlock && signature != IndexBlock2 && signature != IndexBlock3)) { free(ctx); errno = AARUF_ERROR_CANNOT_READ_INDEX; @@ -163,6 +164,8 @@ void *aaruf_open(const char *filepath) index_entries = process_index_v1(ctx); else if(signature == IndexBlock2) index_entries = process_index_v2(ctx); + else if(signature == IndexBlock3) + index_entries = process_index_v3(ctx); if(index_entries == NULL) { diff --git a/src/verify.c b/src/verify.c index 002ac1e..5f5de40 100644 --- a/src/verify.c +++ b/src/verify.c @@ -55,7 +55,7 @@ int32_t aaruf_verify_image(void *context) return AARUF_ERROR_CANNOT_READ_HEADER; } - if(signature != IndexBlock && signature != IndexBlock2) + if(signature != IndexBlock && signature != IndexBlock2 && signature != IndexBlock3) { fprintf(stderr, "Incorrect index signature.\n"); return AARUF_ERROR_CANNOT_READ_INDEX; @@ -66,6 +66,8 @@ int32_t aaruf_verify_image(void *context) err = verify_index_v1(ctx); else if(signature == IndexBlock2) err = verify_index_v2(ctx); + else if(signature == IndexBlock3) + err = verify_index_v3(ctx); if(err != AARUF_STATUS_OK) { @@ -78,6 +80,8 @@ int32_t aaruf_verify_image(void *context) index_entries = process_index_v1(ctx); else if(signature == IndexBlock2) index_entries = process_index_v2(ctx); + else if(signature == IndexBlock3) + index_entries = process_index_v3(ctx); if(index_entries == NULL) {