Пример #1
0
        ScanResults Scsi()
        {
            ScanResults results = new ScanResults();
            MhddLog     mhddLog;
            IbgLog      ibgLog;

            byte[] senseBuf;
            bool   sense = false;

            results.Blocks = 0;
            uint   blockSize      = 0;
            ushort currentProfile = 0x0001;

            if (dev.IsRemovable)
            {
                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                if (sense)
                {
                    InitProgress?.Invoke();
                    FixedSense?decSense = Sense.DecodeFixed(senseBuf);
                    if (decSense.HasValue)
                    {
                        if (decSense.Value.ASC == 0x3A)
                        {
                            int leftRetries = 5;
                            while (leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage?.Invoke("Please insert media in drive");
                                return(results);
                            }
                        }
                        else if (decSense.Value.ASC == 0x04 && decSense.Value.ASCQ == 0x01)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage
                                ?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
                                return(results);
                            }
                        }
                        // These should be trapped by the OS but seems in some cases they're not
                        else if (decSense.Value.ASC == 0x28)
                        {
                            int leftRetries = 10;
                            while (leftRetries > 0)
                            {
                                PulseProgress?.Invoke("Waiting for drive to become ready");
                                Thread.Sleep(2000);
                                sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _);
                                if (!sense)
                                {
                                    break;
                                }

                                leftRetries--;
                            }

                            if (sense)
                            {
                                StoppingErrorMessage
                                ?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
                                return(results);
                            }
                        }
                        else
                        {
                            StoppingErrorMessage
                            ?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
                            return(results);
                        }
                    }
                    else
                    {
                        StoppingErrorMessage?.Invoke("Unknown testing unit was ready.");
                        return(results);
                    }

                    EndProgress?.Invoke();
                }
            }

            Reader scsiReader = null;

            switch (dev.ScsiType)
            {
            case PeripheralDeviceTypes.DirectAccess:
            case PeripheralDeviceTypes.MultiMediaDevice:
            case PeripheralDeviceTypes.OCRWDevice:
            case PeripheralDeviceTypes.OpticalDevice:
            case PeripheralDeviceTypes.SimplifiedDevice:
            case PeripheralDeviceTypes.WriteOnceDevice:
                scsiReader     = new Reader(dev, dev.Timeout, null);
                results.Blocks = scsiReader.GetDeviceBlocks();
                if (scsiReader.FindReadCommand())
                {
                    StoppingErrorMessage?.Invoke("Unable to read medium.");
                    return(results);
                }

                blockSize = scsiReader.LogicalBlockSize;

                if (results.Blocks != 0 && blockSize != 0)
                {
                    results.Blocks++;
                    UpdateStatus
                    ?.Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {results.Blocks * (ulong)blockSize} bytes)");
                }

                break;

            case PeripheralDeviceTypes.SequentialAccess:
                StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." +
                                             Environment.NewLine +
                                             "It has no sense to do it, and it will put too much strain on the tape.");
                return(results);
            }

            if (results.Blocks == 0)
            {
                StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");
                return(results);
            }

            bool compactDisc = true;

            FullTOC.CDFullTOC?toc = null;

            if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
            {
                sense = dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current,
                                             dev.Timeout, out _);
                if (!sense)
                {
                    Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);

                    currentProfile = ftr.CurrentProfile;

                    switch (ftr.CurrentProfile)
                    {
                    case 0x0005:
                    case 0x0008:
                    case 0x0009:
                    case 0x000A:
                    case 0x0020:
                    case 0x0021:
                    case 0x0022: break;

                    default:
                        compactDisc = false;
                        break;
                    }
                }

                if (compactDisc)
                {
                    currentProfile = 0x0008;
                    // We discarded all discs that falsify a TOC before requesting a real TOC
                    // No TOC, no CD (or an empty one)
                    bool tocSense = dev.ReadRawToc(out cmdBuf, out senseBuf, 1, dev.Timeout, out _);
                    if (!tocSense)
                    {
                        toc = FullTOC.Decode(cmdBuf);
                    }
                }
            }
            else
            {
                compactDisc = false;
            }

            uint blocksToRead = 64;

            results.A       = 0; // <3ms
            results.B       = 0; // >=3ms, <10ms
            results.C       = 0; // >=10ms, <50ms
            results.D       = 0; // >=50ms, <150ms
            results.E       = 0; // >=150ms, <500ms
            results.F       = 0; // >=500ms
            results.Errored = 0;
            DateTime start;
            DateTime end;

            results.ProcessingTime = 0;
            results.TotalTime      = 0;
            double currentSpeed = 0;

            results.MaxSpeed          = double.MinValue;
            results.MinSpeed          = double.MaxValue;
            results.UnreadableSectors = new List <ulong>();

            if (compactDisc)
            {
                if (toc == null)
                {
                    StoppingErrorMessage?.Invoke("Error trying to decode TOC...");
                    return(results);
                }

                bool readcd = !dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
                                          MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
                                          dev.Timeout, out _);

                if (readcd)
                {
                    UpdateStatus?.Invoke("Using MMC READ CD command.");
                }

                start = DateTime.UtcNow;

                while (true)
                {
                    if (readcd)
                    {
                        sense = dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false,
                                           false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
                                           MmcSubchannel.None, dev.Timeout, out _);
                        if (dev.Error)
                        {
                            blocksToRead /= 2;
                        }
                    }

                    if (!dev.Error || blocksToRead == 1)
                    {
                        break;
                    }
                }

                if (dev.Error)
                {
                    StoppingErrorMessage
                    ?.Invoke($"Device error {dev.LastError} trying to guess ideal transfer length.");
                    return(results);
                }

                UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile);
                mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                ibgLog  = new IbgLog(ibgLogPath, currentProfile);
                DateTime timeSpeedStart   = DateTime.UtcNow;
                ulong    sectorSpeedStart = 0;

                InitProgress?.Invoke();
                for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                {
                    if (aborted)
                    {
                        break;
                    }

                    double cmdDuration = 0;

                    if (results.Blocks - i < blocksToRead)
                    {
                        blocksToRead = (uint)(results.Blocks - i);
                    }

                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                    if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                    {
                        results.MaxSpeed = currentSpeed;
                    }
                    if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                    {
                        results.MinSpeed = currentSpeed;
                    }
                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                    UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
                                           (long)i, (long)results.Blocks);

                    if (readcd)
                    {
                        sense = dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes,
                                           false, false, true, MmcHeaderCodes.AllHeaders, true, true,
                                           MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out cmdDuration);
                        results.ProcessingTime += cmdDuration;
                    }

                    if (!sense)
                    {
                        if (cmdDuration >= 500)
                        {
                            results.F += blocksToRead;
                        }
                        else if (cmdDuration >= 150)
                        {
                            results.E += blocksToRead;
                        }
                        else if (cmdDuration >= 50)
                        {
                            results.D += blocksToRead;
                        }
                        else if (cmdDuration >= 10)
                        {
                            results.C += blocksToRead;
                        }
                        else if (cmdDuration >= 3)
                        {
                            results.B += blocksToRead;
                        }
                        else
                        {
                            results.A += blocksToRead;
                        }

                        ScanTime?.Invoke(i, cmdDuration);
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }
                    else
                    {
                        DicConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", Sense.PrettifySense(senseBuf));

                        FixedSense?senseDecoded = Sense.DecodeFixed(senseBuf);
                        if (senseDecoded.HasValue)
                        {
                            // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly
                            // LOGICAL BLOCK ADDRESS OUT OF RANGE
                            if ((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) &&
                                // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors
                                // are in a track where subchannel indicates data)
                                (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00))
                            {
                                results.Errored += blocksToRead;
                                for (ulong b = i; b < i + blocksToRead; b++)
                                {
                                    results.UnreadableSectors.Add(b);
                                }

                                ScanUnreadable?.Invoke(i);
                                mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                                ibgLog.Write(i, 0);
                            }
                        }
                        else
                        {
                            ScanUnreadable?.Invoke(i);
                            results.Errored += blocksToRead;
                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

                            mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);

                            ibgLog.Write(i, 0);
                        }
                    }

                    sectorSpeedStart += blocksToRead;

                    double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
                    if (elapsed < 1)
                    {
                        continue;
                    }

                    currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
                    ScanSpeed?.Invoke(i, currentSpeed * 1024);
                    sectorSpeedStart = 0;
                    timeSpeedStart   = DateTime.UtcNow;
                }

                end = DateTime.UtcNow;
                EndProgress?.Invoke();
                mhddLog.Close();
                ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                             blockSize * (double)(results.Blocks + 1) / 1024 /
                             (results.ProcessingTime / 1000),
                             devicePath);
            }
            else
            {
                start = DateTime.UtcNow;

                UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile);
                mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                ibgLog  = new IbgLog(ibgLogPath, currentProfile);
                DateTime timeSpeedStart   = DateTime.UtcNow;
                ulong    sectorSpeedStart = 0;

                InitProgress?.Invoke();
                for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                {
                    if (aborted)
                    {
                        break;
                    }

                    if (results.Blocks - i < blocksToRead)
                    {
                        blocksToRead = (uint)(results.Blocks - i);
                    }

                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                    if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                    {
                        results.MaxSpeed = currentSpeed;
                    }
                    if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                    {
                        results.MinSpeed = currentSpeed;
                    }
                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                    UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
                                           (long)i, (long)results.Blocks);

                    sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration);
                    results.ProcessingTime += cmdDuration;

                    if (!sense && !dev.Error)
                    {
                        if (cmdDuration >= 500)
                        {
                            results.F += blocksToRead;
                        }
                        else if (cmdDuration >= 150)
                        {
                            results.E += blocksToRead;
                        }
                        else if (cmdDuration >= 50)
                        {
                            results.D += blocksToRead;
                        }
                        else if (cmdDuration >= 10)
                        {
                            results.C += blocksToRead;
                        }
                        else if (cmdDuration >= 3)
                        {
                            results.B += blocksToRead;
                        }
                        else
                        {
                            results.A += blocksToRead;
                        }

                        ScanTime?.Invoke(i, cmdDuration);
                        mhddLog.Write(i, cmdDuration);
                        ibgLog.Write(i, currentSpeed * 1024);
                    }
                    // TODO: Separate errors on kind of errors.
                    else
                    {
                        ScanUnreadable?.Invoke(i);
                        results.Errored += blocksToRead;
                        for (ulong b = i; b < i + blocksToRead; b++)
                        {
                            results.UnreadableSectors.Add(b);
                        }

                        mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
                        ibgLog.Write(i, 0);
                    }

                    sectorSpeedStart += blocksToRead;

                    double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
                    if (elapsed < 1)
                    {
                        continue;
                    }

                    currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
                    ScanSpeed?.Invoke(i, currentSpeed * 1024);
                    sectorSpeedStart = 0;
                    timeSpeedStart   = DateTime.UtcNow;
                }

                end = DateTime.UtcNow;
                EndProgress?.Invoke();
                mhddLog.Close();
                ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                             blockSize * (double)(results.Blocks + 1) / 1024 /
                             (results.ProcessingTime / 1000),
                             devicePath);
            }

            results.SeekMax   = double.MinValue;
            results.SeekMin   = double.MaxValue;
            results.SeekTotal = 0;
            const int SEEK_TIMES = 1000;

            Random rnd = new Random();

            InitProgress?.Invoke();
            for (int i = 0; i < SEEK_TIMES; i++)
            {
                if (aborted)
                {
                    break;
                }

                uint seekPos = (uint)rnd.Next((int)results.Blocks);

                PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                double seekCur;
                if (scsiReader.CanSeek)
                {
                    scsiReader.Seek(seekPos, out seekCur);
                }
                else
                {
                    scsiReader.ReadBlock(out _, seekPos, out seekCur);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (seekCur > results.SeekMax && seekCur != 0)
                {
                    results.SeekMax = seekCur;
                }
                if (seekCur < results.SeekMin && seekCur != 0)
                {
                    results.SeekMin = seekCur;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                results.SeekTotal += seekCur;
                GC.Collect();
            }

            EndProgress?.Invoke();

            results.ProcessingTime /= 1000;
            results.TotalTime       = (end - start).TotalSeconds;
            results.AvgSpeed        = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
            results.SeekTimes       = SEEK_TIMES;

            return(results);
        }
Пример #2
0
        /// <summary>
        ///     Scans the media from an ATA device
        /// </summary>
        /// <param name="mhddLogPath">Path to a MHDD log file</param>
        /// <param name="ibgLogPath">Path to a IMGBurn log file</param>
        /// <param name="devicePath">Device path</param>
        /// <param name="dev">Device</param>
        /// <returns>Scanning results</returns>
        public static ScanResults Scan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev)
        {
            ScanResults results = new ScanResults();
            bool        aborted;
            bool        sense;

            results.Blocks = 0;
            const ushort ATA_PROFILE = 0x0001;
            const uint   TIMEOUT     = 5;

            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);
            if (!sense && Identify.Decode(cmdBuf).HasValue)
            {
                // Initializate reader
                Reader ataReader = new Reader(dev, TIMEOUT, cmdBuf);
                // Fill reader blocks
                results.Blocks = ataReader.GetDeviceBlocks();
                if (ataReader.FindReadCommand())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                // Check block sizes
                if (ataReader.GetBlockSize())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                uint blockSize = ataReader.LogicalBlockSize;
                // Check how many blocks to read, if error show and return
                if (ataReader.GetBlocksToRead())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                uint   blocksToRead = ataReader.BlocksToRead;
                ushort cylinders    = ataReader.Cylinders;
                byte   heads        = ataReader.Heads;
                byte   sectors      = ataReader.Sectors;

                results.A       = 0; // <3ms
                results.B       = 0; // >=3ms, <10ms
                results.C       = 0; // >=10ms, <50ms
                results.D       = 0; // >=50ms, <150ms
                results.E       = 0; // >=150ms, <500ms
                results.F       = 0; // >=500ms
                results.Errored = 0;
                DateTime start;
                DateTime end;
                results.ProcessingTime = 0;
                double currentSpeed = 0;
                results.MaxSpeed          = double.MinValue;
                results.MinSpeed          = double.MaxValue;
                results.UnreadableSectors = new List <ulong>();
                results.SeekMax           = double.MinValue;
                results.SeekMin           = double.MaxValue;
                results.SeekTotal         = 0;
                const int SEEK_TIMES = 1000;

                double seekCur;

                Random rnd = new Random();

                aborted = false;
                System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

                MhddLog mhddLog;
                IbgLog  ibgLog;
                double  duration;
                if (ataReader.IsLba)
                {
                    DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    start = DateTime.UtcNow;
                    for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                    {
                        if (aborted)
                        {
                            break;
                        }

                        if (results.Blocks - i < blocksToRead)
                        {
                            blocksToRead = (byte)(results.Blocks - i);
                        }

                        #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                        if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                        {
                            results.MaxSpeed = currentSpeed;
                        }
                        if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                        {
                            results.MinSpeed = currentSpeed;
                        }
                        #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                        DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks,
                                         currentSpeed);

                        bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                        if (!error)
                        {
                            if (duration >= 500)
                            {
                                results.F += blocksToRead;
                            }
                            else if (duration >= 150)
                            {
                                results.E += blocksToRead;
                            }
                            else if (duration >= 50)
                            {
                                results.D += blocksToRead;
                            }
                            else if (duration >= 10)
                            {
                                results.C += blocksToRead;
                            }
                            else if (duration >= 3)
                            {
                                results.B += blocksToRead;
                            }
                            else
                            {
                                results.A += blocksToRead;
                            }

                            mhddLog.Write(i, duration);
                            ibgLog.Write(i, currentSpeed * 1024);
                        }
                        else
                        {
                            results.Errored += blocksToRead;
                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

                            mhddLog.Write(i, duration < 500 ? 65535 : duration);

                            ibgLog.Write(i, 0);
                        }

                        double newSpeed =
                            (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
                        if (!double.IsInfinity(newSpeed))
                        {
                            currentSpeed = newSpeed;
                        }
                    }

                    end = DateTime.UtcNow;
                    DicConsole.WriteLine();
                    mhddLog.Close();
                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 blockSize * (double)(results.Blocks + 1) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    if (ataReader.CanSeekLba)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            uint seekPos = (uint)rnd.Next((int)results.Blocks);

                            DicConsole.Write("\rSeeking to sector {0}...\t\t", seekPos);

                            ataReader.Seek(seekPos, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax && seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }
                            if (seekCur < results.SeekMin && seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }
                }
                else
                {
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    ulong currentBlock = 0;
                    results.Blocks = (ulong)(cylinders * heads * sectors);
                    start          = DateTime.UtcNow;
                    for (ushort cy = 0; cy < cylinders; cy++)
                    {
                        for (byte hd = 0; hd < heads; hd++)
                        {
                            for (byte sc = 1; sc < sectors; sc++)
                            {
                                if (aborted)
                                {
                                    break;
                                }

                                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                                {
                                    results.MaxSpeed = currentSpeed;
                                }
                                if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                                {
                                    results.MinSpeed = currentSpeed;
                                }
                                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", cy, hd,
                                                 sc, currentSpeed);

                                bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                if (!error)
                                {
                                    if (duration >= 500)
                                    {
                                        results.F += blocksToRead;
                                    }
                                    else if (duration >= 150)
                                    {
                                        results.E += blocksToRead;
                                    }
                                    else if (duration >= 50)
                                    {
                                        results.D += blocksToRead;
                                    }
                                    else if (duration >= 10)
                                    {
                                        results.C += blocksToRead;
                                    }
                                    else if (duration >= 3)
                                    {
                                        results.B += blocksToRead;
                                    }
                                    else
                                    {
                                        results.A += blocksToRead;
                                    }

                                    mhddLog.Write(currentBlock, duration);
                                    ibgLog.Write(currentBlock, currentSpeed * 1024);
                                }
                                else
                                {
                                    results.Errored += blocksToRead;
                                    results.UnreadableSectors.Add(currentBlock);
                                    mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                    ibgLog.Write(currentBlock, 0);
                                }

                                double newSpeed =
                                    blockSize / (double)1048576 / (duration / 1000);
                                if (!double.IsInfinity(newSpeed))
                                {
                                    currentSpeed = newSpeed;
                                }

                                currentBlock++;
                            }
                        }
                    }

                    end = DateTime.UtcNow;
                    DicConsole.WriteLine();
                    mhddLog.Close();
                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 blockSize * (double)(results.Blocks + 1) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    if (ataReader.CanSeek)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            ushort seekCy = (ushort)rnd.Next(cylinders);
                            byte   seekHd = (byte)rnd.Next(heads);
                            byte   seekSc = (byte)rnd.Next(sectors);

                            DicConsole.Write("\rSeeking to cylinder {0}, head {1}, sector {2}...\t\t", seekCy, seekHd,
                                             seekSc);

                            ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax && seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }
                            if (seekCur < results.SeekMin && seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }
                }

                DicConsole.WriteLine();

                results.ProcessingTime /= 1000;
                results.TotalTime       = (end - start).TotalSeconds;
                results.AvgSpeed        = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
                results.SeekTimes       = SEEK_TIMES;

                return(results);
            }

            DicConsole.ErrorWriteLine("Unable to communicate with ATA device.");
            return(results);
        }
