[Dumping] Add paranoia mode to check integrity of sectors before writing them to the image.

This commit is contained in:
2025-11-23 13:05:43 +00:00
parent 0f78b128ac
commit 70f005f61f
9 changed files with 256 additions and 48 deletions

View File

@@ -47,6 +47,7 @@ using Aaru.Core.Logging;
using Aaru.Decoders.CD;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Localization;
using Aaru.Logging;
using Humanizer;
using Track = Aaru.CommonTypes.Structs.Track;
@@ -762,6 +763,7 @@ partial class Dump
mhddLog.Write(i + r, _speedStopwatch.Elapsed.TotalMilliseconds);
extents.Add(i + r, 1, true);
_writeStopwatch.Restart();
SectorStatus sectorStatus = SectorStatus.Dumped;
if(supportedSubchannel != MmcSubchannel.None)
{
@@ -772,14 +774,33 @@ partial class Dump
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
if(_paranoia)
{
// Check valid sector
CdChecksums.CheckCdSector(data,
out bool? correctEccP,
out bool? correctEccQ,
out bool? correctEdc);
if(correctEdc != true || correctEccP != true || correctEccQ != true)
{
sectorStatus = SectorStatus.Errored;
_resume.BadBlocks.Add(i + r);
if(correctEdc != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_EDC_in_sector_0, i + r));
if(correctEccP != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_P_in_sector_0, i + r));
if(correctEccQ != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_Q_in_sector_0, i + r));
}
}
if(supportsLongSectors)
{
outputFormat.WriteSectorsLong(data,
i + r,
false,
1,
Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)
.ToArray());
outputFormat.WriteSectorsLong(data, i + r, false, 1, [sectorStatus]);
}
else
{
@@ -797,8 +818,7 @@ partial class Dump
i,
false,
blocksToRead,
Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)
.ToArray());
Enumerable.Repeat(sectorStatus, (int)blocksToRead).ToArray());
}
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
@@ -844,8 +864,32 @@ partial class Dump
}
else
{
if(_paranoia)
{
// Check valid sector
CdChecksums.CheckCdSector(cmdBuf,
out bool? correctEccP,
out bool? correctEccQ,
out bool? correctEdc);
if(correctEdc != true || correctEccP != true || correctEccQ != true)
{
sectorStatus = SectorStatus.Errored;
_resume.BadBlocks.Add(i + r);
if(correctEdc != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_EDC_in_sector_0, i + r));
if(correctEccP != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_P_in_sector_0, i + r));
if(correctEccQ != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_Q_in_sector_0, i + r));
}
}
if(supportsLongSectors)
outputFormat.WriteSectorsLong(cmdBuf, i + r, false, 1, [SectorStatus.Dumped]);
outputFormat.WriteSectorsLong(cmdBuf, i + r, false, 1, [sectorStatus]);
else
{
var cooked = new MemoryStream();
@@ -862,8 +906,7 @@ partial class Dump
i,
false,
blocksToRead,
Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)
.ToArray());
Enumerable.Repeat(sectorStatus, (int)blocksToRead).ToArray());
}
}
@@ -987,29 +1030,53 @@ partial class Dump
if(supportedSubchannel != MmcSubchannel.None)
{
var data = new byte[sectorSize * blocksToRead];
var sub = new byte[subSize * blocksToRead];
var data = new byte[sectorSize * blocksToRead];
var sub = new byte[subSize * blocksToRead];
var sectorStatus = new SectorStatus[blocksToRead];
var sector = new byte[sectorSize];
for(var b = 0; b < blocksToRead; b++)
{
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize);
Array.Copy(cmdBuf, (int)(0 + b * blockSize), sector, 0, sectorSize);
sectorStatus[b] = SectorStatus.Dumped;
if(inData && _paranoia)
{
// Check valid sector
CdChecksums.CheckCdSector(sector,
out bool? correctEccP,
out bool? correctEccQ,
out bool? correctEdc);
if(correctEdc != true || correctEccP != true || correctEccQ != true)
{
sectorStatus[b] = SectorStatus.Errored;
_resume.BadBlocks.Add(i + (ulong)b);
if(correctEdc != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_EDC_in_sector_0, i + (ulong)b));
if(correctEccP != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_P_in_sector_0, i + (ulong)b));
if(correctEccQ != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_Q_in_sector_0, i + (ulong)b));
}
}
}
if(supportsLongSectors)
{
outputFormat.WriteSectorsLong(data,
i,
false,
blocksToRead,
Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)
.ToArray());
outputFormat.WriteSectorsLong(data, i, false, blocksToRead, sectorStatus);
}
else
{
var cooked = new MemoryStream();
var sector = new byte[sectorSize];
for(var b = 0; b < blocksToRead; b++)
{
@@ -1018,11 +1085,7 @@ partial class Dump
cooked.Write(cookedSector, 0, cookedSector.Length);
}
outputFormat.WriteSectors(cooked.ToArray(),
i,
false,
blocksToRead,
Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead).ToArray());
outputFormat.WriteSectors(cooked.ToArray(), i, false, blocksToRead, sectorStatus);
}
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,

