/* * This file is part of the Aaru Data Preservation Suite. * Copyright (c) 2019-2025 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 #include #include #include #include #include "aaruformattool.h" int cli_compare(const char *path1, const char *path2, bool use_long) { aaruformat_context *ctx1 = NULL; aaruformat_context *ctx2 = NULL; uint8_t *buffer1 = NULL; uint8_t *buffer2 = NULL; uint32_t buffer1_length = 0; uint32_t buffer2_length = 0; uint64_t total_sectors = 0; uint64_t different_sectors = 0; uint64_t sectors_processed = 0; int result = 0; int32_t read_result1 = 0; int32_t read_result2 = 0; uint8_t sector_status1 = 0; uint8_t sector_status2 = 0; printf("Opening first image: %s\n", path1); ctx1 = aaruf_open(path1); if(ctx1 == NULL) { fprintf(stderr, "Error: Could not open first image '%s'\n", path1); return -1; } printf("Opening second image: %s\n", path2); ctx2 = aaruf_open(path2); if(ctx2 == NULL) { fprintf(stderr, "Error: Could not open second image '%s'\n", path2); aaruf_close(ctx1); return -1; } // Access image information through context structure printf("\nImage Information:\n"); printf("Image 1: %llu sectors, %u bytes per sector\n", (unsigned long long)ctx1->image_info.Sectors, ctx1->image_info.SectorSize); printf("Image 2: %llu sectors, %u bytes per sector\n", (unsigned long long)ctx2->image_info.Sectors, ctx2->image_info.SectorSize); if(use_long) printf("Mode: Long sector read (including tags and metadata)\n"); else printf("Mode: Normal sector read\n"); if(ctx1->image_info.Sectors != ctx2->image_info.Sectors) { fprintf(stderr, "Warning: Images have different number of sectors\n"); total_sectors = ctx1->image_info.Sectors < ctx2->image_info.Sectors ? ctx1->image_info.Sectors : ctx2->image_info.Sectors; printf("Will compare first %llu sectors\n", (unsigned long long)total_sectors); } else { total_sectors = ctx1->image_info.Sectors; } if(ctx1->image_info.SectorSize != ctx2->image_info.SectorSize) { fprintf(stderr, "Error: Images have different sector sizes (%u vs %u)\n", ctx1->image_info.SectorSize, ctx2->image_info.SectorSize); aaruf_close(ctx1); aaruf_close(ctx2); return -1; } // Allocate buffers for sector data // For long mode, we need larger buffers to accommodate tags and metadata uint32_t buffer_size = use_long ? ctx1->image_info.SectorSize * 2 : ctx1->image_info.SectorSize; buffer1 = malloc(buffer_size); buffer2 = malloc(buffer_size); if(buffer1 == NULL || buffer2 == NULL) { fprintf(stderr, "Error: Could not allocate memory for sector buffers\n"); if(buffer1) free(buffer1); if(buffer2) free(buffer2); aaruf_close(ctx1); aaruf_close(ctx2); return -1; } printf("\nStarting sector-by-sector comparison...\n"); printf("Progress: 0%% (0/%llu sectors)\r", (unsigned long long)total_sectors); fflush(stdout); // Compare sectors for(uint64_t sector = 0; sector < total_sectors; sector++) { buffer1_length = buffer_size; buffer2_length = buffer_size; if(use_long) { read_result1 = aaruf_read_sector_long(ctx1, sector, false, buffer1, &buffer1_length, §or_status1); read_result2 = aaruf_read_sector_long(ctx2, sector, false, buffer2, &buffer2_length, §or_status2); } else { read_result1 = aaruf_read_sector(ctx1, sector, false, buffer1, &buffer1_length, §or_status1); read_result2 = aaruf_read_sector(ctx2, sector, false, buffer2, &buffer2_length, §or_status2); } // Handle read errors or missing sectors const bool sector1_available = read_result1 == AARUF_STATUS_OK; const bool sector2_available = read_result2 == AARUF_STATUS_OK; bool sectors_different = false; if(!sector1_available && !sector2_available) { // Both sectors are not available - consider them the same sectors_different = false; } else if(sector1_available != sector2_available) { // One sector is available, the other is not - they're different sectors_different = true; } else if(sector_status1 != sector_status2) { // Both sectors are available but have different status codes sectors_different = true; } else if(sector1_available && sector2_available && (buffer1_length != buffer2_length || memcmp(buffer1, buffer2, buffer1_length) != 0)) { // Both sectors are available - compare their content sectors_different = true; } if(sectors_different) { printf("Sector %llu: DIFFERENT", (unsigned long long)sector); if(!sector1_available) printf(" (missing in image 1)"); else if(!sector2_available) printf(" (missing in image 2)"); else if(buffer1_length != buffer2_length) printf(" (different sizes: %u vs %u)", buffer1_length, buffer2_length); printf("\n"); different_sectors++; } sectors_processed++; // Update progress every 1000 sectors or at the end if(sectors_processed % 1000 == 0 || sector == total_sectors - 1) { const int progress = (int)(sectors_processed * 100 / total_sectors); printf("Progress: %d%% (%llu/%llu sectors)\r", progress, (unsigned long long)sectors_processed, (unsigned long long)total_sectors); fflush(stdout); } } printf("\n\nComparison completed!\n"); printf("Total sectors compared: %llu\n", (unsigned long long)total_sectors); printf("Different sectors: %llu\n", (unsigned long long)different_sectors); printf("Identical sectors: %llu\n", (unsigned long long)(total_sectors - different_sectors)); if(different_sectors == 0) { printf("✓ Images are identical!\n"); result = 0; } else { printf("✗ Images are different (%llu sectors differ)\n", (unsigned long long)different_sectors); result = 1; // Non-zero exit code to indicate differences } // Cleanup free(buffer1); free(buffer2); aaruf_close(ctx1); aaruf_close(ctx2); return result; }