// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Verify.cs // Author(s) : Natalia Portillo // // Component : Disk image plugins. // // --[ Description ] ---------------------------------------------------------- // // Verifies Aaru Format disk images. // // --[ License ] -------------------------------------------------------------- // // 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 . // // ---------------------------------------------------------------------------- // Copyright © 2011-2021 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using Aaru.Checksums; using Aaru.CommonTypes.Enums; using Aaru.Console; using Aaru.Helpers; namespace Aaru.DiscImages { public sealed partial class AaruFormat { /// public bool? VerifyMediaImage() { // This will traverse all blocks and check their CRC64 without uncompressing them AaruConsole.DebugWriteLine("Aaru Format plugin", "Checking index integrity at {0}", _header.indexOffset); _imageStream.Position = (long)_header.indexOffset; _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); if(idxHeader.identifier != BlockType.Index) { AaruConsole.DebugWriteLine("Aaru Format plugin", "Incorrect index identifier"); return false; } AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", _header.indexOffset, idxHeader.entries); _structureBytes = new byte[Marshal.SizeOf() * idxHeader.entries]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); Crc64Context.Data(_structureBytes, out byte[] verifyCrc); if(BitConverter.ToUInt64(verifyCrc, 0) != idxHeader.crc64) { AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected index CRC {0:X16} but got {1:X16}", idxHeader.crc64, BitConverter.ToUInt64(verifyCrc, 0)); return false; } _imageStream.Position -= _structureBytes.Length; List vrIndex = new(); for(ushort i = 0; i < idxHeader.entries; i++) { _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); AaruConsole.DebugWriteLine("Aaru Format plugin", "Block type {0} with data type {1} is indexed to be at {2}", entry.blockType, entry.dataType, entry.offset); vrIndex.Add(entry); } // Read up to 1MiB at a time for verification const int verifySize = 1024 * 1024; foreach(IndexEntry entry in vrIndex) { _imageStream.Position = (long)entry.offset; Crc64Context crcVerify; ulong readBytes; byte[] verifyBytes; switch(entry.blockType) { case BlockType.DataBlock: _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); crcVerify = new Crc64Context(); readBytes = 0; AaruConsole.DebugWriteLine("Aaru Format plugin", "Verifying data block type {0} at position {1}", entry.dataType, entry.offset); while(readBytes + verifySize < blockHeader.cmpLength) { verifyBytes = new byte[verifySize]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); readBytes += (ulong)verifyBytes.LongLength; } verifyBytes = new byte[blockHeader.cmpLength - readBytes]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); verifyCrc = crcVerify.Final(); if(BitConverter.ToUInt64(verifyCrc, 0) != blockHeader.cmpCrc64) { AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected block CRC {0:X16} but got {1:X16}", blockHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); return false; } break; case BlockType.DeDuplicationTable: _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); crcVerify = new Crc64Context(); readBytes = 0; AaruConsole.DebugWriteLine("Aaru Format plugin", "Verifying deduplication table type {0} at position {1}", entry.dataType, entry.offset); while(readBytes + verifySize < ddtHeader.cmpLength) { verifyBytes = new byte[readBytes]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); readBytes += (ulong)verifyBytes.LongLength; } verifyBytes = new byte[ddtHeader.cmpLength - readBytes]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); verifyCrc = crcVerify.Final(); if(BitConverter.ToUInt64(verifyCrc, 0) != ddtHeader.cmpCrc64) { AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected DDT CRC {0:X16} but got {1:X16}", ddtHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); return false; } break; case BlockType.TracksBlock: _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); TracksHeader trkHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); AaruConsole.DebugWriteLine("Aaru Format plugin", "Track block at {0} contains {1} entries", _header.indexOffset, trkHeader.entries); _structureBytes = new byte[Marshal.SizeOf() * trkHeader.entries]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); Crc64Context.Data(_structureBytes, out verifyCrc); if(BitConverter.ToUInt64(verifyCrc, 0) != trkHeader.crc64) { AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected index CRC {0:X16} but got {1:X16}", trkHeader.crc64, BitConverter.ToUInt64(verifyCrc, 0)); return false; } break; default: AaruConsole.DebugWriteLine("Aaru Format plugin", "Ignored field type {0}", entry.blockType); break; } } return true; } /// public bool? VerifySector(ulong sectorAddress) { if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) return null; ErrorNumber errno = ReadSectorLong(sectorAddress, out byte[] buffer); return errno != ErrorNumber.NoError ? null : CdChecksums.CheckCdSector(buffer); } /// public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, out List unknownLbas) { failingLbas = new List(); unknownLbas = new List(); // Right now only CompactDisc sectors are verifiable if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) { for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i); return null; } ErrorNumber errno = ReadSectorsLong(sectorAddress, length, out byte[] buffer); if(errno != ErrorNumber.NoError) return null; int bps = (int)(buffer.Length / length); byte[] sector = new byte[bps]; failingLbas = new List(); unknownLbas = new List(); for(int i = 0; i < length; i++) { Array.Copy(buffer, i * bps, sector, 0, bps); bool? sectorStatus = CdChecksums.CheckCdSector(sector); switch(sectorStatus) { case null: unknownLbas.Add((ulong)i + sectorAddress); break; case false: failingLbas.Add((ulong)i + sectorAddress); break; } } if(unknownLbas.Count > 0) return null; return failingLbas.Count <= 0; } /// public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, out List unknownLbas) { // Right now only CompactDisc sectors are verifiable if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) { failingLbas = new List(); unknownLbas = new List(); for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i); return null; } failingLbas = new List(); unknownLbas = new List(); ErrorNumber errno = ReadSectorsLong(sectorAddress, length, track, out byte[] buffer); if(errno != ErrorNumber.NoError) return null; int bps = (int)(buffer.Length / length); byte[] sector = new byte[bps]; for(int i = 0; i < length; i++) { Array.Copy(buffer, i * bps, sector, 0, bps); bool? sectorStatus = CdChecksums.CheckCdSector(sector); switch(sectorStatus) { case null: unknownLbas.Add((ulong)i + sectorAddress); break; case false: failingLbas.Add((ulong)i + sectorAddress); break; } } if(unknownLbas.Count > 0) return null; return failingLbas.Count <= 0; } } }