View File

@@ -47,6 +47,7 @@ using Aaru.Core.Logging;
using Aaru.Decoders.CD;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Localization;
using Aaru.Logging;
using Track = Aaru.CommonTypes.Structs.Track;
using TrackType = Aaru.CommonTypes.Enums.TrackType;
@@ -466,8 +467,9 @@ partial class Dump
// MEDIUM ERROR, retry with ignore error below
if(decSense is { ASC: 0x11 })
if(!sectorsNotEvenPartial.Contains(badSector))
sectorsNotEvenPartial.Add(badSector);
{
if(!sectorsNotEvenPartial.Contains(badSector)) sectorsNotEvenPartial.Add(badSector);
}
}
// Because one block has been partially used to fix the offset
@@ -486,17 +488,61 @@ partial class Dump
false);
}
SectorStatus sectorStatus = SectorStatus.Dumped;
if(!sense && !_dev.Error)
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
if(!audioExtents.Contains(badSector) && _paranoia)
{
var sector = new byte[sectorSize];
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_sector_0_in_pass_1,
badSector,
pass));
// Check valid sector
CdChecksums.CheckCdSector(sector,
out bool? correctEccP,
out bool? correctEccQ,
out bool? correctEdc);
sectorsNotEvenPartial.Remove(badSector);
if(correctEdc != true || correctEccP != true || correctEccQ != true)
{
sectorStatus = SectorStatus.Errored;
_mediaGraph?.PaintSectorBad(badSector);
if(correctEdc != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_EDC_in_sector_0, badSector));
if(correctEccP != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_P_in_sector_0, badSector));
if(correctEccQ != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_Q_in_sector_0, badSector));
}
else
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_sector_0_in_pass_1,
badSector,
pass));
sectorsNotEvenPartial.Remove(badSector);
}
}
else
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_sector_0_in_pass_1,
badSector,
pass));
sectorsNotEvenPartial.Remove(badSector);
}
}
else
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
@@ -509,9 +555,9 @@ partial class Dump
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
if(supportsLongSectors)
outputOptical.WriteSectorLong(data, badSector, false, SectorStatus.Dumped);
outputOptical.WriteSectorLong(data, badSector, false, sectorStatus);
else
outputOptical.WriteSector(Sector.GetUserData(data), badSector, false, SectorStatus.Dumped);
outputOptical.WriteSector(Sector.GetUserData(data), badSector, false, sectorStatus);
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
desiredSubchannel,
@@ -542,9 +588,9 @@ partial class Dump
else
{
if(supportsLongSectors)
outputOptical.WriteSectorLong(cmdBuf, badSector, false, SectorStatus.Dumped);
outputOptical.WriteSectorLong(cmdBuf, badSector, false, sectorStatus);
else
outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector, false, SectorStatus.Dumped);
outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector, false, sectorStatus);
}
}
@@ -697,10 +743,12 @@ partial class Dump
if(supportsLongSectors)
outputOptical.WriteSectorLong(cmdBuf, badSector, false, SectorStatus.Errored);
else
{
outputOptical.WriteSector(Sector.GetUserData(cmdBuf),
badSector,
false,
SectorStatus.Errored);
}
}
}

View File

