Пример #1
0
        public static int Invoke(bool debug, bool verbose, string imagePath1, string imagePath2)
        {
            MainClass.PrintCopyright();

            if (debug)
            {
                AaruConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }

            if (verbose)
            {
                AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }

            Statistics.AddCommand("compare");

            AaruConsole.DebugWriteLine("Compare command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Compare command", "--input1={0}", imagePath1);
            AaruConsole.DebugWriteLine("Compare command", "--input2={0}", imagePath2);
            AaruConsole.DebugWriteLine("Compare command", "--verbose={0}", verbose);

            var     filtersList  = new FiltersList();
            IFilter inputFilter1 = filtersList.GetFilter(imagePath1);

            filtersList = new FiltersList();
            IFilter inputFilter2 = filtersList.GetFilter(imagePath2);

            if (inputFilter1 == null)
            {
                AaruConsole.ErrorWriteLine("Cannot open input file 1");

                return((int)ErrorNumber.CannotOpenFile);
            }

            if (inputFilter2 == null)
            {
                AaruConsole.ErrorWriteLine("Cannot open input file 2");

                return((int)ErrorNumber.CannotOpenFile);
            }

            IMediaImage input1Format = ImageFormat.Detect(inputFilter1);
            IMediaImage input2Format = ImageFormat.Detect(inputFilter2);

            if (input1Format == null)
            {
                AaruConsole.ErrorWriteLine("Input file 1 format not identified, not proceeding with comparison.");

                return((int)ErrorNumber.UnrecognizedFormat);
            }

            if (verbose)
            {
                AaruConsole.VerboseWriteLine("Input file 1 format identified by {0} ({1}).", input1Format.Name,
                                             input1Format.Id);
            }
            else
            {
                AaruConsole.WriteLine("Input file 1 format identified by {0}.", input1Format.Name);
            }

            if (input2Format == null)
            {
                AaruConsole.ErrorWriteLine("Input file 2 format not identified, not proceeding with comparison.");

                return((int)ErrorNumber.UnrecognizedFormat);
            }

            if (verbose)
            {
                AaruConsole.VerboseWriteLine("Input file 2 format identified by {0} ({1}).", input2Format.Name,
                                             input2Format.Id);
            }
            else
            {
                AaruConsole.WriteLine("Input file 2 format identified by {0}.", input2Format.Name);
            }

            input1Format.Open(inputFilter1);
            input2Format.Open(inputFilter2);

            Statistics.AddMediaFormat(input1Format.Format);
            Statistics.AddMediaFormat(input2Format.Format);
            Statistics.AddMedia(input1Format.Info.MediaType, false);
            Statistics.AddMedia(input2Format.Info.MediaType, false);
            Statistics.AddFilter(inputFilter1.Name);
            Statistics.AddFilter(inputFilter2.Name);

            var sb = new StringBuilder();

            if (verbose)
            {
                sb.AppendFormat("{0,50}{1,20}", "Disc image 1", "Disc image 2").AppendLine();
                sb.AppendLine("================================================================================================");
                sb.AppendFormat("{0,-38}{1}", "File", imagePath1).AppendLine();

                sb.AppendFormat("                                                          {0}", imagePath2).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disc image format", input1Format.Name, input2Format.Name).
                AppendLine();
            }
            else
            {
                sb.AppendFormat("Disc image 1: {0}", imagePath1).AppendLine();
                sb.AppendFormat("Disc image 2: {0}", imagePath2).AppendLine();
            }

            bool imagesDiffer = false;

            ImageInfo      image1Info     = input1Format.Info;
            ImageInfo      image2Info     = input2Format.Info;
            List <Session> image1Sessions = new List <Session>();
            List <Session> image2Sessions = new List <Session>();
            Dictionary <MediaTagType, byte[]> image1DiskTags = new Dictionary <MediaTagType, byte[]>();
            Dictionary <MediaTagType, byte[]> image2DiskTags = new Dictionary <MediaTagType, byte[]>();

            foreach (MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
            {
                try
                {
                    byte[] temparray = input1Format.ReadDiskTag(disktag);
                    image1DiskTags.Add(disktag, temparray);
                }
                #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                catch
                {
                    // ignored
                }
                #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            }

            foreach (MediaTagType disktag in Enum.GetValues(typeof(MediaTagType)))
            {
                try
                {
                    byte[] temparray = input2Format.ReadDiskTag(disktag);
                    image2DiskTags.Add(disktag, temparray);
                }
                #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                catch
                {
                    // ignored
                }
                #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            }

            if (verbose)
            {
                sb.AppendFormat("{0,-38}{1,-20}{2}", "Has partitions?", image1Info.HasPartitions,
                                image2Info.HasPartitions).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Has sessions?", image1Info.HasSessions, image2Info.HasSessions).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image size", image1Info.ImageSize, image2Info.ImageSize).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Sectors", image1Info.Sectors, image2Info.Sectors).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Sector size", image1Info.SectorSize, image2Info.SectorSize).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Creation time", image1Info.CreationTime, image2Info.CreationTime).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Last modification time", image1Info.LastModificationTime,
                                image2Info.LastModificationTime).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk type", image1Info.MediaType, image2Info.MediaType).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image version", image1Info.Version, image2Info.Version).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image application", image1Info.Application,
                                image2Info.Application).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image application version", image1Info.ApplicationVersion,
                                image2Info.ApplicationVersion).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image creator", image1Info.Creator, image2Info.Creator).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image name", image1Info.MediaTitle, image2Info.MediaTitle).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image comments", image1Info.Comments, image2Info.Comments).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Image comments", image1Info.Comments, image2Info.Comments).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk manufacturer", image1Info.MediaManufacturer,
                                image2Info.MediaManufacturer).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk model", image1Info.MediaModel, image2Info.MediaModel).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk serial number", image1Info.MediaSerialNumber,
                                image2Info.MediaSerialNumber).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk barcode", image1Info.MediaBarcode, image2Info.MediaBarcode).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk part no.", image1Info.MediaPartNumber,
                                image2Info.MediaPartNumber).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Disk sequence", image1Info.MediaSequence,
                                image2Info.MediaSequence).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Last disk on sequence", image1Info.LastMediaSequence,
                                image2Info.LastMediaSequence).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive manufacturer", image1Info.DriveManufacturer,
                                image2Info.DriveManufacturer).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive firmware revision", image1Info.DriveFirmwareRevision,
                                image2Info.DriveFirmwareRevision).AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive model", image1Info.DriveModel, image2Info.DriveModel).
                AppendLine();

                sb.AppendFormat("{0,-38}{1,-20}{2}", "Drive serial number", image1Info.DriveSerialNumber,
                                image2Info.DriveSerialNumber).AppendLine();

                foreach (MediaTagType disktag in
                         (Enum.GetValues(typeof(MediaTagType)) as MediaTagType[]).OrderBy(e => e.ToString()))
                {
                    sb.AppendFormat("{0,-38}{1,-20}{2}", $"Has {disktag}?", image1DiskTags.ContainsKey(disktag),
                                    image2DiskTags.ContainsKey(disktag)).AppendLine();
                }
            }

            AaruConsole.WriteLine("Comparing disk image characteristics");

            if (image1Info.HasPartitions != image2Info.HasPartitions)
            {
                imagesDiffer = true;

                if (!verbose)
                {
                    sb.AppendLine("Image partitioned status differ");
                }
            }

            if (image1Info.HasSessions != image2Info.HasSessions)
            {
                imagesDiffer = true;

                if (!verbose)
                {
                    sb.AppendLine("Image session status differ");
                }
            }

            if (image1Info.Sectors != image2Info.Sectors)
            {
                imagesDiffer = true;

                if (!verbose)
                {
                    sb.AppendLine("Image sectors differ");
                }
            }

            if (image1Info.SectorSize != image2Info.SectorSize)
            {
                imagesDiffer = true;

                if (!verbose)
                {
                    sb.AppendLine("Image sector size differ");
                }
            }

            if (image1Info.MediaType != image2Info.MediaType)
            {
                imagesDiffer = true;

                if (!verbose)
                {
                    sb.AppendLine("Disk type differ");
                }
            }

            ulong leastSectors;

            if (image1Info.Sectors < image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image1Info.Sectors;

                if (!verbose)
                {
                    sb.AppendLine("Image 2 has more sectors");
                }
            }
            else if (image1Info.Sectors > image2Info.Sectors)
            {
                imagesDiffer = true;
                leastSectors = image2Info.Sectors;

                if (!verbose)
                {
                    sb.AppendLine("Image 1 has more sectors");
                }
            }
            else
            {
                leastSectors = image1Info.Sectors;
            }

            AaruConsole.WriteLine("Comparing sectors...");

            for (ulong sector = 0; sector < leastSectors; sector++)
            {
                AaruConsole.Write("\rComparing sector {0} of {1}...", sector + 1, leastSectors);

                try
                {
                    byte[] image1Sector = input1Format.ReadSector(sector);
                    byte[] image2Sector = input2Format.ReadSector(sector);
                    ArrayHelpers.CompareBytes(out bool different, out bool sameSize, image1Sector, image2Sector);

                    if (different)
                    {
                        imagesDiffer = true;
                        sb.AppendFormat("Sector {0} is different", sector).AppendLine();
                    }
                    else if (!sameSize)
                    {
                        imagesDiffer = true;

                        sb.
                        AppendFormat("Sector {0} has different sizes ({1} bytes in image 1, {2} in image 2) but are otherwise identical",
                                     sector, image1Sector.LongLength, image2Sector.LongLength).AppendLine();
                    }
                }
                #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                catch
                {
                    // ignored
                }
                #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            }

            AaruConsole.WriteLine();

            sb.AppendLine(imagesDiffer ? "Images differ" : "Images do not differ");

            AaruConsole.WriteLine(sb.ToString());

            return((int)ErrorNumber.NoError);
        }
Пример #2
0
        /// <summary>
        ///     Dumps a MultiMediaCard or SecureDigital flash card
        /// </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="outputPlugin">Plugin for output file</param>
        /// <param name="retryPasses">How many times to retry</param>
        /// <param name="force">Force to continue dump whenever possible</param>
        /// <param name="dumpRaw">Dump long or scrambled sectors</param>
        /// <param name="persistent">Store whatever data the drive returned on error</param>
        /// <param name="stopOnError">Stop dump on first error</param>
        /// <param name="resume">Information for dump resuming</param>
        /// <param name="dumpLog">Dump logger</param>
        /// <param name="encoding">Encoding to use when analyzing dump</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="ArgumentException">If you asked to dump long sectors from a SCSI Streaming device</exception>
        public static void Dump(Device dev, string devicePath,
                                IWritableImage outputPlugin, ushort retryPasses,
                                bool force, bool dumpRaw,
                                bool persistent, bool stopOnError, ref Resume resume,
                                ref DumpLog dumpLog, Encoding encoding,
                                string outputPrefix, string outputPath,
                                Dictionary <string, string> formatOptions, CICMMetadataType preSidecar,
                                uint skip,
                                bool nometadata, bool notrim)
        {
            bool aborted;

            if (dumpRaw)
            {
                DicConsole.ErrorWriteLine("Raw dumping is not supported in MultiMediaCard or SecureDigital devices.");

                if (force)
                {
                    DicConsole.ErrorWriteLine("Continuing...");
                }
                else
                {
                    DicConsole.ErrorWriteLine("Aborting...");
                    return;
                }
            }

            bool         sense;
            const ushort SD_PROFILE = 0x0001;
            const uint   TIMEOUT    = 5;
            double       duration;

            uint  blocksToRead = 128;
            uint  blockSize    = 512;
            ulong blocks       = 0;

            byte[] csd  = null;
            byte[] ocr  = null;
            byte[] ecsd = null;
            byte[] scr  = null;
            int    physicalBlockSize = 0;
            bool   byteAddressed     = true;

            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            switch (dev.Type)
            {
            case DeviceType.MMC:
            {
                dumpLog.WriteLine("Reading Extended CSD");
                sense = dev.ReadExtendedCsd(out ecsd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd);
                    blocksToRead = ecsdDecoded.OptimalReadSize;
                    blocks       = ecsdDecoded.SectorCount;
                    blockSize    = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);
                    if (ecsdDecoded.NativeSectorSize == 0)
                    {
                        physicalBlockSize = 512;
                    }
                    else if (ecsdDecoded.NativeSectorSize == 1)
                    {
                        physicalBlockSize = 4096;
                    }
                    // Supposing it's high-capacity MMC if it has Extended CSD...
                    byteAddressed = false;
                    mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null);
                }
                else
                {
                    ecsd = null;
                }

                dumpLog.WriteLine("Reading CSD");
                sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    if (blocks == 0)
                    {
                        CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd);
                        blocks    = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
                        blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
                    }

                    mediaTags.Add(MediaTagType.MMC_CSD, null);
                }
                else
                {
                    csd = null;
                }

                dumpLog.WriteLine("Reading OCR");
                sense = dev.ReadOcr(out ocr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.MMC_OCR, null);
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                dumpLog.WriteLine("Reading CSD");
                sense = dev.ReadCsd(out csd, out _, TIMEOUT, out duration);
                if (!sense)
                {
                    Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd);
                    blocks = (ulong)(csdDecoded.Structure == 0
                                             ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)
                                             : (csdDecoded.Size + 1) * 1024);
                    blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);
                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csdDecoded.Structure == 0;
                    mediaTags.Add(MediaTagType.SD_CSD, null);
                }
                else
                {
                    csd = null;
                }

                dumpLog.WriteLine("Reading OCR");
                sense = dev.ReadSdocr(out ocr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_OCR, null);
                }

                dumpLog.WriteLine("Reading SCR");
                sense = dev.ReadScr(out scr, out _, TIMEOUT, out duration);
                if (sense)
                {
                    scr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_SCR, null);
                }

                break;
            }
            }

            dumpLog.WriteLine("Reading CID");
            sense = dev.ReadCid(out byte[] cid, out _, TIMEOUT, out duration);
            if (sense)
            {
                cid = null;
            }
            else
            {
                mediaTags.Add(dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
            }

            DateTime start;
            DateTime end;
            double   totalDuration = 0;
            double   currentSpeed  = 0;
            double   maxSpeed      = double.MinValue;
            double   minSpeed      = double.MaxValue;

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

            if (blocks == 0)
            {
                dumpLog.WriteLine("Cannot get device size.");
                DicConsole.ErrorWriteLine("Unable to get device size.");
                return;
            }

            dumpLog.WriteLine("Device reports {0} blocks.", blocks);

            byte[] cmdBuf;
            bool   error;

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

                if (error)
                {
                    blocksToRead /= 2;
                }

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

            if (error)
            {
                dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", dev.LastError);
                DicConsole.ErrorWriteLine("Device error {0} trying to guess ideal transfer length.", dev.LastError);
                return;
            }

            dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);

            if (skip < blocksToRead)
            {
                skip = blocksToRead;
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, false, blocks, dev.Manufacturer, dev.Model, dev.Serial, dev.PlatformId,
                                  ref resume, ref currentTry, ref extents);
            if (currentTry == null || extents == null)
            {
                throw new InvalidOperationException("Could not process resume file, not continuing...");
            }

            bool ret = true;

            foreach (MediaTagType tag in mediaTags.Keys)
            {
                if (outputPlugin.SupportedMediaTags.Contains(tag))
                {
                    continue;
                }

                ret = false;
                dumpLog.WriteLine($"Output format does not support {tag}.");
                DicConsole.ErrorWriteLine($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                if (!force)
                {
                    return;
                }
            }

            DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

            MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
            IbgLog  ibgLog  = new IbgLog(outputPrefix + ".ibg", SD_PROFILE);

            ret = outputPlugin.Create(outputPath,
                                      dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC,
                                      formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                dumpLog.WriteLine("Error creating output image, not continuing.");
                dumpLog.WriteLine(outputPlugin.ErrorMessage);
                DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
                DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
                return;
            }

            if (resume.NextBlock > 0)
            {
                dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;
            bool   newTrim            = false;

            for (ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    dumpLog.WriteLine("Aborted!");
                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(blocks - i);
                }

                #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 sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);

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

                if (!error)
                {
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    if (i + skip > blocks)
                    {
                        skip = (uint)(blocks - i);
                    }

                    for (ulong b = i; b < i + skip; b++)
                    {
                        resume.BadBlocks.Add(b);
                    }

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

                    ibgLog.Write(i, 0);
                    DateTime writeStart = DateTime.Now;
                    outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
                    i      += skip - blocksToRead;
                    newTrim = true;
                }

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

            end = DateTime.Now;
            DicConsole.WriteLine();
            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 write speed {0:F3} KiB/sec.",
                              (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);

            #region Trimming
            if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim)
            {
                start = DateTime.UtcNow;
                dumpLog.WriteLine("Trimming bad sectors");

                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rTrimming sector {0}", badSector);

                    error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                     out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        continue;
                    }

                    resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    outputPlugin.WriteSector(cmdBuf, badSector);
                }

                DicConsole.WriteLine();
                end = DateTime.UtcNow;
                dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

repeatRetryLba:
                ulong[] tmpArray = resume.BadBlocks.ToArray();
                foreach (ulong badSector in tmpArray)
                {
                    if (aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        dumpLog.WriteLine("Aborted!");
                        break;
                    }

                    DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                     forward ? "forward" : "reverse",
                                     runningPersistent ? "recovering partial data, " : "");

                    error = dev.Read(out cmdBuf, out _, (uint)badSector, blockSize, 1, byteAddressed, TIMEOUT,
                                     out duration);

                    totalDuration += duration;

                    if (!error)
                    {
                        resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        outputPlugin.WriteSector(cmdBuf, badSector);
                        dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        outputPlugin.WriteSector(cmdBuf, badSector);
                    }
                }

                if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    resume.BadBlocks.Sort();
                    resume.BadBlocks.Reverse();
                    goto repeatRetryLba;
                }

                DicConsole.WriteLine();
            }
            #endregion Error handling

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            outputPlugin.SetDumpHardware(resume.Tries);
            if (preSidecar != null)
            {
                outputPlugin.SetCicmMetadata(preSidecar);
            }
            dumpLog.WriteLine("Closing output file.");
            DicConsole.WriteLine("Closing output file.");
            DateTime closeStart = DateTime.Now;
            outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (aborted)
            {
                dumpLog.WriteLine("Aborted!");
                return;
            }

            double totalChkDuration = 0;
            if (!nometadata)
            {
                dumpLog.WriteLine("Creating sidecar.");
                FiltersList filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);
                if (!inputPlugin.Open(filter))
                {
                    throw new ArgumentException("Could not open created image.");
                }

                DateTime         chkStart = DateTime.UtcNow;
                CICMMetadataType sidecar  = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);

                if (preSidecar != null)
                {
                    preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = preSidecar;
                }

                switch (dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    break;
                }

                DumpType cidDump = null;
                DumpType csdDump = null;
                DumpType ocrDump = null;

                if (cid != null)
                {
                    cidDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = cid.Length,
                        Checksums = Checksum.GetChecksums(cid).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(cid,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_CID
                                                       : MediaTagType.MMC_CID);

                    // Cannot write CID to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write CID to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (csd != null)
                {
                    csdDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = csd.Length,
                        Checksums = Checksum.GetChecksums(csd).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(csd,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_CSD
                                                       : MediaTagType.MMC_CSD);

                    // Cannot write CSD to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write CSD to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (ecsd != null)
                {
                    sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
                    {
                        Image     = outputPath,
                        Size      = ecsd.Length,
                        Checksums = Checksum.GetChecksums(ecsd).ToArray()
                    };

                    ret = outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);

                    // Cannot write Extended CSD to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write Extended CSD to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (ocr != null)
                {
                    ocrDump = new DumpType
                    {
                        Image     = outputPath,
                        Size      = ocr.Length,
                        Checksums = Checksum.GetChecksums(ocr).ToArray()
                    };

                    ret =
                        outputPlugin.WriteMediaTag(ocr,
                                                   dev.Type == DeviceType.SecureDigital
                                                       ? MediaTagType.SD_OCR
                                                       : MediaTagType.MMC_OCR);

                    // Cannot write OCR to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write OCR to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                if (scr != null)
                {
                    sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
                    {
                        Image     = outputPath,
                        Size      = scr.Length,
                        Checksums = Checksum.GetChecksums(scr).ToArray()
                    };

                    ret = outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);

                    // Cannot write SCR to image
                    if (!ret && !force)
                    {
                        dumpLog.WriteLine("Cannot write SCR to output image.");
                        throw new ArgumentException(outputPlugin.ErrorMessage);
                    }
                }

                switch (dev.Type)
                {
                case DeviceType.MMC:
                    sidecar.BlockMedia[0].MultiMediaCard.CID = cidDump;
                    sidecar.BlockMedia[0].MultiMediaCard.CSD = csdDump;
                    sidecar.BlockMedia[0].MultiMediaCard.OCR = ocrDump;
                    break;

                case DeviceType.SecureDigital:
                    sidecar.BlockMedia[0].SecureDigital.CID = cidDump;
                    sidecar.BlockMedia[0].SecureDigital.CSD = csdDump;
                    sidecar.BlockMedia[0].SecureDigital.OCR = ocrDump;
                    break;
                }

                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
                dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                  (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

                string xmlDskTyp = null, xmlDskSubTyp = null;
                switch (dev.Type)
                {
                case DeviceType.MMC:
                    Metadata.MediaType.MediaTypeToString(MediaType.MMC, out xmlDskTyp, out xmlDskSubTyp);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);
                    break;

                case DeviceType.SecureDigital:
                    Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital, out xmlDskTyp, out xmlDskSubTyp);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital);
                    break;
                }

                sidecar.BlockMedia[0].DiskType    = xmlDskTyp;
                sidecar.BlockMedia[0].DiskSubType = xmlDskSubTyp;
                // TODO: Implement device firmware revision
                sidecar.BlockMedia[0].LogicalBlocks     = (long)blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : (int)blockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = (int)blockSize;
                sidecar.BlockMedia[0].Manufacturer      = dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = dev.Model;
                sidecar.BlockMedia[0].Serial            = dev.Serial;
                sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);

                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();
            }

            DicConsole.WriteLine();

            DicConsole.WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).",
                                 (end - start).TotalSeconds, totalDuration / 1000,
                                 totalChkDuration / 1000,
                                 imageWriteDuration, (closeEnd - closeStart).TotalSeconds);
            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);
            DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
            if (resume.BadBlocks.Count > 0)
            {
                resume.BadBlocks.Sort();
            }
            DicConsole.WriteLine();

            switch (dev.Type)
            {
            case DeviceType.MMC:
                Statistics.AddMedia(MediaType.MMC, true);
                break;

            case DeviceType.SecureDigital:
                Statistics.AddMedia(MediaType.SecureDigital, true);
                break;
            }
        }