Пример #3
0
        /// <summary>Scans the media from an ATA device</summary>
        /// <returns>Scanning results</returns>
        ScanResults Ata()
        {
            var  results = new ScanResults();
            bool sense;

            results.Blocks = 0;
            const ushort ATA_PROFILE = 0x0001;
            const uint   TIMEOUT     = 5;

            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);

            if (!sense &&
                Identify.Decode(cmdBuf).HasValue)
            {
                // Initializate reader
                var ataReader = new Reader(dev, TIMEOUT, cmdBuf);

                // Fill reader blocks
                results.Blocks = ataReader.GetDeviceBlocks();

                if (ataReader.FindReadCommand())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                // Check block sizes
                if (ataReader.GetBlockSize())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                uint blockSize = ataReader.LogicalBlockSize;

                // Check how many blocks to read, if error show and return
                if (ataReader.GetBlocksToRead())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                uint   blocksToRead = ataReader.BlocksToRead;
                ushort cylinders    = ataReader.Cylinders;
                byte   heads        = ataReader.Heads;
                byte   sectors      = ataReader.Sectors;

                results.A       = 0; // <3ms
                results.B       = 0; // >=3ms, <10ms
                results.C       = 0; // >=10ms, <50ms
                results.D       = 0; // >=50ms, <150ms
                results.E       = 0; // >=150ms, <500ms
                results.F       = 0; // >=500ms
                results.Errored = 0;
                DateTime start;
                DateTime end;
                results.ProcessingTime = 0;
                double currentSpeed = 0;
                results.MaxSpeed          = double.MinValue;
                results.MinSpeed          = double.MaxValue;
                results.UnreadableSectors = new List <ulong>();
                results.SeekMax           = double.MinValue;
                results.SeekMin           = double.MaxValue;
                results.SeekTotal         = 0;
                const int SEEK_TIMES = 1000;

                double seekCur;

                var rnd = new Random();

                MhddLog mhddLog;
                IbgLog  ibgLog;
                double  duration;

                if (ataReader.IsLba)
                {
                    UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    start = DateTime.UtcNow;
                    DateTime timeSpeedStart   = DateTime.UtcNow;
                    ulong    sectorSpeedStart = 0;
                    InitProgress?.Invoke();

                    for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                    {
                        if (aborted)
                        {
                            break;
                        }

                        if (results.Blocks - i < blocksToRead)
                        {
                            blocksToRead = (byte)(results.Blocks - i);
                        }

                        #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                        if (currentSpeed > results.MaxSpeed &&
                            currentSpeed != 0)
                        {
                            results.MaxSpeed = currentSpeed;
                        }

                        if (currentSpeed < results.MinSpeed &&
                            currentSpeed != 0)
                        {
                            results.MinSpeed = currentSpeed;
                        }
                        #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                        UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
                                               (long)i, (long)results.Blocks);

                        bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                        if (!error)
                        {
                            if (duration >= 500)
                            {
                                results.F += blocksToRead;
                            }
                            else if (duration >= 150)
                            {
                                results.E += blocksToRead;
                            }
                            else if (duration >= 50)
                            {
                                results.D += blocksToRead;
                            }
                            else if (duration >= 10)
                            {
                                results.C += blocksToRead;
                            }
                            else if (duration >= 3)
                            {
                                results.B += blocksToRead;
                            }
                            else
                            {
                                results.A += blocksToRead;
                            }

                            ScanTime?.Invoke(i, duration);
                            mhddLog.Write(i, duration);
                            ibgLog.Write(i, currentSpeed * 1024);
                        }
                        else
                        {
                            ScanUnreadable?.Invoke(i);
                            results.Errored += blocksToRead;

                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

                            mhddLog.Write(i, duration < 500 ? 65535 : duration);

                            ibgLog.Write(i, 0);
                        }

                        sectorSpeedStart += blocksToRead;

                        double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                        if (elapsed < 1)
                        {
                            continue;
                        }

                        currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                        ScanSpeed?.Invoke(i, currentSpeed * 1024);
                        sectorSpeedStart = 0;
                        timeSpeedStart   = DateTime.UtcNow;
                    }

                    end = DateTime.UtcNow;
                    EndProgress?.Invoke();
                    mhddLog.Close();

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeekLba)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            uint seekPos = (uint)rnd.Next((int)results.Blocks);

                            PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                            ataReader.Seek(seekPos, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax &&
                                seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }

                            if (seekCur < results.SeekMin &&
                                seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }
                else
                {
                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    ulong currentBlock = 0;
                    results.Blocks = (ulong)(cylinders * heads * sectors);
                    start          = DateTime.UtcNow;
                    DateTime timeSpeedStart   = DateTime.UtcNow;
                    ulong    sectorSpeedStart = 0;
                    InitProgress?.Invoke();

                    for (ushort cy = 0; cy < cylinders; cy++)
                    {
                        for (byte hd = 0; hd < heads; hd++)
                        {
                            for (byte sc = 1; sc < sectors; sc++)
                            {
                                if (aborted)
                                {
                                    break;
                                }

                                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                if (currentSpeed > results.MaxSpeed &&
                                    currentSpeed != 0)
                                {
                                    results.MaxSpeed = currentSpeed;
                                }

                                if (currentSpeed < results.MinSpeed &&
                                    currentSpeed != 0)
                                {
                                    results.MinSpeed = currentSpeed;
                                }
                                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                PulseProgress?.
                                Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)");

                                bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                if (!error)
                                {
                                    if (duration >= 500)
                                    {
                                        results.F += blocksToRead;
                                    }
                                    else if (duration >= 150)
                                    {
                                        results.E += blocksToRead;
                                    }
                                    else if (duration >= 50)
                                    {
                                        results.D += blocksToRead;
                                    }
                                    else if (duration >= 10)
                                    {
                                        results.C += blocksToRead;
                                    }
                                    else if (duration >= 3)
                                    {
                                        results.B += blocksToRead;
                                    }
                                    else
                                    {
                                        results.A += blocksToRead;
                                    }

                                    ScanTime?.Invoke(currentBlock, duration);
                                    mhddLog.Write(currentBlock, duration);
                                    ibgLog.Write(currentBlock, currentSpeed * 1024);
                                }
                                else
                                {
                                    ScanUnreadable?.Invoke(currentBlock);
                                    results.Errored += blocksToRead;
                                    results.UnreadableSectors.Add(currentBlock);
                                    mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                    ibgLog.Write(currentBlock, 0);
                                }

                                sectorSpeedStart++;
                                currentBlock++;

                                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                                if (elapsed < 1)
                                {
                                    continue;
                                }

                                currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                                ScanSpeed?.Invoke(currentBlock, currentSpeed * 1024);
                                sectorSpeedStart = 0;
                                timeSpeedStart   = DateTime.UtcNow;
                            }
                        }
                    }

                    end = DateTime.UtcNow;
                    EndProgress?.Invoke();
                    mhddLog.Close();

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeek)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            ushort seekCy = (ushort)rnd.Next(cylinders);
                            byte   seekHd = (byte)rnd.Next(heads);
                            byte   seekSc = (byte)rnd.Next(sectors);

                            PulseProgress?.
                            Invoke($"\rSeeking to cylinder {seekCy}, head {seekHd}, sector {seekSc}...\t\t");

                            ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax &&
                                seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }

                            if (seekCur < results.SeekMin &&
                                seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }

                results.ProcessingTime /= 1000;
                results.TotalTime       = (end - start).TotalSeconds;
                results.AvgSpeed        = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime;
                results.SeekTimes       = SEEK_TIMES;

                return(results);
            }

            StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");

            return(results);
        }