@@ -46,6 +46,7 @@ using Aaru.Core.Logging;
using Aaru.Decoders.CD;
using Aaru.Decoders.SCSI;
using Aaru.Devices;
using Aaru.Localization;
using Humanizer;
using Track = Aaru.CommonTypes.Structs.Track;
@@ -392,11 +393,48 @@ partial class Dump
continue;
}
SectorStatus sectorStatus = SectorStatus.Dumped;
if(!sense && !_dev.Error)
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
if(!audioExtents.Contains(badSector) && _paranoia)
{
var sector = new byte[sectorSize];
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
// Check valid sector
CdChecksums.CheckCdSector(sector,
out bool? correctEccP,
out bool? correctEccQ,
out bool? correctEdc);
if(correctEdc != true || correctEccP != true || correctEccQ != true)
{
sectorStatus = SectorStatus.Errored;
_mediaGraph?.PaintSectorBad(badSector);
if(correctEdc != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_EDC_in_sector_0, badSector));
if(correctEccP != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_P_in_sector_0, badSector));
if(correctEccQ != true)
UpdateStatus?.Invoke(string.Format(UI.Incorrect_ECC_Q_in_sector_0, badSector));
}
else
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
}
}
else
{
_resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
_mediaGraph?.PaintSectorGood(badSector);
}
}
// Because one block has been partially used to fix the offset
@@ -415,6 +453,7 @@ partial class Dump
false);
}
if(supportedSubchannel != MmcSubchannel.None)
{
var data = new byte[sectorSize];
@@ -423,9 +462,9 @@ partial class Dump
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
if(supportsLongSectors)
outputOptical.WriteSectorLong(data, badSector, false, SectorStatus.Dumped);
outputOptical.WriteSectorLong(data, badSector, false, sectorStatus);
else
outputOptical.WriteSector(Sector.GetUserData(data), badSector, false, SectorStatus.Dumped);
outputOptical.WriteSector(Sector.GetUserData(data), badSector, false, sectorStatus);
ulong trkStartBefore = track.StartSector;
@@ -467,9 +506,9 @@ partial class Dump
}
if(supportsLongSectors)
outputOptical.WriteSectorLong(cmdBuf, badSector, false, SectorStatus.Dumped);
outputOptical.WriteSectorLong(cmdBuf, badSector, false, sectorStatus);
else
outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector, false, SectorStatus.Dumped);
outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector, false, sectorStatus);
}
_trimStopwatch.Stop();

View File

@@ -93,6 +93,7 @@ public partial class Dump
readonly string _outputPath;
readonly IBaseWritableImage _outputPlugin;
readonly string _outputPrefix;
readonly bool _paranoia;
readonly bool _persistent;
readonly Metadata _preSidecar;
readonly bool _private;
@@ -165,6 +166,7 @@ public partial class Dump
/// <param name="ignoreCdrRunOuts">How many CD-R(W) run end sectors to ignore and regenerate</param>
/// <param name="createGraph">If set to <c>true</c> creates a graph of the dump.</param>
/// <param name="dimensions">Dimensions of graph in pixels for a square</param>
/// <param name="paranoia">Check sectors integrity before writing to image</param>
public Dump(bool doResume, Device dev, string devicePath, IBaseWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, Encoding encoding,
string outputPrefix, string outputPath, Dictionary<string, string> formatOptions, Metadata preSidecar,
@@ -172,7 +174,7 @@ public partial class Dump
DumpSubchannel subchannel, int speed, bool @private, bool fixSubchannelPosition, bool retrySubchannel,
bool fixSubchannel, bool fixSubchannelCrc, bool skipCdireadyHole, ErrorLog errorLog,
bool generateSubchannels, uint maximumReadable, bool useBufferedReads, bool storeEncrypted,
bool titleKeys, uint ignoreCdrRunOuts, bool createGraph, uint dimensions)
bool titleKeys, uint ignoreCdrRunOuts, bool createGraph, uint dimensions, bool paranoia)
{
_doResume = doResume;
_dev = dev;
@@ -214,6 +216,7 @@ public partial class Dump
_ignoreCdrRunOuts = ignoreCdrRunOuts;
_createGraph = createGraph;
_dimensions = dimensions;
_paranoia = paranoia;
_dumpStopwatch = new Stopwatch();
_sidecarStopwatch = new Stopwatch();
_speedStopwatch = new Stopwatch();

View File

@@ -722,7 +722,8 @@ public sealed partial class MediaDumpViewModel : ViewModelBase
false,
10,
true,
1080);
1080,
false);
new Thread(DoWork).Start();
}