Пример #3
0
        public bool Open(IFilter imageFilter)
        {
            if (imageFilter == null)
            {
                return(false);
            }

            try
            {
                imageFilter.GetDataForkStream().Seek(0, SeekOrigin.Begin);
                gdiStream = new StreamReader(imageFilter.GetDataForkStream());
                int  lineNumber  = 0;
                bool highDensity = false;

                // Initialize all RegExs
                Regex regexTrack = new Regex(REGEX_TRACK);

                // Initialize all RegEx matches

                // Initialize disc
                discimage = new GdiDisc {
                    Sessions = new List <Session>(), Tracks = new List <GdiTrack>()
                };

                ulong currentStart = 0;
                offsetmap = new Dictionary <uint, ulong>();
                densitySeparationSectors = 0;

                while (gdiStream.Peek() >= 0)
                {
                    lineNumber++;
                    string line = gdiStream.ReadLine();

                    if (lineNumber == 1)
                    {
                        if (!int.TryParse(line, out _))
                        {
                            throw new ImageNotSupportedException("Not a correct Dreamcast GDI image");
                        }
                    }
                    else
                    {
                        Match trackMatch = regexTrack.Match(line ?? throw new InvalidOperationException());

                        if (!trackMatch.Success)
                        {
                            throw new ImageNotSupportedException($"Unknown line \"{line}\" at line {lineNumber}");
                        }

                        DicConsole.DebugWriteLine("GDI plugin",
                                                  "Found track {0} starts at {1} flags {2} type {3} file {4} offset {5} at line {6}",
                                                  trackMatch.Groups["track"].Value, trackMatch.Groups["start"].Value,
                                                  trackMatch.Groups["flags"].Value, trackMatch.Groups["type"].Value,
                                                  trackMatch.Groups["filename"].Value,
                                                  trackMatch.Groups["offset"].Value, lineNumber);

                        FiltersList filtersList  = new FiltersList();
                        GdiTrack    currentTrack = new GdiTrack
                        {
                            Bps         = ushort.Parse(trackMatch.Groups["type"].Value),
                            Flags       = byte.Parse(trackMatch.Groups["flags"].Value),
                            Offset      = long.Parse(trackMatch.Groups["offset"].Value),
                            Sequence    = uint.Parse(trackMatch.Groups["track"].Value),
                            StartSector = ulong.Parse(trackMatch.Groups["start"].Value),
                            Trackfilter =
                                filtersList.GetFilter(Path.Combine(imageFilter.GetParentFolder(),
                                                                   trackMatch.Groups["filename"].Value
                                                                   .Replace("\\\"", "\"").Trim('"')))
                        };
                        currentTrack.Trackfile = currentTrack.Trackfilter.GetFilename();

                        if (currentTrack.StartSector - currentStart > 0)
                        {
                            if (currentTrack.StartSector == 45000)
                            {
                                highDensity = true;
                                offsetmap.Add(0, currentStart);
                                densitySeparationSectors = currentTrack.StartSector - currentStart;
                                currentStart             = currentTrack.StartSector;
                            }
                            else
                            {
                                currentTrack.Pregap       = currentTrack.StartSector - currentStart;
                                currentTrack.StartSector -= currentTrack.StartSector - currentStart;
                            }
                        }

                        if ((currentTrack.Trackfilter.GetDataForkLength() - currentTrack.Offset) % currentTrack.Bps != 0)
                        {
                            throw new ImageNotSupportedException("Track size not a multiple of sector size");
                        }

                        currentTrack.Sectors =
                            (ulong)((currentTrack.Trackfilter.GetDataForkLength() - currentTrack.Offset) /
                                    currentTrack.Bps);
                        currentTrack.Sectors    += currentTrack.Pregap;
                        currentStart            += currentTrack.Sectors;
                        currentTrack.HighDensity = highDensity;

                        currentTrack.Tracktype =
                            (currentTrack.Flags & 0x4) == 0x4 ? TrackType.CdMode1 : TrackType.Audio;

                        discimage.Tracks.Add(currentTrack);
                    }
                }

                Session[] sessions = new Session[2];
                for (int s = 0; s < sessions.Length; s++)
                {
                    if (s == 0)
                    {
                        sessions[s].SessionSequence = 1;

                        foreach (GdiTrack trk in discimage.Tracks.Where(trk => !trk.HighDensity))
                        {
                            if (sessions[s].StartTrack == 0)
                            {
                                sessions[s].StartTrack = trk.Sequence;
                            }
                            else if (sessions[s].StartTrack > trk.Sequence)
                            {
                                sessions[s].StartTrack = trk.Sequence;
                            }

                            if (sessions[s].EndTrack < trk.Sequence)
                            {
                                sessions[s].EndTrack = trk.Sequence;
                            }

                            if (sessions[s].StartSector > trk.StartSector)
                            {
                                sessions[s].StartSector = trk.StartSector;
                            }

                            if (sessions[s].EndSector < trk.Sectors + trk.StartSector - 1)
                            {
                                sessions[s].EndSector = trk.Sectors + trk.StartSector - 1;
                            }
                        }
                    }
                    else
                    {
                        sessions[s].SessionSequence = 2;

                        foreach (GdiTrack trk in discimage.Tracks.Where(trk => trk.HighDensity))
                        {
                            if (sessions[s].StartTrack == 0)
                            {
                                sessions[s].StartTrack = trk.Sequence;
                            }
                            else if (sessions[s].StartTrack > trk.Sequence)
                            {
                                sessions[s].StartTrack = trk.Sequence;
                            }

                            if (sessions[s].EndTrack < trk.Sequence)
                            {
                                sessions[s].EndTrack = trk.Sequence;
                            }

                            if (sessions[s].StartSector > trk.StartSector)
                            {
                                sessions[s].StartSector = trk.StartSector;
                            }

                            if (sessions[s].EndSector < trk.Sectors + trk.StartSector - 1)
                            {
                                sessions[s].EndSector = trk.Sectors + trk.StartSector - 1;
                            }
                        }
                    }
                }

                discimage.Sessions.Add(sessions[0]);
                discimage.Sessions.Add(sessions[1]);

                discimage.Disktype = MediaType.GDROM;

                // DEBUG information
                DicConsole.DebugWriteLine("GDI plugin", "Disc image parsing results");

                DicConsole.DebugWriteLine("GDI plugin", "Session information:");
                DicConsole.DebugWriteLine("GDI plugin", "\tDisc contains {0} sessions", discimage.Sessions.Count);
                for (int i = 0; i < discimage.Sessions.Count; i++)
                {
                    DicConsole.DebugWriteLine("GDI plugin", "\tSession {0} information:", i + 1);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\tStarting track: {0}",
                                              discimage.Sessions[i].StartTrack);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\tStarting sector: {0}",
                                              discimage.Sessions[i].StartSector);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\tEnding track: {0}", discimage.Sessions[i].EndTrack);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\tEnding sector: {0}", discimage.Sessions[i].EndSector);
                }

                DicConsole.DebugWriteLine("GDI plugin", "Track information:");
                DicConsole.DebugWriteLine("GDI plugin", "\tDisc contains {0} tracks", discimage.Tracks.Count);
                for (int i = 0; i < discimage.Tracks.Count; i++)
                {
                    DicConsole.DebugWriteLine("GDI plugin", "\tTrack {0} information:", discimage.Tracks[i].Sequence);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\t{0} bytes per sector", discimage.Tracks[i].Bps);
                    DicConsole.DebugWriteLine("GDI plugin", "\t\tPregap: {0} sectors", discimage.Tracks[i].Pregap);

                    if ((discimage.Tracks[i].Flags & 0x8) == 0x8)
                    {
                        DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is flagged as quadraphonic");
                    }
                    if ((discimage.Tracks[i].Flags & 0x4) == 0x4)
                    {
                        DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack is data");
                    }
                    if ((discimage.Tracks[i].Flags & 0x2) == 0x2)
                    {
                        DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack allows digital copy");
                    }
                    if ((discimage.Tracks[i].Flags & 0x1) == 0x1)
                    {
                        DicConsole.DebugWriteLine("GDI plugin", "\t\tTrack has pre-emphasis applied");
                    }

                    DicConsole.DebugWriteLine("GDI plugin",
                                              "\t\tTrack resides in file {0}, type defined as {1}, starting at byte {2}",
                                              discimage.Tracks[i].Trackfilter, discimage.Tracks[i].Tracktype,
                                              discimage.Tracks[i].Offset);
                }

                DicConsole.DebugWriteLine("GDI plugin", "Building offset map");

                Partitions = new List <Partition>();
                ulong byteOffset = 0;

                for (int i = 0; i < discimage.Tracks.Count; i++)
                {
                    if (discimage.Tracks[i].Sequence == 1 && i != 0)
                    {
                        throw new ImageNotSupportedException("Unordered tracks");
                    }

                    // Index 01
                    Partition partition = new Partition
                    {
                        Description = $"Track {discimage.Tracks[i].Sequence}.",
                        Name        = null,
                        Start       = discimage.Tracks[i].StartSector,
                        Size        = discimage.Tracks[i].Sectors * discimage.Tracks[i].Bps,
                        Length      = discimage.Tracks[i].Sectors,
                        Sequence    = discimage.Tracks[i].Sequence,
                        Offset      = byteOffset,
                        Type        = discimage.Tracks[i].Tracktype.ToString()
                    };

                    byteOffset += partition.Size;
                    offsetmap.Add(discimage.Tracks[i].Sequence, partition.Start);
                    Partitions.Add(partition);
                }

                foreach (GdiTrack track in discimage.Tracks)
                {
                    imageInfo.ImageSize += track.Bps * track.Sectors;
                }
                foreach (GdiTrack track in discimage.Tracks)
                {
                    imageInfo.Sectors += track.Sectors;
                }

                imageInfo.Sectors += densitySeparationSectors;

                imageInfo.SectorSize = 2352; // All others

                foreach (GdiTrack unused in discimage.Tracks.Where(track => (track.Flags & 0x4) == 0x4 &&
                                                                   track.Bps == 2352))
                {
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ);
                    imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
                }

                imageInfo.CreationTime         = imageFilter.GetCreationTime();
                imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();

                imageInfo.MediaType = discimage.Disktype;

                imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);

                imageInfo.XmlMediaType = XmlMediaType.OpticalDisc;

                DicConsole.VerboseWriteLine("GDI image describes a disc of type {0}", imageInfo.MediaType);

                return(true);
            }
            catch (Exception ex)
            {
                DicConsole.ErrorWriteLine("Exception trying to identify image file {0}", imageFilter.GetBasePath());
                DicConsole.ErrorWriteLine("Exception: {0}", ex.Message);
                DicConsole.ErrorWriteLine("Stack trace: {0}", ex.StackTrace);
                return(false);
            }
        }
Пример #4
0
        internal static void DoEntropy(EntropyOptions options)
        {
            DicConsole.DebugWriteLine("Entropy command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Entropy command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Entropy command", "--separated-tracks={0}", options.SeparatedTracks);
            DicConsole.DebugWriteLine("Entropy command", "--whole-disc={0}", options.WholeDisc);
            DicConsole.DebugWriteLine("Entropy command", "--input={0}", options.InputFile);
            DicConsole.DebugWriteLine("Entropy command", "--duplicated-sectors={0}", options.DuplicatedSectors);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return;
            }

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.ErrorWriteLine("Unable to recognize image format, not checksumming");
                return;
            }

            inputFormat.Open(inputFilter);
            Core.Statistics.AddMediaFormat(inputFormat.Format);
            Core.Statistics.AddMedia(inputFormat.Info.MediaType, false);
            Core.Statistics.AddFilter(inputFilter.Name);
            double entropy = 0;

            ulong[] entTable;
            ulong   sectors;

            if (options.SeparatedTracks)
            {
                try
                {
                    List <Track> inputTracks = inputFormat.Tracks;

                    foreach (Track currentTrack in inputTracks)
                    {
                        entTable = new ulong[256];
                        ulong         trackSize             = 0;
                        List <string> uniqueSectorsPerTrack = new List <string>();

                        sectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector + 1;
                        DicConsole.WriteLine("Track {0} has {1} sectors", currentTrack.TrackSequence, sectors);

                        for (ulong i = currentTrack.TrackStartSector; i <= currentTrack.TrackEndSector; i++)
                        {
                            DicConsole.Write("\rEntropying sector {0} of track {1}", i + 1, currentTrack.TrackSequence);
                            byte[] sector = inputFormat.ReadSector(i, currentTrack.TrackSequence);

                            if (options.DuplicatedSectors)
                            {
                                string sectorHash = Sha1Context.Data(sector, out _);
                                if (!uniqueSectorsPerTrack.Contains(sectorHash))
                                {
                                    uniqueSectorsPerTrack.Add(sectorHash);
                                }
                            }

                            foreach (byte b in sector)
                            {
                                entTable[b]++;
                            }

                            trackSize += (ulong)sector.LongLength;
                        }

                        entropy += entTable.Select(l => (double)l / (double)trackSize)
                                   .Select(frequency => - (frequency * Math.Log(frequency, 2))).Sum();

                        DicConsole.WriteLine("Entropy for track {0} is {1:F4}.", currentTrack.TrackSequence, entropy);

                        if (options.DuplicatedSectors)
                        {
                            DicConsole.WriteLine("Track {0} has {1} unique sectors ({1:P3})",
                                                 currentTrack.TrackSequence, uniqueSectorsPerTrack.Count,
                                                 (double)uniqueSectorsPerTrack.Count / (double)sectors);
                        }

                        DicConsole.WriteLine();
                    }
                }
                catch (Exception ex)
                {
                    if (options.Debug)
                    {
                        DicConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message);
                    }
                    else
                    {
                        DicConsole.ErrorWriteLine("Unable to get separate tracks, not calculating their entropy");
                    }
                }
            }

            if (!options.WholeDisc)
            {
                return;
            }

            entTable = new ulong[256];
            ulong         diskSize      = 0;
            List <string> uniqueSectors = new List <string>();

            sectors = inputFormat.Info.Sectors;
            DicConsole.WriteLine("Sectors {0}", sectors);

            for (ulong i = 0; i < sectors; i++)
            {
                DicConsole.Write("\rEntropying sector {0}", i + 1);
                byte[] sector = inputFormat.ReadSector(i);

                if (options.DuplicatedSectors)
                {
                    string sectorHash = Sha1Context.Data(sector, out _);
                    if (!uniqueSectors.Contains(sectorHash))
                    {
                        uniqueSectors.Add(sectorHash);
                    }
                }

                foreach (byte b in sector)
                {
                    entTable[b]++;
                }

                diskSize += (ulong)sector.LongLength;
            }

            entropy += entTable.Select(l => (double)l / (double)diskSize)
                       .Select(frequency => - (frequency * Math.Log(frequency, 2))).Sum();

            DicConsole.WriteLine();

            DicConsole.WriteLine("Entropy for disk is {0:F4}.", entropy);

            if (options.DuplicatedSectors)
            {
                DicConsole.WriteLine("Disk has {0} unique sectors ({1:P3})", uniqueSectors.Count,
                                     (double)uniqueSectors.Count / (double)sectors);
            }

            Core.Statistics.AddCommand("entropy");
        }
Пример #5
0
        internal static void DoAnalyze(AnalyzeOptions options)
        {
            DicConsole.DebugWriteLine("Analyze command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Analyze command", "--input={0}", options.InputFile);
            DicConsole.DebugWriteLine("Analyze command", "--filesystems={0}", options.SearchForFilesystems);
            DicConsole.DebugWriteLine("Analyze command", "--partitions={0}", options.SearchForPartitions);
            DicConsole.DebugWriteLine("Analyze command", "--encoding={0}", options.EncodingName);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return;
            }

            Encoding encoding = null;

            if (options.EncodingName != null)
            {
                try
                {
                    encoding = Claunia.Encoding.Encoding.GetEncoding(options.EncodingName);
                    if (options.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    DicConsole.ErrorWriteLine("Specified encoding is not supported.");
                    return;
                }
            }

            PluginBase plugins = GetPluginBase.Instance;

            bool checkraw = false;

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
                    return;
                }

                if (options.Verbose)
                {
                    DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
                                                imageFormat.Id);
                }
                else
                {
                    DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
                }
                DicConsole.WriteLine();

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        DicConsole.WriteLine("Unable to open image format");
                        DicConsole.WriteLine("No error given");
                        return;
                    }

                    if (options.Verbose)
                    {
                        Core.ImageInfo.PrintImageInfo(imageFormat);
                        DicConsole.WriteLine();
                    }

                    Core.Statistics.AddMediaFormat(imageFormat.Format);
                    Core.Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Core.Statistics.AddFilter(inputFilter.Name);
                }
                catch (Exception ex)
                {
                    DicConsole.ErrorWriteLine("Unable to open image format");
                    DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
                    DicConsole.DebugWriteLine("Analyze command", "Stack trace: {0}", ex.StackTrace);
                    return;
                }

                List <string> idPlugins;
                IFilesystem   plugin;
                string        information;
                if (options.SearchForPartitions)
                {
                    List <Partition> partitions = Core.Partitions.GetAll(imageFormat);
                    Core.Partitions.AddSchemesToStats(partitions);

                    if (partitions.Count == 0)
                    {
                        DicConsole.DebugWriteLine("Analyze command", "No partitions found");
                        if (!options.SearchForFilesystems)
                        {
                            DicConsole.WriteLine("No partitions founds, not searching for filesystems");
                            return;
                        }

                        checkraw = true;
                    }
                    else
                    {
                        DicConsole.WriteLine("{0} partitions found.", partitions.Count);

                        for (int i = 0; i < partitions.Count; i++)
                        {
                            DicConsole.WriteLine();
                            DicConsole.WriteLine("Partition {0}:", partitions[i].Sequence);
                            DicConsole.WriteLine("Partition name: {0}", partitions[i].Name);
                            DicConsole.WriteLine("Partition type: {0}", partitions[i].Type);
                            DicConsole.WriteLine("Partition start: sector {0}, byte {1}", partitions[i].Start,
                                                 partitions[i].Offset);
                            DicConsole.WriteLine("Partition length: {0} sectors, {1} bytes", partitions[i].Length,
                                                 partitions[i].Size);
                            DicConsole.WriteLine("Partition scheme: {0}", partitions[i].Scheme);
                            DicConsole.WriteLine("Partition description:");
                            DicConsole.WriteLine(partitions[i].Description);

                            if (!options.SearchForFilesystems)
                            {
                                continue;
                            }

                            DicConsole.WriteLine("Identifying filesystem on partition");

                            Core.Filesystems.Identify(imageFormat, out idPlugins, partitions[i]);
                            if (idPlugins.Count == 0)
                            {
                                DicConsole.WriteLine("Filesystem not identified");
                            }
                            else if (idPlugins.Count > 1)
                            {
                                DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                                foreach (string pluginName in idPlugins)
                                {
                                    if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                                    {
                                        DicConsole.WriteLine($"As identified by {plugin.Name}.");
                                        plugin.GetInformation(imageFormat, partitions[i], out information, encoding);
                                        DicConsole.Write(information);
                                        Core.Statistics.AddFilesystem(plugin.XmlFsType.Type);
                                    }
                                }
                            }
                            else
                            {
                                plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);
                                if (plugin == null)
                                {
                                    continue;
                                }

                                DicConsole.WriteLine($"Identified by {plugin.Name}.");
                                plugin.GetInformation(imageFormat, partitions[i], out information, encoding);
                                DicConsole.Write("{0}", information);
                                Core.Statistics.AddFilesystem(plugin.XmlFsType.Type);
                            }
                        }
                    }
                }

                if (checkraw)
                {
                    Partition wholePart = new Partition
                    {
                        Name   = "Whole device",
                        Length = imageFormat.Info.Sectors,
                        Size   = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
                    };

                    Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);
                    if (idPlugins.Count == 0)
                    {
                        DicConsole.WriteLine("Filesystem not identified");
                    }
                    else if (idPlugins.Count > 1)
                    {
                        DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                        foreach (string pluginName in idPlugins)
                        {
                            if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                            {
                                DicConsole.WriteLine($"As identified by {plugin.Name}.");
                                plugin.GetInformation(imageFormat, wholePart, out information, encoding);
                                DicConsole.Write(information);
                                Core.Statistics.AddFilesystem(plugin.XmlFsType.Type);
                            }
                        }
                    }
                    else
                    {
                        plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);
                        if (plugin != null)
                        {
                            DicConsole.WriteLine($"Identified by {plugin.Name}.");
                            plugin.GetInformation(imageFormat, wholePart, out information, encoding);
                            DicConsole.Write(information);
                            Core.Statistics.AddFilesystem(plugin.XmlFsType.Type);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                DicConsole.DebugWriteLine("Analyze command", ex.StackTrace);
            }

            Core.Statistics.AddCommand("analyze");
        }
Пример #6
0
        public static int Invoke(bool debug, bool verbose, bool duplicatedSectors, 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("entropy");

            AaruConsole.DebugWriteLine("Entropy command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Entropy command", "--duplicated-sectors={0}", duplicatedSectors);
            AaruConsole.DebugWriteLine("Entropy command", "--input={0}", imagePath);
            AaruConsole.DebugWriteLine("Entropy command", "--separated-tracks={0}", separatedTracks);
            AaruConsole.DebugWriteLine("Entropy command", "--verbose={0}", verbose);
            AaruConsole.DebugWriteLine("Entropy 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 entropyCalculator = new Entropy(debug, inputFormat);

            entropyCalculator.InitProgressEvent    += Progress.InitProgress;
            entropyCalculator.InitProgress2Event   += Progress.InitProgress2;
            entropyCalculator.UpdateProgressEvent  += Progress.UpdateProgress;
            entropyCalculator.UpdateProgress2Event += Progress.UpdateProgress2;
            entropyCalculator.EndProgressEvent     += Progress.EndProgress;
            entropyCalculator.EndProgress2Event    += Progress.EndProgress2;

            if (separatedTracks)
            {
                EntropyResults[] tracksEntropy = entropyCalculator.CalculateTracksEntropy(duplicatedSectors);

                foreach (EntropyResults trackEntropy in tracksEntropy)
                {
                    AaruConsole.WriteLine("Entropy for track {0} is {1:F4}.", trackEntropy.Track, trackEntropy.Entropy);

                    if (trackEntropy.UniqueSectors != null)
                    {
                        AaruConsole.WriteLine("Track {0} has {1} unique sectors ({2:P3})", trackEntropy.Track,
                                              trackEntropy.UniqueSectors,
                                              (double)trackEntropy.UniqueSectors / (double)trackEntropy.Sectors);
                    }
                }
            }

            if (!wholeDisc)
            {
                return((int)ErrorNumber.NoError);
            }

            EntropyResults entropy = entropyCalculator.CalculateMediaEntropy(duplicatedSectors);

            AaruConsole.WriteLine("Entropy for disk is {0:F4}.", entropy.Entropy);

            if (entropy.UniqueSectors != null)
            {
                AaruConsole.WriteLine("Disk has {0} unique sectors ({1:P3})", entropy.UniqueSectors,
                                      (double)entropy.UniqueSectors / (double)entropy.Sectors);
            }

            return((int)ErrorNumber.NoError);
        }
Пример #7
0
        protected void OnMenuOpen(object sender, EventArgs e)
        {
            // TODO: Extensions
            var dlgOpenImage = new OpenFileDialog
            {
                Title = "Choose image to open"
            };

            DialogResult result = dlgOpenImage.ShowDialog(this);

            if (result != DialogResult.Ok)
            {
                return;
            }

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(dlgOpenImage.FileName);

            if (inputFilter == null)
            {
                MessageBox.Show("Cannot open specified file.", MessageBoxType.Error);

                return;
            }

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    MessageBox.Show("Image format not identified.", MessageBoxType.Error);

                    return;
                }

                DicConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id);

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        MessageBox.Show("Unable to open image format", MessageBoxType.Error);
                        DicConsole.ErrorWriteLine("Unable to open image format");
                        DicConsole.ErrorWriteLine("No error given");

                        return;
                    }

                    // TODO: SVG
                    Stream logo =
                        ResourceHandler.
                        GetResourceStream($"DiscImageChef.Gui.Assets.Logos.Media.{imageFormat.Info.MediaType}.png");

                    var imageGridItem = new TreeGridItem
                    {
                        Values = new object[]
                        {
                            logo == null ? null : new Bitmap(logo),
                            $"{Path.GetFileName(dlgOpenImage.FileName)} ({imageFormat.Info.MediaType})",
                            dlgOpenImage.FileName, new pnlImageInfo(dlgOpenImage.FileName, inputFilter, imageFormat),
                            inputFilter, imageFormat
                        }
                    };

                    List <Partition> partitions = Core.Partitions.GetAll(imageFormat);
                    Core.Partitions.AddSchemesToStats(partitions);

                    bool          checkraw = false;
                    List <string> idPlugins;
                    IFilesystem   plugin;
                    PluginBase    plugins = GetPluginBase.Instance;

                    if (partitions.Count == 0)
                    {
                        DicConsole.DebugWriteLine("Analyze command", "No partitions found");

                        checkraw = true;
                    }
                    else
                    {
                        DicConsole.WriteLine("{0} partitions found.", partitions.Count);

                        foreach (string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s))
                        {
                            var schemeGridItem = new TreeGridItem
                            {
                                Values = new object[]
                                {
                                    nullImage, // TODO: Add icons to partition schemes
                                    scheme
                                }
                            };

                            foreach (Partition partition in partitions.
                                     Where(p => p.Scheme == scheme).OrderBy(p => p.Start))
                            {
                                var partitionGridItem = new TreeGridItem
                                {
                                    Values = new object[]
                                    {
                                        nullImage, // TODO: Add icons to partition schemes
                                        $"{partition.Name} ({partition.Type})", null, new pnlPartition(partition)
                                    }
                                };

                                DicConsole.WriteLine("Identifying filesystem on partition");

                                Core.Filesystems.Identify(imageFormat, out idPlugins, partition);

                                if (idPlugins.Count == 0)
                                {
                                    DicConsole.WriteLine("Filesystem not identified");
                                }
                                else
                                {
                                    DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                                    foreach (string pluginName in idPlugins)
                                    {
                                        if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                                        {
                                            plugin.GetInformation(imageFormat, partition, out string information, null);

                                            var fsPlugin = plugin as IReadOnlyFilesystem;

                                            if (fsPlugin != null)
                                            {
                                                Errno error =
                                                    fsPlugin.Mount(imageFormat, partition, null,
                                                                   new Dictionary <string, string>(), null);

                                                if (error != Errno.NoError)
                                                {
                                                    fsPlugin = null;
                                                }
                                            }

                                            var filesystemGridItem = new TreeGridItem
                                            {
                                                Values = new object[]
                                                {
                                                    nullImage, // TODO: Add icons to filesystems
                                                    plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
                                                        : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
                                                    fsPlugin, new pnlFilesystem(plugin.XmlFsType, information)
                                                }
                                            };

                                            if (fsPlugin != null)
                                            {
                                                Statistics.AddCommand("ls");
                                                filesystemGridItem.Children.Add(placeholderItem);
                                            }

                                            Statistics.AddFilesystem(plugin.XmlFsType.Type);
                                            partitionGridItem.Children.Add(filesystemGridItem);
                                        }
                                    }
                                }

                                schemeGridItem.Children.Add(partitionGridItem);
                            }

                            imageGridItem.Children.Add(schemeGridItem);
                        }
                    }

                    if (checkraw)
                    {
                        var wholePart = new Partition
                        {
                            Name = "Whole device", Length = imageFormat.Info.Sectors,
                            Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
                        };

                        Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);

                        if (idPlugins.Count == 0)
                        {
                            DicConsole.WriteLine("Filesystem not identified");
                        }
                        else
                        {
                            DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                            foreach (string pluginName in idPlugins)
                            {
                                if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                                {
                                    plugin.GetInformation(imageFormat, wholePart, out string information, null);

                                    var fsPlugin = plugin as IReadOnlyFilesystem;

                                    if (fsPlugin != null)
                                    {
                                        Errno error = fsPlugin.Mount(imageFormat, wholePart, null,
                                                                     new Dictionary <string, string>(), null);

                                        if (error != Errno.NoError)
                                        {
                                            fsPlugin = null;
                                        }
                                    }

                                    var filesystemGridItem = new TreeGridItem
                                    {
                                        Values = new object[]
                                        {
                                            nullImage, // TODO: Add icons to filesystems
                                            plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
                                                : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
                                            fsPlugin, new pnlFilesystem(plugin.XmlFsType, information)
                                        }
                                    };

                                    if (fsPlugin != null)
                                    {
                                        Statistics.AddCommand("ls");
                                        filesystemGridItem.Children.Add(placeholderItem);
                                    }

                                    Statistics.AddFilesystem(plugin.XmlFsType.Type);
                                    imageGridItem.Children.Add(filesystemGridItem);
                                }
                            }
                        }
                    }

                    imagesRoot.Children.Add(imageGridItem);
                    treeImages.ReloadData();

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Statistics.AddFilter(inputFilter.Name);
                }
