diff --git a/CMakeLists.txt b/CMakeLists.txt
index 776ad66..40ab76f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,7 +98,7 @@ add_library(aaruformat SHARED include/aaruformat/consts.h include/aaruformat/enu
src/close.c include/aaruformat/errors.h src/read.c include/aaruformat/crc64.h src/cst.c src/ecc_cd.c src/helpers.c
src/simd.c include/aaruformat/simd.h src/crc64/crc64.c src/crc64/crc64_clmul.c src/crc64/crc64_vmull.c
src/crc64/arm_vmull.c src/crc64/arm_vmull.h src/spamsum.c include/aaruformat/spamsum.h include/aaruformat/flac.h
- src/flac.c src/lzma.c src/lru.c include/aaruformat/lru.h include/aaruformat/endian.h)
+ src/flac.c src/lzma.c src/lru.c include/aaruformat/lru.h include/aaruformat/endian.h src/verify.c)
include_directories(include include/aaruformat)
diff --git a/include/aaruformat/decls.h b/include/aaruformat/decls.h
index a005a10..f73f32f 100644
--- a/include/aaruformat/decls.h
+++ b/include/aaruformat/decls.h
@@ -79,6 +79,8 @@ AARU_EXPORT int32_t AARU_CALL aaruf_read_sector_long(void* context,
uint8_t* data,
uint32_t* length);
+AARU_EXPORT int32_t AARU_CALL aaruf_verify_image(void* context);
+
AARU_EXPORT int32_t AARU_CALL aaruf_cst_transform(const uint8_t* interleaved, uint8_t* sequential, size_t length);
AARU_EXPORT int32_t AARU_CALL aaruf_cst_untransform(const uint8_t* sequential, uint8_t* interleaved, size_t length);
diff --git a/include/aaruformat/errors.h b/include/aaruformat/errors.h
index 3a5b550..48144c5 100644
--- a/include/aaruformat/errors.h
+++ b/include/aaruformat/errors.h
@@ -36,6 +36,7 @@
#define AARUF_ERROR_INVALID_TRACK_FORMAT -15
#define AARUF_ERROR_SECTOR_TAG_NOT_PRESENT -16
#define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK -17
+#define AARUF_ERROR_INVALID_BLOCK_CRC -18
#define AARUF_STATUS_OK 0
#define AARUF_STATUS_SECTOR_NOT_DUMPED 1
diff --git a/src/verify.c b/src/verify.c
new file mode 100644
index 0000000..bdf0ac5
--- /dev/null
+++ b/src/verify.c
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the Aaru Data Preservation Suite.
+ * Copyright (c) 2019-2022 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
+
+#define VERIFY_SIZE 1048576
+
+int32_t aaruf_verify_image(void* context)
+{
+ aaruformatContext* ctx;
+ uint64_t crc64;
+ int i;
+ IndexHeader index_header;
+ IndexEntry* index_entries;
+ size_t read_bytes;
+ void* buffer;
+ crc64_ctx* crc64_context;
+ BlockHeader block_header;
+ uint64_t verified_bytes;
+ DdtHeader ddt_header;
+ TracksHeader tracks_header;
+
+ if(context == NULL) return AARUF_ERROR_NOT_AARUFORMAT;
+
+ ctx = context;
+
+ // Not a libaaruformat context
+ if(ctx->magic != AARU_MAGIC) return AARUF_ERROR_NOT_AARUFORMAT;
+
+ // This will traverse all blocks and check their CRC64 without uncompressing them
+ fprintf(stderr, "Checking index integrity at %lu.\n", ctx->header.indexOffset);
+ fseek(ctx->imageStream, ctx->header.indexOffset, SEEK_SET);
+
+ read_bytes = fread(&index_header, 1, sizeof(IndexHeader), ctx->imageStream);
+
+ if(read_bytes != sizeof(IndexHeader))
+ {
+ fprintf(stderr, "Could not read index header.\n");
+ return AARUF_ERROR_CANNOT_READ_HEADER;
+ }
+
+ if(index_header.identifier != IndexBlock)
+ {
+ fprintf(stderr, "Incorrect index identifier.\n");
+ return AARUF_ERROR_CANNOT_READ_INDEX;
+ }
+
+ fprintf(stderr, "Index at %lu 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) crc64 = bswap_64(crc64);
+
+ if(crc64 != index_header.crc64)
+ {
+ fprintf(stderr, "Expected index CRC 0x%16lX but got 0x%16lX.\n", index_header.crc64, crc64);
+ free(index_entries);
+ return AARUF_ERROR_INVALID_BLOCK_CRC;
+ }
+
+ buffer = malloc(VERIFY_SIZE);
+
+ if(buffer == NULL)
+ {
+ fprintf(stderr, "Cannot allocate memory for buffer.\n");
+ free(index_entries);
+ return AARUF_ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ for(i = 0; i < index_header.entries; i++)
+ {
+ fprintf(stderr,
+ "Checking block with type %4.4s at position %" PRIu64 "\n",
+ (char*)&index_entries[i].blockType,
+ index_entries[i].offset);
+
+ fseek(ctx->imageStream, index_entries[i].offset, SEEK_SET);
+
+ switch(index_entries[i].blockType)
+ {
+ case DataBlock:
+ read_bytes = fread(&block_header, 1, sizeof(BlockHeader), ctx->imageStream);
+ if(read_bytes != sizeof(BlockHeader))
+ {
+ fprintf(stderr, "Could not read block header.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ crc64_context = aaruf_crc64_init();
+
+ if(crc64_context == NULL)
+ {
+ fprintf(stderr, "Could not initialize CRC64.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ verified_bytes = 0;
+
+ while(verified_bytes + VERIFY_SIZE < block_header.cmpLength)
+ {
+ read_bytes = fread(buffer, 1, VERIFY_SIZE, ctx->imageStream);
+ aaruf_crc64_update(crc64_context, buffer, read_bytes);
+ verified_bytes += read_bytes;
+ }
+
+ read_bytes = fread(buffer, 1, block_header.cmpLength - verified_bytes, ctx->imageStream);
+ aaruf_crc64_update(crc64_context, buffer, read_bytes);
+
+ aaruf_crc64_final(crc64_context, &crc64);
+
+ // Due to how C# wrote it, it is effectively reversed
+ if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64);
+
+ if(crc64 != block_header.cmpCrc64)
+ {
+ fprintf(stderr, "Expected block CRC 0x%16lX but got 0x%16lX.\n", block_header.cmpCrc64, crc64);
+ free(index_entries);
+ return AARUF_ERROR_INVALID_BLOCK_CRC;
+ }
+
+ break;
+ case DeDuplicationTable:
+ read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader), ctx->imageStream);
+ if(read_bytes != sizeof(DdtHeader))
+ {
+ fprintf(stderr, "Could not read DDT header.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ crc64_context = aaruf_crc64_init();
+
+ if(crc64_context == NULL)
+ {
+ fprintf(stderr, "Could not initialize CRC64.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ verified_bytes = 0;
+
+ while(verified_bytes + VERIFY_SIZE < ddt_header.cmpLength)
+ {
+ read_bytes = fread(buffer, 1, VERIFY_SIZE, ctx->imageStream);
+ aaruf_crc64_update(crc64_context, buffer, read_bytes);
+ verified_bytes += read_bytes;
+ }
+
+ read_bytes = fread(buffer, 1, ddt_header.cmpLength - verified_bytes, ctx->imageStream);
+ aaruf_crc64_update(crc64_context, buffer, read_bytes);
+
+ aaruf_crc64_final(crc64_context, &crc64);
+
+ // Due to how C# wrote it, it is effectively reversed
+ if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64);
+
+ if(crc64 != ddt_header.cmpCrc64)
+ {
+ fprintf(stderr, "Expected DDT CRC 0x%16lX but got 0x%16lX.\n", ddt_header.cmpCrc64, crc64);
+ free(index_entries);
+ return AARUF_ERROR_INVALID_BLOCK_CRC;
+ }
+
+ break;
+ case TracksBlock:
+ read_bytes = fread(&tracks_header, 1, sizeof(TracksHeader), ctx->imageStream);
+ if(read_bytes != sizeof(TracksHeader))
+ {
+ fprintf(stderr, "Could not read tracks header.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ crc64_context = aaruf_crc64_init();
+
+ if(crc64_context == NULL)
+ {
+ fprintf(stderr, "Could not initialize CRC64.\n");
+ free(index_entries);
+ return AARUF_ERROR_CANNOT_READ_BLOCK;
+ }
+
+ read_bytes = fread(buffer, 1, tracks_header.entries * sizeof(TrackEntry), ctx->imageStream);
+ aaruf_crc64_update(crc64_context, buffer, read_bytes);
+
+ aaruf_crc64_final(crc64_context, &crc64);
+
+ // Due to how C# wrote it, it is effectively reversed
+ if(ctx->header.imageMajorVersion <= AARUF_VERSION) crc64 = bswap_64(crc64);
+
+ if(crc64 != tracks_header.crc64)
+ {
+ fprintf(stderr, "Expected DDT CRC 0x%16lX but got 0x%16lX.\n", tracks_header.crc64, crc64);
+ free(index_entries);
+ return AARUF_ERROR_INVALID_BLOCK_CRC;
+ }
+
+ break;
+ default: fprintf(stderr, "Ignoring block type %4.4s.\n", (char*)&index_entries[i].blockType); break;
+ }
+ }
+
+ return AARUF_STATUS_OK;
+}
\ No newline at end of file
diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt
index b0c7218..4c8dbce 100644
--- a/tool/CMakeLists.txt
+++ b/tool/CMakeLists.txt
@@ -4,5 +4,5 @@ find_package(ICU COMPONENTS uc REQUIRED)
include_directories(${ICU_INCLUDE_DIRS})
-add_executable(aaruformattool main.c main.h aaruformattool.h identify.c info.c helpers.c read.c printhex.c)
+add_executable(aaruformattool main.c main.h aaruformattool.h identify.c info.c helpers.c read.c printhex.c verify.c)
target_link_libraries(aaruformattool "aaruformat" ICU::uc)
diff --git a/tool/aaruformattool.h b/tool/aaruformattool.h
index 52fed52..d28a6c6 100644
--- a/tool/aaruformattool.h
+++ b/tool/aaruformattool.h
@@ -27,5 +27,6 @@ char* byte_array_to_hex_string(const unsigned char* array, int array_size);
int read(unsigned long long sector_no, char* path);
int printhex(unsigned char* array, unsigned int length, int width, bool color);
int read_long(unsigned long long sector_no, char* path);
+int verify(char* path);
#endif // LIBAARUFORMAT_TOOL_AARUFORMATTOOL_H_
diff --git a/tool/main.c b/tool/main.c
index f83e16d..b200ee4 100644
--- a/tool/main.c
+++ b/tool/main.c
@@ -37,6 +37,7 @@ void usage()
printf("\tinfo\tPrints information about a given AaruFormat image.\n");
printf("\tread\tReads a sector and prints it out on screen.\n");
printf("\tread_long\tReads a sector with all its prefixes and suffixes and prints it out on screen.\n");
+ printf("\tverify\tVerifies the integrity of all blocks in a AaruFormat image.\n");
printf("\n");
printf("For help on the verb invoke the tool with the verb and no arguments.\n");
}
@@ -87,6 +88,17 @@ void usage_read_long()
printf("\t\tPath to AaruFormat image to print information from.\n");
}
+void usage_verify()
+{
+ printf("\n");
+ printf("Usage:\n");
+ printf("aaruformattool verify \n");
+ printf("Verifies the integrity of all blocks in a AaruFormat image.\n");
+ printf("\n");
+ printf("Arguments:\n");
+ printf("\t\tPath to AaruFormat image to verify.\n");
+}
+
int main(int argc, char* argv[])
{
uint64_t sector_no = 0;
@@ -195,5 +207,23 @@ int main(int argc, char* argv[])
return read(sector_no, argv[3]);
}
+ if(strncmp(argv[1], "verify", strlen("verify")) == 0)
+ {
+ if(argc == 2)
+ {
+ usage_verify();
+ return -1;
+ }
+
+ if(argc > 3)
+ {
+ fprintf(stderr, "Invalid number of arguments\n");
+ usage_verify();
+ return -1;
+ }
+
+ return verify(argv[2]);
+ }
+
return 0;
}
diff --git a/tool/verify.c b/tool/verify.c
new file mode 100644
index 0000000..52120e2
--- /dev/null
+++ b/tool/verify.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Aaru Data Preservation Suite.
+ * Copyright (c) 2019-2022 Natalia Portillo.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include
+
+#include
+
+int verify(char* path)
+{
+ aaruformatContext* ctx;
+ uint32_t res;
+
+ ctx = aaruf_open(path);
+
+ if(ctx == NULL)
+ {
+ printf("Error %d when opening AaruFormat image.\n", errno);
+ return errno;
+ }
+
+ res = aaruf_verify_image(ctx);
+
+ if(res == AARUF_STATUS_OK) printf("Image blocks contain no errors.\n");
+ else if(res == AARUF_ERROR_INVALID_BLOCK_CRC)
+ printf("A block contains an invalid CRC value.\n");
+ else
+ printf("Error %d verifying image.\n", res);
+
+ return res;
+}
\ No newline at end of file