Пример #4
0
        ScanResults SecureDigital()
        {
            ScanResults results = new ScanResults();

            byte[] cmdBuf;
            bool   sense;

            results.Blocks = 0;
            const uint   TIMEOUT = 5;
            double       duration;
            const ushort SD_PROFILE    = 0x0001;
            uint         blocksToRead  = 128;
            uint         blockSize     = 512;
            bool         byteAddressed = true;

            switch (dev.Type)
            {
            case DeviceType.MMC:
            {
                sense = dev.ReadExtendedCsd(out cmdBuf, out _, TIMEOUT, out _);
                if (!sense)
                {
                    ExtendedCSD ecsd = Decoders.MMC.Decoders.DecodeExtendedCSD(cmdBuf);
                    blocksToRead   = ecsd.OptimalReadSize;
                    results.Blocks = ecsd.SectorCount;
                    blockSize      = (uint)(ecsd.SectorSize == 1 ? 4096 : 512);
                    // Supposing it's high-capacity MMC if it has Extended CSD...
                    byteAddressed = false;
                }

                if (sense || results.Blocks == 0)
                {
                    sense = dev.ReadCsd(out cmdBuf, out _, TIMEOUT, out _);
                    if (!sense)
                    {
                        CSD csd = Decoders.MMC.Decoders.DecodeCSD(cmdBuf);
                        results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2));
                        blockSize      = (uint)Math.Pow(2, csd.ReadBlockLength);
                    }
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                sense = dev.ReadCsd(out cmdBuf, out _, TIMEOUT, out _);
                if (!sense)
                {
                    Decoders.SecureDigital.CSD csd = Decoders.SecureDigital.Decoders.DecodeCSD(cmdBuf);
                    results.Blocks = (ulong)(csd.Structure == 0
                                                     ? (csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2)
                                                     : (csd.Size + 1) * 1024);
                    blockSize = (uint)Math.Pow(2, csd.ReadBlockLength);
                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csd.Structure == 0;
                }

                break;
            }
            }

            if (results.Blocks == 0)
            {
                StoppingErrorMessage?.Invoke("Unable to get device size.");
                return(results);
            }

            while (true)
            {
                sense = dev.Read(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, TIMEOUT, out duration);

                if (sense)
                {
                    blocksToRead /= 2;
                }

                if (!sense || blocksToRead == 1)
                {
                    break;
                }
            }

            if (sense)
            {
                StoppingErrorMessage?.Invoke($"Device error {dev.LastError} trying to guess ideal transfer length.");
                return(results);
            }

            results.A       = 0; // <3ms
            results.B       = 0; // >=3ms, <10ms
            results.C       = 0; // >=10ms, <50ms
            results.D       = 0; // >=50ms, <150ms
            results.E       = 0; // >=150ms, <500ms
            results.F       = 0; // >=500ms
            results.Errored = 0;
            DateTime start;
            DateTime end;

            results.ProcessingTime = 0;
            double currentSpeed = 0;

            results.MaxSpeed          = double.MinValue;
            results.MinSpeed          = double.MaxValue;
            results.UnreadableSectors = new List <ulong>();
            results.SeekMax           = double.MinValue;
            results.SeekMin           = double.MaxValue;
            results.SeekTotal         = 0;
            const int SEEK_TIMES = 1000;

            Random rnd = new Random();

            UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

            InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, SD_PROFILE);
            MhddLog mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
            IbgLog  ibgLog  = new IbgLog(ibgLogPath, SD_PROFILE);

            start = DateTime.UtcNow;
            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();
            for (ulong i = 0; i < results.Blocks; i += blocksToRead)
            {
                if (aborted)
                {
                    break;
                }

                if (results.Blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(results.Blocks - i);
                }

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                {
                    results.MaxSpeed = currentSpeed;
                }
                if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                {
                    results.MinSpeed = currentSpeed;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
                                       (long)results.Blocks);

                bool error = dev.Read(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, TIMEOUT,
                                      out duration);

                if (!error)
                {
                    if (duration >= 500)
                    {
                        results.F += blocksToRead;
                    }
                    else if (duration >= 150)
                    {
                        results.E += blocksToRead;
                    }
                    else if (duration >= 50)
                    {
                        results.D += blocksToRead;
                    }
                    else if (duration >= 10)
                    {
                        results.C += blocksToRead;
                    }
                    else if (duration >= 3)
                    {
                        results.B += blocksToRead;
                    }
                    else
                    {
                        results.A += blocksToRead;
                    }

                    ScanTime?.Invoke(i, duration);
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                }
                else
                {
                    ScanUnreadable?.Invoke(i);
                    results.Errored += blocksToRead;
                    for (ulong b = i; b < i + blocksToRead; b++)
                    {
                        results.UnreadableSectors.Add(b);
                    }

                    mhddLog.Write(i, duration < 500 ? 65535 : duration);

                    ibgLog.Write(i, 0);
                }

                sectorSpeedStart += blocksToRead;

                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
                ScanSpeed?.Invoke(i, currentSpeed * 1024);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            end = DateTime.UtcNow;
            EndProgress?.Invoke();
            mhddLog.Close();
            ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         blockSize * (double)(results.Blocks + 1) / 1024 /
                         (results.ProcessingTime / 1000), devicePath);

            InitProgress?.Invoke();
            for (int i = 0; i < SEEK_TIMES; i++)
            {
                if (aborted)
                {
                    break;
                }

                uint seekPos = (uint)rnd.Next((int)results.Blocks);

                PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                dev.Read(out cmdBuf, out _, seekPos, blockSize, blocksToRead, byteAddressed, TIMEOUT,
                         out double seekCur);

                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                if (seekCur > results.SeekMax && seekCur != 0)
                {
                    results.SeekMax = seekCur;
                }
                if (seekCur < results.SeekMin && seekCur != 0)
                {
                    results.SeekMin = seekCur;
                }
                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                results.SeekTotal += seekCur;
                GC.Collect();
            }

            EndProgress?.Invoke();

            results.ProcessingTime /= 1000;
            results.TotalTime       = (end - start).TotalSeconds;
            results.AvgSpeed        = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
            results.SeekTimes       = SEEK_TIMES;

            return(results);
        }