Пример #8
0
        internal static void DoLs(LsOptions options)
        {
            DicConsole.DebugWriteLine("Ls command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Ls command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Ls command", "--input={0}", options.InputFile);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

            Dictionary <string, string> parsedOptions = Options.Parse(options.Options);

            DicConsole.DebugWriteLine("Ls command", "Parsed options:");
            foreach (KeyValuePair <string, string> parsedOption in parsedOptions)
            {
                DicConsole.DebugWriteLine("Ls command", "{0} = {1}", parsedOption.Key, parsedOption.Value);
            }
            parsedOptions.Add("debug", options.Debug.ToString());

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return;
            }

            Encoding encoding = null;

            if (options.EncodingName != null)
            {
                try
                {
                    encoding = Claunia.Encoding.Encoding.GetEncoding(options.EncodingName);
                    if (options.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    DicConsole.ErrorWriteLine("Specified encoding is not supported.");
                    return;
                }
            }

            PluginBase plugins = GetPluginBase.Instance;

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
                    return;
                }

                if (options.Verbose)
                {
                    DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
                                                imageFormat.Id);
                }
                else
                {
                    DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
                }

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        DicConsole.WriteLine("Unable to open image format");
                        DicConsole.WriteLine("No error given");
                        return;
                    }

                    DicConsole.DebugWriteLine("Ls command", "Correctly opened image file.");
                    DicConsole.DebugWriteLine("Ls command", "Image without headers is {0} bytes.",
                                              imageFormat.Info.ImageSize);
                    DicConsole.DebugWriteLine("Ls command", "Image has {0} sectors.", imageFormat.Info.Sectors);
                    DicConsole.DebugWriteLine("Ls command", "Image identifies disk type as {0}.",
                                              imageFormat.Info.MediaType);

                    Core.Statistics.AddMediaFormat(imageFormat.Format);
                    Core.Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Core.Statistics.AddFilter(inputFilter.Name);
                }
                catch (Exception ex)
                {
                    DicConsole.ErrorWriteLine("Unable to open image format");
                    DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
                    return;
                }

                List <Partition> partitions = Core.Partitions.GetAll(imageFormat);
                Core.Partitions.AddSchemesToStats(partitions);

                List <string>       idPlugins;
                IReadOnlyFilesystem plugin;
                Errno error;
                if (partitions.Count == 0)
                {
                    DicConsole.DebugWriteLine("Ls command", "No partitions found");
                }
                else
                {
                    DicConsole.WriteLine("{0} partitions found.", partitions.Count);

                    for (int i = 0; i < partitions.Count; i++)
                    {
                        DicConsole.WriteLine();
                        DicConsole.WriteLine("Partition {0}:", partitions[i].Sequence);

                        DicConsole.WriteLine("Identifying filesystem on partition");

                        Core.Filesystems.Identify(imageFormat, out idPlugins, partitions[i]);
                        if (idPlugins.Count == 0)
                        {
                            DicConsole.WriteLine("Filesystem not identified");
                        }
                        else if (idPlugins.Count > 1)
                        {
                            DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                            foreach (string pluginName in idPlugins)
                            {
                                if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin))
                                {
                                    DicConsole.WriteLine($"As identified by {plugin.Name}.");
                                    IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin
                                                             .GetType()
                                                             .GetConstructor(Type.EmptyTypes)
                                                             ?.Invoke(new object[] { });

                                    if (fs == null)
                                    {
                                        continue;
                                    }

                                    error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions);
                                    if (error == Errno.NoError)
                                    {
                                        error = fs.ReadDir("/", out List <string> rootDir);
                                        if (error == Errno.NoError)
                                        {
                                            foreach (string entry in rootDir)
                                            {
                                                DicConsole.WriteLine("{0}", entry);
                                            }
                                        }
                                        else
                                        {
                                            DicConsole.ErrorWriteLine("Error {0} reading root directory {0}",
                                                                      error.ToString());
                                        }

                                        Core.Statistics.AddFilesystem(fs.XmlFsType.Type);
                                    }
                                    else
                                    {
                                        DicConsole.ErrorWriteLine("Unable to mount device, error {0}",
                                                                  error.ToString());
                                    }
                                }
                            }
                        }
                        else
                        {
                            plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin);
                            if (plugin == null)
                            {
                                continue;
                            }

                            DicConsole.WriteLine($"Identified by {plugin.Name}.");
                            IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin
                                                     .GetType().GetConstructor(Type.EmptyTypes)
                                                     ?.Invoke(new object[] { });
                            if (fs == null)
                            {
                                continue;
                            }

                            error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions);
                            if (error == Errno.NoError)
                            {
                                error = fs.ReadDir("/", out List <string> rootDir);
                                if (error == Errno.NoError)
                                {
                                    foreach (string entry in rootDir)
                                    {
                                        DicConsole.WriteLine("{0}", entry);
                                    }
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
                                }

                                Core.Statistics.AddFilesystem(fs.XmlFsType.Type);
                            }
                            else
                            {
                                DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
                            }
                        }
                    }
                }

                Partition wholePart = new Partition
                {
                    Name   = "Whole device",
                    Length = imageFormat.Info.Sectors,
                    Size   = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
                };

                Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);
                if (idPlugins.Count == 0)
                {
                    DicConsole.WriteLine("Filesystem not identified");
                }
                else if (idPlugins.Count > 1)
                {
                    DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                    foreach (string pluginName in idPlugins)
                    {
                        if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin))
                        {
                            DicConsole.WriteLine($"As identified by {plugin.Name}.");
                            IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin
                                                     .GetType().GetConstructor(Type.EmptyTypes)
                                                     ?.Invoke(new object[] { });
                            if (fs == null)
                            {
                                continue;
                            }

                            error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions);
                            if (error == Errno.NoError)
                            {
                                error = fs.ReadDir("/", out List <string> rootDir);
                                if (error == Errno.NoError)
                                {
                                    foreach (string entry in rootDir)
                                    {
                                        DicConsole.WriteLine("{0}", entry);
                                    }
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
                                }

                                Core.Statistics.AddFilesystem(fs.XmlFsType.Type);
                            }
                            else
                            {
                                DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
                            }
                        }
                    }
                }
                else
                {
                    plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin);
                    if (plugin != null)
                    {
                        DicConsole.WriteLine($"Identified by {plugin.Name}.");
                        IReadOnlyFilesystem fs =
                            (IReadOnlyFilesystem)plugin
                            .GetType().GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { });
                        if (fs != null)
                        {
                            error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions);
                            if (error == Errno.NoError)
                            {
                                error = fs.ReadDir("/", out List <string> rootDir);
                                if (error == Errno.NoError)
                                {
                                    foreach (string entry in rootDir)
                                    {
                                        if (options.Long)
                                        {
                                            error = fs.Stat(entry, out FileEntryInfo stat);
                                            if (error == Errno.NoError)
                                            {
                                                DicConsole.WriteLine("{0}\t{1}\t{2} bytes\t{3}", stat.CreationTimeUtc,
                                                                     stat.Inode, stat.Length, entry);

                                                error = fs.ListXAttr(entry, out List <string> xattrs);
                                                if (error != Errno.NoError)
                                                {
                                                    continue;
                                                }

                                                foreach (string xattr in xattrs)
                                                {
                                                    byte[] xattrBuf = new byte[0];
                                                    error = fs.GetXattr(entry, xattr, ref xattrBuf);
                                                    if (error == Errno.NoError)
                                                    {
                                                        DicConsole.WriteLine("\t\t{0}\t{1} bytes", xattr,
                                                                             xattrBuf.Length);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                DicConsole.WriteLine("{0}", entry);
                                            }
                                        }
                                        else
                                        {
                                            DicConsole.WriteLine("{0}", entry);
                                        }
                                    }
                                }
                                else
                                {
                                    DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString());
                                }

                                Core.Statistics.AddFilesystem(fs.XmlFsType.Type);
                            }
                            else
                            {
                                DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                DicConsole.DebugWriteLine("Ls command", ex.StackTrace);
            }

            Core.Statistics.AddCommand("ls");
        }
Пример #9
0
        public void Hashes()
        {
            Environment.CurrentDirectory = Environment.CurrentDirectory = DataFolder;

            Assert.Multiple(() =>
            {
                Parallel.For(0L, Tests.Length, (i, state) =>
                {
                    string testFile = Tests[i].TestFile;

                    bool exists = File.Exists(testFile);
                    Assert.True(exists, $"{testFile} not found");

                    // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                    // It arrives here...
                    if (!exists)
                    {
                        return;
                    }

                    var filtersList = new FiltersList();
                    IFilter filter  = filtersList.GetFilter(testFile);
                    filter.Open(testFile);

                    var image = Activator.CreateInstance(_plugin.GetType()) as IOpticalMediaImage;
                    Assert.NotNull(image, $"Could not instantiate filesystem for {testFile}");

                    bool opened = image.Open(filter);
                    Assert.AreEqual(true, opened, $"Open: {testFile}");

                    if (!opened)
                    {
                        return;
                    }

                    Md5Context ctx;

                    if (image.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                    {
                        foreach (bool @long in new[]
                        {
                            false, true
                        })
                        {
                            ctx = new Md5Context();

                            foreach (Track currentTrack in image.Tracks)
                            {
                                ulong sectors     = currentTrack.TrackEndSector - currentTrack.TrackStartSector + 1;
                                ulong doneSectors = 0;

                                while (doneSectors < sectors)
                                {
                                    byte[] sector;

                                    if (sectors - doneSectors >= SECTORS_TO_READ)
                                    {
                                        sector =
                                            @long ? image.ReadSectorsLong(doneSectors, SECTORS_TO_READ,
                                                                          currentTrack.TrackSequence)
                                                : image.ReadSectors(doneSectors, SECTORS_TO_READ,
                                                                    currentTrack.TrackSequence);

                                        doneSectors += SECTORS_TO_READ;
                                    }
                                    else
                                    {
                                        sector =
                                            @long ? image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
                                                                          currentTrack.TrackSequence)
                                                : image.ReadSectors(doneSectors, (uint)(sectors - doneSectors),
                                                                    currentTrack.TrackSequence);

                                        doneSectors += sectors - doneSectors;
                                    }

                                    ctx.Update(sector);
                                }
                            }

                            Assert.AreEqual(@long ? Tests[i].LongMD5 : Tests[i].MD5, ctx.End(),
                                            $"{(@long ? "Long hash" : "Hash")}: {testFile}");
                        }

                        if (!image.Info.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
                        {
                            return;
                        }

                        ctx = new Md5Context();

                        foreach (Track currentTrack in image.Tracks)
                        {
                            ulong sectors     = currentTrack.TrackEndSector - currentTrack.TrackStartSector + 1;
                            ulong doneSectors = 0;

                            while (doneSectors < sectors)
                            {
                                byte[] sector;

                                if (sectors - doneSectors >= SECTORS_TO_READ)
                                {
                                    sector = image.ReadSectorsTag(doneSectors, SECTORS_TO_READ,
                                                                  currentTrack.TrackSequence,
                                                                  SectorTagType.CdSectorSubchannel);

                                    doneSectors += SECTORS_TO_READ;
                                }
                                else
                                {
                                    sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
                                                                  currentTrack.TrackSequence,
                                                                  SectorTagType.CdSectorSubchannel);

                                    doneSectors += sectors - doneSectors;
                                }

                                ctx.Update(sector);
                            }
                        }

                        Assert.AreEqual(Tests[i].SubchannelMD5, ctx.End(), $"Subchannel hash: {testFile}");
                    }
                    else
                    {
                        ctx = new Md5Context();
                        ulong doneSectors = 0;

                        while (doneSectors < image.Info.Sectors)
                        {
                            byte[] sector;

                            if (image.Info.Sectors - doneSectors >= SECTORS_TO_READ)
                            {
                                sector       = image.ReadSectors(doneSectors, SECTORS_TO_READ);
                                doneSectors += SECTORS_TO_READ;
                            }
                            else
                            {
                                sector       = image.ReadSectors(doneSectors, (uint)(image.Info.Sectors - doneSectors));
                                doneSectors += image.Info.Sectors - doneSectors;
                            }

                            ctx.Update(sector);
                        }

                        Assert.AreEqual(Tests[i].MD5, ctx.End(), $"Hash: {testFile}");
                    }
                });
            });
        }
 /// <include file="SpecificFilterDoc.xml" path='docs/members[@name="getUploadSharesFilter"]/AddNodeIdFilter/*'/>
 public void AddNodeIdFilter(DracoonFilterType <NodeIdFilter> nodeIdFilter)
 {
     CheckFilter(nodeIdFilter, nameof(nodeIdFilter));
     FiltersList.Add(nodeIdFilter);
 }
Пример #11
0
        public void ApplySelectedFilter()
        {
            if (SelectedFilter.sortType != SortType.None || SelectedFilter.FilterCriteria.Where(f => f.IsChecked).Any())
            {
                ColumnHeaderTextBlocks[SelectedFilter.columnId].Foreground = (Windows.UI.Xaml.Media.Brush)Windows.UI.Xaml.Application.Current.Resources["HudlBlue"];

                if (ColumnHeaderTextBlocks[SelectedFilter.columnId].Inlines.Count > 1)
                {
                    ColumnHeaderTextBlocks[SelectedFilter.columnId].Inlines.RemoveAt(1);
                }
                if (SelectedFilter.sortType == SortType.Ascending)
                {
                    Run text = new Run();
                    text.Text = " \u25B2";
                    ColumnHeaderTextBlocks[SelectedFilter.columnId].Text = GridHeadersTextSorted[SelectedFilter.columnId];
                    ColumnHeaderTextBlocks[SelectedFilter.columnId].Inlines.Add(text);
                }
                else if (SelectedFilter.sortType == SortType.Descending)
                {
                    Run text = new Run();
                    text.Text = " \u25BC";
                    ColumnHeaderTextBlocks[SelectedFilter.columnId].Text = GridHeadersTextSorted[SelectedFilter.columnId];
                    ColumnHeaderTextBlocks[SelectedFilter.columnId].Inlines.Add(text);
                }

                List <Clip> newFilteredClips = new List <Clip>();
                List <Clip> currentFilteredClips;

                if (FiltersList.Contains(SelectedFilter))
                {
                    currentFilteredClips = removeFilter();
                }
                else
                {
                    currentFilteredClips = FilteredClips.ToList();
                }

                if (SelectedFilter.FilterCriteria != null && SelectedFilter.FilterCriteria.Any(c => c.IsChecked))
                {
                    List <String> filtersApplied = new List <String>();
                    foreach (FilterCriteriaViewModel criteria in SelectedFilter.FilterCriteria.Where(c => c.IsChecked))
                    {
                        newFilteredClips.AddRange(currentFilteredClips.Where(clip => clip.breakDownData[SelectedFilter.columnId].Equals(criteria.Name)));
                        filtersApplied.Add(criteria.Name);
                    }
                    Logger.Instance.LogFilterApplied(filtersApplied);
                }
                else
                {
                    newFilteredClips.AddRange(currentFilteredClips);
                }

                FilterViewModel currentSortFilter = FiltersList.FirstOrDefault(f => f.sortType != SortType.None);
                if (SelectedFilter.sortType == SortType.Ascending || SelectedFilter.sortType == SortType.Descending)
                {
                    if (currentSortFilter != null)
                    {
                        if (!currentSortFilter.FilterCriteria.Any(c => c.IsChecked))
                        {
                            ColumnHeaderTextBlocks[currentSortFilter.columnId].Foreground = (Windows.UI.Xaml.Media.Brush)Windows.UI.Xaml.Application.Current.Resources["HudlMediumDarkGray"];
                            if (ColumnHeaderTextBlocks[currentSortFilter.columnId].Inlines.Count > 1)
                            {
                                ColumnHeaderTextBlocks[currentSortFilter.columnId].Inlines.RemoveAt(1);
                                ColumnHeaderTextBlocks[currentSortFilter.columnId].Text = GridHeadersTextUnsorted[currentSortFilter.columnId];
                            }
                            FiltersList.Remove(currentSortFilter);
                        }
                        else
                        {
                            if (ColumnHeaderTextBlocks[currentSortFilter.columnId].Inlines.Count > 1)
                            {
                                ColumnHeaderTextBlocks[currentSortFilter.columnId].Inlines.RemoveAt(1);
                                ColumnHeaderTextBlocks[currentSortFilter.columnId].Text = GridHeadersTextUnsorted[currentSortFilter.columnId];
                            }
                            currentSortFilter.setSortType(SortType.None);
                        }
                    }
                    currentSortFilter = SelectedFilter;
                }

                sortClips(ref newFilteredClips, currentSortFilter);
                FiltersList.Add(SelectedFilter);
                applyFilter(newFilteredClips);
            }
            else
            {
                if (FiltersList.Contains(SelectedFilter))
                {
                    RemoveSelectedFilter();
                }
            }
        }
 /// <include file="SpecificFilterDoc.xml" path='docs/members[@name="getUploadSharesFilter"]/AddCreatedByFilter/*'/>
 public void AddCreatedByFilter(DracoonFilterType <CreatedByFilter> createdByFilter)
 {
     CheckFilter(createdByFilter, nameof(createdByFilter));
     FiltersList.Add(createdByFilter);
 }
 /// <include file="SpecificFilterDoc.xml" path='docs/members[@name="getUploadSharesFilter"]/AddUserIdFilter/*'/>
 public void AddUserIdFilter(DracoonFilterType <UserIdFilter> userIdFilter)
 {
     CheckFilter(userIdFilter, nameof(userIdFilter));
     FiltersList.Add(userIdFilter);
 }
 /// <include file="SpecificFilterDoc.xml" path='docs/members[@name="getUploadSharesFilter"]/AddNameFilter/*'/>
 public void AddNameFilter(DracoonFilterType <NameFilter> nameFilter)
 {
     CheckFilter(nameFilter, nameof(nameFilter));
     FiltersList.Add(nameFilter);
 }
        public void Convert()
        {
            Environment.CurrentDirectory = DataFolder;

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(InputPath);

            Assert.IsNotNull(inputFilter, "Cannot open specified file.");

            string outputPath = Path.Combine(Path.GetTempPath(), SuggestedOutputFilename);

            Assert.IsFalse(File.Exists(outputPath), "Output file already exists, not continuing.");

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            Assert.IsNotNull(inputFormat, "Input image format not identified, not proceeding with conversion.");

            Assert.IsTrue(inputFormat.Open(inputFilter), "Unable to open image format");

            Assert.IsTrue(OutputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType),
                          "Output format does not support media type, cannot continue...");

            if (inputFormat.Info.ReadableSectorTags.Count == 0)
            {
                Assert.IsFalse(UseLong, "Input image does not support long sectors.");
            }

            var inputOptical  = inputFormat as IOpticalMediaImage;
            var outputOptical = OutputFormat as IWritableOpticalImage;

            Assert.IsNotNull(inputOptical, "Could not treat existing image as optical disc.");
            Assert.IsNotNull(outputOptical, "Could not treat new image as optical disc.");
            Assert.IsNotNull(inputOptical.Tracks, "Existing image contains no tracks.");

            Assert.IsTrue(outputOptical.Create(outputPath, inputFormat.Info.MediaType, ParsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize),
                          $"Error {outputOptical.ErrorMessage} creating output image.");

            var metadata = new ImageInfo
            {
                Application           = "Aaru",
                ApplicationVersion    = Version.GetVersion(),
                Comments              = inputFormat.Info.Comments,
                Creator               = inputFormat.Info.Creator,
                DriveFirmwareRevision = inputFormat.Info.DriveFirmwareRevision,
                DriveManufacturer     = inputFormat.Info.DriveManufacturer,
                DriveModel            = inputFormat.Info.DriveModel,
                DriveSerialNumber     = inputFormat.Info.DriveSerialNumber,
                LastMediaSequence     = inputFormat.Info.LastMediaSequence,
                MediaBarcode          = inputFormat.Info.MediaBarcode,
                MediaManufacturer     = inputFormat.Info.MediaManufacturer,
                MediaModel            = inputFormat.Info.MediaModel,
                MediaPartNumber       = inputFormat.Info.MediaPartNumber,
                MediaSequence         = inputFormat.Info.MediaSequence,
                MediaSerialNumber     = inputFormat.Info.MediaSerialNumber,
                MediaTitle            = inputFormat.Info.MediaTitle
            };

            Assert.IsTrue(outputOptical.SetMetadata(metadata),
                          $"Error {outputOptical.ErrorMessage} setting metadata, ");

            CICMMetadataType        cicmMetadata = inputFormat.CicmMetadata;
            List <DumpHardwareType> dumpHardware = inputFormat.DumpHardware;

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
                                                                                       outputOptical.SupportedMediaTags.Contains(mediaTag)))
            {
                AaruConsole.WriteLine("Converting media tag {0}", mediaTag);
                byte[] tag = inputFormat.ReadDiskTag(mediaTag);

                Assert.IsTrue(outputOptical.WriteMediaTag(tag, mediaTag));
            }

            AaruConsole.WriteLine("{0} sectors to convert", inputFormat.Info.Sectors);
            ulong doneSectors;

            Assert.IsTrue(outputOptical.SetTracks(inputOptical.Tracks),
                          $"Error {outputOptical.ErrorMessage} sending tracks list to output image.");

            foreach (Track track in inputOptical.Tracks)
            {
                doneSectors = 0;
                ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1;

                while (doneSectors < trackSectors)
                {
                    byte[] sector;

                    uint sectorsToDo;

                    if (trackSectors - doneSectors >= SECTORS_TO_READ)
                    {
                        sectorsToDo = SECTORS_TO_READ;
                    }
                    else
                    {
                        sectorsToDo = (uint)(trackSectors - doneSectors);
                    }

                    bool useNotLong = false;
                    bool result     = false;

                    if (UseLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSectorLong(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectorsLong(sector, doneSectors + track.TrackStartSector,
                                                                    sectorsToDo);
                        }

                        if (!result &&
                            sector.Length % 2352 != 0)
                        {
                            useNotLong = true;
                        }
                    }

                    if (!UseLong || useNotLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSector(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectors(sector, doneSectors + track.TrackStartSector,
                                                                sectorsToDo);
                        }
                    }

                    Assert.IsTrue(result,
                                  $"Error {outputOptical.ErrorMessage} writing sector {doneSectors + track.TrackStartSector}, not continuing...");

                    doneSectors += sectorsToDo;
                }
            }

            Dictionary <byte, string> isrcs                  = new Dictionary <byte, string>();
            Dictionary <byte, byte>   trackFlags             = new Dictionary <byte, byte>();
            string                 mcn                       = null;
            HashSet <int>          subchannelExtents         = new HashSet <int>();
            Dictionary <byte, int> smallestPregapLbaPerTrack = new Dictionary <byte, int>();

            Track[] tracks = new Track[inputOptical.Tracks.Count];

            for (int i = 0; i < tracks.Length; i++)
            {
                tracks[i] = new Track
                {
                    Indexes                = new Dictionary <ushort, int>(),
                    TrackDescription       = inputOptical.Tracks[i].TrackDescription,
                    TrackEndSector         = inputOptical.Tracks[i].TrackEndSector,
                    TrackStartSector       = inputOptical.Tracks[i].TrackStartSector,
                    TrackPregap            = inputOptical.Tracks[i].TrackPregap,
                    TrackSequence          = inputOptical.Tracks[i].TrackSequence,
                    TrackSession           = inputOptical.Tracks[i].TrackSession,
                    TrackBytesPerSector    = inputOptical.Tracks[i].TrackBytesPerSector,
                    TrackRawBytesPerSector = inputOptical.Tracks[i].TrackRawBytesPerSector,
                    TrackType              = inputOptical.Tracks[i].TrackType,
                    TrackSubchannelType    = inputOptical.Tracks[i].TrackSubchannelType
                };

                foreach (KeyValuePair <ushort, int> idx in inputOptical.Tracks[i].Indexes)
                {
                    tracks[i].Indexes[idx.Key] = idx.Value;
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackIsrc).
                     OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (isrc is null)
                    {
                        continue;
                    }

                    isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc);
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.
                     Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (flags is null)
                    {
                        continue;
                    }

                    trackFlags[(byte)track.TrackSequence] = flags[0];
                }
            }

            for (ulong s = 0; s < inputFormat.Info.Sectors; s++)
            {
                if (s > int.MaxValue)
                {
                    break;
                }

                subchannelExtents.Add((int)s);
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t).TakeWhile(tag => UseLong))
            {
                switch (tag)
                {
                case SectorTagType.AppleSectorTag:
                case SectorTagType.CdSectorSync:
                case SectorTagType.CdSectorHeader:
                case SectorTagType.CdSectorSubHeader:
                case SectorTagType.CdSectorEdc:
                case SectorTagType.CdSectorEccP:
                case SectorTagType.CdSectorEccQ:
                case SectorTagType.CdSectorEcc:
                    // This tags are inline in long sector
                    continue;
                }

                if (!outputOptical.SupportedSectorTags.Contains(tag))
                {
                    continue;
                }

                foreach (Track track in inputOptical.Tracks)
                {
                    doneSectors = 0;
                    ulong  trackSectors = track.TrackEndSector - track.TrackStartSector + 1;
                    byte[] sector;
                    bool   result;

                    switch (tag)
                    {
                    case SectorTagType.CdTrackFlags:
                    case SectorTagType.CdTrackIsrc:
                        sector = inputFormat.ReadSectorTag(track.TrackSequence, tag);
                        result = outputOptical.WriteSectorTag(sector, track.TrackSequence, tag);

                        Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing tag, not continuing...");

                        continue;
                    }

                    while (doneSectors < trackSectors)
                    {
                        uint sectorsToDo;

                        if (trackSectors - doneSectors >= SECTORS_TO_READ)
                        {
                            sectorsToDo = SECTORS_TO_READ;
                        }
                        else
                        {
                            sectorsToDo = (uint)(trackSectors - doneSectors);
                        }

                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, 1, null,
                                                                                         isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, true,
                                                                                         outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorTag(sector, doneSectors + track.TrackStartSector,
                                                                      tag);
                            }
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, sectorsToDo,
                                                                                         null, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents,
                                                                                         true, outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
                                                                       sectorsToDo, tag);
                            }
                        }

                        Assert.IsTrue(result,
                                      $"Error {outputOptical.ErrorMessage} writing tag for sector {doneSectors + track.TrackStartSector}, not continuing...");

                        doneSectors += sectorsToDo;
                    }
                }
            }

            if (isrcs.Count > 0)
            {
                foreach (KeyValuePair <byte, string> isrc in isrcs)
                {
                    outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key,
                                                 SectorTagType.CdTrackIsrc);
                }
            }

            if (trackFlags.Count > 0)
            {
                foreach ((byte track, byte flags) in trackFlags)
                {
                    outputOptical.WriteSectorTag(new[]
                    {
                        flags
                    }, track, SectorTagType.CdTrackFlags);
                }
            }

            if (mcn != null)
            {
                outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
            }

            if (resume != null ||
                dumpHardware != null)
            {
                if (resume != null)
                {
                    outputOptical.SetDumpHardware(resume.Tries);
                }
                else if (dumpHardware != null)
                {
                    outputOptical.SetDumpHardware(dumpHardware);
                }
            }

            if (sidecar != null ||
                cicmMetadata != null)
            {
                if (sidecar != null)
                {
                    outputOptical.SetCicmMetadata(sidecar);
                }
                else if (cicmMetadata != null)
                {
                    outputOptical.SetCicmMetadata(cicmMetadata);
                }
            }

            Assert.True(outputOptical.Close(),
                        $"Error {outputOptical.ErrorMessage} closing output image... Contents are not correct.");

            // Some images will never generate the same
            if (Md5 != null)
            {
                string md5 = Md5Context.File(outputPath, out _);

                Assert.AreEqual(Md5, md5, "Hashes are different");
            }

            File.Delete(outputPath);
        }