View File

@@ -6621,5 +6621,29 @@ namespace Aaru.Localization {
return ResourceManager.GetString("Path_to_log_file", resourceCulture);
}
}
public static string Incorrect_EDC_in_sector_0 {
get {
return ResourceManager.GetString("Incorrect_EDC_in_sector_0", resourceCulture);
}
}
public static string Incorrect_ECC_P_in_sector_0 {
get {
return ResourceManager.GetString("Incorrect_ECC_P_in_sector_0", resourceCulture);
}
}
public static string Incorrect_ECC_Q_in_sector_0 {
get {
return ResourceManager.GetString("Incorrect_ECC_Q_in_sector_0", resourceCulture);
}
}
public static string Paranoia_help {
get {
return ResourceManager.GetString("Paranoia_help", resourceCulture);
}
}
}
}

View File

@@ -3311,4 +3311,16 @@ Probadores:
<data name="Path_to_log_file" xml:space="preserve">
<value>Ruta al archivo de registro.</value>
</data>
<data name="Incorrect_EDC_in_sector_0" xml:space="preserve">
<value>[red]EDC incorrecto en el sector [violet]{0}[/][/]</value>
</data>
<data name="Incorrect_ECC_P_in_sector_0" xml:space="preserve">
<value>[red]ECC P incorrecto en el sector [violet]{0}[/][/]</value>
</data>
<data name="Incorrect_ECC_Q_in_sector_0" xml:space="preserve">
<value>[red]ECC Q incorrecto en el sector [violet]{0}[/][/]</value>
</data>
<data name="Paranoia_help" xml:space="preserve">
<value>No confiar en la unidad, comprobar la integridad de los sectores antes de escribirlos en la imagen. Válido sólo para CD/GD.</value>
</data>
</root>

View File

@@ -3386,4 +3386,16 @@ Do you want to continue?</value>
<data name="Path_to_log_file" xml:space="preserve">
<value>Path to log file.</value>
</data>
<data name="Incorrect_EDC_in_sector_0" xml:space="preserve">
<value>[red]Incorrect EDC in sector [violet]{0}[/][/]</value>
</data>
<data name="Incorrect_ECC_P_in_sector_0" xml:space="preserve">
<value>[red]Incorrect ECC P in sector [violet]{0}[/][/]</value>
</data>
<data name="Incorrect_ECC_Q_in_sector_0" xml:space="preserve">
<value>[red]Incorrect ECC Q in sector [violet]{0}[/][/]</value>
</data>
<data name="Paranoia_help" xml:space="preserve">
<value>Do not trust the drive, check the sectors integrity before writing them to the image. Valid only for CD/GD.</value>
</data>
</root>

View File

@@ -121,6 +121,7 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
AaruLogging.Debug(MODULE_NAME, "--create-graph={0}", settings.CreateGraph);
AaruLogging.Debug(MODULE_NAME, "--dimensions={0}", settings.Dimensions);
AaruLogging.Debug(MODULE_NAME, "--aaru-metadata={0}", Markup.Escape(settings.AaruMetadata ?? ""));
AaruLogging.Debug(MODULE_NAME, "--paranoia={0}", settings.Paranoia);
// TODO: Disabled temporarily
//AaruLogging.DebugWriteLine(MODULE_NAME, "--raw={0}", Markup.Escape(raw ?? ""));
@@ -524,7 +525,8 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
settings.TitleKeys,
(uint)settings.IgnoreCdrRunOuts,
settings.CreateGraph,
(uint)settings.Dimensions);
(uint)settings.Dimensions,
settings.Paranoia);
AnsiConsole.Progress()
.AutoClear(true)
@@ -772,6 +774,10 @@ sealed class DumpMediaCommand : Command<DumpMediaCommand.Settings>
[CommandOption("--aaru-metadata")]
[DefaultValue(null)]
public string AaruMetadata { get; init; }
[LocalizedDescription(nameof(UI.Paranoia_help))]
[CommandOption("--paranoia")]
[DefaultValue(false)]
public bool Paranoia { get; init; }
[LocalizedDescription(nameof(UI.Device_path))]
[CommandArgument(0, "<device-path>")]
public string DevicePath { get; init; }