private uint ChecksumCallback(Native.ENetBuffer *buffers, UIntPtr buffersCount) { Checksum.Begin(); byte[] input = null; for (int i = 0; i < buffersCount.ToUInt32(); i++) { var buffer = buffers[i]; if (Checksum.Method == ENetChecksum.ChecksumMethod.Pointer) { Checksum.Sum((byte *)buffer.Data, (int)buffer.DataLength); continue; } if (Checksum.Method == ENetChecksum.ChecksumMethod.Array) { var count = (int)buffer.DataLength.ToUInt32(); if (input == null) { input = ByteArrayPool.Shared.Rent(count); } else if (input.Length < count) { ByteArrayPool.Shared.Return(input); input = ByteArrayPool.Shared.Rent(count); } fixed(byte *dest = input) { Platform.Current.MemoryCopy((IntPtr)dest, buffer.Data, buffer.DataLength); } Checksum.Sum(input, count); continue; } throw new NotImplementedException(Checksum.Method.ToString()); } if (input != null) { ByteArrayPool.Shared.Return(input); } return(Checksum.End()); }
public static int Invoke(bool debug, bool verbose, bool adler32, bool crc16, bool crc32, bool crc64, bool fletcher16, bool fletcher32, bool md5, bool sha1, bool sha256, bool sha384, bool sha512, bool spamSum, string imagePath, bool separatedTracks, bool wholeDisc) { MainClass.PrintCopyright(); if (debug) { AaruConsole.DebugWriteLineEvent += System.Console.Error.WriteLine; } if (verbose) { AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine; } Statistics.AddCommand("checksum"); AaruConsole.DebugWriteLine("Checksum command", "--adler32={0}", adler32); AaruConsole.DebugWriteLine("Checksum command", "--crc16={0}", crc16); AaruConsole.DebugWriteLine("Checksum command", "--crc32={0}", crc32); AaruConsole.DebugWriteLine("Checksum command", "--crc64={0}", crc64); AaruConsole.DebugWriteLine("Checksum command", "--debug={0}", debug); AaruConsole.DebugWriteLine("Checksum command", "--fletcher16={0}", fletcher16); AaruConsole.DebugWriteLine("Checksum command", "--fletcher32={0}", fletcher32); AaruConsole.DebugWriteLine("Checksum command", "--input={0}", imagePath); AaruConsole.DebugWriteLine("Checksum command", "--md5={0}", md5); AaruConsole.DebugWriteLine("Checksum command", "--separated-tracks={0}", separatedTracks); AaruConsole.DebugWriteLine("Checksum command", "--sha1={0}", sha1); AaruConsole.DebugWriteLine("Checksum command", "--sha256={0}", sha256); AaruConsole.DebugWriteLine("Checksum command", "--sha384={0}", sha384); AaruConsole.DebugWriteLine("Checksum command", "--sha512={0}", sha512); AaruConsole.DebugWriteLine("Checksum command", "--spamsum={0}", spamSum); AaruConsole.DebugWriteLine("Checksum command", "--verbose={0}", verbose); AaruConsole.DebugWriteLine("Checksum command", "--whole-disc={0}", wholeDisc); var filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(imagePath); if (inputFilter == null) { AaruConsole.ErrorWriteLine("Cannot open specified file."); return((int)ErrorNumber.CannotOpenFile); } IMediaImage inputFormat = ImageFormat.Detect(inputFilter); if (inputFormat == null) { AaruConsole.ErrorWriteLine("Unable to recognize image format, not checksumming"); return((int)ErrorNumber.UnrecognizedFormat); } inputFormat.Open(inputFilter); Statistics.AddMediaFormat(inputFormat.Format); Statistics.AddMedia(inputFormat.Info.MediaType, false); Statistics.AddFilter(inputFilter.Name); var enabledChecksums = new EnableChecksum(); if (adler32) { enabledChecksums |= EnableChecksum.Adler32; } if (crc16) { enabledChecksums |= EnableChecksum.Crc16; } if (crc32) { enabledChecksums |= EnableChecksum.Crc32; } if (crc64) { enabledChecksums |= EnableChecksum.Crc64; } if (md5) { enabledChecksums |= EnableChecksum.Md5; } if (sha1) { enabledChecksums |= EnableChecksum.Sha1; } if (sha256) { enabledChecksums |= EnableChecksum.Sha256; } if (sha384) { enabledChecksums |= EnableChecksum.Sha384; } if (sha512) { enabledChecksums |= EnableChecksum.Sha512; } if (spamSum) { enabledChecksums |= EnableChecksum.SpamSum; } if (fletcher16) { enabledChecksums |= EnableChecksum.Fletcher16; } if (fletcher32) { enabledChecksums |= EnableChecksum.Fletcher32; } Checksum mediaChecksum = null; switch (inputFormat) { case IOpticalMediaImage opticalInput when opticalInput.Tracks != null: try { Checksum trackChecksum = null; if (wholeDisc) { mediaChecksum = new Checksum(enabledChecksums); } ulong previousTrackEnd = 0; List <Track> inputTracks = opticalInput.Tracks; foreach (Track currentTrack in inputTracks) { /* * if(currentTrack.TrackStartSector - previousTrackEnd != 0 && wholeDisc) * for(ulong i = previousTrackEnd + 1; i < currentTrack.TrackStartSector; i++) * { * AaruConsole.Write("\rHashing track-less sector {0}", i); * * byte[] hiddenSector = inputFormat.ReadSector(i); * * mediaChecksum?.Update(hiddenSector); * } */ AaruConsole.DebugWriteLine("Checksum command", "Track {0} starts at sector {1} and ends at sector {2}", currentTrack.TrackSequence, currentTrack.TrackStartSector, currentTrack.TrackEndSector); if (separatedTracks) { trackChecksum = new Checksum(enabledChecksums); } ulong sectors = (currentTrack.TrackEndSector - currentTrack.TrackStartSector) + 1; ulong doneSectors = 0; AaruConsole.WriteLine("Track {0} has {1} sectors", currentTrack.TrackSequence, sectors); while (doneSectors < sectors) { byte[] sector; if (sectors - doneSectors >= SECTORS_TO_READ) { sector = opticalInput.ReadSectors(doneSectors, SECTORS_TO_READ, currentTrack.TrackSequence); AaruConsole.Write("\rHashing sectors {0} to {2} of track {1}", doneSectors, currentTrack.TrackSequence, doneSectors + SECTORS_TO_READ); doneSectors += SECTORS_TO_READ; } else { sector = opticalInput.ReadSectors(doneSectors, (uint)(sectors - doneSectors), currentTrack.TrackSequence); AaruConsole.Write("\rHashing sectors {0} to {2} of track {1}", doneSectors, currentTrack.TrackSequence, doneSectors + (sectors - doneSectors)); doneSectors += sectors - doneSectors; } if (wholeDisc) { mediaChecksum?.Update(sector); } if (separatedTracks) { trackChecksum?.Update(sector); } } AaruConsole.WriteLine(); if (separatedTracks) { if (trackChecksum != null) { foreach (ChecksumType chk in trackChecksum.End()) { AaruConsole.WriteLine("Track {0}'s {1}: {2}", currentTrack.TrackSequence, chk.type, chk.Value); } } } previousTrackEnd = currentTrack.TrackEndSector; } /* * if(opticalInput.Info.Sectors - previousTrackEnd != 0 && wholeDisc) * for(ulong i = previousTrackEnd + 1; i < opticalInput.Info.Sectors; i++) * { * AaruConsole.Write("\rHashing track-less sector {0}", i); * * byte[] hiddenSector = inputFormat.ReadSector(i); * mediaChecksum?.Update(hiddenSector); * } */ if (wholeDisc) { if (mediaChecksum != null) { foreach (ChecksumType chk in mediaChecksum.End()) { AaruConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value); } } } } catch (Exception ex) { if (debug) { AaruConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message); } else { AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them"); } } break; case ITapeImage tapeImage when tapeImage.IsTape && tapeImage.Files?.Count > 0: { Checksum trackChecksum = null; if (wholeDisc) { mediaChecksum = new Checksum(enabledChecksums); } ulong previousTrackEnd = 0; foreach (TapeFile currentFile in tapeImage.Files) { if (currentFile.FirstBlock - previousTrackEnd != 0 && wholeDisc) { for (ulong i = previousTrackEnd + 1; i < currentFile.FirstBlock; i++) { AaruConsole.Write("\rHashing file-less block {0}", i); byte[] hiddenSector = inputFormat.ReadSector(i); mediaChecksum?.Update(hiddenSector); } } AaruConsole.DebugWriteLine("Checksum command", "Track {0} starts at sector {1} and ends at block {2}", currentFile.File, currentFile.FirstBlock, currentFile.LastBlock); if (separatedTracks) { trackChecksum = new Checksum(enabledChecksums); } ulong sectors = (currentFile.LastBlock - currentFile.FirstBlock) + 1; ulong doneSectors = 0; AaruConsole.WriteLine("File {0} has {1} sectors", currentFile.File, sectors); while (doneSectors < sectors) { byte[] sector; if (sectors - doneSectors >= SECTORS_TO_READ) { sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock, SECTORS_TO_READ); AaruConsole.Write("\rHashing blocks {0} to {2} of file {1}", doneSectors, currentFile.File, doneSectors + SECTORS_TO_READ); doneSectors += SECTORS_TO_READ; } else { sector = tapeImage.ReadSectors(doneSectors + currentFile.FirstBlock, (uint)(sectors - doneSectors)); AaruConsole.Write("\rHashing blocks {0} to {2} of file {1}", doneSectors, currentFile.File, doneSectors + (sectors - doneSectors)); doneSectors += sectors - doneSectors; } if (wholeDisc) { mediaChecksum?.Update(sector); } if (separatedTracks) { trackChecksum?.Update(sector); } } AaruConsole.WriteLine(); if (separatedTracks) { if (trackChecksum != null) { foreach (ChecksumType chk in trackChecksum.End()) { AaruConsole.WriteLine("File {0}'s {1}: {2}", currentFile.File, chk.type, chk.Value); } } } previousTrackEnd = currentFile.LastBlock; } if (tapeImage.Info.Sectors - previousTrackEnd != 0 && wholeDisc) { for (ulong i = previousTrackEnd + 1; i < tapeImage.Info.Sectors; i++) { AaruConsole.Write("\rHashing file-less sector {0}", i); byte[] hiddenSector = inputFormat.ReadSector(i); mediaChecksum?.Update(hiddenSector); } } if (wholeDisc) { if (mediaChecksum != null) { foreach (ChecksumType chk in mediaChecksum.End()) { AaruConsole.WriteLine("Tape's {0}: {1}", chk.type, chk.Value); } } } break; } default: { mediaChecksum = new Checksum(enabledChecksums); ulong sectors = inputFormat.Info.Sectors; AaruConsole.WriteLine("Sectors {0}", sectors); ulong doneSectors = 0; while (doneSectors < sectors) { byte[] sector; if (sectors - doneSectors >= SECTORS_TO_READ) { sector = inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ); AaruConsole.Write("\rHashing sectors {0} to {1}", doneSectors, doneSectors + SECTORS_TO_READ); doneSectors += SECTORS_TO_READ; } else { sector = inputFormat.ReadSectors(doneSectors, (uint)(sectors - doneSectors)); AaruConsole.Write("\rHashing sectors {0} to {1}", doneSectors, doneSectors + (sectors - doneSectors)); doneSectors += sectors - doneSectors; } mediaChecksum.Update(sector); } AaruConsole.WriteLine(); foreach (ChecksumType chk in mediaChecksum.End()) { AaruConsole.WriteLine("Disk's {0}: {1}", chk.type, chk.Value); } break; } } return((int)ErrorNumber.NoError); }
void DoWork() { IOpticalMediaImage opticalMediaImage = inputFormat as IOpticalMediaImage; bool formatHasTracks = false; if (opticalMediaImage != null) { try { formatHasTracks = opticalMediaImage.Tracks?.Count > 0; } catch { formatHasTracks = false; } } // Setup progress bars Application.Instance.Invoke(() => { stkProgress.Visible = true; prgProgress.MaxValue = 1; prgProgress2.MaxValue = (int)(inputFormat.Info.Sectors / SECTORS_TO_READ); if (formatHasTracks && chkChecksumTracks.Checked == true && opticalMediaImage != null) { prgProgress.MaxValue += opticalMediaImage.Tracks.Count; } else { prgProgress.MaxValue = 2; prgProgress2.Visible = false; lblProgress2.Visible = false; } }); EnableChecksum enabledChecksums = new EnableChecksum(); if (chkAdler32.Checked == true) { enabledChecksums |= EnableChecksum.Adler32; } if (chkCrc16.Checked == true) { enabledChecksums |= EnableChecksum.Crc16; } if (chkCrc32.Checked == true) { enabledChecksums |= EnableChecksum.Crc32; } if (chkCrc64.Checked == true) { enabledChecksums |= EnableChecksum.Crc64; } if (chkMd5.Checked == true) { enabledChecksums |= EnableChecksum.Md5; } if (chkSha1.Checked == true) { enabledChecksums |= EnableChecksum.Sha1; } if (chkSha256.Checked == true) { enabledChecksums |= EnableChecksum.Sha256; } if (chkSha384.Checked == true) { enabledChecksums |= EnableChecksum.Sha384; } if (chkSha512.Checked == true) { enabledChecksums |= EnableChecksum.Sha512; } if (chkSpamsum.Checked == true) { enabledChecksums |= EnableChecksum.SpamSum; } if (chkFletcher16.Checked == true) { enabledChecksums |= EnableChecksum.Fletcher16; } if (chkFletcher32.Checked == true) { enabledChecksums |= EnableChecksum.Fletcher32; } Checksum mediaChecksum = null; TreeGridItemCollection trackHashes = new TreeGridItemCollection(); TreeGridItemCollection mediaHashes = new TreeGridItemCollection(); if (opticalMediaImage != null) { try { Checksum trackChecksum = null; if (chkChecksumMedia.Checked == true) { mediaChecksum = new Checksum(enabledChecksums); } ulong previousTrackEnd = 0; foreach (Track currentTrack in opticalMediaImage.Tracks) { Application.Instance.Invoke(() => { lblProgress.Text = $"Hashing track {currentTrack.TrackSequence} of {opticalMediaImage.Tracks.Count}"; prgProgress.Value++; }); if (currentTrack.TrackStartSector - previousTrackEnd != 0 && chkChecksumMedia.Checked == true) { for (ulong i = previousTrackEnd + 1; i < currentTrack.TrackStartSector; i++) { ulong sector = i; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(sector / SECTORS_TO_READ); lblProgress2.Text = $"Hashing track-less sector {sector}"; }); byte[] hiddenSector = opticalMediaImage.ReadSector(i); mediaChecksum?.Update(hiddenSector); } } DicConsole.DebugWriteLine("Checksum command", "Track {0} starts at sector {1} and ends at sector {2}", currentTrack.TrackSequence, currentTrack.TrackStartSector, currentTrack.TrackEndSector); if (chkChecksumTracks.Checked == true) { trackChecksum = new Checksum(enabledChecksums); } ulong sectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector + 1; ulong doneSectors = 0; while (doneSectors < sectors) { if (cancel) { Application.Instance.Invoke(() => { btnClose.Visible = true; btnStart.Visible = false; btnStop.Visible = false; }); return; } byte[] sector; if (sectors - doneSectors >= SECTORS_TO_READ) { sector = opticalMediaImage.ReadSectors(doneSectors, SECTORS_TO_READ, currentTrack.TrackSequence); ulong doneSectorsToInvoke = doneSectors; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); lblProgress2.Text = $"Hashings sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ} of track {currentTrack.TrackSequence}"; }); doneSectors += SECTORS_TO_READ; } else { sector = opticalMediaImage.ReadSectors(doneSectors, (uint)(sectors - doneSectors), currentTrack.TrackSequence); ulong doneSectorsToInvoke = doneSectors; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); lblProgress2.Text = $"Hashings sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (sectors - doneSectorsToInvoke)} of track {currentTrack.TrackSequence}"; }); doneSectors += sectors - doneSectors; } if (chkChecksumMedia.Checked == true) { mediaChecksum?.Update(sector); } if (chkChecksumTracks.Checked == true) { trackChecksum?.Update(sector); } } if (chkChecksumTracks.Checked == true) { if (trackChecksum != null) { foreach (ChecksumType chk in trackChecksum.End()) { trackHashes.Add(new TreeGridItem { Values = new object[] { currentTrack.TrackSequence, chk.type, chk.Value } }); } } } previousTrackEnd = currentTrack.TrackEndSector; } if (opticalMediaImage.Info.Sectors - previousTrackEnd != 0 && chkChecksumMedia.Checked == true) { for (ulong i = previousTrackEnd + 1; i < opticalMediaImage.Info.Sectors; i++) { ulong sector = i; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(sector / SECTORS_TO_READ); lblProgress2.Text = $"Hashing track-less sector {sector}"; }); byte[] hiddenSector = opticalMediaImage.ReadSector(i); mediaChecksum?.Update(hiddenSector); } } if (chkChecksumMedia.Checked == true) { if (mediaChecksum != null) { foreach (ChecksumType chk in mediaChecksum.End()) { mediaHashes.Add(new TreeGridItem { Values = new object[] { chk.type, chk.Value } }); } } } } catch (Exception ex) { DicConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message); DicConsole.WriteLine("Unable to get separate tracks, not checksumming them"); } } else { Application.Instance.Invoke(() => { stkProgress1.Visible = false; }); mediaChecksum = new Checksum(enabledChecksums); ulong doneSectors = 0; while (doneSectors < inputFormat.Info.Sectors) { if (cancel) { Application.Instance.Invoke(() => { btnClose.Visible = true; btnStart.Visible = false; btnStop.Visible = false; }); return; } byte[] sector; if (inputFormat.Info.Sectors - doneSectors >= SECTORS_TO_READ) { sector = inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ); ulong doneSectorsToInvoke = doneSectors; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); lblProgress2.Text = $"Hashings sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ}"; }); doneSectors += SECTORS_TO_READ; } else { sector = inputFormat.ReadSectors(doneSectors, (uint)(inputFormat.Info.Sectors - doneSectors)); ulong doneSectorsToInvoke = doneSectors; Application.Instance.Invoke(() => { prgProgress2.Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); lblProgress2.Text = $"Hashings sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (inputFormat.Info.Sectors - doneSectorsToInvoke)}"; }); doneSectors += inputFormat.Info.Sectors - doneSectors; } mediaChecksum.Update(sector); } foreach (ChecksumType chk in mediaChecksum.End()) { mediaHashes.Add(new TreeGridItem { Values = new object[] { chk.type, chk.Value } }); } } if (chkChecksumTracks.Checked == true) { Application.Instance.Invoke(() => { grpTrackChecksums.Text = "Track checksums:"; stkTrackChecksums.Visible = true; treeTrackChecksums.Columns.Add(new GridColumn { HeaderText = "Track", DataCell = new TextBoxCell(0) }); treeTrackChecksums.Columns.Add(new GridColumn { HeaderText = "Algorithm", DataCell = new TextBoxCell(1) }); treeTrackChecksums.Columns.Add(new GridColumn { HeaderText = "Hash", DataCell = new TextBoxCell(2) }); treeTrackChecksums.AllowMultipleSelection = false; treeTrackChecksums.ShowHeader = true; treeTrackChecksums.DataStore = trackHashes; }); } if (chkChecksumMedia.Checked == true) { Application.Instance.Invoke(() => { grpMediaChecksums.Text = "Media checksums:"; stkMediaChecksums.Visible = true; treeMediaChecksums.Columns.Add(new GridColumn { HeaderText = "Algorithm", DataCell = new TextBoxCell(0) }); treeMediaChecksums.Columns.Add(new GridColumn { HeaderText = "Hash", DataCell = new TextBoxCell(1) }); treeMediaChecksums.AllowMultipleSelection = false; treeMediaChecksums.ShowHeader = true; treeMediaChecksums.DataStore = mediaHashes; }); } Statistics.AddCommand("checksum"); Application.Instance.Invoke(() => { stkOptions.Visible = false; stkResults.Visible = true; stkProgress.Visible = false; btnStart.Visible = false; btnStop.Visible = false; btnClose.Visible = true; }); }
/// <summary> /// Dumps the tape from a SCSI Streaming device /// </summary> /// <param name="dev">Device</param> /// <param name="devicePath">Path to the device</param> /// <param name="outputPrefix">Prefix for output data files</param> /// <param name="resume">Information for dump resuming</param> /// <param name="dumpLog">Dump logger</param> internal static void Dump(Device dev, string outputPrefix, string devicePath, ref Resume resume, ref DumpLog dumpLog, CICMMetadataType preSidecar) { FixedSense? fxSense; bool aborted; bool sense; ulong blocks = 0; uint blockSize; MediaType dskType = MediaType.Unknown; DateTime start; DateTime end; double totalDuration = 0; double totalChkDuration = 0; double currentSpeed = 0; double maxSpeed = double.MinValue; double minSpeed = double.MaxValue; CICMMetadataType sidecar = preSidecar ?? new CICMMetadataType(); dev.RequestSense(out byte[] senseBuf, dev.Timeout, out double duration); fxSense = Sense.DecodeFixed(senseBuf, out string strSense); if (fxSense.HasValue && fxSense.Value.SenseKey != SenseKeys.NoSense) { dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); DicConsole.ErrorWriteLine("Drive has status error, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); return; } // Not in BOM/P if (fxSense.HasValue && fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x00 && fxSense.Value.ASCQ != 0x04 && fxSense.Value.SenseKey != SenseKeys.IllegalRequest) { dumpLog.WriteLine("Rewinding, please wait..."); DicConsole.Write("Rewinding, please wait..."); // Rewind, let timeout apply dev.Rewind(out senseBuf, dev.Timeout, out duration); // Still rewinding? // TODO: Pause? do { DicConsole.Write("\rRewinding, please wait..."); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ != 0x04)); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); // And yet, did not rewind! if (fxSense.HasValue && (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 || fxSense.Value.ASC != 0x00)) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } DicConsole.WriteLine(); } // Check position sense = dev.ReadPosition(out byte[] cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout, out duration); if (sense) { // READ POSITION is mandatory starting SCSI-2, so do not cry if the drive does not recognize the command (SCSI-1 or earlier) // Anyway, <=SCSI-1 tapes do not support partitions fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.HasValue && (fxSense.Value.ASC == 0x20 && fxSense.Value.ASCQ != 0x00 || fxSense.Value.ASC != 0x20 && fxSense.Value.SenseKey != SenseKeys.IllegalRequest)) { DicConsole.ErrorWriteLine("Could not get position. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Could not get position. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { // Not in partition 0 if (cmdBuf[1] != 0) { DicConsole.Write("Drive not in partition 0. Rewinding, please wait..."); dumpLog.WriteLine("Drive not in partition 0. Rewinding, please wait..."); // Rewind, let timeout apply sense = dev.Locate(out senseBuf, false, 0, 0, dev.Timeout, out duration); if (sense) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } // Still rewinding? // TODO: Pause? do { Thread.Sleep(1000); DicConsole.Write("\rRewinding, please wait..."); dev.RequestSense(out senseBuf, dev.Timeout, out duration); fxSense = Sense.DecodeFixed(senseBuf, out strSense); }while(fxSense.HasValue && fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x1A || fxSense.Value.ASCQ == 0x19)); // And yet, did not rewind! if (fxSense.HasValue && (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ != 0x04 || fxSense.Value.ASC != 0x00)) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } sense = dev.ReadPosition(out cmdBuf, out senseBuf, SscPositionForms.Short, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.ErrorWriteLine("Drive could not rewind, please correct. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } // Still not in partition 0!!!? if (cmdBuf[1] != 0) { DicConsole.ErrorWriteLine("Drive could not rewind to partition 0 but no error occurred..."); dumpLog.WriteLine("Drive could not rewind to partition 0 but no error occurred..."); return; } DicConsole.WriteLine(); } } sidecar.BlockMedia = new BlockMediaType[1]; sidecar.BlockMedia[0] = new BlockMediaType { SCSI = new SCSIType() }; byte scsiMediumTypeTape = 0; byte scsiDensityCodeTape = 0; dumpLog.WriteLine("Requesting MODE SENSE (10)."); sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out duration); if (!sense || dev.Error) { sense = dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); } Modes.DecodedMode?decMode = null; if (!sense && !dev.Error) { if (Modes.DecodeMode10(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode10(cmdBuf, dev.ScsiType); sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType { Image = outputPrefix + ".modesense10.bin", Size = cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense10.Image, cmdBuf); } } dumpLog.WriteLine("Requesting MODE SENSE (6)."); sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); if (sense || dev.Error) { sense = dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out duration); } if (sense || dev.Error) { sense = dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration); } if (!sense && !dev.Error) { if (Modes.DecodeMode6(cmdBuf, dev.ScsiType).HasValue) { decMode = Modes.DecodeMode6(cmdBuf, dev.ScsiType); sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType { Image = outputPrefix + ".modesense.bin", Size = cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() }; DataFile.WriteTo("SCSI Dump", sidecar.BlockMedia[0].SCSI.ModeSense.Image, cmdBuf); } } // TODO: Check partitions page if (decMode.HasValue) { scsiMediumTypeTape = (byte)decMode.Value.Header.MediumType; if (decMode.Value.Header.BlockDescriptors != null && decMode.Value.Header.BlockDescriptors.Length >= 1) { scsiDensityCodeTape = (byte)decMode.Value.Header.BlockDescriptors[0].Density; } blockSize = decMode.Value.Header.BlockDescriptors[0].BlockLength; dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); } else { blockSize = 1; } if (dskType == MediaType.Unknown) { dskType = MediaTypeFromScsi.Get((byte)dev.ScsiType, dev.Manufacturer, dev.Model, scsiMediumTypeTape, scsiDensityCodeTape, blocks, blockSize); } DicConsole.WriteLine("Media identified as {0}", dskType); dumpLog.WriteLine("SCSI device type: {0}.", dev.ScsiType); dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumTypeTape); dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCodeTape); dumpLog.WriteLine("Media identified as {0}.", dskType); bool endOfMedia = false; ulong currentBlock = 0; ulong currentFile = 0; byte currentPartition = 0; byte totalPartitions = 1; // TODO: Handle partitions. ulong currentSize = 0; ulong currentPartitionSize = 0; ulong currentFileSize = 0; bool fixedLen = false; uint transferLen = blockSize; sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.HasValue) { if (fxSense.Value.SenseKey == SenseKeys.IllegalRequest) { sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (!fxSense.HasValue || !fxSense.Value.EOM) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not return back. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not return back. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } fixedLen = true; transferLen = 1; sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); if (sense) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not read. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not read. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not read. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not read. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } else { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot read device, don't know why, exiting..."); dumpLog.WriteLine("Cannot read device, don't know why, exiting..."); return; } } sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (!fxSense.HasValue || !fxSense.Value.EOM) { DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not return back. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpLog.WriteLine("Drive could not return back. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } } List <TapePartitionType> partitions = new List <TapePartitionType>(); List <TapeFileType> files = new List <TapeFileType>(); DicConsole.WriteLine(); DataFile dumpFile = new DataFile(outputPrefix + ".bin"); Checksum dataChk = new Checksum(); start = DateTime.UtcNow; MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, 1); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", 0x0008); TapeFileType currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; Checksum fileChk = new Checksum(); TapePartitionType currentTapePartition = new TapePartitionType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = currentPartition, StartBlock = (long)currentBlock }; Checksum partitionChk = new Checksum(); aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; while (currentPartition < totalPartitions) { if (aborted) { dumpLog.WriteLine("Aborted!"); break; } if (endOfMedia) { DicConsole.WriteLine(); DicConsole.WriteLine("Finished partition {0}", currentPartition); dumpLog.WriteLine("Finished partition {0}", currentPartition); currentTapePartition.File = files.ToArray(); currentTapePartition.Checksums = partitionChk.End().ToArray(); currentTapePartition.EndBlock = (long)(currentBlock - 1); currentTapePartition.Size = (long)currentPartitionSize; partitions.Add(currentTapePartition); currentPartition++; if (currentPartition < totalPartitions) { currentFile++; currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; currentFileSize = 0; fileChk = new Checksum(); files = new List <TapeFileType>(); currentTapePartition = new TapePartitionType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = currentPartition, StartBlock = (long)currentBlock }; currentPartitionSize = 0; partitionChk = new Checksum(); DicConsole.WriteLine("Seeking to partition {0}", currentPartition); dev.Locate(out senseBuf, false, currentPartition, 0, dev.Timeout, out duration); totalDuration += duration; } continue; } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator if (currentSpeed > maxSpeed && currentSpeed != 0) { maxSpeed = currentSpeed; } if (currentSpeed < minSpeed && currentSpeed != 0) { minSpeed = currentSpeed; } #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator DicConsole.Write("\rReading block {0} ({1:F3} MiB/sec.)", currentBlock, currentSpeed); sense = dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, dev.Timeout, out duration); totalDuration += duration; if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); if (fxSense.Value.ASC == 0x00 && fxSense.Value.ASCQ == 0x00 && fxSense.Value.ILI && fxSense.Value.InformationValid) { blockSize = (uint)((int)blockSize - BitConverter.ToInt32(BitConverter.GetBytes(fxSense.Value.Information), 0)); currentTapeFile.BlockSize = blockSize; DicConsole.WriteLine(); DicConsole.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); sense = dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, dev.Timeout, out duration); totalDuration += duration; if (sense) { fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Drive could not go back one block. Sense follows..."); DicConsole.ErrorWriteLine("{0}", strSense); dumpFile.Close(); dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } continue; } switch (fxSense.Value.SenseKey) { case SenseKeys.BlankCheck when currentBlock == 0: DicConsole.WriteLine(); DicConsole.ErrorWriteLine("Cannot dump a blank tape..."); dumpFile.Close(); dumpLog.WriteLine("Cannot dump a blank tape..."); return; // For sure this is an end-of-tape/partition case SenseKeys.BlankCheck when fxSense.Value.ASC == 0x00 && (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM): // TODO: Detect end of partition endOfMedia = true; dumpLog.WriteLine("Found end-of-tape/partition..."); continue; case SenseKeys.BlankCheck: DicConsole.WriteLine(); DicConsole.WriteLine("Blank block found, end of tape?"); endOfMedia = true; dumpLog.WriteLine("Blank block found, end of tape?..."); continue; } if ((fxSense.Value.SenseKey == SenseKeys.NoSense || fxSense.Value.SenseKey == SenseKeys.RecoveredError) && (fxSense.Value.ASCQ == 0x02 || fxSense.Value.ASCQ == 0x05 || fxSense.Value.EOM)) { // TODO: Detect end of partition endOfMedia = true; dumpLog.WriteLine("Found end-of-tape/partition..."); continue; } if ((fxSense.Value.SenseKey == SenseKeys.NoSense || fxSense.Value.SenseKey == SenseKeys.RecoveredError) && (fxSense.Value.ASCQ == 0x01 || fxSense.Value.Filemark)) { currentTapeFile.Checksums = fileChk.End().ToArray(); currentTapeFile.EndBlock = (long)(currentBlock - 1); currentTapeFile.Size = (long)currentFileSize; files.Add(currentTapeFile); currentFile++; currentTapeFile = new TapeFileType { Image = new ImageType { format = "BINARY", offset = (long)currentSize, offsetSpecified = true, Value = outputPrefix + ".bin" }, Sequence = (long)currentFile, StartBlock = (long)currentBlock, BlockSize = blockSize }; currentFileSize = 0; fileChk = new Checksum(); DicConsole.WriteLine(); DicConsole.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); dumpLog.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); continue; } // TODO: Add error recovering for tapes fxSense = Sense.DecodeFixed(senseBuf, out strSense); DicConsole.ErrorWriteLine("Drive could not read block. Sense follows..."); DicConsole.ErrorWriteLine("{0} {1}", fxSense.Value.SenseKey, strSense); dumpLog.WriteLine("Drive could not read block. Sense follows..."); dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", fxSense.Value.SenseKey, fxSense.Value.ASC, fxSense.Value.ASCQ); return; } mhddLog.Write(currentBlock, duration); ibgLog.Write(currentBlock, currentSpeed * 1024); dumpFile.Write(cmdBuf); DateTime chkStart = DateTime.UtcNow; dataChk.Update(cmdBuf); fileChk.Update(cmdBuf); partitionChk.Update(cmdBuf); DateTime chkEnd = DateTime.UtcNow; double chkDuration = (chkEnd - chkStart).TotalMilliseconds; totalChkDuration += chkDuration; if (currentBlock % 10 == 0) { double newSpeed = blockSize / (double)1048576 / (duration / 1000); if (!double.IsInfinity(newSpeed)) { currentSpeed = newSpeed; } } currentBlock++; currentSize += blockSize; currentFileSize += blockSize; currentPartitionSize += blockSize; } blocks = currentBlock + 1; DicConsole.WriteLine(); end = DateTime.UtcNow; mhddLog.Close(); ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), devicePath); dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming).", (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000); DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.", (double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000)); DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed); DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed); sidecar.BlockMedia[0].Checksums = dataChk.End().ToArray(); sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); CommonTypes.Metadata.MediaType.MediaTypeToString(dskType, out string xmlDskTyp, out string xmlDskSubTyp); sidecar.BlockMedia[0].DiskType = xmlDskTyp; sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp; // TODO: Implement device firmware revision sidecar.BlockMedia[0].Image = new ImageType { format = "Raw disk image (sector by sector copy)", Value = outputPrefix + ".bin" }; sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; sidecar.BlockMedia[0].Size = (long)currentSize; sidecar.BlockMedia[0].DumpHardwareArray = new DumpHardwareType[1]; sidecar.BlockMedia[0].DumpHardwareArray[0] = new DumpHardwareType { Extents = new ExtentType[1] }; sidecar.BlockMedia[0].DumpHardwareArray[0].Extents[0] = new ExtentType { Start = 0, End = blocks - 1 }; sidecar.BlockMedia[0].DumpHardwareArray[0].Manufacturer = dev.Manufacturer; sidecar.BlockMedia[0].DumpHardwareArray[0].Model = dev.Model; sidecar.BlockMedia[0].DumpHardwareArray[0].Revision = dev.Revision; sidecar.BlockMedia[0].DumpHardwareArray[0].Serial = dev.Serial; sidecar.BlockMedia[0].DumpHardwareArray[0].Software = Version.GetSoftwareType(); sidecar.BlockMedia[0].TapeInformation = partitions.ToArray(); if (!aborted) { DicConsole.WriteLine("Writing metadata sidecar"); FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create); XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType)); xmlSer.Serialize(xmlFs, sidecar); xmlFs.Close(); } Statistics.AddMedia(dskType, true); }