Пример #16
0
        public void Info()
        {
            Environment.CurrentDirectory = DataFolder;

            Assert.Multiple(() =>
            {
                foreach (OpticalImageTestExpected test in Tests)
                {
                    string testFile = test.TestFile;

                    bool exists = File.Exists(testFile);
                    Assert.True(exists, $"{testFile} not found");

                    // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                    // It arrives here...
                    if (!exists)
                    {
                        continue;
                    }

                    var filtersList = new FiltersList();
                    IFilter filter  = filtersList.GetFilter(testFile);
                    filter.Open(testFile);

                    var image = Activator.CreateInstance(_plugin.GetType()) as IOpticalMediaImage;
                    Assert.NotNull(image, $"Could not instantiate filesystem for {testFile}");

                    bool opened = image.Open(filter);
                    Assert.AreEqual(true, opened, $"Open: {testFile}");

                    if (!opened)
                    {
                        continue;
                    }

                    using (new AssertionScope())
                    {
                        Assert.Multiple(() =>
                        {
                            Assert.AreEqual(test.Sectors, image.Info.Sectors, $"Sectors: {testFile}");

                            if (test.SectorSize > 0)
                            {
                                Assert.AreEqual(test.SectorSize, image.Info.SectorSize, $"Sector size: {testFile}");
                            }

                            Assert.AreEqual(test.MediaType, image.Info.MediaType, $"Media type: {testFile}");

                            if (image.Info.XmlMediaType != XmlMediaType.OpticalDisc)
                            {
                                return;
                            }

                            Assert.AreEqual(test.Tracks.Length, image.Tracks.Count, $"Tracks: {testFile}");

                            image.Tracks.Select(t => t.TrackSession).Should().
                            BeEquivalentTo(test.Tracks.Select(s => s.Session), $"Track session: {testFile}");

                            image.Tracks.Select(t => t.TrackStartSector).Should().
                            BeEquivalentTo(test.Tracks.Select(s => s.Start), $"Track start: {testFile}");

                            image.Tracks.Select(t => t.TrackEndSector).Should().
                            BeEquivalentTo(test.Tracks.Select(s => s.End), $"Track end: {testFile}");

                            image.Tracks.Select(t => t.TrackPregap).Should().
                            BeEquivalentTo(test.Tracks.Select(s => s.Pregap), $"Track pregap: {testFile}");

                            int trackNo = 0;

                            byte?[] flags = new byte?[image.Tracks.Count];

                            foreach (Track currentTrack in image.Tracks)
                            {
                                if (image.Info.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags))
                                {
                                    flags[trackNo] = image.ReadSectorTag(currentTrack.TrackSequence,
                                                                         SectorTagType.CdTrackFlags)[0];
                                }

                                trackNo++;
                            }

                            flags.Should().BeEquivalentTo(test.Tracks.Select(s => s.Flags), $"Track flags: {testFile}");
                        });
                    }
                }
            });
        }
Пример #17
0
        /// <summary>Dumps a MiniDisc Data device</summary>
        void MiniDisc()
        {
            bool         sense;
            byte         scsiMediumType = 0;
            const ushort sbcProfile     = 0x0001;
            DateTime     start;
            DateTime     end;
            double       totalDuration = 0;
            double       currentSpeed  = 0;
            double       maxSpeed      = double.MinValue;
            double       minSpeed      = double.MaxValue;

            byte[]            readBuffer;
            Modes.DecodedMode?decMode = null;
            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            byte[] cmdBuf;
            bool   ret;

            _dumpLog.WriteLine("Initializing reader.");
            var   scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog);
            ulong blocks     = scsiReader.GetDeviceBlocks();
            uint  blockSize  = scsiReader.LogicalBlockSize;

            _dumpLog.WriteLine("Requesting MODE SENSE (6).");
            UpdateStatus?.Invoke("Requesting MODE SENSE (6).");

            sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x3F, 5, out _);

            if (!sense &&
                !_dev.Error &&
                Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue)
            {
                decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
            }

            if (decMode.HasValue)
            {
                scsiMediumType = (byte)decMode.Value.Header.MediumType;
            }

            if (blockSize != 2048)
            {
                _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped.");

                StoppingErrorMessage?.
                Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped.");

                return;
            }

            MediaType dskType = MediaType.MDData;

            if (scsiReader.FindReadCommand())
            {
                _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage);
                StoppingErrorMessage?.Invoke("Unable to read medium.");

                return;
            }

            if (blocks != 0 &&
                blockSize != 0)
            {
                blocks++;

                ulong totalSize = blocks * blockSize;

                if (totalSize > 1099511627776)
                {
                    UpdateStatus?.
                    Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)");
                }
                else if (totalSize > 1073741824)
                {
                    UpdateStatus?.
                    Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)");
                }
                else if (totalSize > 1048576)
                {
                    UpdateStatus?.
                    Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)");
                }
                else if (totalSize > 1024)
                {
                    UpdateStatus?.
                    Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)");
                }
                else
                {
                    UpdateStatus?.
                    Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)");
                }
            }

            // Check how many blocks to read, if error show and return
            // 64 works, gets maximum speed (150KiB/s), slow I know...
            if (scsiReader.GetBlocksToRead())
            {
                _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage);
                StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage);

                return;
            }

            uint blocksToRead      = scsiReader.BlocksToRead;
            uint logicalBlockSize  = blockSize;
            uint physicalBlockSize = scsiReader.PhysicalBlockSize;

            if (blocks == 0)
            {
                _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present...");
                StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");

                return;
            }

            UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
            UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
            UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
            UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block.");
            UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
            UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}.");
            UpdateStatus?.Invoke($"Media identified as {dskType}");

            _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
            _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
            _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
            _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize);
            _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
            _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType);
            _dumpLog.WriteLine("Media identified as {0}.", dskType);

            sense = _dev.MiniDiscGetType(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscType, cmdBuf);
            }

            sense = _dev.MiniDiscD5(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscD5, cmdBuf);
            }

            sense = _dev.MiniDiscReadDataTOC(out cmdBuf, out _, _dev.Timeout, out _);

            if (!sense &&
                !_dev.Error)
            {
                mediaTags.Add(MediaTagType.MiniDiscDTOC, cmdBuf);
            }

            var utocMs = new MemoryStream();

            for (uint i = 0; i < 3; i++)
            {
                sense = _dev.MiniDiscReadUserTOC(out cmdBuf, out _, i, _dev.Timeout, out _);

                if (sense || _dev.Error)
                {
                    break;
                }

                utocMs.Write(cmdBuf, 0, 2336);
            }

            if (utocMs.Length > 0)
            {
                mediaTags.Add(MediaTagType.MiniDiscUTOC, utocMs.ToArray());
            }

            ret = true;

            foreach (MediaTagType tag in mediaTags.Keys.Where(tag => !_outputPlugin.SupportedMediaTags.Contains(tag)))
            {
                ret = false;
                _dumpLog.WriteLine($"Output format does not support {tag}.");
                ErrorMessage?.Invoke($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                if (_force)
                {
                    _dumpLog.WriteLine("Several media tags not supported, continuing...");
                    ErrorMessage?.Invoke("Several media tags not supported, continuing...");
                }
                else
                {
                    _dumpLog.WriteLine("Several media tags not supported, not continuing...");
                    StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                    return;
                }
            }

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

            var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
            var ibgLog  = new IbgLog(_outputPrefix + ".ibg", sbcProfile);

            ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                _dumpLog.WriteLine("Error creating output image, not continuing.");
                _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);

                return;
            }

            start = DateTime.UtcNow;
            double imageWriteDuration = 0;

            if (decMode?.Pages != null)
            {
                bool setGeometry = false;

                foreach (Modes.ModePage page in decMode.Value.Pages)
                {
                    if (page.Page == 0x04 &&
                        page.Subpage == 0x00)
                    {
                        Modes.ModePage_04?rigidPage = Modes.DecodeModePage_04(page.PageResponse);

                        if (!rigidPage.HasValue || setGeometry)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
                                           rigidPage.Value.Cylinders, rigidPage.Value.Heads,
                                           (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));

                        UpdateStatus?.
                        Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track");

                        _outputPlugin.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads,
                                                  (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads)));

                        setGeometry = true;
                    }
                    else if (page.Page == 0x05 &&
                             page.Subpage == 0x00)
                    {
                        Modes.ModePage_05?flexiblePage = Modes.DecodeModePage_05(page.PageResponse);

                        if (!flexiblePage.HasValue)
                        {
                            continue;
                        }

                        _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track",
                                           flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
                                           flexiblePage.Value.SectorsPerTrack);

                        UpdateStatus?.
                        Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track");

                        _outputPlugin.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads,
                                                  flexiblePage.Value.SectorsPerTrack);

                        setGeometry = true;
                    }
                }
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial,
                                  _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision,
                                  _private);

            if (currentTry == null ||
                extents == null)
            {
                StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                return;
            }

            if (_resume.NextBlock > 0)
            {
                UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
            }

            bool     newTrim          = false;
            DateTime timeSpeedStart   = DateTime.UtcNow;
            ulong    sectorSpeedStart = 0;

            InitProgress?.Invoke();

            for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (uint)(blocks - i);
                }

                if (currentSpeed > maxSpeed &&
                    currentSpeed > 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed > 0)
                {
                    minSpeed = currentSpeed;
                }

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

                sense = _dev.Read6(out readBuffer, out _, (uint)i, blockSize, (byte)blocksToRead, _dev.Timeout,
                                   out double cmdDuration);

                totalDuration += cmdDuration;

                if (!sense &&
                    !_dev.Error)
                {
                    mhddLog.Write(i, cmdDuration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(readBuffer, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    // TODO: Reset device after X errors
                    if (_stopOnError)
                    {
                        return; // TODO: Return more cleanly
                    }
                    if (i + _skip > blocks)
                    {
                        _skip = (uint)(blocks - i);
                    }

                    // Write empty data
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;

                    for (ulong b = i; b < i + _skip; b++)
                    {
                        _resume.BadBlocks.Add(b);
                    }

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

                    ibgLog.Write(i, 0);
                    _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                    i      += _skip - blocksToRead;
                    newTrim = true;
                }

                sectorSpeedStart += blocksToRead;
                _resume.NextBlock = i + blocksToRead;

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

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();

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

            ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

            UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

            UpdateStatus?.
            Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

            UpdateStatus?.
            Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

            _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 write speed {0:F3} KiB/sec.",
                               ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);

            #region Trimming
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _trim &&
                newTrim)
            {
                start = DateTime.UtcNow;
                UpdateStatus?.Invoke("Trimming skipped sectors");
                _dumpLog.WriteLine("Trimming skipped sectors");

                ulong[] tmpArray = _resume.BadBlocks.ToArray();
                InitProgress?.Invoke();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke($"Trimming sector {badSector}");

                    sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout,
                                       out double _);

                    if (sense || _dev.Error)
                    {
                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(readBuffer, badSector);
                }

                EndProgress?.Invoke();
                end = DateTime.UtcNow;
                UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
                _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                Modes.ModePage?currentModePage = null;
                byte[]         md6;

                if (_persistent)
                {
                    Modes.ModePage_01 pg;

                    sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
                                            _dev.Timeout, out _);

                    if (!sense)
                    {
                        Modes.DecodedMode?dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType);

                        if (dcMode6?.Pages != null)
                        {
                            foreach (Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage =>
                                                                                          modePage.Page == 0x01 && modePage.Subpage == 0x00))
                            {
                                currentModePage = modePage;
                            }
                        }
                    }

                    if (currentModePage == null)
                    {
                        pg = new Modes.ModePage_01
                        {
                            PS             = false,
                            AWRE           = true,
                            ARRE           = true,
                            TB             = false,
                            RC             = false,
                            EER            = true,
                            PER            = false,
                            DTE            = true,
                            DCR            = false,
                            ReadRetryCount = 32
                        };

                        currentModePage = new Modes.ModePage
                        {
                            Page         = 0x01,
                            Subpage      = 0x00,
                            PageResponse = Modes.EncodeModePage_01(pg)
                        };
                    }

                    pg = new Modes.ModePage_01
                    {
                        PS             = false,
                        AWRE           = false,
                        ARRE           = false,
                        TB             = true,
                        RC             = false,
                        EER            = true,
                        PER            = false,
                        DTE            = false,
                        DCR            = false,
                        ReadRetryCount = 255
                    };

                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(),
                        Pages  = new[]
                        {
                            new Modes.ModePage
                            {
                                Page         = 0x01,
                                Subpage      = 0x00,
                                PageResponse = Modes.EncodeModePage_01(pg)
                            }
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
                    sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _);

                    if (sense)
                    {
                        UpdateStatus?.
                        Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");

                        AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));

                        _dumpLog.
                        WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
                    }
                    else
                    {
                        runningPersistent = true;
                    }
                }

                InitProgress?.Invoke();
repeatRetry:
                ulong[] tmpArray = _resume.BadBlocks.ToArray();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                        forward ? "forward" : "reverse",
                                                        runningPersistent ? "recovering partial data, " : ""));

                    sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout,
                                       out double cmdDuration);

                    totalDuration += cmdDuration;

                    if (!sense &&
                        !_dev.Error)
                    {
                        _resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        _outputPlugin.WriteSector(readBuffer, badSector);
                        UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                        _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        _outputPlugin.WriteSector(readBuffer, badSector);
                    }
                }

                if (pass < _retryPasses &&
                    !_aborted &&
                    _resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    _resume.BadBlocks.Sort();

                    if (!forward)
                    {
                        _resume.BadBlocks.Reverse();
                    }

                    goto repeatRetry;
                }

                if (runningPersistent && currentModePage.HasValue)
                {
                    var md = new Modes.DecodedMode
                    {
                        Header = new Modes.ModeHeader(),
                        Pages  = new[]
                        {
                            currentModePage.Value
                        }
                    };

                    md6 = Modes.EncodeMode6(md, _dev.ScsiType);

                    UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status).");
                    _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
                    _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
                }

                EndProgress?.Invoke();
            }
            #endregion Error handling

            _resume.BadBlocks.Sort();

            foreach (ulong bad in _resume.BadBlocks)
            {
                _dumpLog.WriteLine("Sector {0} could not be read.", bad);
            }

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            _outputPlugin.SetDumpHardware(_resume.Tries);

            var metadata = new CommonTypes.Structs.ImageInfo
            {
                Application        = "Aaru",
                ApplicationVersion = Version.GetVersion()
            };

            if (!_outputPlugin.SetMetadata(metadata))
            {
                ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                     _outputPlugin.ErrorMessage);
            }

            if (_preSidecar != null)
            {
                _outputPlugin.SetCicmMetadata(_preSidecar);
            }

            _dumpLog.WriteLine("Closing output file.");
            UpdateStatus?.Invoke("Closing output file.");
            DateTime closeStart = DateTime.Now;
            _outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (_aborted)
            {
                UpdateStatus?.Invoke("Aborted!");
                _dumpLog.WriteLine("Aborted!");

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                UpdateStatus?.Invoke("Creating sidecar.");
                _dumpLog.WriteLine("Creating sidecar.");
                var         filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(_outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);

                if (!inputPlugin.Open(filter))
                {
                    StoppingErrorMessage?.Invoke("Could not open created image.");

                    return;
                }

                DateTime chkStart = DateTime.UtcNow;
                _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
                _sidecarClass.InitProgressEvent    += InitProgress;
                _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                _sidecarClass.EndProgressEvent     += EndProgress;
                _sidecarClass.InitProgressEvent2   += InitProgress2;
                _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                _sidecarClass.EndProgressEvent2    += EndProgress2;
                _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                CICMMetadataType sidecar = _sidecarClass.Create();
                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");

                UpdateStatus?.
                Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

                _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                   ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));

                if (_preSidecar != null)
                {
                    _preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = _preSidecar;
                }

                List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

                if (sidecar.BlockMedia[0].FileSystemInformation != null)
                {
                    filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                         where partition.FileSystems != null from fileSystem in partition.FileSystems
                                         select(partition.StartSector, fileSystem.Type));
                }

                if (filesystems.Count > 0)
                {
                    foreach (var filesystem in filesystems.Select(o => new
                    {
                        o.start,
                        o.type
                    }).Distinct())
                    {
                        UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");
                        _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
                    }
                }

                sidecar.BlockMedia[0].Dimensions     = Dimensions.DimensionsFromMediaType(dskType);
                (string type, string subType)xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
                sidecar.BlockMedia[0].DiskType       = xmlType.type;
                sidecar.BlockMedia[0].DiskSubType    = xmlType.subType;

                if (!_dev.IsRemovable ||
                    _dev.IsUsb)
                {
                    if (_dev.Type == DeviceType.ATAPI)
                    {
                        sidecar.BlockMedia[0].Interface = "ATAPI";
                    }
                    else if (_dev.IsUsb)
                    {
                        sidecar.BlockMedia[0].Interface = "USB";
                    }
                    else if (_dev.IsFireWire)
                    {
                        sidecar.BlockMedia[0].Interface = "FireWire";
                    }
                    else
                    {
                        sidecar.BlockMedia[0].Interface = "SCSI";
                    }
                }

                sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = logicalBlockSize;
                sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = _dev.Model;

                if (!_private)
                {
                    sidecar.BlockMedia[0].Serial = _dev.Serial;
                }

                sidecar.BlockMedia[0].Size = blocks * blockSize;

                if (_dev.IsRemovable)
                {
                    sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray();
                }

                UpdateStatus?.Invoke("Writing metadata sidecar");

                var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            UpdateStatus?.Invoke("");

            UpdateStatus?.
            Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

            UpdateStatus?.
            Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

            if (maxSpeed > 0)
            {
                UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
            }

            if (minSpeed > 0 &&
                minSpeed < double.MaxValue)
            {
                UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
            }

            UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
            UpdateStatus?.Invoke("");

            Statistics.AddMedia(dskType, true);
        }
Пример #18
0
        public static int Invoke(bool verbose, bool debug, string encoding, bool filesystems, bool partitions,
                                 string imagePath)
        {
            MainClass.PrintCopyright();

            if (debug)
            {
                AaruConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }

            if (verbose)
            {
                AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }

            Statistics.AddCommand("analyze");

            AaruConsole.DebugWriteLine("Analyze command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Analyze command", "--encoding={0}", encoding);
            AaruConsole.DebugWriteLine("Analyze command", "--filesystems={0}", filesystems);
            AaruConsole.DebugWriteLine("Analyze command", "--input={0}", imagePath);
            AaruConsole.DebugWriteLine("Analyze command", "--partitions={0}", partitions);
            AaruConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(imagePath);

            if (inputFilter == null)
            {
                AaruConsole.ErrorWriteLine("Cannot open specified file.");

                return((int)ErrorNumber.CannotOpenFile);
            }

            Encoding encodingClass = null;

            if (encoding != null)
            {
                try
                {
                    encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);

                    if (verbose)
                    {
                        AaruConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    AaruConsole.ErrorWriteLine("Specified encoding is not supported.");

                    return((int)ErrorNumber.EncodingUnknown);
                }
            }

            PluginBase plugins = GetPluginBase.Instance;

            bool checkRaw = false;

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    AaruConsole.WriteLine("Image format not identified, not proceeding with analysis.");

                    return((int)ErrorNumber.UnrecognizedFormat);
                }

                if (verbose)
                {
                    AaruConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
                                                 imageFormat.Id);
                }
                else
                {
                    AaruConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
                }

                AaruConsole.WriteLine();

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        AaruConsole.WriteLine("Unable to open image format");
                        AaruConsole.WriteLine("No error given");

                        return((int)ErrorNumber.CannotOpenFormat);
                    }

                    if (verbose)
                    {
                        ImageInfo.PrintImageInfo(imageFormat);
                        AaruConsole.WriteLine();
                    }

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Statistics.AddFilter(inputFilter.Name);
                }
                catch (Exception ex)
                {
                    AaruConsole.ErrorWriteLine("Unable to open image format");
                    AaruConsole.ErrorWriteLine("Error: {0}", ex.Message);
                    AaruConsole.DebugWriteLine("Analyze command", "Stack trace: {0}", ex.StackTrace);

                    return((int)ErrorNumber.CannotOpenFormat);
                }

                List <string> idPlugins;
                IFilesystem   plugin;
                string        information;

                if (partitions)
                {
                    List <Partition> partitionsList = Core.Partitions.GetAll(imageFormat);
                    Core.Partitions.AddSchemesToStats(partitionsList);

                    if (partitionsList.Count == 0)
                    {
                        AaruConsole.DebugWriteLine("Analyze command", "No partitions found");

                        if (!filesystems)
                        {
                            AaruConsole.WriteLine("No partitions founds, not searching for filesystems");

                            return((int)ErrorNumber.NothingFound);
                        }

                        checkRaw = true;
                    }
                    else
                    {
                        AaruConsole.WriteLine("{0} partitions found.", partitionsList.Count);

                        for (int i = 0; i < partitionsList.Count; i++)
                        {
                            AaruConsole.WriteLine();
                            AaruConsole.WriteLine("Partition {0}:", partitionsList[i].Sequence);
                            AaruConsole.WriteLine("Partition name: {0}", partitionsList[i].Name);
                            AaruConsole.WriteLine("Partition type: {0}", partitionsList[i].Type);

                            AaruConsole.WriteLine("Partition start: sector {0}, byte {1}", partitionsList[i].Start,
                                                  partitionsList[i].Offset);

                            AaruConsole.WriteLine("Partition length: {0} sectors, {1} bytes", partitionsList[i].Length,
                                                  partitionsList[i].Size);

                            AaruConsole.WriteLine("Partition scheme: {0}", partitionsList[i].Scheme);
                            AaruConsole.WriteLine("Partition description:");
                            AaruConsole.WriteLine(partitionsList[i].Description);

                            if (!filesystems)
                            {
                                continue;
                            }

                            AaruConsole.WriteLine("Identifying filesystem on partition");

                            Core.Filesystems.Identify(imageFormat, out idPlugins, partitionsList[i]);

                            if (idPlugins.Count == 0)
                            {
                                AaruConsole.WriteLine("Filesystem not identified");
                            }
                            else if (idPlugins.Count > 1)
                            {
                                AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                                foreach (string pluginName in idPlugins)
                                {
                                    if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                                    {
                                        AaruConsole.WriteLine($"As identified by {plugin.Name}.");

                                        plugin.GetInformation(imageFormat, partitionsList[i], out information,
                                                              encodingClass);

                                        AaruConsole.Write(information);
                                        Statistics.AddFilesystem(plugin.XmlFsType.Type);
                                    }
                                }
                            }
                            else
                            {
                                plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);

                                if (plugin == null)
                                {
                                    continue;
                                }

                                AaruConsole.WriteLine($"Identified by {plugin.Name}.");
                                plugin.GetInformation(imageFormat, partitionsList[i], out information, encodingClass);
                                AaruConsole.Write("{0}", information);
                                Statistics.AddFilesystem(plugin.XmlFsType.Type);
                            }
                        }
                    }
                }

                if (checkRaw)
                {
                    var wholePart = new Partition
                    {
                        Name   = "Whole device",
                        Length = imageFormat.Info.Sectors,
                        Size   = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
                    };

                    Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);

                    if (idPlugins.Count == 0)
                    {
                        AaruConsole.WriteLine("Filesystem not identified");
                    }
                    else if (idPlugins.Count > 1)
                    {
                        AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                        foreach (string pluginName in idPlugins)
                        {
                            if (plugins.PluginsList.TryGetValue(pluginName, out plugin))
                            {
                                AaruConsole.WriteLine($"As identified by {plugin.Name}.");
                                plugin.GetInformation(imageFormat, wholePart, out information, encodingClass);
                                AaruConsole.Write(information);
                                Statistics.AddFilesystem(plugin.XmlFsType.Type);
                            }
                        }
                    }
                    else
                    {
                        plugins.PluginsList.TryGetValue(idPlugins[0], out plugin);

                        if (plugin != null)
                        {
                            AaruConsole.WriteLine($"Identified by {plugin.Name}.");
                            plugin.GetInformation(imageFormat, wholePart, out information, encodingClass);
                            AaruConsole.Write(information);
                            Statistics.AddFilesystem(plugin.XmlFsType.Type);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                AaruConsole.DebugWriteLine("Analyze command", ex.StackTrace);

                return((int)ErrorNumber.UnexpectedException);
            }

            return((int)ErrorNumber.NoError);
        }
Пример #19
0
        public bool Open(IFilter imageFilter)
        {
            Stream imageStream = imageFilter.GetDataForkStream();

            byte[] header = new byte[512];
            byte[] footer;

            imageStream.Seek(0, SeekOrigin.Begin);
            imageStream.Read(header, 0, 512);

            if (imageStream.Length % 2 == 0)
            {
                footer = new byte[512];
                imageStream.Seek(-512, SeekOrigin.End);
                imageStream.Read(footer, 0, 512);
            }
            else
            {
                footer = new byte[511];
                imageStream.Seek(-511, SeekOrigin.End);
                imageStream.Read(footer, 0, 511);
            }

            uint  headerChecksum = BigEndianBitConverter.ToUInt32(header, 0x40);
            uint  footerChecksum = BigEndianBitConverter.ToUInt32(footer, 0x40);
            ulong headerCookie   = BigEndianBitConverter.ToUInt64(header, 0);
            ulong footerCookie   = BigEndianBitConverter.ToUInt64(footer, 0);

            header[0x40] = 0;
            header[0x41] = 0;
            header[0x42] = 0;
            header[0x43] = 0;
            footer[0x40] = 0;
            footer[0x41] = 0;
            footer[0x42] = 0;
            footer[0x43] = 0;

            uint headerCalculatedChecksum = VhdChecksum(header);
            uint footerCalculatedChecksum = VhdChecksum(footer);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}",
                                       headerChecksum, headerCalculatedChecksum);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}",
                                       footerChecksum, footerCalculatedChecksum);

            byte[] usableHeader;
            uint   usableChecksum;

            if (headerCookie == IMAGE_COOKIE &&
                headerChecksum == headerCalculatedChecksum)
            {
                usableHeader   = header;
                usableChecksum = headerChecksum;
            }
            else if (footerCookie == IMAGE_COOKIE &&
                     footerChecksum == footerCalculatedChecksum)
            {
                usableHeader   = footer;
                usableChecksum = footerChecksum;
            }
            else
            {
                throw new
                      ImageNotSupportedException("(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened.");
            }

            _thisFooter = new HardDiskFooter
            {
                Cookie             = BigEndianBitConverter.ToUInt64(usableHeader, 0x00),
                Features           = BigEndianBitConverter.ToUInt32(usableHeader, 0x08),
                Version            = BigEndianBitConverter.ToUInt32(usableHeader, 0x0C),
                Offset             = BigEndianBitConverter.ToUInt64(usableHeader, 0x10),
                Timestamp          = BigEndianBitConverter.ToUInt32(usableHeader, 0x18),
                CreatorApplication = BigEndianBitConverter.ToUInt32(usableHeader, 0x1C),
                CreatorVersion     = BigEndianBitConverter.ToUInt32(usableHeader, 0x20),
                CreatorHostOs      = BigEndianBitConverter.ToUInt32(usableHeader, 0x24),
                OriginalSize       = BigEndianBitConverter.ToUInt64(usableHeader, 0x28),
                CurrentSize        = BigEndianBitConverter.ToUInt64(usableHeader, 0x30),
                DiskGeometry       = BigEndianBitConverter.ToUInt32(usableHeader, 0x38),
                DiskType           = BigEndianBitConverter.ToUInt32(usableHeader, 0x3C),
                Checksum           = usableChecksum,
                UniqueId           = BigEndianBitConverter.ToGuid(usableHeader, 0x44),
                SavedState         = usableHeader[0x54],
                Reserved           = new byte[usableHeader.Length - 0x55]
            };

            Array.Copy(usableHeader, 0x55, _thisFooter.Reserved, 0, usableHeader.Length - 0x55);

            _thisDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            _thisDateTime = _thisDateTime.AddSeconds(_thisFooter.Timestamp);

            var sha1Ctx = new Sha1Context();

            sha1Ctx.Update(_thisFooter.Reserved);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.cookie = 0x{0:X8}", _thisFooter.Cookie);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.features = 0x{0:X8}", _thisFooter.Features);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.version = 0x{0:X8}", _thisFooter.Version);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.offset = {0}", _thisFooter.Offset);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.timestamp = 0x{0:X8} ({1})", _thisFooter.Timestamp,
                                       _thisDateTime);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorApplication = 0x{0:X8} (\"{1}\")",
                                       _thisFooter.CreatorApplication,
                                       Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.
                                                                                               CreatorApplication)));

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorVersion = 0x{0:X8}",
                                       _thisFooter.CreatorVersion);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorHostOS = 0x{0:X8} (\"{1}\")",
                                       _thisFooter.CreatorHostOs,
                                       Encoding.ASCII.GetString(BigEndianBitConverter.
                                                                GetBytes(_thisFooter.CreatorHostOs)));

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.originalSize = {0}", _thisFooter.OriginalSize);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.currentSize = {0}", _thisFooter.CurrentSize);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.diskGeometry = 0x{0:X8} (C/H/S: {1}/{2}/{3})",
                                       _thisFooter.DiskGeometry, (_thisFooter.DiskGeometry & 0xFFFF0000) >> 16,
                                       (_thisFooter.DiskGeometry & 0xFF00) >> 8, _thisFooter.DiskGeometry & 0xFF);

            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.diskType = 0x{0:X8}", _thisFooter.DiskType);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.checksum = 0x{0:X8}", _thisFooter.Checksum);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.uniqueId = {0}", _thisFooter.UniqueId);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.savedState = 0x{0:X2}", _thisFooter.SavedState);
            AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.reserved's SHA1 = 0x{0}", sha1Ctx.End());

            if (_thisFooter.Version == VERSION1)
            {
                _imageInfo.Version = "1.0";
            }
            else
            {
                throw new
                      ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image.");
            }

            switch (_thisFooter.CreatorApplication)
            {
            case CREATOR_QEMU:
            {
                _imageInfo.Application = "QEMU";

                // QEMU always set same version
                _imageInfo.ApplicationVersion = "Unknown";

                break;
            }

            case CREATOR_VIRTUAL_BOX:
            {
                _imageInfo.ApplicationVersion =
                    $"{(_thisFooter.CreatorVersion & 0xFFFF0000) >> 16}.{_thisFooter.CreatorVersion & 0x0000FFFF:D2}";

                switch (_thisFooter.CreatorHostOs)
                {
                case CREATOR_MACINTOSH:
                case CREATOR_MACINTOSH_OLD:
                    _imageInfo.Application = "VirtualBox for Mac";

                    break;

                case CREATOR_WINDOWS:
                    // VirtualBox uses Windows creator for any other OS
                    _imageInfo.Application = "VirtualBox";

                    break;

                default:
                    _imageInfo.Application =
                        $"VirtualBox for unknown OS \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\"";

                    break;
                }

                break;
            }

            case CREATOR_VIRTUAL_SERVER:
            {
                _imageInfo.Application = "Microsoft Virtual Server";

                switch (_thisFooter.CreatorVersion)
                {
                case VERSION_VIRTUAL_SERVER2004:
                    _imageInfo.ApplicationVersion = "2004";

                    break;

                default:
                    _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}";

                    break;
                }

                break;
            }

            case CREATOR_VIRTUAL_PC:
            {
                switch (_thisFooter.CreatorHostOs)
                {
                case CREATOR_MACINTOSH:
                case CREATOR_MACINTOSH_OLD:
                    switch (_thisFooter.CreatorVersion)
                    {
                    case VERSION_VIRTUAL_PC_MAC:
                        _imageInfo.Application        = "Connectix Virtual PC";
                        _imageInfo.ApplicationVersion = "5, 6 or 7";

                        break;

                    default:
                        _imageInfo.ApplicationVersion =
                            $"Unknown version 0x{_thisFooter.CreatorVersion:X8}";

                        break;
                    }

                    break;

                case CREATOR_WINDOWS:
                    switch (_thisFooter.CreatorVersion)
                    {
                    case VERSION_VIRTUAL_PC_MAC:
                        _imageInfo.Application        = "Connectix Virtual PC";
                        _imageInfo.ApplicationVersion = "5, 6 or 7";

                        break;

                    case VERSION_VIRTUAL_PC2004:
                        _imageInfo.Application        = "Microsoft Virtual PC";
                        _imageInfo.ApplicationVersion = "2004";

                        break;

                    case VERSION_VIRTUAL_PC2007:
                        _imageInfo.Application        = "Microsoft Virtual PC";
                        _imageInfo.ApplicationVersion = "2007";

                        break;

                    default:
                        _imageInfo.ApplicationVersion =
                            $"Unknown version 0x{_thisFooter.CreatorVersion:X8}";

                        break;
                    }

                    break;

                default:
                    _imageInfo.Application =
                        $"Virtual PC for unknown OS \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\"";

                    _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}";

                    break;
                }

                break;
            }

            case CREATOR_DISCIMAGECHEF:
            {
                _imageInfo.Application = "DiscImageChef";

                _imageInfo.ApplicationVersion =
                    $"{(_thisFooter.CreatorVersion & 0xFF000000) >> 24}.{(_thisFooter.CreatorVersion & 0xFF0000) >> 16}.{(_thisFooter.CreatorVersion & 0xFF00) >> 8}.{_thisFooter.CreatorVersion & 0xFF}";
            }

            break;

            case CREATOR_AARU:
            {
                _imageInfo.Application = "Aaru";

                _imageInfo.ApplicationVersion =
                    $"{(_thisFooter.CreatorVersion & 0xFF000000) >> 24}.{(_thisFooter.CreatorVersion & 0xFF0000) >> 16}.{(_thisFooter.CreatorVersion & 0xFF00) >> 8}.{_thisFooter.CreatorVersion & 0xFF}";
            }

            break;

            default:
            {
                _imageInfo.Application =
                    $"Unknown application \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\"";

                _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}";

                break;
            }
            }

            _thisFilter           = imageFilter;
            _imageInfo.ImageSize  = _thisFooter.CurrentSize;
            _imageInfo.Sectors    = _thisFooter.CurrentSize / 512;
            _imageInfo.SectorSize = 512;

            _imageInfo.CreationTime         = imageFilter.GetCreationTime();
            _imageInfo.LastModificationTime = _thisDateTime;
            _imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());

            _imageInfo.Cylinders       = (_thisFooter.DiskGeometry & 0xFFFF0000) >> 16;
            _imageInfo.Heads           = (_thisFooter.DiskGeometry & 0xFF00) >> 8;
            _imageInfo.SectorsPerTrack = _thisFooter.DiskGeometry & 0xFF;

            if (_thisFooter.DiskType == TYPE_DYNAMIC ||
                _thisFooter.DiskType == TYPE_DIFFERENCING)
            {
                imageStream.Seek((long)_thisFooter.Offset, SeekOrigin.Begin);
                byte[] dynamicBytes = new byte[1024];
                imageStream.Read(dynamicBytes, 0, 1024);

                uint dynamicChecksum = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x24);

                dynamicBytes[0x24] = 0;
                dynamicBytes[0x25] = 0;
                dynamicBytes[0x26] = 0;
                dynamicBytes[0x27] = 0;

                uint dynamicChecksumCalculated = VhdChecksum(dynamicBytes);

                AaruConsole.DebugWriteLine("VirtualPC plugin",
                                           "Dynamic header checksum = 0x{0:X8}, calculated = 0x{1:X8}", dynamicChecksum,
                                           dynamicChecksumCalculated);

                if (dynamicChecksum != dynamicChecksumCalculated)
                {
                    throw new
                          ImageNotSupportedException("(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened.");
                }

                _thisDynamic = new DynamicDiskHeader
                {
                    LocatorEntries = new ParentLocatorEntry[8],
                    Reserved2      = new byte[256]
                };

                for (int i = 0; i < 8; i++)
                {
                    _thisDynamic.LocatorEntries[i] = new ParentLocatorEntry();
                }

                _thisDynamic.Cookie          = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x00);
                _thisDynamic.DataOffset      = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x08);
                _thisDynamic.TableOffset     = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x10);
                _thisDynamic.HeaderVersion   = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x18);
                _thisDynamic.MaxTableEntries = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x1C);
                _thisDynamic.BlockSize       = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x20);
                _thisDynamic.Checksum        = dynamicChecksum;
                _thisDynamic.ParentId        = BigEndianBitConverter.ToGuid(dynamicBytes, 0x28);
                _thisDynamic.ParentTimestamp = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x38);
                _thisDynamic.Reserved        = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x3C);
                _thisDynamic.ParentName      = Encoding.BigEndianUnicode.GetString(dynamicBytes, 0x40, 512);

                for (int i = 0; i < 8; i++)
                {
                    _thisDynamic.LocatorEntries[i].PlatformCode =
                        BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x00 + (24 * i));

                    _thisDynamic.LocatorEntries[i].PlatformDataSpace =
                        BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x04 + (24 * i));

                    _thisDynamic.LocatorEntries[i].PlatformDataLength =
                        BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x08 + (24 * i));

                    _thisDynamic.LocatorEntries[i].Reserved =
                        BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x0C + (24 * i));

                    _thisDynamic.LocatorEntries[i].PlatformDataOffset =
                        BigEndianBitConverter.ToUInt64(dynamicBytes, 0x240 + 0x10 + (24 * i));
                }

                Array.Copy(dynamicBytes, 0x300, _thisDynamic.Reserved2, 0, 256);

                _parentDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                _parentDateTime = _parentDateTime.AddSeconds(_thisDynamic.ParentTimestamp);

                sha1Ctx = new Sha1Context();
                sha1Ctx.Update(_thisDynamic.Reserved2);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.cookie = 0x{0:X8}", _thisDynamic.Cookie);
                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.dataOffset = {0}", _thisDynamic.DataOffset);
                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.tableOffset = {0}", _thisDynamic.TableOffset);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.headerVersion = 0x{0:X8}",
                                           _thisDynamic.HeaderVersion);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.maxTableEntries = {0}",
                                           _thisDynamic.MaxTableEntries);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.blockSize = {0}", _thisDynamic.BlockSize);
                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.checksum = 0x{0:X8}", _thisDynamic.Checksum);
                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentID = {0}", _thisDynamic.ParentId);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentTimestamp = 0x{0:X8} ({1})",
                                           _thisDynamic.ParentTimestamp, _parentDateTime);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.reserved = 0x{0:X8}", _thisDynamic.Reserved);

                for (int i = 0; i < 8; i++)
                {
                    AaruConsole.DebugWriteLine("VirtualPC plugin",
                                               "dynamic.locatorEntries[{0}].platformCode = 0x{1:X8} (\"{2}\")", i,
                                               _thisDynamic.LocatorEntries[i].PlatformCode,
                                               Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisDynamic.
                                                                                                       LocatorEntries
                                                                                                       [i].
                                                                                                       PlatformCode)));

                    AaruConsole.DebugWriteLine("VirtualPC plugin",
                                               "dynamic.locatorEntries[{0}].platformDataSpace = {1}", i,
                                               _thisDynamic.LocatorEntries[i].PlatformDataSpace);

                    AaruConsole.DebugWriteLine("VirtualPC plugin",
                                               "dynamic.locatorEntries[{0}].platformDataLength = {1}", i,
                                               _thisDynamic.LocatorEntries[i].PlatformDataLength);

                    AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].reserved = 0x{1:X8}", i,
                                               _thisDynamic.LocatorEntries[i].Reserved);

                    AaruConsole.DebugWriteLine("VirtualPC plugin",
                                               "dynamic.locatorEntries[{0}].platformDataOffset = {1}", i,
                                               _thisDynamic.LocatorEntries[i].PlatformDataOffset);
                }

                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentName = \"{0}\"", _thisDynamic.ParentName);
                AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.reserved2's SHA1 = 0x{0}", sha1Ctx.End());

                if (_thisDynamic.HeaderVersion != VERSION1)
                {
                    throw new
                          ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image.");
                }

                DateTime startTime = DateTime.UtcNow;

                _blockAllocationTable = new uint[_thisDynamic.MaxTableEntries];

                // How many sectors uses the BAT
                int batSectorCount = (int)Math.Ceiling(((double)_thisDynamic.MaxTableEntries * 4) / 512);

                byte[] bat = new byte[_thisDynamic.MaxTableEntries * 4];
                imageStream.Seek((long)_thisDynamic.TableOffset, SeekOrigin.Begin);
                imageStream.Read(bat, 0, batSectorCount);

                ReadOnlySpan <byte> span = bat;

                _blockAllocationTable = MemoryMarshal.
                                        Cast <byte, uint>(span).Slice(0, (int)_thisDynamic.MaxTableEntries).ToArray();

                for (int i = 0; i < _blockAllocationTable.Length; i++)
                {
                    _blockAllocationTable[i] = Swapping.Swap(_blockAllocationTable[i]);
                }

                DateTime endTime = DateTime.UtcNow;

                AaruConsole.DebugWriteLine("VirtualPC plugin", "Filling the BAT took {0} seconds",
                                           (endTime - startTime).TotalSeconds);

                _bitmapSize = (uint)Math.Ceiling((double)_thisDynamic.BlockSize / 512

                                                 // 1 bit per sector on the bitmap
                                                 / 8

                                                 // and aligned to 512 byte boundary
                                                 / 512);

                AaruConsole.DebugWriteLine("VirtualPC plugin", "Bitmap is {0} sectors", _bitmapSize);
            }

            _imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            switch (_thisFooter.DiskType)
            {
            case TYPE_FIXED:
            case TYPE_DYNAMIC:
            {
                // Nothing to do here, really.
                return(true);
            }

            case TYPE_DIFFERENCING:
            {
                _locatorEntriesData = new byte[8][];

                for (int i = 0; i < 8; i++)
                {
                    if (_thisDynamic.LocatorEntries[i].PlatformCode != 0x00000000)
                    {
                        _locatorEntriesData[i] = new byte[_thisDynamic.LocatorEntries[i].PlatformDataLength];
                        imageStream.Seek((long)_thisDynamic.LocatorEntries[i].PlatformDataOffset, SeekOrigin.Begin);

                        imageStream.Read(_locatorEntriesData[i], 0,
                                         (int)_thisDynamic.LocatorEntries[i].PlatformDataLength);

                        switch (_thisDynamic.LocatorEntries[i].PlatformCode)
                        {
                        case PLATFORM_CODE_WINDOWS_ABSOLUTE:
                        case PLATFORM_CODE_WINDOWS_RELATIVE:
                            AaruConsole.DebugWriteLine("VirtualPC plugin",
                                                       "dynamic.locatorEntries[{0}] = \"{1}\"", i,
                                                       Encoding.ASCII.GetString(_locatorEntriesData[i]));

                            break;

                        case PLATFORM_CODE_WINDOWS_ABSOLUTE_U:
                        case PLATFORM_CODE_WINDOWS_RELATIVE_U:
                            AaruConsole.DebugWriteLine("VirtualPC plugin",
                                                       "dynamic.locatorEntries[{0}] = \"{1}\"", i,
                                                       Encoding.BigEndianUnicode.
                                                       GetString(_locatorEntriesData[i]));

                            break;

                        case PLATFORM_CODE_MACINTOSH_URI:
                            AaruConsole.DebugWriteLine("VirtualPC plugin",
                                                       "dynamic.locatorEntries[{0}] = \"{1}\"", i,
                                                       Encoding.UTF8.GetString(_locatorEntriesData[i]));

                            break;

                        default:
                            AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}] =", i);
                            PrintHex.PrintHexArray(_locatorEntriesData[i], 64);

                            break;
                        }
                    }
                }

                int    currentLocator = 0;
                bool   locatorFound   = false;
                string parentPath     = null;

                while (!locatorFound &&
                       currentLocator < 8)
                {
                    switch (_thisDynamic.LocatorEntries[currentLocator].PlatformCode)
                    {
                    case PLATFORM_CODE_WINDOWS_ABSOLUTE:
                    case PLATFORM_CODE_WINDOWS_RELATIVE:
                        parentPath = Encoding.ASCII.GetString(_locatorEntriesData[currentLocator]);

                        break;

                    case PLATFORM_CODE_WINDOWS_ABSOLUTE_U:
                    case PLATFORM_CODE_WINDOWS_RELATIVE_U:
                        parentPath = Encoding.BigEndianUnicode.GetString(_locatorEntriesData[currentLocator]);

                        break;

                    case PLATFORM_CODE_MACINTOSH_URI:
                        parentPath =
                            Uri.UnescapeDataString(Encoding.UTF8.GetString(_locatorEntriesData
                                                                           [currentLocator]));

                        if (parentPath.StartsWith("file://localhost", StringComparison.InvariantCulture))
                        {
                            parentPath = parentPath.Remove(0, 16);
                        }
                        else
                        {
                            AaruConsole.DebugWriteLine("VirtualPC plugin",
                                                       "Unsupported protocol classified found in URI parent path: \"{0}\"",
                                                       parentPath);

                            parentPath = null;
                        }

                        break;
                    }

                    if (parentPath != null)
                    {
                        AaruConsole.DebugWriteLine("VirtualPC plugin", "Possible parent path: \"{0}\"", parentPath);

                        IFilter parentFilter =
                            new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), parentPath));

                        if (parentFilter != null)
                        {
                            locatorFound = true;
                        }

                        if (!locatorFound)
                        {
                            parentPath = null;
                        }
                    }

                    currentLocator++;
                }

                if (!locatorFound)
                {
                    throw new
                          FileNotFoundException("(VirtualPC plugin): Cannot find parent file for differencing disk image");
                }

                {
                    _parentImage = new Vhd();

                    IFilter parentFilter =
                        new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), parentPath));

                    if (parentFilter == null)
                    {
                        throw new ImageNotSupportedException("(VirtualPC plugin): Cannot find parent image filter");
                    }

                    /*                            PluginBase plugins = new PluginBase();
                     *                          plugins.RegisterAllPlugins();
                     *                          if (!plugins.ImagePluginsList.TryGetValue(Name.ToLower(), out parentImage))
                     *                              throw new SystemException("(VirtualPC plugin): Unable to open myself");*/

                    if (!_parentImage.Identify(parentFilter))
                    {
                        throw new
                              ImageNotSupportedException("(VirtualPC plugin): Parent image is not a Virtual PC disk image");
                    }

                    if (!_parentImage.Open(parentFilter))
                    {
                        throw new ImageNotSupportedException("(VirtualPC plugin): Cannot open parent disk image");
                    }

                    // While specification says that parent and child disk images should contain UUID relationship
                    // in reality it seems that old differencing disk images stored a parent UUID that, nonetheless
                    // the parent never stored itself. So the only real way to know that images are related is
                    // because the parent IS found and SAME SIZE. Ugly...
                    // More funny even, tested parent images show an empty host OS, and child images a correct one.
                    if (_parentImage.Info.Sectors != _imageInfo.Sectors)
                    {
                        throw new
                              ImageNotSupportedException("(VirtualPC plugin): Parent image is of different size");
                    }
                }

                return(true);
            }

            case TYPE_DEPRECATED1:
            case TYPE_DEPRECATED2:
            case TYPE_DEPRECATED3:
            {
                throw new
                      ImageNotSupportedException("(VirtualPC plugin): Deprecated image type found. Please submit a bug with an example image.");
            }

            default:
            {
                throw new
                      ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image.");
            }
            }
        }
Пример #20
0
        public override int Invoke(IEnumerable <string> arguments)
        {
            List <string> extra = Options.Parse(arguments);

            if (showHelp)
            {
                Options.WriteOptionDescriptions(CommandSet.Out);
                return((int)ErrorNumber.HelpRequested);
            }

            MainClass.PrintCopyright();
            if (MainClass.Debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }
            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }
            Statistics.AddCommand("verify");

            if (extra.Count > 1)
            {
                DicConsole.ErrorWriteLine("Too many arguments.");
                return((int)ErrorNumber.UnexpectedArgumentCount);
            }

            if (extra.Count == 0)
            {
                DicConsole.ErrorWriteLine("Missing input image.");
                return((int)ErrorNumber.MissingArgument);
            }

            inputFile = extra[0];

            DicConsole.DebugWriteLine("Verify command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Verify command", "--input={0}", inputFile);
            DicConsole.DebugWriteLine("Verify command", "--verbose={0}", MainClass.Verbose);
            DicConsole.DebugWriteLine("Verify command", "--verify-disc={0}", verifyDisc);
            DicConsole.DebugWriteLine("Verify command", "--verify-sectors={0}", verifySectors);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(inputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return((int)ErrorNumber.CannotOpenFile);
            }

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying");
                return((int)ErrorNumber.FormatNotFound);
            }

            inputFormat.Open(inputFilter);
            Statistics.AddMediaFormat(inputFormat.Format);
            Statistics.AddMedia(inputFormat.Info.MediaType, false);
            Statistics.AddFilter(inputFilter.Name);

            bool?correctImage   = null;
            long totalSectors   = 0;
            long errorSectors   = 0;
            bool?correctSectors = null;
            long unknownSectors = 0;

            IVerifiableImage        verifiableImage        = inputFormat as IVerifiableImage;
            IVerifiableSectorsImage verifiableSectorsImage = inputFormat as IVerifiableSectorsImage;

            if (verifiableImage is null && verifiableSectorsImage is null)
            {
                DicConsole.ErrorWriteLine("The specified image does not support any kind of verification");
                return((int)ErrorNumber.NotVerificable);
            }

            if (verifyDisc)
            {
                DateTime startCheck      = DateTime.UtcNow;
                bool?    discCheckStatus = verifiableImage.VerifyMediaImage();
                DateTime endCheck        = DateTime.UtcNow;

                TimeSpan checkTime = endCheck - startCheck;

                switch (discCheckStatus)
                {
                case true:
                    DicConsole.WriteLine("Disc image checksums are correct");
                    break;

                case false:
                    DicConsole.WriteLine("Disc image checksums are incorrect");
                    break;

                case null:
                    DicConsole.WriteLine("Disc image does not contain checksums");
                    break;
                }

                correctImage = discCheckStatus;
                DicConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", checkTime.TotalSeconds);
            }

            if (verifySectors)
            {
                DateTime     startCheck  = DateTime.Now;
                DateTime     endCheck    = startCheck;
                List <ulong> failingLbas = new List <ulong>();
                List <ulong> unknownLbas = new List <ulong>();

                if (inputFormat is IOpticalMediaImage opticalMediaImage)
                {
                    List <Track> inputTracks      = opticalMediaImage.Tracks;
                    ulong        currentSectorAll = 0;

                    startCheck = DateTime.UtcNow;
                    foreach (Track currentTrack in inputTracks)
                    {
                        ulong remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector;
                        ulong currentSector    = 0;

                        while (remainingSectors > 0)
                        {
                            DicConsole.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll,
                                             inputFormat.Info.Sectors, currentTrack.TrackSequence);

                            List <ulong> tempfailingLbas;
                            List <ulong> tempunknownLbas;

                            if (remainingSectors < 512)
                            {
                                opticalMediaImage.VerifySectors(currentSector, (uint)remainingSectors,
                                                                currentTrack.TrackSequence, out tempfailingLbas,
                                                                out tempunknownLbas);
                            }
                            else
                            {
                                opticalMediaImage.VerifySectors(currentSector, 512, currentTrack.TrackSequence,
                                                                out tempfailingLbas, out tempunknownLbas);
                            }

                            failingLbas.AddRange(tempfailingLbas);

                            unknownLbas.AddRange(tempunknownLbas);

                            if (remainingSectors < 512)
                            {
                                currentSector    += remainingSectors;
                                currentSectorAll += remainingSectors;
                                remainingSectors  = 0;
                            }
                            else
                            {
                                currentSector    += 512;
                                currentSectorAll += 512;
                                remainingSectors -= 512;
                            }
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }
                else if (verifiableSectorsImage != null)
                {
                    ulong remainingSectors = inputFormat.Info.Sectors;
                    ulong currentSector    = 0;

                    startCheck = DateTime.UtcNow;
                    while (remainingSectors > 0)
                    {
                        DicConsole.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.Info.Sectors);

                        List <ulong> tempfailingLbas;
                        List <ulong> tempunknownLbas;

                        if (remainingSectors < 512)
                        {
                            verifiableSectorsImage.VerifySectors(currentSector, (uint)remainingSectors,
                                                                 out tempfailingLbas, out tempunknownLbas);
                        }
                        else
                        {
                            verifiableSectorsImage.VerifySectors(currentSector, 512, out tempfailingLbas,
                                                                 out tempunknownLbas);
                        }

                        failingLbas.AddRange(tempfailingLbas);

                        unknownLbas.AddRange(tempunknownLbas);

                        if (remainingSectors < 512)
                        {
                            currentSector   += remainingSectors;
                            remainingSectors = 0;
                        }
                        else
                        {
                            currentSector    += 512;
                            remainingSectors -= 512;
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }

                TimeSpan checkTime = endCheck - startCheck;

                DicConsole.Write("\r" + new string(' ', System.Console.WindowWidth - 1) + "\r");

                if (unknownSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector that does not contain a checksum");
                }
                if (errorSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector with incorrect checksum or errors");
                }
                if (unknownSectors == 0 && errorSectors == 0)
                {
                    DicConsole.WriteLine("All sector checksums are correct");
                }

                DicConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds);

                if (MainClass.Verbose)
                {
                    DicConsole.VerboseWriteLine("LBAs with error:");
                    if (failingLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in failingLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }

                    DicConsole.WriteLine("LBAs without checksum:");
                    if (unknownLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in unknownLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }
                }

                DicConsole.WriteLine("Total sectors........... {0}", inputFormat.Info.Sectors);
                DicConsole.WriteLine("Total errors............ {0}", failingLbas.Count);
                DicConsole.WriteLine("Total unknowns.......... {0}", unknownLbas.Count);
                DicConsole.WriteLine("Total errors+unknowns... {0}", failingLbas.Count + unknownLbas.Count);

                totalSectors   = (long)inputFormat.Info.Sectors;
                errorSectors   = failingLbas.Count;
                unknownSectors = unknownLbas.Count;
                if (failingLbas.Count > 0)
                {
                    correctSectors = false;
                }
                else if ((ulong)unknownLbas.Count < inputFormat.Info.Sectors)
                {
                    correctSectors = true;
                }
            }

            switch (correctImage)
            {
            case null when correctSectors is null:   return((int)ErrorNumber.NotVerificable);

            case null when correctSectors == false:  return((int)ErrorNumber.BadSectorsImageNotVerified);

            case null when correctSectors == true:   return((int)ErrorNumber.CorrectSectorsImageNotVerified);

            case false when correctSectors is null:  return((int)ErrorNumber.BadImageSectorsNotVerified);

            case false when correctSectors == false: return((int)ErrorNumber.BadImageBadSectors);

            case false when correctSectors == true:  return((int)ErrorNumber.CorrectSectorsBadImage);

            case true when correctSectors is null:   return((int)ErrorNumber.CorrectImageSectorsNotVerified);

            case true when correctSectors == false:  return((int)ErrorNumber.CorrectImageBadSectors);

            case true when correctSectors == true:   return((int)ErrorNumber.NoError);
            }

            return((int)ErrorNumber.NoError);
        }
Пример #21
0
        public static int Invoke(bool debug, bool verbose, string imagePath)
        {
            MainClass.PrintCopyright();

            if (debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }

            if (verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }

            Statistics.AddCommand("image-info");

            DicConsole.DebugWriteLine("Analyze command", "--debug={0}", debug);
            DicConsole.DebugWriteLine("Analyze command", "--input={0}", imagePath);
            DicConsole.DebugWriteLine("Analyze command", "--verbose={0}", verbose);

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(imagePath);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");

                return((int)ErrorNumber.CannotOpenFile);
            }

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    DicConsole.WriteLine("Image format not identified.");

                    return((int)ErrorNumber.UnrecognizedFormat);
                }

                DicConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id);
                DicConsole.WriteLine();

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        DicConsole.WriteLine("Unable to open image format");
                        DicConsole.WriteLine("No error given");

                        return((int)ErrorNumber.CannotOpenFormat);
                    }

                    ImageInfo.PrintImageInfo(imageFormat);

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Statistics.AddFilter(inputFilter.Name);
                }
                catch (Exception ex)
                {
                    DicConsole.ErrorWriteLine("Unable to open image format");
                    DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
                    DicConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace);

                    return((int)ErrorNumber.CannotOpenFormat);
                }
            }
            catch (Exception ex)
            {
                DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                DicConsole.DebugWriteLine("Image-info command", ex.StackTrace);

                return((int)ErrorNumber.UnexpectedException);
            }

            return((int)ErrorNumber.NoError);
        }
Пример #22
0
        public void Build()
        {
            Environment.CurrentDirectory = DataFolder;

            foreach (FileSystemTest test in Tests)
            {
                string testFile  = test.TestFile;
                bool   found     = false;
                var    partition = new Partition();

                bool exists = File.Exists(testFile);

                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                // It arrives here...
                if (!exists)
                {
                    continue;
                }

                var         filtersList = new FiltersList();
                IFilter     inputFilter = filtersList.GetFilter(testFile);
                IMediaImage image       = ImageFormat.Detect(inputFilter);

                if (!image.Open(inputFilter))
                {
                    continue;
                }

                List <string> idPlugins;

                if (Partitions)
                {
                    List <Partition> partitionsList = Core.Partitions.GetAll(image);

                    // In reverse to skip boot partitions we're not interested in
                    for (int index = partitionsList.Count - 1; index >= 0; index--)
                    {
                        Core.Filesystems.Identify(image, out idPlugins, partitionsList[index], true);

                        if (idPlugins.Count == 0)
                        {
                            continue;
                        }

                        if (!idPlugins.Contains(Plugin.Id.ToString()))
                        {
                            continue;
                        }

                        found     = true;
                        partition = partitionsList[index];

                        break;
                    }
                }
                else
                {
                    partition = new Partition
                    {
                        Name   = "Whole device",
                        Length = image.Info.Sectors,
                        Size   = image.Info.Sectors * image.Info.SectorSize
                    };

                    Core.Filesystems.Identify(image, out idPlugins, partition, true);

                    found = idPlugins.Contains(Plugin.Id.ToString());
                }

                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                // It is not the case, it changes
                if (!found)
                {
                    continue;
                }

                var fs = Activator.CreateInstance(Plugin.GetType()) as IReadOnlyFilesystem;

                test.Encoding ??= Encoding.ASCII;

                fs?.Mount(image, partition, test.Encoding, null, test.Namespace);

                Dictionary <string, FileData> contents = BuildDirectory(fs, "/");

                var serializer = new JsonSerializer
                {
                    Formatting        = Formatting.Indented,
                    MaxDepth          = 16384,
                    NullValueHandling = NullValueHandling.Ignore
                };

                serializer.Converters.Add(new StringEnumConverter());

                var sw = new StreamWriter($"{testFile}.contents.json");
                serializer.Serialize(sw, contents);
                sw.Close();
            }
        }
Пример #23
0
        internal static void DoVerify(VerifyOptions options)
        {
            DicConsole.DebugWriteLine("Verify command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Verify command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Verify command", "--input={0}", options.InputFile);
            DicConsole.DebugWriteLine("Verify command", "--verify-disc={0}", options.VerifyDisc);
            DicConsole.DebugWriteLine("Verify command", "--verify-sectors={0}", options.VerifySectors);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return;
            }

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying");
                return;
            }

            inputFormat.Open(inputFilter);
            Core.Statistics.AddMediaFormat(inputFormat.Format);
            Core.Statistics.AddMedia(inputFormat.Info.MediaType, false);
            Core.Statistics.AddFilter(inputFilter.Name);

            bool?correctDisc    = null;
            long totalSectors   = 0;
            long errorSectors   = 0;
            long correctSectors = 0;
            long unknownSectors = 0;

            if (options.VerifyDisc)
            {
                DateTime startCheck      = DateTime.UtcNow;
                bool?    discCheckStatus = inputFormat.VerifyMediaImage();
                DateTime endCheck        = DateTime.UtcNow;

                TimeSpan checkTime = endCheck - startCheck;

                switch (discCheckStatus)
                {
                case true:
                    DicConsole.WriteLine("Disc image checksums are correct");
                    break;

                case false:
                    DicConsole.WriteLine("Disc image checksums are incorrect");
                    break;

                case null:
                    DicConsole.WriteLine("Disc image does not contain checksums");
                    break;
                }

                correctDisc = discCheckStatus;
                DicConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", checkTime.TotalSeconds);
            }

            if (options.VerifySectors)
            {
                bool formatHasTracks;
                try
                {
                    List <Track> inputTracks = inputFormat.Tracks;
                    formatHasTracks = inputTracks.Count > 0;
                }
                catch { formatHasTracks = false; }

                DateTime     startCheck;
                DateTime     endCheck;
                List <ulong> failingLbas = new List <ulong>();
                List <ulong> unknownLbas = new List <ulong>();

                if (formatHasTracks)
                {
                    List <Track> inputTracks      = inputFormat.Tracks;
                    ulong        currentSectorAll = 0;

                    startCheck = DateTime.UtcNow;
                    foreach (Track currentTrack in inputTracks)
                    {
                        ulong remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector;
                        ulong currentSector    = 0;

                        while (remainingSectors > 0)
                        {
                            DicConsole.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll,
                                             inputFormat.Info.Sectors, currentTrack.TrackSequence);

                            List <ulong> tempfailingLbas;
                            List <ulong> tempunknownLbas;

                            if (remainingSectors < 512)
                            {
                                inputFormat.VerifySectors(currentSector, (uint)remainingSectors,
                                                          currentTrack.TrackSequence, out tempfailingLbas,
                                                          out tempunknownLbas);
                            }
                            else
                            {
                                inputFormat.VerifySectors(currentSector, 512, currentTrack.TrackSequence,
                                                          out tempfailingLbas, out tempunknownLbas);
                            }

                            failingLbas.AddRange(tempfailingLbas);

                            unknownLbas.AddRange(tempunknownLbas);

                            if (remainingSectors < 512)
                            {
                                currentSector    += remainingSectors;
                                currentSectorAll += remainingSectors;
                                remainingSectors  = 0;
                            }
                            else
                            {
                                currentSector    += 512;
                                currentSectorAll += 512;
                                remainingSectors -= 512;
                            }
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }
                else
                {
                    ulong remainingSectors = inputFormat.Info.Sectors;
                    ulong currentSector    = 0;

                    startCheck = DateTime.UtcNow;
                    while (remainingSectors > 0)
                    {
                        DicConsole.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.Info.Sectors);

                        List <ulong> tempfailingLbas;
                        List <ulong> tempunknownLbas;

                        if (remainingSectors < 512)
                        {
                            inputFormat.VerifySectors(currentSector, (uint)remainingSectors, out tempfailingLbas,
                                                      out tempunknownLbas);
                        }
                        else
                        {
                            inputFormat.VerifySectors(currentSector, 512, out tempfailingLbas, out tempunknownLbas);
                        }

                        failingLbas.AddRange(tempfailingLbas);

                        unknownLbas.AddRange(tempunknownLbas);

                        if (remainingSectors < 512)
                        {
                            currentSector   += remainingSectors;
                            remainingSectors = 0;
                        }
                        else
                        {
                            currentSector    += 512;
                            remainingSectors -= 512;
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }

                TimeSpan checkTime = endCheck - startCheck;

                DicConsole.Write("\r" + new string(' ', System.Console.WindowWidth - 1) + "\r");

                if (unknownSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector that does not contain a checksum");
                }
                if (errorSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector with incorrect checksum or errors");
                }
                if (unknownSectors == 0 && errorSectors == 0)
                {
                    DicConsole.WriteLine("All sector checksums are correct");
                }

                DicConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds);

                if (options.Verbose)
                {
                    DicConsole.VerboseWriteLine("LBAs with error:");
                    if (failingLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in failingLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }

                    DicConsole.WriteLine("LBAs without checksum:");
                    if (unknownLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in unknownLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }
                }

                DicConsole.WriteLine("Total sectors........... {0}", inputFormat.Info.Sectors);
                DicConsole.WriteLine("Total errors............ {0}", failingLbas.Count);
                DicConsole.WriteLine("Total unknowns.......... {0}", unknownLbas.Count);
                DicConsole.WriteLine("Total errors+unknowns... {0}", failingLbas.Count + unknownLbas.Count);

                totalSectors   = (long)inputFormat.Info.Sectors;
                errorSectors   = failingLbas.Count;
                unknownSectors = unknownLbas.Count;
                correctSectors = totalSectors - errorSectors - unknownSectors;
            }

            Core.Statistics.AddCommand("verify");
            Core.Statistics.AddVerify(correctDisc, correctSectors, errorSectors, unknownSectors, totalSectors);
        }
Пример #24
0
        public void Contents()
        {
            Environment.CurrentDirectory = DataFolder;

            Assert.Multiple(() =>
            {
                foreach (FileSystemTest test in Tests)
                {
                    string testFile = test.TestFile;
                    bool found      = false;
                    var partition   = new Partition();

                    bool exists = File.Exists(testFile);
                    Assert.True(exists, $"{testFile} not found");

                    // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                    // It arrives here...
                    if (!exists)
                    {
                        continue;
                    }

                    var filtersList     = new FiltersList();
                    IFilter inputFilter = filtersList.GetFilter(testFile);

                    Assert.IsNotNull(inputFilter, $"Filter: {testFile}");

                    IMediaImage image = ImageFormat.Detect(inputFilter);

                    Assert.IsNotNull(image, $"Image format: {testFile}");

                    Assert.AreEqual(true, image.Open(inputFilter), $"Cannot open image for {testFile}");

                    List <string> idPlugins;

                    if (Partitions)
                    {
                        List <Partition> partitionsList = Core.Partitions.GetAll(image);

                        Assert.Greater(partitionsList.Count, 0, $"No partitions found for {testFile}");

                        // In reverse to skip boot partitions we're not interested in
                        for (int index = partitionsList.Count - 1; index >= 0; index--)
                        {
                            Core.Filesystems.Identify(image, out idPlugins, partitionsList[index], true);

                            if (idPlugins.Count == 0)
                            {
                                continue;
                            }

                            if (!idPlugins.Contains(Plugin.Id.ToString()))
                            {
                                continue;
                            }

                            found     = true;
                            partition = partitionsList[index];

                            break;
                        }
                    }
                    else
                    {
                        partition = new Partition
                        {
                            Name   = "Whole device",
                            Length = image.Info.Sectors,
                            Size   = image.Info.Sectors * image.Info.SectorSize
                        };

                        Core.Filesystems.Identify(image, out idPlugins, partition, true);

                        Assert.Greater(idPlugins.Count, 0, $"No filesystems found for {testFile}");

                        found = idPlugins.Contains(Plugin.Id.ToString());
                    }

                    Assert.True(found, $"Filesystem not identified for {testFile}");

                    // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                    // It is not the case, it changes
                    if (!found)
                    {
                        continue;
                    }

                    var fs = Activator.CreateInstance(Plugin.GetType()) as IReadOnlyFilesystem;

                    Assert.NotNull(fs, $"Could not instantiate filesystem for {testFile}");

                    test.Encoding ??= Encoding.ASCII;

                    Errno ret = fs.Mount(image, partition, test.Encoding, null, test.Namespace);

                    Assert.AreEqual(Errno.NoError, ret, $"Unmountable: {testFile}");

                    var serializer = new JsonSerializer
                    {
                        Formatting        = Formatting.Indented,
                        MaxDepth          = 16384,
                        NullValueHandling = NullValueHandling.Ignore
                    };

                    serializer.Converters.Add(new StringEnumConverter());

                    if (test.ContentsJson != null)
                    {
                        test.Contents =
                            serializer.
                            Deserialize <
                                Dictionary <string,
                                            FileData> >(new JsonTextReader(new StringReader(test.ContentsJson)));
                    }
                    else if (File.Exists($"{testFile}.contents.json"))
                    {
                        var sr        = new StreamReader($"{testFile}.contents.json");
                        test.Contents = serializer.Deserialize <Dictionary <string, FileData> >(new JsonTextReader(sr));
                    }

                    if (test.Contents is null)
                    {
                        continue;
                    }

                    TestDirectory(fs, "/", test.Contents, testFile);
                }
            });
        }
Пример #25
0
        public static int Invoke(bool debug, bool verbose, string encoding, bool xattrs, string imagePath,
                                 string @namespace, string outputDir, string options)
        {
            MainClass.PrintCopyright();

            if (debug)
            {
                AaruConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }

            if (verbose)
            {
                AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }

            Statistics.AddCommand("extract-files");

            AaruConsole.DebugWriteLine("Extract-Files command", "--debug={0}", debug);
            AaruConsole.DebugWriteLine("Extract-Files command", "--encoding={0}", encoding);
            AaruConsole.DebugWriteLine("Extract-Files command", "--input={0}", imagePath);
            AaruConsole.DebugWriteLine("Extract-Files command", "--options={0}", options);
            AaruConsole.DebugWriteLine("Extract-Files command", "--output={0}", outputDir);
            AaruConsole.DebugWriteLine("Extract-Files command", "--verbose={0}", verbose);
            AaruConsole.DebugWriteLine("Extract-Files command", "--xattrs={0}", xattrs);

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(imagePath);

            Dictionary <string, string> parsedOptions = Core.Options.Parse(options);

            AaruConsole.DebugWriteLine("Extract-Files command", "Parsed options:");

            foreach (KeyValuePair <string, string> parsedOption in parsedOptions)
            {
                AaruConsole.DebugWriteLine("Extract-Files command", "{0} = {1}", parsedOption.Key, parsedOption.Value);
            }

            parsedOptions.Add("debug", debug.ToString());

            if (inputFilter == null)
            {
                AaruConsole.ErrorWriteLine("Cannot open specified file.");

                return((int)ErrorNumber.CannotOpenFile);
            }

            Encoding encodingClass = null;

            if (encoding != null)
            {
                try
                {
                    encodingClass = Claunia.Encoding.Encoding.GetEncoding(encoding);

                    if (verbose)
                    {
                        AaruConsole.VerboseWriteLine("Using encoding for {0}.", encodingClass.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    AaruConsole.ErrorWriteLine("Specified encoding is not supported.");

                    return((int)ErrorNumber.EncodingUnknown);
                }
            }

            PluginBase plugins = GetPluginBase.Instance;

            try
            {
                IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                if (imageFormat == null)
                {
                    AaruConsole.WriteLine("Image format not identified, not proceeding with analysis.");

                    return((int)ErrorNumber.UnrecognizedFormat);
                }

                if (verbose)
                {
                    AaruConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
                                                 imageFormat.Id);
                }
                else
                {
                    AaruConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
                }

                if (outputDir == null)
                {
                    AaruConsole.WriteLine("Output directory missing.");

                    return((int)ErrorNumber.MissingArgument);
                }

                if (Directory.Exists(outputDir) ||
                    File.Exists(outputDir))
                {
                    AaruConsole.ErrorWriteLine("Destination exists, aborting.");

                    return((int)ErrorNumber.DestinationExists);
                }

                Directory.CreateDirectory(outputDir);

                try
                {
                    if (!imageFormat.Open(inputFilter))
                    {
                        AaruConsole.WriteLine("Unable to open image format");
                        AaruConsole.WriteLine("No error given");

                        return((int)ErrorNumber.CannotOpenFormat);
                    }

                    AaruConsole.DebugWriteLine("Extract-Files command", "Correctly opened image file.");

                    AaruConsole.DebugWriteLine("Extract-Files command", "Image without headers is {0} bytes.",
                                               imageFormat.Info.ImageSize);

                    AaruConsole.DebugWriteLine("Extract-Files command", "Image has {0} sectors.",
                                               imageFormat.Info.Sectors);

                    AaruConsole.DebugWriteLine("Extract-Files command", "Image identifies disk type as {0}.",
                                               imageFormat.Info.MediaType);

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddMedia(imageFormat.Info.MediaType, false);
                    Statistics.AddFilter(inputFilter.Name);
                }
                catch (Exception ex)
                {
                    AaruConsole.ErrorWriteLine("Unable to open image format");
                    AaruConsole.ErrorWriteLine("Error: {0}", ex.Message);

                    return((int)ErrorNumber.CannotOpenFormat);
                }

                List <Partition> partitions = Core.Partitions.GetAll(imageFormat);
                Core.Partitions.AddSchemesToStats(partitions);

                if (partitions.Count == 0)
                {
                    AaruConsole.DebugWriteLine("Ls command", "No partitions found");

                    partitions.Add(new Partition
                    {
                        Description = "Whole device",
                        Length      = imageFormat.Info.Sectors,
                        Offset      = 0,
                        Size        = imageFormat.Info.SectorSize * imageFormat.Info.Sectors,
                        Sequence    = 1,
                        Start       = 0
                    });
                }

                AaruConsole.WriteLine("{0} partitions found.", partitions.Count);

                for (int i = 0; i < partitions.Count; i++)
                {
                    AaruConsole.WriteLine();
                    AaruConsole.WriteLine("Partition {0}:", partitions[i].Sequence);

                    AaruConsole.WriteLine("Identifying filesystem on partition");

                    Core.Filesystems.Identify(imageFormat, out List <string> idPlugins, partitions[i]);

                    if (idPlugins.Count == 0)
                    {
                        AaruConsole.WriteLine("Filesystem not identified");
                    }
                    else
                    {
                        IReadOnlyFilesystem plugin;
                        Errno error;

                        if (idPlugins.Count > 1)
                        {
                            AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");

                            foreach (string pluginName in idPlugins)
                            {
                                if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin))
                                {
                                    AaruConsole.WriteLine($"As identified by {plugin.Name}.");

                                    var fs = (IReadOnlyFilesystem)plugin.GetType().GetConstructor(Type.EmptyTypes)?.
                                             Invoke(new object[]
                                                    {});

                                    error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions,
                                                     @namespace);

                                    if (error == Errno.NoError)
                                    {
                                        string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME"
                                                                : fs.XmlFsType.VolumeName;

                                        ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs);

                                        Statistics.AddFilesystem(fs.XmlFsType.Type);
                                    }
                                    else
                                    {
                                        AaruConsole.ErrorWriteLine("Unable to mount device, error {0}",
                                                                   error.ToString());
                                    }
                                }
                            }
                        }
                        else
                        {
                            plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin);

                            if (plugin == null)
                            {
                                continue;
                            }

                            AaruConsole.WriteLine($"Identified by {plugin.Name}.");

                            var fs = (IReadOnlyFilesystem)plugin.GetType().GetConstructor(Type.EmptyTypes)?.
                                     Invoke(new object[]
                                            {});

                            error = fs.Mount(imageFormat, partitions[i], encodingClass, parsedOptions, @namespace);

                            if (error == Errno.NoError)
                            {
                                string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME"
                                                        : fs.XmlFsType.VolumeName;

                                ExtractFilesInDir("/", fs, volumeName, outputDir, xattrs);

                                Statistics.AddFilesystem(fs.XmlFsType.Type);
                            }
                            else
                            {
                                AaruConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                AaruConsole.DebugWriteLine("Extract-Files command", ex.StackTrace);

                return((int)ErrorNumber.UnexpectedException);
            }

            return((int)ErrorNumber.NoError);
        }
Пример #26
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            if (stream.Length < 512)
            {
                return(false);
            }

            byte[] vhdxIdB = new byte[Marshal.SizeOf <Identifier>()];
            stream.Read(vhdxIdB, 0, Marshal.SizeOf <Identifier>());
            _id = Marshal.ByteArrayToStructureLittleEndian <Identifier>(vhdxIdB);

            if (_id.signature != VHDX_SIGNATURE)
            {
                return(false);
            }

            _imageInfo.Application = Encoding.Unicode.GetString(_id.creator);

            stream.Seek(64 * 1024, SeekOrigin.Begin);
            byte[] vHdrB = new byte[Marshal.SizeOf <Header>()];
            stream.Read(vHdrB, 0, Marshal.SizeOf <Header>());
            _vHdr = Marshal.ByteArrayToStructureLittleEndian <Header>(vHdrB);

            if (_vHdr.Signature != VHDX_HEADER_SIG)
            {
                stream.Seek(128 * 1024, SeekOrigin.Begin);
                vHdrB = new byte[Marshal.SizeOf <Header>()];
                stream.Read(vHdrB, 0, Marshal.SizeOf <Header>());
                _vHdr = Marshal.ByteArrayToStructureLittleEndian <Header>(vHdrB);

                if (_vHdr.Signature != VHDX_HEADER_SIG)
                {
                    throw new ImageNotSupportedException("VHDX header not found");
                }
            }

            stream.Seek(192 * 1024, SeekOrigin.Begin);
            byte[] vRegTableB = new byte[Marshal.SizeOf <RegionTableHeader>()];
            stream.Read(vRegTableB, 0, Marshal.SizeOf <RegionTableHeader>());
            _vRegHdr = Marshal.ByteArrayToStructureLittleEndian <RegionTableHeader>(vRegTableB);

            if (_vRegHdr.signature != VHDX_REGION_SIG)
            {
                stream.Seek(256 * 1024, SeekOrigin.Begin);
                vRegTableB = new byte[Marshal.SizeOf <RegionTableHeader>()];
                stream.Read(vRegTableB, 0, Marshal.SizeOf <RegionTableHeader>());
                _vRegHdr = Marshal.ByteArrayToStructureLittleEndian <RegionTableHeader>(vRegTableB);

                if (_vRegHdr.signature != VHDX_REGION_SIG)
                {
                    throw new ImageNotSupportedException("VHDX region table not found");
                }
            }

            _vRegs = new RegionTableEntry[_vRegHdr.entries];

            for (int i = 0; i < _vRegs.Length; i++)
            {
                byte[] vRegB = new byte[System.Runtime.InteropServices.Marshal.SizeOf(_vRegs[i])];
                stream.Read(vRegB, 0, System.Runtime.InteropServices.Marshal.SizeOf(_vRegs[i]));
                _vRegs[i] = Marshal.ByteArrayToStructureLittleEndian <RegionTableEntry>(vRegB);

                if (_vRegs[i].guid == _batGuid)
                {
                    _batOffset = (long)_vRegs[i].offset;
                }
                else if (_vRegs[i].guid == _metadataGuid)
                {
                    _metadataOffset = (long)_vRegs[i].offset;
                }
                else if ((_vRegs[i].flags & REGION_FLAGS_REQUIRED) == REGION_FLAGS_REQUIRED)
                {
                    throw new
                          ImageNotSupportedException($"Found unsupported and required region Guid {_vRegs[i].guid}, not proceeding with image.");
                }
            }

            if (_batOffset == 0)
            {
                throw new Exception("BAT not found, cannot continue.");
            }

            if (_metadataOffset == 0)
            {
                throw new Exception("Metadata not found, cannot continue.");
            }

            uint fileParamsOff = 0, vdSizeOff = 0, p83Off = 0, logOff = 0, physOff = 0, parentOff = 0;

            stream.Seek(_metadataOffset, SeekOrigin.Begin);
            byte[] metTableB = new byte[Marshal.SizeOf <MetadataTableHeader>()];
            stream.Read(metTableB, 0, Marshal.SizeOf <MetadataTableHeader>());
            _vMetHdr = Marshal.ByteArrayToStructureLittleEndian <MetadataTableHeader>(metTableB);

            _vMets = new MetadataTableEntry[_vMetHdr.entries];

            for (int i = 0; i < _vMets.Length; i++)
            {
                byte[] vMetB = new byte[System.Runtime.InteropServices.Marshal.SizeOf(_vMets[i])];
                stream.Read(vMetB, 0, System.Runtime.InteropServices.Marshal.SizeOf(_vMets[i]));
                _vMets[i] = Marshal.ByteArrayToStructureLittleEndian <MetadataTableEntry>(vMetB);

                if (_vMets[i].itemId == _fileParametersGuid)
                {
                    fileParamsOff = _vMets[i].offset;
                }
                else if (_vMets[i].itemId == _virtualDiskSizeGuid)
                {
                    vdSizeOff = _vMets[i].offset;
                }
                else if (_vMets[i].itemId == _page83DataGuid)
                {
                    p83Off = _vMets[i].offset;
                }
                else if (_vMets[i].itemId == _logicalSectorSizeGuid)
                {
                    logOff = _vMets[i].offset;
                }
                else if (_vMets[i].itemId == _physicalSectorSizeGuid)
                {
                    physOff = _vMets[i].offset;
                }
                else if (_vMets[i].itemId == _parentLocatorGuid)
                {
                    parentOff = _vMets[i].offset;
                }
                else if ((_vMets[i].flags & METADATA_FLAGS_REQUIRED) == METADATA_FLAGS_REQUIRED)
                {
                    throw new
                          ImageNotSupportedException($"Found unsupported and required metadata Guid {_vMets[i].itemId}, not proceeding with image.");
                }
            }

            byte[] tmp;

            if (fileParamsOff != 0)
            {
                stream.Seek(fileParamsOff + _metadataOffset, SeekOrigin.Begin);
                tmp = new byte[8];
                stream.Read(tmp, 0, 8);

                _vFileParms = new FileParameters
                {
                    blockSize = BitConverter.ToUInt32(tmp, 0),
                    flags     = BitConverter.ToUInt32(tmp, 4)
                };
            }
            else
            {
                throw new Exception("File parameters not found.");
            }

            if (vdSizeOff != 0)
            {
                stream.Seek(vdSizeOff + _metadataOffset, SeekOrigin.Begin);
                tmp = new byte[8];
                stream.Read(tmp, 0, 8);
                _virtualDiskSize = BitConverter.ToUInt64(tmp, 0);
            }
            else
            {
                throw new Exception("Virtual disk size not found.");
            }

            if (p83Off != 0)
            {
                stream.Seek(p83Off + _metadataOffset, SeekOrigin.Begin);
                tmp = new byte[16];
                stream.Read(tmp, 0, 16);
                _page83Data = new Guid(tmp);
            }

            if (logOff != 0)
            {
                stream.Seek(logOff + _metadataOffset, SeekOrigin.Begin);
                tmp = new byte[4];
                stream.Read(tmp, 0, 4);
                _logicalSectorSize = BitConverter.ToUInt32(tmp, 0);
            }
            else
            {
                throw new Exception("Logical sector size not found.");
            }

            if (physOff != 0)
            {
                stream.Seek(physOff + _metadataOffset, SeekOrigin.Begin);
                tmp = new byte[4];
                stream.Read(tmp, 0, 4);
                _physicalSectorSize = BitConverter.ToUInt32(tmp, 0);
            }
            else
            {
                throw new Exception("Physical sector size not found.");
            }

            if (parentOff != 0 &&
                (_vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT)
            {
                stream.Seek(parentOff + _metadataOffset, SeekOrigin.Begin);
                byte[] vParHdrB = new byte[Marshal.SizeOf <ParentLocatorHeader>()];
                stream.Read(vParHdrB, 0, Marshal.SizeOf <ParentLocatorHeader>());
                _vParHdr = Marshal.ByteArrayToStructureLittleEndian <ParentLocatorHeader>(vParHdrB);

                if (_vParHdr.locatorType != _parentTypeVhdxGuid)
                {
                    throw new
                          ImageNotSupportedException($"Found unsupported and required parent locator type {_vParHdr.locatorType}, not proceeding with image.");
                }

                _vPars = new ParentLocatorEntry[_vParHdr.keyValueCount];

                for (int i = 0; i < _vPars.Length; i++)
                {
                    byte[] vParB = new byte[System.Runtime.InteropServices.Marshal.SizeOf(_vPars[i])];
                    stream.Read(vParB, 0, System.Runtime.InteropServices.Marshal.SizeOf(_vPars[i]));
                    _vPars[i] = Marshal.ByteArrayToStructureLittleEndian <ParentLocatorEntry>(vParB);
                }
            }
            else if ((_vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT)
            {
                throw new Exception("Parent locator not found.");
            }

            if ((_vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT &&
                _vParHdr.locatorType == _parentTypeVhdxGuid)
            {
                _parentImage = new Vhdx();
                bool parentWorks = false;

                foreach (ParentLocatorEntry parentEntry in _vPars)
                {
                    stream.Seek(parentEntry.keyOffset + _metadataOffset, SeekOrigin.Begin);
                    byte[] tmpKey = new byte[parentEntry.keyLength];
                    stream.Read(tmpKey, 0, tmpKey.Length);
                    string entryType = Encoding.Unicode.GetString(tmpKey);

                    IFilter parentFilter;

                    if (string.Compare(entryType, RELATIVE_PATH_KEY, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        stream.Seek(parentEntry.valueOffset + _metadataOffset, SeekOrigin.Begin);
                        byte[] tmpVal = new byte[parentEntry.valueLength];
                        stream.Read(tmpVal, 0, tmpVal.Length);
                        string entryValue = Encoding.Unicode.GetString(tmpVal);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), entryValue));

                            if (parentFilter != null &&
                                _parentImage.Open(parentFilter))
                            {
                                parentWorks = true;

                                break;
                            }
                        }
                        catch
                        {
                            parentWorks = false;
                        }

                        string relEntry = Path.Combine(Path.GetDirectoryName(imageFilter.GetPath()), entryValue);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), relEntry));

                            if (parentFilter == null ||
                                !_parentImage.Open(parentFilter))
                            {
                                continue;
                            }

                            parentWorks = true;

                            break;
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                    else if (string.Compare(entryType, VOLUME_PATH_KEY, StringComparison.OrdinalIgnoreCase) == 0 ||
                             string.Compare(entryType, ABSOLUTE_WIN32_PATH_KEY, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        stream.Seek(parentEntry.valueOffset + _metadataOffset, SeekOrigin.Begin);
                        byte[] tmpVal = new byte[parentEntry.valueLength];
                        stream.Read(tmpVal, 0, tmpVal.Length);
                        string entryValue = Encoding.Unicode.GetString(tmpVal);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), entryValue));

                            if (parentFilter == null ||
                                !_parentImage.Open(parentFilter))
                            {
                                continue;
                            }

                            parentWorks = true;

                            break;
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                }

                if (!parentWorks)
                {
                    throw new Exception("Image is differential but parent cannot be opened.");
                }

                _hasParent = true;
            }

            _chunkRatio = (long)((Math.Pow(2, 23) * _logicalSectorSize) / _vFileParms.blockSize);
            _dataBlocks = _virtualDiskSize / _vFileParms.blockSize;

            if (_virtualDiskSize % _vFileParms.blockSize > 0)
            {
                _dataBlocks++;
            }

            long batEntries;

            if (_hasParent)
            {
                long sectorBitmapBlocks = (long)_dataBlocks / _chunkRatio;

                if (_dataBlocks % (ulong)_chunkRatio > 0)
                {
                    sectorBitmapBlocks++;
                }

                _sectorBitmapPointers = new ulong[sectorBitmapBlocks];

                batEntries = sectorBitmapBlocks * (_chunkRatio - 1);
            }
            else
            {
                batEntries = (long)(_dataBlocks + ((_dataBlocks - 1) / (ulong)_chunkRatio));
            }

            AaruConsole.DebugWriteLine("VHDX plugin", "Reading BAT");

            long readChunks = 0;

            _blockAllocationTable = new ulong[_dataBlocks];
            byte[] batB = new byte[batEntries * 8];
            stream.Seek(_batOffset, SeekOrigin.Begin);
            stream.Read(batB, 0, batB.Length);

            ulong skipSize = 0;

            for (ulong i = 0; i < _dataBlocks; i++)
            {
                if (readChunks == _chunkRatio)
                {
                    if (_hasParent)
                    {
                        _sectorBitmapPointers[skipSize / 8] = BitConverter.ToUInt64(batB, (int)((i * 8) + skipSize));
                    }

                    readChunks = 0;
                    skipSize  += 8;
                }
                else
                {
                    _blockAllocationTable[i] = BitConverter.ToUInt64(batB, (int)((i * 8) + skipSize));
                    readChunks++;
                }
            }

            if (_hasParent)
            {
                AaruConsole.DebugWriteLine("VHDX plugin", "Reading Sector Bitmap");

                var sectorBmpMs = new MemoryStream();

                foreach (ulong pt in _sectorBitmapPointers)
                {
                    switch (pt & BAT_FLAGS_MASK)
                    {
                    case SECTOR_BITMAP_NOT_PRESENT:
                        sectorBmpMs.Write(new byte[1048576], 0, 1048576);

                        break;

                    case SECTOR_BITMAP_PRESENT:
                        stream.Seek((long)((pt & BAT_FILE_OFFSET_MASK) * 1048576), SeekOrigin.Begin);
                        byte[] bmp = new byte[1048576];
                        stream.Read(bmp, 0, bmp.Length);
                        sectorBmpMs.Write(bmp, 0, bmp.Length);

                        break;

                    default:
                        if ((pt & BAT_FLAGS_MASK) != 0)
                        {
                            throw new
                                  ImageNotSupportedException($"Unsupported sector bitmap block flags (0x{pt & BAT_FLAGS_MASK:X16}) found, not proceeding.");
                        }

                        break;
                    }
                }

                _sectorBitmap = sectorBmpMs.ToArray();
                sectorBmpMs.Close();
            }

            _maxBlockCache  = (int)(MAX_CACHE_SIZE / _vFileParms.blockSize);
            _maxSectorCache = (int)(MAX_CACHE_SIZE / _logicalSectorSize);

            _imageStream = stream;

            _sectorCache = new Dictionary <ulong, byte[]>();
            _blockCache  = new Dictionary <ulong, byte[]>();

            _imageInfo.CreationTime         = imageFilter.GetCreationTime();
            _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            _imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            _imageInfo.SectorSize           = _logicalSectorSize;
            _imageInfo.XmlMediaType         = XmlMediaType.BlockMedia;
            _imageInfo.MediaType            = MediaType.GENERIC_HDD;
            _imageInfo.ImageSize            = _virtualDiskSize;
            _imageInfo.Sectors           = _imageInfo.ImageSize / _imageInfo.SectorSize;
            _imageInfo.DriveSerialNumber = _page83Data.ToString();

            // TODO: Separate image application from version, need several samples.

            _imageInfo.Cylinders       = (uint)(_imageInfo.Sectors / 16 / 63);
            _imageInfo.Heads           = 16;
            _imageInfo.SectorsPerTrack = 63;

            return(true);
        }
Пример #27
0
        /// <summary>Dumps a MultiMediaCard or SecureDigital flash card</summary>
        void SecureDigital()
        {
            if (_dumpRaw)
            {
                if (_force)
                {
                    ErrorMessage?.
                    Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing...");
                }
                else
                {
                    StoppingErrorMessage?.
                    Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting...");

                    return;
                }
            }

            bool         sense;
            const ushort sdProfile = 0x0001;
            const uint   timeout   = 5;
            double       duration;
            ushort       blocksToRead = 128;
            uint         blockSize    = 512;
            ulong        blocks       = 0;

            byte[] csd  = null;
            byte[] ocr  = null;
            byte[] ecsd = null;
            byte[] scr  = null;
            uint   physicalBlockSize = 0;
            bool   byteAddressed     = true;

            uint[] response;
            bool   supportsCmd23 = false;

            Dictionary <MediaTagType, byte[]> mediaTags = new Dictionary <MediaTagType, byte[]>();

            switch (_dev.Type)
            {
            case DeviceType.MMC:
            {
                UpdateStatus?.Invoke("Reading CSD");
                _dumpLog.WriteLine("Reading CSD");
                sense = _dev.ReadCsd(out csd, out response, timeout, out duration);

                if (!sense)
                {
                    CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd);
                    blocks    = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2));
                    blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);

                    mediaTags.Add(MediaTagType.MMC_CSD, null);

                    // Found at least since MMC System Specification 3.31
                    supportsCmd23 = csdDecoded.Version >= 3;

                    if (csdDecoded.Size == 0xFFF)
                    {
                        UpdateStatus?.Invoke("Reading Extended CSD");
                        _dumpLog.WriteLine("Reading Extended CSD");
                        sense = _dev.ReadExtendedCsd(out ecsd, out response, timeout, out duration);

                        if (!sense)
                        {
                            ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd);
                            blocks    = ecsdDecoded.SectorCount;
                            blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512);

                            if (ecsdDecoded.NativeSectorSize == 0)
                            {
                                physicalBlockSize = 512;
                            }
                            else if (ecsdDecoded.NativeSectorSize == 1)
                            {
                                physicalBlockSize = 4096;
                            }

                            blocksToRead = (ushort)(ecsdDecoded.OptimalReadSize * 4096 / blockSize);

                            if (blocksToRead == 0)
                            {
                                blocksToRead = 128;
                            }

                            // Supposing it's high-capacity MMC if it has Extended CSD...
                            byteAddressed = false;
                            mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null);
                        }
                        else
                        {
                            _errorLog?.WriteLine("Read eCSD", _dev.Error, _dev.LastError, response);
                            ecsd = null;
                        }
                    }
                }
                else
                {
                    _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response);
                    csd = null;
                }

                UpdateStatus?.Invoke("Reading OCR");
                _dumpLog.WriteLine("Reading OCR");
                sense = _dev.ReadOcr(out ocr, out response, timeout, out duration);

                if (sense)
                {
                    _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response);
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.MMC_OCR, null);
                }

                break;
            }

            case DeviceType.SecureDigital:
            {
                UpdateStatus?.Invoke("Reading CSD");
                _dumpLog.WriteLine("Reading CSD");
                sense = _dev.ReadCsd(out csd, out response, timeout, out duration);

                if (!sense)
                {
                    Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd);

                    blocks = (ulong)(csdDecoded.Structure == 0
                                             ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)
                                             : (csdDecoded.Size + 1) * 1024);

                    blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength);

                    // Structure >=1 for SDHC/SDXC, so that's block addressed
                    byteAddressed = csdDecoded.Structure == 0;
                    mediaTags.Add(MediaTagType.SD_CSD, null);

                    physicalBlockSize = blockSize;

                    if (blockSize != 512)
                    {
                        uint ratio = blockSize / 512;
                        blocks   *= ratio;
                        blockSize = 512;
                    }
                }
                else
                {
                    _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response);
                    csd = null;
                }

                UpdateStatus?.Invoke("Reading OCR");
                _dumpLog.WriteLine("Reading OCR");
                sense = _dev.ReadSdocr(out ocr, out response, timeout, out duration);

                if (sense)
                {
                    _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response);
                    ocr = null;
                }
                else
                {
                    mediaTags.Add(MediaTagType.SD_OCR, null);
                }

                UpdateStatus?.Invoke("Reading SCR");
                _dumpLog.WriteLine("Reading SCR");
                sense = _dev.ReadScr(out scr, out response, timeout, out duration);

                if (sense)
                {
                    _errorLog?.WriteLine("Read SCR", _dev.Error, _dev.LastError, response);
                    scr = null;
                }
                else
                {
                    supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(scr)?.CommandSupport.
                                    HasFlag(CommandSupport.SetBlockCount) ?? false;

                    mediaTags.Add(MediaTagType.SD_SCR, null);
                }

                break;
            }
            }

            UpdateStatus?.Invoke("Reading CID");
            _dumpLog.WriteLine("Reading CID");
            sense = _dev.ReadCid(out byte[] cid, out response, timeout, out duration);

            if (sense)
            {
                _errorLog?.WriteLine("Read CID", _dev.Error, _dev.LastError, response);
                cid = null;
            }
            else
            {
                mediaTags.Add(_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null);
            }

            DateTime start;
            DateTime end;
            double   totalDuration = 0;
            double   currentSpeed  = 0;
            double   maxSpeed      = double.MinValue;
            double   minSpeed      = double.MaxValue;

            if (blocks == 0)
            {
                _dumpLog.WriteLine("Unable to get device size.");
                StoppingErrorMessage?.Invoke("Unable to get device size.");

                return;
            }

            UpdateStatus?.Invoke($"Device reports {blocks} blocks.");
            _dumpLog.WriteLine("Device reports {0} blocks.", blocks);

            byte[] cmdBuf;
            bool   error;

            if (blocksToRead > _maximumReadable)
            {
                blocksToRead = (ushort)_maximumReadable;
            }

            if (supportsCmd23 && blocksToRead > 1)
            {
                sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout,
                                                out duration);

                if (sense || _dev.Error)
                {
                    supportsCmd23 = false;
                }

                // Need to restart device, otherwise is it just busy streaming data with no one listening
                sense = _dev.ReOpen();

                if (sense)
                {
                    StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device.");

                    return;
                }
            }

            if (supportsCmd23 && blocksToRead > 1)
            {
                while (true)
                {
                    error = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed,
                                                    timeout, out duration);

                    if (error)
                    {
                        blocksToRead /= 2;
                    }

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

                if (error)
                {
                    _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError);

                    StoppingErrorMessage?.
                    Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");

                    return;
                }
            }

            if (supportsCmd23 || blocksToRead == 1)
            {
                UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
                _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
            }
            else if (_useBufferedReads)
            {
                UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time using OS buffered reads.");
                _dumpLog.WriteLine("Device can read {0} blocks at a time using OS buffered reads.", blocksToRead);
            }
            else
            {
                UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks using sequential commands.");
                _dumpLog.WriteLine("Device can read {0} blocks using sequential commands.", blocksToRead);
            }

            if (_skip < blocksToRead)
            {
                _skip = blocksToRead;
            }

            DumpHardwareType currentTry = null;
            ExtentsULong     extents    = null;

            ResumeSupport.Process(true, false, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
                                  ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private);

            if (currentTry == null ||
                extents == null)
            {
                StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                return;
            }

            bool ret = true;

            foreach (MediaTagType tag in mediaTags.Keys.Where(tag => !_outputPlugin.SupportedMediaTags.Contains(tag)))
            {
                ret = false;
                _dumpLog.WriteLine($"Output format does not support {tag}.");
                ErrorMessage?.Invoke($"Output format does not support {tag}.");
            }

            if (!ret)
            {
                if (_force)
                {
                    _dumpLog.WriteLine("Several media tags not supported, continuing...");
                    ErrorMessage?.Invoke("Several media tags not supported, continuing...");
                }
                else
                {
                    _dumpLog.WriteLine("Several media tags not supported, not continuing...");
                    StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                    return;
                }
            }

            var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
            var ibgLog  = new IbgLog(_outputPrefix + ".ibg", sdProfile);

            ret = _outputPlugin.Create(_outputPath,
                                       _dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC,
                                       _formatOptions, blocks, blockSize);

            // Cannot create image
            if (!ret)
            {
                _dumpLog.WriteLine("Error creating output image, not continuing.");
                _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);

                return;
            }

            if (cid != null)
            {
                if (_dev.Type == DeviceType.SecureDigital && _private)
                {
                    // Clear serial number and manufacturing date
                    cid[9]  = 0;
                    cid[10] = 0;
                    cid[11] = 0;
                    cid[12] = 0;
                    cid[13] = 0;
                    cid[14] = 0;
                }
                else if (_dev.Type == DeviceType.MMC && _private)
                {
                    // Clear serial number and manufacturing date
                    cid[10] = 0;
                    cid[11] = 0;
                    cid[12] = 0;
                    cid[13] = 0;
                    cid[14] = 0;
                }

                ret =
                    _outputPlugin.WriteMediaTag(cid,
                                                _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID
                                                    : MediaTagType.MMC_CID);

                // Cannot write CID to image
                if (!ret &&
                    !_force)
                {
                    _dumpLog.WriteLine("Cannot write CID to output image.");

                    StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine +
                                                 _outputPlugin.ErrorMessage);

                    return;
                }
            }

            if (csd != null)
            {
                ret =
                    _outputPlugin.WriteMediaTag(csd,
                                                _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD
                                                    : MediaTagType.MMC_CSD);

                // Cannot write CSD to image
                if (!ret &&
                    !_force)
                {
                    _dumpLog.WriteLine("Cannot write CSD to output image.");

                    StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine +
                                                 _outputPlugin.ErrorMessage);

                    return;
                }
            }

            if (ecsd != null)
            {
                ret = _outputPlugin.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD);

                // Cannot write Extended CSD to image
                if (!ret &&
                    !_force)
                {
                    _dumpLog.WriteLine("Cannot write Extended CSD to output image.");

                    StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." + Environment.NewLine +
                                                 _outputPlugin.ErrorMessage);

                    return;
                }
            }

            if (ocr != null)
            {
                ret =
                    _outputPlugin.WriteMediaTag(ocr,
                                                _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR
                                                    : MediaTagType.MMC_OCR);

                // Cannot write OCR to image
                if (!ret &&
                    !_force)
                {
                    _dumpLog.WriteLine("Cannot write OCR to output image.");

                    StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine +
                                                 _outputPlugin.ErrorMessage);

                    return;
                }
            }

            if (scr != null)
            {
                ret = _outputPlugin.WriteMediaTag(scr, MediaTagType.SD_SCR);

                // Cannot write SCR to image
                if (!ret &&
                    !_force)
                {
                    _dumpLog.WriteLine("Cannot write SCR to output image.");

                    StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine +
                                                 _outputPlugin.ErrorMessage);

                    return;
                }
            }

            if (_resume.NextBlock > 0)
            {
                UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
            }

            start = DateTime.UtcNow;
            double   imageWriteDuration = 0;
            bool     newTrim            = false;
            DateTime timeSpeedStart     = DateTime.UtcNow;
            ulong    sectorSpeedStart   = 0;

            InitProgress?.Invoke();

            for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
            {
                if (_aborted)
                {
                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    UpdateStatus?.Invoke("Aborted!");
                    _dumpLog.WriteLine("Aborted!");

                    break;
                }

                if (blocks - i < blocksToRead)
                {
                    blocksToRead = (byte)(blocks - i);
                }

                if (currentSpeed > maxSpeed &&
                    currentSpeed > 0)
                {
                    maxSpeed = currentSpeed;
                }

                if (currentSpeed < minSpeed &&
                    currentSpeed > 0)
                {
                    minSpeed = currentSpeed;
                }

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

                if (blocksToRead == 1)
                {
                    error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout,
                                                 out duration);
                }
                else if (supportsCmd23)
                {
                    error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed,
                                                    timeout, out duration);
                }
                else if (_useBufferedReads)
                {
                    error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead,
                                                out duration);
                }
                else
                {
                    error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead,
                                                         byteAddressed, timeout, out duration);
                }

                if (!error)
                {
                    mhddLog.Write(i, duration);
                    ibgLog.Write(i, currentSpeed * 1024);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    extents.Add(i, blocksToRead, true);
                }
                else
                {
                    _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, byteAddressed, response);

                    if (i + _skip > blocks)
                    {
                        _skip = (uint)(blocks - i);
                    }

                    for (ulong b = i; b < i + _skip; b++)
                    {
                        _resume.BadBlocks.Add(b);
                    }

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

                    ibgLog.Write(i, 0);
                    DateTime writeStart = DateTime.Now;
                    _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                    imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                    _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                    i      += _skip - blocksToRead;
                    newTrim = true;
                }

                sectorSpeedStart += blocksToRead;
                _resume.NextBlock = i + blocksToRead;

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

                if (elapsed < 1)
                {
                    continue;
                }

                currentSpeed     = sectorSpeedStart * blockSize / (1048576 * elapsed);
                sectorSpeedStart = 0;
                timeSpeedStart   = DateTime.UtcNow;
            }

            _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();

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

            ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                         blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath);

            UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

            UpdateStatus?.
            Invoke($"Average dump speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

            UpdateStatus?.
            Invoke($"Average write speed {(double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");

            _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 write speed {0:F3} KiB/sec.",
                               (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);

            #region Trimming
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _trim &&
                newTrim)
            {
                start = DateTime.UtcNow;
                UpdateStatus?.Invoke("Trimming skipped sectors");
                _dumpLog.WriteLine("Trimming skipped sectors");

                ulong[] tmpArray = _resume.BadBlocks.ToArray();
                InitProgress?.Invoke();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke($"Trimming sector {badSector}");

                    error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed,
                                                 timeout, out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response);

                        continue;
                    }

                    _resume.BadBlocks.Remove(badSector);
                    extents.Add(badSector);
                    _outputPlugin.WriteSector(cmdBuf, badSector);
                }

                EndProgress?.Invoke();
                end = DateTime.UtcNow;
                UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
                _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
            }
            #endregion Trimming

            #region Error handling
            if (_resume.BadBlocks.Count > 0 &&
                !_aborted &&
                _retryPasses > 0)
            {
                int  pass              = 1;
                bool forward           = true;
                bool runningPersistent = false;

                InitProgress?.Invoke();
repeatRetryLba:
                ulong[] tmpArray = _resume.BadBlocks.ToArray();

                foreach (ulong badSector in tmpArray)
                {
                    if (_aborted)
                    {
                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                        UpdateStatus?.Invoke("Aborted!");
                        _dumpLog.WriteLine("Aborted!");

                        break;
                    }

                    PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                        forward ? "forward" : "reverse",
                                                        runningPersistent ? "recovering partial data, " : ""));

                    error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed,
                                                 timeout, out duration);

                    totalDuration += duration;

                    if (error)
                    {
                        _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response);
                    }

                    if (!error)
                    {
                        _resume.BadBlocks.Remove(badSector);
                        extents.Add(badSector);
                        _outputPlugin.WriteSector(cmdBuf, badSector);
                        UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                        _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                    }
                    else if (runningPersistent)
                    {
                        _outputPlugin.WriteSector(cmdBuf, badSector);
                    }
                }

                if (pass < _retryPasses &&
                    !_aborted &&
                    _resume.BadBlocks.Count > 0)
                {
                    pass++;
                    forward = !forward;
                    _resume.BadBlocks.Sort();

                    if (!forward)
                    {
                        _resume.BadBlocks.Reverse();
                    }

                    goto repeatRetryLba;
                }

                EndProgress?.Invoke();
            }
            #endregion Error handling

            currentTry.Extents = ExtentsConverter.ToMetadata(extents);

            _outputPlugin.SetDumpHardware(_resume.Tries);

            // TODO: Drive info
            var metadata = new CommonTypes.Structs.ImageInfo
            {
                Application        = "Aaru",
                ApplicationVersion = Version.GetVersion()
            };

            if (!_outputPlugin.SetMetadata(metadata))
            {
                ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                     _outputPlugin.ErrorMessage);
            }

            if (_preSidecar != null)
            {
                _outputPlugin.SetCicmMetadata(_preSidecar);
            }

            _dumpLog.WriteLine("Closing output file.");
            UpdateStatus?.Invoke("Closing output file.");
            DateTime closeStart = DateTime.Now;
            _outputPlugin.Close();
            DateTime closeEnd = DateTime.Now;
            UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
            _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

            if (_aborted)
            {
                UpdateStatus?.Invoke("Aborted!");
                _dumpLog.WriteLine("Aborted!");

                return;
            }

            double totalChkDuration = 0;

            if (_metadata)
            {
                UpdateStatus?.Invoke("Creating sidecar.");
                _dumpLog.WriteLine("Creating sidecar.");
                var         filters     = new FiltersList();
                IFilter     filter      = filters.GetFilter(_outputPath);
                IMediaImage inputPlugin = ImageFormat.Detect(filter);

                if (!inputPlugin.Open(filter))
                {
                    StoppingErrorMessage?.Invoke("Could not open created image.");
                }

                DateTime chkStart = DateTime.UtcNow;
                _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
                _sidecarClass.InitProgressEvent    += InitProgress;
                _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                _sidecarClass.EndProgressEvent     += EndProgress;
                _sidecarClass.InitProgressEvent2   += InitProgress2;
                _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                _sidecarClass.EndProgressEvent2    += EndProgress2;
                _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                CICMMetadataType sidecar = _sidecarClass.Create();

                if (_preSidecar != null)
                {
                    _preSidecar.BlockMedia = sidecar.BlockMedia;
                    sidecar = _preSidecar;
                }

                end = DateTime.UtcNow;

                totalChkDuration = (end - chkStart).TotalMilliseconds;
                UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");

                UpdateStatus?.
                Invoke($"Average checksum speed {(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);

                _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                   (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

                (string type, string subType)xmlType = (null, null);

                switch (_dev.Type)
                {
                case DeviceType.MMC:
                    xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC);

                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC);

                    break;

                case DeviceType.SecureDigital:
                    CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital);
                    sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.SecureDigital);

                    break;
                }

                sidecar.BlockMedia[0].DiskType    = xmlType.type;
                sidecar.BlockMedia[0].DiskSubType = xmlType.subType;

                // TODO: Implement device firmware revision
                sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize;
                sidecar.BlockMedia[0].LogicalBlockSize  = blockSize;
                sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                sidecar.BlockMedia[0].Model             = _dev.Model;

                if (!_private)
                {
                    sidecar.BlockMedia[0].Serial = _dev.Serial;
                }

                sidecar.BlockMedia[0].Size = blocks * blockSize;

                UpdateStatus?.Invoke("Writing metadata sidecar");

                var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }

            UpdateStatus?.Invoke("");

            UpdateStatus?.
            Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

            UpdateStatus?.
            Invoke($"Average speed: {(double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

            if (maxSpeed > 0)
            {
                UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
            }

            if (minSpeed > 0 &&
                minSpeed < double.MaxValue)
            {
                UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
            }

            UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
            UpdateStatus?.Invoke("");

            if (_resume.BadBlocks.Count > 0)
            {
                _resume.BadBlocks.Sort();
            }

            switch (_dev.Type)
            {
            case DeviceType.MMC:
                Statistics.AddMedia(MediaType.MMC, true);

                break;

            case DeviceType.SecureDigital:
                Statistics.AddMedia(MediaType.SecureDigital, true);

                break;
            }
        }
Пример #28
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();
            stream.Seek(0, SeekOrigin.Begin);

            if(stream.Length < 512) return false;

            byte[] vhdxIdB = new byte[Marshal.SizeOf(vhdxId)];
            stream.Read(vhdxIdB, 0, Marshal.SizeOf(vhdxId));
            vhdxId = new VhdxIdentifier();
            IntPtr idPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vhdxId));
            Marshal.Copy(vhdxIdB, 0, idPtr, Marshal.SizeOf(vhdxId));
            vhdxId = (VhdxIdentifier)Marshal.PtrToStructure(idPtr, typeof(VhdxIdentifier));
            Marshal.FreeHGlobal(idPtr);

            if(vhdxId.signature != VHDX_SIGNATURE) return false;

            imageInfo.Application = Encoding.Unicode.GetString(vhdxId.creator);

            stream.Seek(64 * 1024, SeekOrigin.Begin);
            byte[] vHdrB = new byte[Marshal.SizeOf(vHdr)];
            stream.Read(vHdrB, 0, Marshal.SizeOf(vHdr));
            vHdr = new VhdxHeader();
            IntPtr headerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vHdr));
            Marshal.Copy(vHdrB, 0, headerPtr, Marshal.SizeOf(vHdr));
            vHdr = (VhdxHeader)Marshal.PtrToStructure(headerPtr, typeof(VhdxHeader));
            Marshal.FreeHGlobal(headerPtr);

            if(vHdr.Signature != VHDX_HEADER_SIG)
            {
                stream.Seek(128 * 1024, SeekOrigin.Begin);
                vHdrB = new byte[Marshal.SizeOf(vHdr)];
                stream.Read(vHdrB, 0, Marshal.SizeOf(vHdr));
                vHdr      = new VhdxHeader();
                headerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vHdr));
                Marshal.Copy(vHdrB, 0, headerPtr, Marshal.SizeOf(vHdr));
                vHdr = (VhdxHeader)Marshal.PtrToStructure(headerPtr, typeof(VhdxHeader));
                Marshal.FreeHGlobal(headerPtr);

                if(vHdr.Signature != VHDX_HEADER_SIG) throw new ImageNotSupportedException("VHDX header not found");
            }

            stream.Seek(192 * 1024, SeekOrigin.Begin);
            byte[] vRegTableB = new byte[Marshal.SizeOf(vRegHdr)];
            stream.Read(vRegTableB, 0, Marshal.SizeOf(vRegHdr));
            vRegHdr = new VhdxRegionTableHeader();
            IntPtr vRegTabPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vRegHdr));
            Marshal.Copy(vRegTableB, 0, vRegTabPtr, Marshal.SizeOf(vRegHdr));
            vRegHdr = (VhdxRegionTableHeader)Marshal.PtrToStructure(vRegTabPtr, typeof(VhdxRegionTableHeader));
            Marshal.FreeHGlobal(vRegTabPtr);

            if(vRegHdr.signature != VHDX_REGION_SIG)
            {
                stream.Seek(256 * 1024, SeekOrigin.Begin);
                vRegTableB = new byte[Marshal.SizeOf(vRegHdr)];
                stream.Read(vRegTableB, 0, Marshal.SizeOf(vRegHdr));
                vRegHdr    = new VhdxRegionTableHeader();
                vRegTabPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vRegHdr));
                Marshal.Copy(vRegTableB, 0, vRegTabPtr, Marshal.SizeOf(vRegHdr));
                vRegHdr = (VhdxRegionTableHeader)Marshal.PtrToStructure(vRegTabPtr, typeof(VhdxRegionTableHeader));
                Marshal.FreeHGlobal(vRegTabPtr);

                if(vRegHdr.signature != VHDX_REGION_SIG)
                    throw new ImageNotSupportedException("VHDX region table not found");
            }

            vRegs = new VhdxRegionTableEntry[vRegHdr.entries];
            for(int i = 0; i < vRegs.Length; i++)
            {
                byte[] vRegB = new byte[Marshal.SizeOf(vRegs[i])];
                stream.Read(vRegB, 0, Marshal.SizeOf(vRegs[i]));
                vRegs[i] = new VhdxRegionTableEntry();
                IntPtr vRegPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vRegs[i]));
                Marshal.Copy(vRegB, 0, vRegPtr, Marshal.SizeOf(vRegs[i]));
                vRegs[i] = (VhdxRegionTableEntry)Marshal.PtrToStructure(vRegPtr, typeof(VhdxRegionTableEntry));
                Marshal.FreeHGlobal(vRegPtr);

                if(vRegs[i].guid == batGuid)
                    batOffset = (long)vRegs[i].offset;
                else if(vRegs[i].guid == metadataGuid)
                    metadataOffset = (long)vRegs[i].offset;
                else if((vRegs[i].flags & REGION_FLAGS_REQUIRED) == REGION_FLAGS_REQUIRED)
                    throw new
                        ImageNotSupportedException($"Found unsupported and required region Guid {vRegs[i].guid}, not proceeding with image.");
            }

            if(batOffset == 0) throw new Exception("BAT not found, cannot continue.");

            if(metadataOffset == 0) throw new Exception("Metadata not found, cannot continue.");

            uint fileParamsOff = 0, vdSizeOff = 0, p83Off = 0, logOff = 0, physOff = 0, parentOff = 0;

            stream.Seek(metadataOffset, SeekOrigin.Begin);
            byte[] metTableB = new byte[Marshal.SizeOf(vMetHdr)];
            stream.Read(metTableB, 0, Marshal.SizeOf(vMetHdr));
            vMetHdr = new VhdxMetadataTableHeader();
            IntPtr metTablePtr = Marshal.AllocHGlobal(Marshal.SizeOf(vMetHdr));
            Marshal.Copy(metTableB, 0, metTablePtr, Marshal.SizeOf(vMetHdr));
            vMetHdr = (VhdxMetadataTableHeader)Marshal.PtrToStructure(metTablePtr, typeof(VhdxMetadataTableHeader));
            Marshal.FreeHGlobal(metTablePtr);

            vMets = new VhdxMetadataTableEntry[vMetHdr.entries];
            for(int i = 0; i < vMets.Length; i++)
            {
                byte[] vMetB = new byte[Marshal.SizeOf(vMets[i])];
                stream.Read(vMetB, 0, Marshal.SizeOf(vMets[i]));
                vMets[i] = new VhdxMetadataTableEntry();
                IntPtr vMetPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vMets[i]));
                Marshal.Copy(vMetB, 0, vMetPtr, Marshal.SizeOf(vMets[i]));
                vMets[i] = (VhdxMetadataTableEntry)Marshal.PtrToStructure(vMetPtr, typeof(VhdxMetadataTableEntry));
                Marshal.FreeHGlobal(vMetPtr);

                if(vMets[i].itemId == fileParametersGuid)
                    fileParamsOff = vMets[i].offset;
                else if(vMets[i].itemId == virtualDiskSizeGuid)
                    vdSizeOff = vMets[i].offset;
                else if(vMets[i].itemId == page83DataGuid)
                    p83Off = vMets[i].offset;
                else if(vMets[i].itemId == logicalSectorSizeGuid)
                    logOff = vMets[i].offset;
                else if(vMets[i].itemId == physicalSectorSizeGuid)
                    physOff = vMets[i].offset;
                else if(vMets[i].itemId == parentLocatorGuid)
                    parentOff = vMets[i].offset;
                else if((vMets[i].flags & METADATA_FLAGS_REQUIRED) == METADATA_FLAGS_REQUIRED)
                    throw new
                        ImageNotSupportedException($"Found unsupported and required metadata Guid {vMets[i].itemId}, not proceeding with image.");
            }

            byte[] tmp;

            if(fileParamsOff != 0)
            {
                stream.Seek(fileParamsOff + metadataOffset, SeekOrigin.Begin);
                tmp = new byte[8];
                stream.Read(tmp, 0, 8);
                vFileParms = new VhdxFileParameters
                {
                    blockSize = BitConverter.ToUInt32(tmp, 0),
                    flags     = BitConverter.ToUInt32(tmp, 4)
                };
            }
            else throw new Exception("File parameters not found.");

            if(vdSizeOff != 0)
            {
                stream.Seek(vdSizeOff + metadataOffset, SeekOrigin.Begin);
                tmp = new byte[8];
                stream.Read(tmp, 0, 8);
                virtualDiskSize = BitConverter.ToUInt64(tmp, 0);
            }
            else throw new Exception("Virtual disk size not found.");

            if(p83Off != 0)
            {
                stream.Seek(p83Off + metadataOffset, SeekOrigin.Begin);
                tmp = new byte[16];
                stream.Read(tmp, 0, 16);
                page83Data = new Guid(tmp);
            }

            if(logOff != 0)
            {
                stream.Seek(logOff + metadataOffset, SeekOrigin.Begin);
                tmp = new byte[4];
                stream.Read(tmp, 0, 4);
                logicalSectorSize = BitConverter.ToUInt32(tmp, 0);
            }
            else throw new Exception("Logical sector size not found.");

            if(physOff != 0)
            {
                stream.Seek(physOff + metadataOffset, SeekOrigin.Begin);
                tmp = new byte[4];
                stream.Read(tmp, 0, 4);
                physicalSectorSize = BitConverter.ToUInt32(tmp, 0);
            }
            else throw new Exception("Physical sector size not found.");

            if(parentOff != 0 && (vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT)
            {
                stream.Seek(parentOff + metadataOffset, SeekOrigin.Begin);
                byte[] vParHdrB = new byte[Marshal.SizeOf(vMetHdr)];
                stream.Read(vParHdrB, 0, Marshal.SizeOf(vMetHdr));
                vParHdr = new VhdxParentLocatorHeader();
                IntPtr vParHdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vMetHdr));
                Marshal.Copy(vParHdrB, 0, vParHdrPtr, Marshal.SizeOf(vMetHdr));
                vParHdr = (VhdxParentLocatorHeader)Marshal.PtrToStructure(vParHdrPtr, typeof(VhdxParentLocatorHeader));
                Marshal.FreeHGlobal(vParHdrPtr);

                if(vParHdr.locatorType != parentTypeVhdxGuid)
                    throw new
                        ImageNotSupportedException($"Found unsupported and required parent locator type {vParHdr.locatorType}, not proceeding with image.");

                vPars = new VhdxParentLocatorEntry[vParHdr.keyValueCount];
                for(int i = 0; i < vPars.Length; i++)
                {
                    byte[] vParB = new byte[Marshal.SizeOf(vPars[i])];
                    stream.Read(vParB, 0, Marshal.SizeOf(vPars[i]));
                    vPars[i] = new VhdxParentLocatorEntry();
                    IntPtr vParPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vPars[i]));
                    Marshal.Copy(vParB, 0, vParPtr, Marshal.SizeOf(vPars[i]));
                    vPars[i] = (VhdxParentLocatorEntry)Marshal.PtrToStructure(vParPtr, typeof(VhdxParentLocatorEntry));
                    Marshal.FreeHGlobal(vParPtr);
                }
            }
            else if((vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT)
                throw new Exception("Parent locator not found.");

            if((vFileParms.flags & FILE_FLAGS_HAS_PARENT) == FILE_FLAGS_HAS_PARENT &&
               vParHdr.locatorType                        == parentTypeVhdxGuid)
            {
                parentImage = new Vhdx();
                bool parentWorks = false;

                foreach(VhdxParentLocatorEntry parentEntry in vPars)
                {
                    stream.Seek(parentEntry.keyOffset + metadataOffset, SeekOrigin.Begin);
                    byte[] tmpKey = new byte[parentEntry.keyLength];
                    stream.Read(tmpKey, 0, tmpKey.Length);
                    string entryType = Encoding.Unicode.GetString(tmpKey);

                    IFilter parentFilter;
                    if(string.Compare(entryType, RELATIVE_PATH_KEY, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        stream.Seek(parentEntry.valueOffset + metadataOffset, SeekOrigin.Begin);
                        byte[] tmpVal = new byte[parentEntry.valueLength];
                        stream.Read(tmpVal, 0, tmpVal.Length);
                        string entryValue = Encoding.Unicode.GetString(tmpVal);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), entryValue));
                            if(parentFilter != null && parentImage.Open(parentFilter))
                            {
                                parentWorks = true;
                                break;
                            }
                        }
                        catch { parentWorks = false; }

                        string relEntry = Path.Combine(Path.GetDirectoryName(imageFilter.GetPath()), entryValue);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), relEntry));
                            if(parentFilter == null || !parentImage.Open(parentFilter)) continue;

                            parentWorks = true;
                            break;
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                    else if(string.Compare(entryType, VOLUME_PATH_KEY, StringComparison.OrdinalIgnoreCase) ==
                            0 ||
                            string.Compare(entryType, ABSOLUTE_WIN32_PATH_KEY, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        stream.Seek(parentEntry.valueOffset + metadataOffset, SeekOrigin.Begin);
                        byte[] tmpVal = new byte[parentEntry.valueLength];
                        stream.Read(tmpVal, 0, tmpVal.Length);
                        string entryValue = Encoding.Unicode.GetString(tmpVal);

                        try
                        {
                            parentFilter =
                                new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), entryValue));
                            if(parentFilter == null || !parentImage.Open(parentFilter)) continue;

                            parentWorks = true;
                            break;
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                }

                if(!parentWorks) throw new Exception("Image is differential but parent cannot be opened.");

                hasParent = true;
            }

            chunkRatio = (long)(Math.Pow(2, 23) * logicalSectorSize / vFileParms.blockSize);
            dataBlocks = virtualDiskSize / vFileParms.blockSize;
            if(virtualDiskSize           % vFileParms.blockSize > 0) dataBlocks++;

            long batEntries;
            if(hasParent)
            {
                long sectorBitmapBlocks = (long)dataBlocks / chunkRatio;
                if(dataBlocks % (ulong)chunkRatio > 0) sectorBitmapBlocks++;
                sectorBitmapPointers = new ulong[sectorBitmapBlocks];

                batEntries = sectorBitmapBlocks * (chunkRatio - 1);
            }
            else batEntries = (long)(dataBlocks + (dataBlocks - 1) / (ulong)chunkRatio);

            DicConsole.DebugWriteLine("VHDX plugin", "Reading BAT");

            long readChunks = 0;
            blockAllocationTable = new ulong[dataBlocks];
            byte[] batB = new byte[batEntries * 8];
            stream.Seek(batOffset, SeekOrigin.Begin);
            stream.Read(batB, 0, batB.Length);

            ulong skipSize = 0;
            for(ulong i = 0; i < dataBlocks; i++)
                if(readChunks == chunkRatio)
                {
                    if(hasParent)
                        sectorBitmapPointers[skipSize / 8] = BitConverter.ToUInt64(batB, (int)(i * 8 + skipSize));

                    readChunks =  0;
                    skipSize   += 8;
                }
                else
                {
                    blockAllocationTable[i] = BitConverter.ToUInt64(batB, (int)(i * 8 + skipSize));
                    readChunks++;
                }

            if(hasParent)
            {
                DicConsole.DebugWriteLine("VHDX plugin", "Reading Sector Bitmap");

                MemoryStream sectorBmpMs = new MemoryStream();
                foreach(ulong pt in sectorBitmapPointers)
                    switch(pt & BAT_FLAGS_MASK)
                    {
                        case SECTOR_BITMAP_NOT_PRESENT:
                            sectorBmpMs.Write(new byte[1048576], 0, 1048576);
                            break;
                        case SECTOR_BITMAP_PRESENT:
                            stream.Seek((long)((pt & BAT_FILE_OFFSET_MASK) * 1048576), SeekOrigin.Begin);
                            byte[] bmp = new byte[1048576];
                            stream.Read(bmp, 0, bmp.Length);
                            sectorBmpMs.Write(bmp, 0, bmp.Length);
                            break;
                        default:
                            if((pt & BAT_FLAGS_MASK) != 0)
                                throw new
                                    ImageNotSupportedException($"Unsupported sector bitmap block flags (0x{pt & BAT_FLAGS_MASK:X16}) found, not proceeding.");

                            break;
                    }

                sectorBitmap = sectorBmpMs.ToArray();
                sectorBmpMs.Close();
            }

            maxBlockCache  = (int)(MAX_CACHE_SIZE / vFileParms.blockSize);
            maxSectorCache = (int)(MAX_CACHE_SIZE / logicalSectorSize);

            imageStream = stream;

            sectorCache = new Dictionary<ulong, byte[]>();
            blockCache  = new Dictionary<ulong, byte[]>();

            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            imageInfo.SectorSize           = logicalSectorSize;
            imageInfo.XmlMediaType         = XmlMediaType.BlockMedia;
            imageInfo.MediaType            = MediaType.GENERIC_HDD;
            imageInfo.ImageSize            = virtualDiskSize;
            imageInfo.Sectors              = imageInfo.ImageSize / imageInfo.SectorSize;
            imageInfo.DriveSerialNumber    = page83Data.ToString();

            // TODO: Separate image application from version, need several samples.

            imageInfo.Cylinders       = (uint)(imageInfo.Sectors / 16 / 63);
            imageInfo.Heads           = 16;
            imageInfo.SectorsPerTrack = 63;

            return true;
        }
Пример #29
0
        public override int Invoke(IEnumerable <string> arguments)
        {
            List <string> extra = Options.Parse(arguments);

            if (showHelp)
            {
                Options.WriteOptionDescriptions(CommandSet.Out);
                return((int)ErrorNumber.HelpRequested);
            }

            MainClass.PrintCopyright();
            if (MainClass.Debug)
            {
                DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
            }
            if (MainClass.Verbose)
            {
                DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
            }
            Statistics.AddCommand("create-sidecar");

            if (extra.Count > 1)
            {
                DicConsole.ErrorWriteLine("Too many arguments.");
                return((int)ErrorNumber.UnexpectedArgumentCount);
            }

            if (extra.Count == 0)
            {
                DicConsole.ErrorWriteLine("Missing input image.");
                return((int)ErrorNumber.MissingArgument);
            }

            inputFile = extra[0];

            DicConsole.DebugWriteLine("Create sidecar command", "--block-size={0}", blockSize);
            DicConsole.DebugWriteLine("Create sidecar command", "--debug={0}", MainClass.Debug);
            DicConsole.DebugWriteLine("Create sidecar command", "--encoding={0}", encodingName);
            DicConsole.DebugWriteLine("Create sidecar command", "--input={0}", inputFile);
            DicConsole.DebugWriteLine("Create sidecar command", "--tape={0}", tape);
            DicConsole.DebugWriteLine("Create sidecar command", "--verbose={0}", MainClass.Verbose);

            Encoding encoding = null;

            if (encodingName != null)
            {
                try
                {
                    encoding = Claunia.Encoding.Encoding.GetEncoding(encodingName);
                    if (MainClass.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName);
                    }
                }
                catch (ArgumentException)
                {
                    DicConsole.ErrorWriteLine("Specified encoding is not supported.");
                    return((int)ErrorNumber.EncodingUnknown);
                }
            }

            if (File.Exists(inputFile))
            {
                if (tape)
                {
                    DicConsole.ErrorWriteLine("You cannot use --tape option when input is a file.");
                    return((int)ErrorNumber.ExpectedDirectory);
                }

                FiltersList filtersList = new FiltersList();
                IFilter     inputFilter = filtersList.GetFilter(inputFile);

                if (inputFilter == null)
                {
                    DicConsole.ErrorWriteLine("Cannot open specified file.");
                    return((int)ErrorNumber.CannotOpenFile);
                }

                try
                {
                    IMediaImage imageFormat = ImageFormat.Detect(inputFilter);

                    if (imageFormat == null)
                    {
                        DicConsole.WriteLine("Image format not identified, not proceeding with analysis.");
                        return((int)ErrorNumber.UnrecognizedFormat);
                    }

                    if (MainClass.Verbose)
                    {
                        DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name,
                                                    imageFormat.Id);
                    }
                    else
                    {
                        DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name);
                    }

                    try
                    {
                        if (!imageFormat.Open(inputFilter))
                        {
                            DicConsole.WriteLine("Unable to open image format");
                            DicConsole.WriteLine("No error given");
                            return((int)ErrorNumber.CannotOpenFormat);
                        }

                        DicConsole.DebugWriteLine("Analyze command", "Correctly opened image file.");
                    }
                    catch (Exception ex)
                    {
                        DicConsole.ErrorWriteLine("Unable to open image format");
                        DicConsole.ErrorWriteLine("Error: {0}", ex.Message);
                        return((int)ErrorNumber.CannotOpenFormat);
                    }

                    Statistics.AddMediaFormat(imageFormat.Format);
                    Statistics.AddFilter(inputFilter.Name);

                    Sidecar sidecarClass = new Sidecar(imageFormat, inputFile, inputFilter.Id, encoding);
                    sidecarClass.InitProgressEvent    += Progress.InitProgress;
                    sidecarClass.UpdateProgressEvent  += Progress.UpdateProgress;
                    sidecarClass.EndProgressEvent     += Progress.EndProgress;
                    sidecarClass.InitProgressEvent2   += Progress.InitProgress2;
                    sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
                    sidecarClass.EndProgressEvent2    += Progress.EndProgress2;
                    sidecarClass.UpdateStatusEvent    += Progress.UpdateStatus;
                    System.Console.CancelKeyPress     += (sender, e) =>
                    {
                        e.Cancel = true;
                        sidecarClass.Abort();
                    };
                    CICMMetadataType sidecar = sidecarClass.Create();

                    DicConsole.WriteLine("Writing metadata sidecar");

                    FileStream xmlFs =
                        new
                        FileStream(Path.Combine(Path.GetDirectoryName(inputFile) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(inputFile) + ".cicm.xml"),
                                   FileMode.CreateNew);

                    XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                    xmlSer.Serialize(xmlFs, sidecar);
                    xmlFs.Close();
                }
                catch (Exception ex)
                {
                    DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
                    DicConsole.DebugWriteLine("Analyze command", ex.StackTrace);
                    return((int)ErrorNumber.UnexpectedException);
                }
            }
            else if (Directory.Exists(inputFile))
            {
                if (!tape)
                {
                    DicConsole.ErrorWriteLine("Cannot create a sidecar from a directory.");
                    return((int)ErrorNumber.ExpectedFile);
                }

                string[]      contents = Directory.GetFiles(inputFile, "*", SearchOption.TopDirectoryOnly);
                List <string> files    = contents.Where(file => new FileInfo(file).Length % blockSize == 0).ToList();

                files.Sort(StringComparer.CurrentCultureIgnoreCase);

                Sidecar sidecarClass = new Sidecar();
                sidecarClass.InitProgressEvent    += Progress.InitProgress;
                sidecarClass.UpdateProgressEvent  += Progress.UpdateProgress;
                sidecarClass.EndProgressEvent     += Progress.EndProgress;
                sidecarClass.InitProgressEvent2   += Progress.InitProgress2;
                sidecarClass.UpdateProgressEvent2 += Progress.UpdateProgress2;
                sidecarClass.EndProgressEvent2    += Progress.EndProgress2;
                sidecarClass.UpdateStatusEvent    += Progress.UpdateStatus;
                CICMMetadataType sidecar = sidecarClass.BlockTape(Path.GetFileName(inputFile), files, blockSize);

                DicConsole.WriteLine("Writing metadata sidecar");

                FileStream xmlFs =
                    new
                    FileStream(Path.Combine(Path.GetDirectoryName(inputFile) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(inputFile) + ".cicm.xml"),
                               FileMode.CreateNew);

                XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                xmlSer.Serialize(xmlFs, sidecar);
                xmlFs.Close();
            }
            else
            {
                DicConsole.ErrorWriteLine("The specified input file cannot be found.");
            }

            return((int)ErrorNumber.NoError);
        }
Пример #30
0
 // This is only override method to simplify programming
 /// <summary>
 /// Adds a new filter to filter expression
 /// </summary>
 /// <param name="columnName">column Name</param>
 /// <param name="value">value</param>
 public void AddFilter(string columnName, object value, FilterOperatorEnum filterOperator)
 {
     FiltersList.Add(new Filter(columnName, value, filterOperator));
 }