Example #1
0
        } // End BeginProcessing

        protected override void ProcessRecord()
        {
            MasterBootRecord mbr = MasterBootRecord.Get(drivePath);

            if (mbr.PartitionTable[0].SystemID == "EFI_GPT_DISK")
            {
                if (asBytes)
                {
                    WriteObject(GuidPartitionTable.GetBytes(drivePath));
                }
                else
                {
                    WriteObject(new GuidPartitionTable(drivePath));
                }
            }
            else
            {
                if (asBytes)
                {
                    WriteObject(MasterBootRecord.GetBytes(drivePath));
                }
                else
                {
                    WriteObject(mbr);
                }
            }
        } // End ProcessRecord
Example #2
0
        public static void Main4(string[] args)
        {
            string label     = "Zap!";
            long   capacity  = 2L << 40;
            int    blockSize = 4 << 20;

            using (var diskStream = File.Create("big.vhdx"))
                using (var disk = Disk.InitializeDynamic(diskStream, Ownership.Dispose, capacity, blockSize))
                {
                    var gpt = GuidPartitionTable.Initialize(disk);
                    gpt.Create(gpt.FirstUsableSector, gpt.LastUsableSector, GuidPartitionTypes.WindowsBasicData, 0, null);
                    var  volume         = VolumeManager.GetPhysicalVolumes(disk).First();
                    uint bytesPerSector = (uint)(volume.PhysicalGeometry?.BytesPerSector ?? 512);
                    var  clusterCount   = 1 << 25;// uint.MaxValue - 16;
                    var  clusterSize    = capacity / clusterCount;
                    var  clusterBits    = (int)Math.Ceiling(Math.Log(clusterSize) / Math.Log(2));
                    if (clusterBits > 18)
                    {
                        clusterBits = 18;
                    }
                    //clusterBits = 20;
                    using (var fs = ExFatFileSystem.Format(volume, new ExFatFormatOptions {
                        SectorsPerCluster = (1u << clusterBits) / bytesPerSector
                    }, label: label))
                    { }
                }
        }
Example #3
0
        private void btnOK_Click(object sender, EventArgs e)
        {
            long firstUsableLBA            = 64; // for alignment purposes
            long reservedPartitionSizeInMB = (long)numericMicrosoftReservedPartitionSize.Value;
            long bytesAvailable            = (m_disk.TotalSectors - firstUsableLBA) * m_disk.BytesPerSector;

            if (reservedPartitionSizeInMB * 1024 * 1024 > bytesAvailable)
            {
                MessageBox.Show("Invalid Reserved Partition Size specified, not enough space on the disk.", "Error");
                return;
            }

            if (m_disk is PhysicalDisk)
            {
                bool success = ((PhysicalDisk)m_disk).ExclusiveLock();
                if (!success)
                {
                    MessageBox.Show("Failed to lock the disk.", "Error");
                    return;
                }
            }

            long reservedPartitionSizeLBA = reservedPartitionSizeInMB * 1024 * 1024 / m_disk.BytesPerSector;

            GuidPartitionTable.InitializeDisk(m_disk, firstUsableLBA, reservedPartitionSizeLBA);

            if (m_disk is PhysicalDisk)
            {
                ((PhysicalDisk)m_disk).ReleaseLock();
                ((PhysicalDisk)m_disk).UpdateProperties();
            }

            this.DialogResult = DialogResult.OK;
            this.Close();
        }
        protected override void ProcessRecord()
        {
            MasterBootRecord mbr = MasterBootRecord.Get(drivePath);

                if (mbr.PartitionTable[0].SystemID != "EFI_GPT_DISK")
                {
                    foreach (PartitionEntry partition in mbr.PartitionTable)
                    {

                        if (partition.SystemID != "EMPTY")
                        {
                            WriteObject(partition);
                        }

                        else if (partition.SystemID.Contains("EXTENDED"))
                        {
                            // Add code to parse EXTENDED partitions
                        }
                    }
                }
                else
                {
                    GuidPartitionTable gpt = new GuidPartitionTable(drivePath);
                    foreach (GuidPartitionTableEntry entry in gpt.PartitionTable)
                    {
                        WriteObject(entry);
                    }
                }
        }
        public static bool IsDynamicDisk(Disk disk)
        {
            MasterBootRecord mbr = MasterBootRecord.ReadFromDisk(disk);

            if (mbr != null)
            {
                if (mbr.PartitionTable[0].PartitionType == (byte)PartitionTypeName.DynamicData)
                {
                    return(true);
                }
                else if (mbr.IsGPTBasedDisk)
                {
                    List <GuidPartitionEntry> entries = GuidPartitionTable.ReadEntriesFromDisk(disk);
                    if (entries != null)
                    {
                        if (GuidPartitionEntryCollection.ContainsPartitionTypeGuid(entries, GPTPartition.PrivateRegionPartitionTypeGuid) &&
                            GuidPartitionEntryCollection.ContainsPartitionTypeGuid(entries, GPTPartition.PublicRegionPartitionTypeGuid))
                        {
                            return(true);
                        }
                    }
                }

                return(false);
            }
            else
            {
                // if the disk has no master boot record, it can be a dynamic disk if it has a valid PrivateHeader at sector 6
                PrivateHeader privateHeader = PrivateHeader.ReadFromDiskStart(disk);
                return(privateHeader != null);
            }
        }
Example #6
0
        public static Geometry FindGeometry(Stream stream)
        {
            var      geometryType = "UNKN";
            Geometry geometry     = null;

            // Always check GPT first as it as an MBR-encapsulated partition scheme
            if (GuidPartitionTable.Detect(stream))
            {
                geometry     = GuidPartitionTable.DetectGeometry(stream);
                geometryType = "GPT";
            }
            else if (BiosPartitionTable.IsValid(stream))
            {
                geometry     = BiosPartitionTable.DetectGeometry(stream);
                geometryType = "MBR";
            }

            if (geometry == null)
            {
                Logger.Warn("Could not find valid partitioning table to read geometry");
                return(Geometry.FromCapacity(stream.Length));
            }

            Logger.Info("{0}/BPS:{1}; SPT:{2}; HPC:{3}; CL:{4}; TS:{5}; CP:{6}", geometryType,
                        geometry.BytesPerSector, geometry.SectorsPerTrack, geometry.HeadsPerCylinder,
                        geometry.Cylinders, geometry.TotalSectorsLong, geometry.Capacity);

            return(geometry);
        }
        protected override void ProcessRecord()
        {
            MasterBootRecord mbr = MasterBootRecord.Get(drivePath);

            if (mbr.PartitionTable[0].SystemID != "EFI_GPT_DISK")
            {
                foreach (PartitionEntry partition in mbr.PartitionTable)
                {
                    if (partition.SystemID != "EMPTY")
                    {
                        WriteObject(partition);
                    }

                    else if (partition.SystemID.Contains("EXTENDED"))
                    {
                        // Add code to parse EXTENDED partitions
                    }
                }
            }
            else
            {
                GuidPartitionTable gpt = new GuidPartitionTable(drivePath);
                foreach (GuidPartitionTableEntry entry in gpt.PartitionTable)
                {
                    WriteObject(entry);
                }
            }
        }     // ProcessRecord
Example #8
0
        public static PartitionTable FindPartitionTable(Stream stream)
        {
            PartitionTable partitionTable = null;

            // Always check GPT first as it as an MBR-encapsulated partition scheme
            if (GuidPartitionTable.Detect(stream))
            {
                partitionTable = new GuidPartitionTable(
                    stream,
                    GuidPartitionTable.DetectGeometry(stream));

                Logger.Info("Detected partition table: GPT");
            }
            else if (BiosPartitionTable.IsValid(stream))
            {
                partitionTable = new BiosPartitionTable(
                    stream,
                    BiosPartitionTable.DetectGeometry(stream));

                Logger.Info("Detected partition table: MBR");
            }

            if (partitionTable == null)
            {
                Logger.Info("Could not find valid partition table");
                return(null);
            }

            return(partitionTable);
        }
Example #9
0
        private void CreateVhdx(bool allowKeepDebug, long length)
        {
            var diskStream = CreateVhdxStream(allowKeepDebug);

            Disk = Disk.InitializeDynamic(diskStream, Ownership.Dispose, length, 128 << 20);
            var gpt = GuidPartitionTable.Initialize(Disk);

            gpt.Create(gpt.FirstUsableSector, gpt.LastUsableSector, GuidPartitionTypes.WindowsBasicData, 0, null);
            var  volume         = VolumeManager.GetPhysicalVolumes(Disk).First();
            uint bytesPerSector = (uint)(volume.PhysicalGeometry?.BytesPerSector ?? 512);
            var  clusterCount   = 1 << 25;
            var  clusterSize    = length / clusterCount;
            var  clusterBits    = (int)Math.Ceiling(Math.Log(clusterSize) / Math.Log(2));

            if (clusterBits > 18)
            {
                clusterBits = 18;
            }
            else if (clusterBits < 11)
            {
                clusterBits = 11;
            }
            FileSystem = ExFatEntryFilesystem.Format(volume.Open(),
                                                     new ExFatFormatOptions {
                SectorsPerCluster = (1u << clusterBits) / bytesPerSector
            });
        }
Example #10
0
        private void ResizeVolume()
        {
            long additionalNumberOfSectors = m_additionalNumberOfBytes / m_volume.BytesPerSector;

            btnBack.Enabled = false;
            btnNext.Enabled = false;

            m_processing           = true;
            m_resizeTimer          = new System.Windows.Forms.Timer();
            m_resizeTimer.Interval = 1000;
            m_resizeTimer.Tick    += new EventHandler(ResizeTimer_Tick);
            m_resizeTimer.Start();

            Thread thread = new Thread(delegate()
            {
                // Because of performance implications, we must fill the container
                // before writing the updated TrueCrypt header to the end of the container.
                // See here for more details: http://blogs.msdn.com/b/oldnewthing/archive/2011/09/22/10215053.aspx
                long oldSize  = m_disk.Size;
                m_bytesToFill = additionalNumberOfSectors * m_disk.BytesPerSector;
                try
                {
                    m_disk.Extend(m_bytesToFill);
                }
                catch (IOException ex)
                {
                    MessageBox.Show(ex.Message, "Error");
                    this.Invoke((MethodInvoker) delegate()
                    {
                        btnBack.Enabled = true;
                        btnNext.Enabled = true;
                    });
                    return;
                }
                if (chkFillWithRandomData.Checked)
                {
                    TrueCryptResize.FillAllocatedSpaceWithData(m_disk, oldSize, ref m_bytesFilled);
                }
                MasterBootRecord mbr = MasterBootRecord.ReadFromDisk(m_disk);
                if (mbr != null && mbr.IsGPTBasedDisk)
                {
                    GuidPartitionTable.RebaseDisk(m_disk, mbr);
                }
                m_resizeStatus = TrueCryptResize.ExtendVolumeAndFileSystem(m_disk, m_password, additionalNumberOfSectors);
                // reinitialize the partition / volume
                m_partition  = VolumeSelectionHelper.GetLastPartition(m_disk);
                m_volume     = new TrueCryptVolume(m_partition, m_password);
                m_processing = false;
            });

            thread.Start();

            if (chkFillWithRandomData.Checked)
            {
                progressBarFillWithRandomData.Visible = true;
            }
        }
Example #11
0
        } // End BeginProcessing

        protected override void ProcessRecord()
        {
            if (asBytes)
            {
                WriteObject(GuidPartitionTable.GetBytes(drivePath));
            }
            else
            {
                WriteObject(new GuidPartitionTable(drivePath));
            }
        } // End ProcessRecord
        public void Initialize()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 3 * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                Assert.Equal(0, table.Count);
            }
        }
Example #13
0
        public Nand(Stream stream, Keyset keyset)
        {
            var disc = new GuidPartitionTable(stream, Geometry.Null);

            GuidPartitionInfo[] partitions = disc.Partitions.Select(x => (GuidPartitionInfo)x).ToArray();
            ProdInfo  = partitions.FirstOrDefault(x => x.Name == "PRODINFO");
            ProdInfoF = partitions.FirstOrDefault(x => x.Name == "PRODINFOF");
            Safe      = partitions.FirstOrDefault(x => x.Name == "SAFE");
            System    = partitions.FirstOrDefault(x => x.Name == "SYSTEM");
            User      = partitions.FirstOrDefault(x => x.Name == "USER");
            Keyset    = keyset;
        }
Example #14
0
        private static void CreateVirtualDisk(string filePath, int size)
        {
            using (FileStream fsStream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                using (VirtualDisk disk = DiscUtils.Vhd.Disk.InitializeDynamic(fsStream, DiscUtils.Streams.Ownership.Dispose, size * 1024 * 1024))
                {
                    GuidPartitionTable.Initialize(disk, WellKnownPartitionType.WindowsNtfs);

                    PhysicalVolumeInfo volume = VolumeManager.GetPhysicalVolumes(disk)[0];

                    NtfsFileSystem.Format(volume, "CryptoBox");
                }
        }
Example #15
0
        /// <summary>
        /// The ProcessRecord instantiates a MasterBootRecord Object
        /// and outputs all Partitions that are not of the EMPTY type
        /// </summary>
        protected override void ProcessRecord()
        {
            MasterBootRecord mbr = MasterBootRecord.Get(drivePath);

            if (mbr.PartitionTable[0].SystemId != "EFI_GPT_DISK")
            {
                WriteObject(mbr.GetPartitionTable(), true);
            }
            else
            {
                WriteObject(GuidPartitionTable.Get(drivePath).GetPartitionTable(), true);
            }
        }
        /// <summary> 
        /// The ProcessRecord instantiates a MasterBootRecord Object
        /// and outputs all Partitions that are not of the EMPTY type
        /// </summary> 
        protected override void ProcessRecord()
        {
            MasterBootRecord mbr = MasterBootRecord.Get(drivePath);

            if (mbr.PartitionTable[0].SystemID != "EFI_GPT_DISK")
            {
                WriteObject(mbr.GetPartitionTable(), true);
            }
            else
            {
                GuidPartitionTable gpt = new GuidPartitionTable(drivePath);
                WriteObject(gpt.GetPartitionTable(), true);
            }
        }
        public void CreateSmallWholeDisk()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 3 * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                int idx = table.Create(WellKnownPartitionType.WindowsFat, true);

                // Make sure the partition fills from first to last usable.
                Assert.Equal(table.FirstUsableSector, table[idx].FirstSector);
                Assert.Equal(table.LastUsableSector, table[idx].LastSector);
            }
        }
        public void CreateBySize()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 3 * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                int idx = table.Create(2 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false);

                // Make sure the partition is within 10% of the size requested.
                Assert.True((2 * 1024 * 2) * 0.9 < table[idx].SectorCount);

                Assert.Equal(table.FirstUsableSector, table[idx].FirstSector);
            }
        }
        public void CreateBySizeInGap()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 300 * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                table.Create(10 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false);

                table.Create((20 * 1024 * 1024) / 512, ((30 * 1024 * 1024) / 512) - 1, GuidPartitionTypes.WindowsBasicData, 0, "Data Partition");

                table.Create((60 * 1024 * 1024) / 512, ((70 * 1024 * 1024) / 512) - 1, GuidPartitionTypes.WindowsBasicData, 0, "Data Partition");

                int idx = table.Create(20 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false);
                Assert.Equal(((30 * 1024 * 1024) / 512), table[idx].FirstSector);
                Assert.Equal(((50 * 1024 * 1024) / 512) - 1, table[idx].LastSector);
            }
        }
        public void CreateLargeWholeDisk()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 200 * 1024L * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                int idx = table.Create(WellKnownPartitionType.WindowsFat, true);

                Assert.Equal(2, table.Partitions.Count);
                Assert.Equal(GuidPartitionTypes.MicrosoftReserved, table[0].GuidType);
                Assert.Equal(128 * 1024 * 1024, table[0].SectorCount * 512);

                // Make sure the partition fills from first to last usable, allowing for MicrosoftReserved sector.
                Assert.Equal(table[0].LastSector + 1, table[idx].FirstSector);
                Assert.Equal(table.LastUsableSector, table[idx].LastSector);
            }
        }
        public void Delete()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 10 * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                Assert.Equal(0, table.Create(1 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false));
                Assert.Equal(1, table.Create(2 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false));
                Assert.Equal(2, table.Create(3 * 1024 * 1024, WellKnownPartitionType.WindowsFat, false));

                long[] sectorCount = new long[] { table[0].SectorCount, table[1].SectorCount, table[2].SectorCount };

                table.Delete(1);

                Assert.Equal(2, table.Count);
                Assert.Equal(sectorCount[2], table[1].SectorCount);
            }
        }
Example #22
0
        bool GetGptPartitionTable(out GuidPartitionTable partitionTable)
        {
            try
            {
                partitionTable = new GuidPartitionTable(_fs, DiskGeometry);
            }
            catch (Exception)
            {
                partitionTable = null;
            }

            // CHECKME: Count refers to the number of PARTITION TABLES, not partitions?
            if (partitionTable == null || partitionTable.Count <= 0)
            {
                Debug.WriteLine("No GPT partition table detected");
                return(false);
            }

            return(true);
        }
        public void DeviceValue_Gpt()
        {
            SparseMemoryStream ms = new SparseMemoryStream();

            ms.SetLength(80 * 1024 * 1024);
            GuidPartitionTable gpt = GuidPartitionTable.Initialize(ms, Geometry.FromCapacity(ms.Length));

            gpt.Create(WellKnownPartitionType.WindowsNtfs, true);
            VolumeManager volMgr = new VolumeManager(ms);

            RegistryHive hive = RegistryHive.Create(new MemoryStream());
            Store        s    = Store.Initialize(hive.Root);
            BcdObject    obj  = s.CreateInherit(InheritType.AnyObject);

            Element el = obj.AddElement(WellKnownElement.LibraryApplicationDevice, ElementValue.ForDevice(Guid.Empty, volMgr.GetPhysicalVolumes()[0]));

            el = obj.GetElement(WellKnownElement.LibraryApplicationDevice);

            Assert.IsNotNullOrEmpty(el.Value.ToString());
        }
Example #24
0
        public Nand(Stream stream, Keyset keyset)
        {
            var disc = new GuidPartitionTable(stream, Geometry.Null);

            GuidPartitionInfo[] partitions = disc.Partitions.Select(x => (GuidPartitionInfo)x).ToArray();
            ProdInfo  = partitions.FirstOrDefault(x => x.Name == "PRODINFO");
            ProdInfoF = partitions.FirstOrDefault(x => x.Name == "PRODINFOF");
            Package2  = new GuidPartitionInfo[]
            {
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-1-Normal-Main"),
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-2-Normal-Sub"),
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-3-SafeMode-Main"),
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-4-SafeMode-Sub"),
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-5-Repair-Main"),
                partitions.FirstOrDefault(x => x.Name == "BCPKG2-6-Repair-Sub")
            };
            Safe   = partitions.FirstOrDefault(x => x.Name == "SAFE");
            System = partitions.FirstOrDefault(x => x.Name == "SYSTEM");
            User   = partitions.FirstOrDefault(x => x.Name == "USER");
            Keyset = keyset;
        }
Example #25
0
        public static PrivateHeader ReadFromGPTBasedDisk(Disk disk)
        {
            List <GuidPartitionEntry> entries = GuidPartitionTable.ReadEntriesFromDisk(disk);
            int index = GuidPartitionEntryCollection.GetIndexOfPartitionTypeGuid(entries, GPTPartition.PrivateRegionPartitionTypeGuid);
            // the private header will be located at the last sector of the private region
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(disk, (long)entries[index].LastLBA, true);

            if (privateHeader != null)
            {
                if (privateHeader.IsChecksumValid)
                {
                    return(privateHeader);
                }
                else
                {
                    // primary has invalid checksum, try secondary private header
                    long sectorIndex = (long)(privateHeader.PrivateRegionStartLBA + privateHeader.SecondaryPrivateHeaderLBA);
                    return(ReadFromDisk(disk, sectorIndex, false));
                }
            }
            return(null);
        }
        public void CreateAlignedWholeDisk()
        {
            MemoryStream ms = new MemoryStream();

            using (Disk disk = Disk.InitializeDynamic(ms, Ownership.Dispose, 200 * 1024L * 1024 * 1024))
            {
                GuidPartitionTable table = GuidPartitionTable.Initialize(disk);

                int idx = table.CreateAligned(WellKnownPartitionType.WindowsFat, true, 1024 * 1024);

                Assert.Equal(2, table.Partitions.Count);
                Assert.Equal(GuidPartitionTypes.MicrosoftReserved, table[0].GuidType);
                Assert.Equal(128 * 1024 * 1024, table[0].SectorCount * 512);

                // Make sure the partition is aligned
                Assert.Equal(0, table[idx].FirstSector % 2048);
                Assert.Equal(0, (table[idx].LastSector + 1) % 2048);

                // Ensure partition fills most of the disk
                Assert.True((table[idx].SectorCount * 512) > disk.Capacity * 0.9);
            }
        }
Example #27
0
        private static int StartInternal()
        {
            if (!GuidPartitionTable.Detect(mSourceStream))
            {
                throw new InvalidDataException(
                          "Source does not contain a supported partitioning scheme");
            }

            Logger.Debug("Detecting disc geometry of source...");
            var geometry = GuidPartitionTable.DetectGeometry(mSourceStream);

            Logger.Debug("Reading GPT partition table from source...");
            var partitionTable = new GuidPartitionTable(mSourceStream, geometry);

            if (partitionTable.Count <= 0)
            {
                return(SUCCESS); // nothing to clone here
            }
            // adjust source length
            if (FixedLengthStream.IsFixedDiskStream(mSourceStream))
            {
                mSourceStream.SetLength(geometry.Capacity);
            }

            Geometry destGeometry = null;

            if (FixedLengthStream.IsFixedDiskStream(mDestinationStream))
            {
                // If we write to a disk, we need our own geometry for that destination
                destGeometry = Geometry.FromCapacity(mDestinationStream.Length,
                                                     geometry.BytesPerSector);
            }
            else
            {
                // If we are just writing to a file, we take the most exact copy we get
                destGeometry = geometry;
            }

            // Set the new size of the destination stream
            Logger.Verbose("Updating length of destination: {0} -> {1}",
                           mDestinationStream.Length, destGeometry.Capacity);
            mDestinationStream.SetLength(destGeometry.Capacity);

            if (mFullClone)
            {
                Logger.Warn("Starting full clone...");
                CloneStream(0, 0, mSourceStream, mDestinationStream);
                return(SUCCESS);
            }

            Logger.Debug("Initializing new GPT partition table on destination...");
            var destPartitionTable = GuidPartitionTable.Initialize(mDestinationStream,
                                                                   destGeometry);

            var availableSpace = destGeometry.Capacity;
            var usedSpace      = 0L;

            // correct available space by remove unusable sectors
            // heading sectors
            availableSpace -= (destPartitionTable.FirstUsableSector
                               * destGeometry.BytesPerSector);

            // ending sectors
            availableSpace -= ((destGeometry.TotalSectorsLong
                                - destPartitionTable.FirstUsableSector
                                - destPartitionTable.LastUsableSector)
                               * destGeometry.BytesPerSector);

            // 1. calculate unresizeable space if we are required too
            if (destGeometry.Capacity < geometry.Capacity)
            {
                Logger.Info("Calculating space for new destination partitions...");

                var unresizeableSpace = 0L;
                for (int i = 0; i < partitionTable.Count; i++)
                {
                    var srcPartition       = (GuidPartitionInfo)partitionTable[i];
                    var srcPartitionStream = srcPartition.Open();
                    var srcPartitionType   = GetWellKnownPartitionType(srcPartition);
                    var srcPartitionSize   = srcPartition.SectorCount *
                                             geometry.BytesPerSector;

                    var isResizeable = true;

                    // If the file system is not support, we have to perform
                    // a byte-for-byte cloning and cannot resize this partition
                    if (!DiscHandler.IsStreamSupportedFileSystem(srcPartitionStream))
                    {
                        Logger.Verbose("Partition{0}: contains no or unsupported file system");
                        isResizeable = false;
                    }

                    // If we have a critical partition, we shouldn't resize that either
                    if (!IsSupportedPartitionType(srcPartitionType))
                    {
                        Logger.Verbose("Partition{0}: unsupported partition type");
                        isResizeable = false;
                    }

                    // if caller want to do a byte-for-byte clone, mark every partition
                    // as unresizeable
                    if (mForceExactCloning)
                    {
                        isResizeable = false;
                    }

                    Logger.Debug("Partition #{0} on source: isResizeable={1}", i,
                                 isResizeable);

                    if (!isResizeable)
                    {
                        // if it's not resizeable, account the space and continue
                        unresizeableSpace += srcPartitionSize;
                        Logger.Debug("Partition #{0} on source: unresizeable, size is {1} " +
                                     "(total unresizable {2}/used {3})", i,
                                     srcPartitionSize, unresizeableSpace, usedSpace);
                    }
                    else
                    {
                        // if it is resizeable, we have to report how much space we need
                        var srcPartitionFS = DiscHandler.GetFileSystem(srcPartitionStream);
                        usedSpace += srcPartitionFS.UsedSpace;

                        Logger.Debug("Partition #{0} on source: resizeable, space is {1} " +
                                     "(total unresizable {2}/used {3})", i,
                                     usedSpace, unresizeableSpace, usedSpace);
                    }
                }

                // reduce the dynamically available space ...
                availableSpace -= unresizeableSpace;

                // ... and assert it
                if (availableSpace < 0)
                {
                    throw new InvalidOperationException("Cannot clone, destination is " +
                                                        String.Format("{0:#,##0}", -availableSpace) + " Bytes too small");
                }

                // assert the used space too
                if (availableSpace - usedSpace < 0)
                {
                    throw new InvalidOperationException("Cannot clone, destination is " +
                                                        String.Format("{0:#,##0}", availableSpace - usedSpace) +
                                                        " Bytes too small");
                }
            }
            else
            {
                Logger.Info("Destination can contain source, no need to resize partitions!");
            }

            // 2. calculate the size for each new partition
            var destPartitionSizes = new long[partitionTable.Count];

            for (int i = 0; i < partitionTable.Count; i++)
            {
                var srcPartition       = (GuidPartitionInfo)partitionTable[i];
                var srcPartitionStream = srcPartition.Open();
                var srcPartitionType   = GetWellKnownPartitionType(srcPartition);
                var srcPartitionSize   = srcPartition.SectorCount *
                                         geometry.BytesPerSector;

                // if the destination can take more data, skip every check
                if (geometry.Capacity <= destGeometry.Capacity)
                {
                    Logger.Debug("Partition{0}: Destination can contain full partition, continue...", i);

                    destPartitionSizes[i] = srcPartitionSize;
                    availableSpace       -= srcPartitionSize;
                    continue;
                }

                var isResizeable = true;

                // If the device-systme is not support, we have to perform
                // a byte-for-byte cloning and cannot resize this partition
                if (!DiscHandler.IsStreamSupportedFileSystem(srcPartitionStream))
                {
                    isResizeable = false;
                }

                // If we have a critical partition, we shouldn't resize that either
                if (!IsSupportedPartitionType(srcPartitionType))
                {
                    isResizeable = false;
                }

                // if caller want to do a byte-for-byte clone, mark every partition
                // as unresizeable
                if (mForceExactCloning)
                {
                    isResizeable = false;
                }

                Logger.Debug("Partition #{0} on source: isResizeable={1}", i,
                             isResizeable);

                // If our friend is not resizeable, set size and stop processing it
                if (!isResizeable)
                {
                    destPartitionSizes[i] = srcPartitionSize;
                    Logger.Debug("Partition #{0} on source: unresizeable, size is {1} " +
                                 "(total available {2}/used {3})", i,
                                 usedSpace, availableSpace, usedSpace);

                    // DO NOT ALIGN <availableSpace> HERE!!!
                    // Has already been done in step 1
                    continue;
                }

                //
                // OK. If we are here, a resizeable partition awaits processing
                //
                var srcPartitionFS = DiscHandler.GetFileSystem(srcPartitionStream);

                // First we need to check if we need to reqsize the current partition.
                // For that, calculate the space that is left for future partitions. If the
                // result is OK (less or equal to the available size), we can skip
                // resizing the the current one.
                // Make sure to remove the factor of this partition from usedSpace first
                if (((usedSpace - srcPartitionFS.UsedSpace) + srcPartitionSize) <= availableSpace)
                {
                    // update usedSpace
                    usedSpace -= srcPartitionFS.UsedSpace;

                    // align availableSpace
                    availableSpace -= srcPartitionSize;

                    // we are good to skip resizing this one. assign size and early exit
                    destPartitionSizes[i] = srcPartitionSize;
                    Logger.Debug(
                        "Partition #{0} on source: resizeable, space is {1}, size is {2} " +
                        "(total available {3}/used {4})", i,
                        srcPartitionFS.UsedSpace, srcPartitionSize, availableSpace, usedSpace);

                    continue;
                }

                // So this partition is too large, let's resize it to the biggest size possible
                var maxPartitionSize = Math.Max(
                    // Occupied space is the absolute minimal space we need
                    srcPartitionFS.UsedSpace,

                    // This is the largest space possible. Take the still available space
                    // and remove still required space, while also remove the factor for
                    // the current partition
                    availableSpace - (usedSpace - srcPartitionFS.UsedSpace)
                    );

                Logger.Debug(
                    "Partition #{0} on source: resizeable, max. space is {1} " +
                    "(total available {2}/used {3})", i,
                    maxPartitionSize, availableSpace, usedSpace);

                // update usedSpace
                usedSpace -= srcPartitionFS.UsedSpace;

                // align availableSpace
                availableSpace -= maxPartitionSize;

                destPartitionSizes[i] = maxPartitionSize;
            }

            // a last assert of the available space, just to be sure
            if (availableSpace < 0)
            {
                throw new InvalidOperationException("Cannot clone, destination is " +
                                                    String.Format("{0:#.##0}", -availableSpace) + " Bytes too small");
            }

            // 2. create the new partitions with the aligned sizes
            for (int i = 0; i < partitionTable.Count; i++)
            {
                var srcPartition       = (GuidPartitionInfo)partitionTable[i];
                var srcPartitionStream = srcPartition.Open();
                var srcPartitionType   = GetWellKnownPartitionType(srcPartition);

                // manueal type adjusting
                if (NtfsFileSystem.Detect(srcPartitionStream))
                {
                    srcPartitionType = WellKnownPartitionType.WindowsNtfs;
                }
                else if (FatFileSystem.Detect(srcPartitionStream))
                {
                    srcPartitionType = WellKnownPartitionType.WindowsFat;
                }

                var destPartitionSize = destPartitionSizes[i];

                Logger.Debug("Creating partition table: #{0}; {1}/{2}@0x{3}-{4}", i,
                             srcPartition.Name, srcPartition.Identity,
                             srcPartition.FirstSector.ToString("X2"), destPartitionSize);
                destPartitionTable.Create(
                    destPartitionSize,
                    srcPartitionType,
                    true //doesn't matter on GPT tables
                    );
            }

            // 3. make sure the count of partitions is the same
            if (partitionTable.Count != destPartitionTable.Count)
            {
                throw new InvalidOperationException(
                          "Failed to create proper GUID partition table");
            }

            // 4. do the real cloning
            for (int i = 0; i < destPartitionTable.Count; i++)
            {
                var srcPartition       = (GuidPartitionInfo)partitionTable[i];
                var srcPartitionStream = srcPartition.Open();
                var srcPartitionType   = GetWellKnownPartitionType(srcPartition);

                var destPartition       = (GuidPartitionInfo)destPartitionTable[i];
                var destPartitionStream = destPartition.Open();
                var destPartitionSize   = destPartitionSizes[i];

                var requiresExactCloning = false;

                // To support all possible file-systems, perform a byte-for-byte
                // cloning if the file-system is not supported by us.
                if (!DiscHandler.IsStreamSupportedFileSystem(srcPartitionStream))
                {
                    requiresExactCloning = true;
                }

                // If we have a critical partition, we should skip that one too
                if (srcPartitionType == WellKnownPartitionType.BiosBoot ||
                    srcPartitionType == WellKnownPartitionType.EfiSystem ||
                    srcPartitionType == WellKnownPartitionType.MicrosoftReserved)
                {
                    requiresExactCloning = true;
                }

                // if caller want to do a byte-for-byte clone, let's enable it
                if (mForceExactCloning)
                {
                    requiresExactCloning = true;
                }

                if (requiresExactCloning)
                {
                    var taskId = (mTaskIdCounter++);
                    Logger.Info("[{0}/{1}] cp-fs  : {2}/{3}@0x{4}", i, taskId,
                                srcPartition.Name, srcPartition.Identity,
                                srcPartition.FirstSector.ToString("X2"));

                    CloneStream(i, taskId, srcPartitionStream, destPartitionStream);
                }
                else
                {
                    var srcPartitionFS       = DiscHandler.GetFileSystem(srcPartitionStream);
                    var srcPartitionBootCode = srcPartitionFS.ReadBootCode();

                    var destPartitionFS = DiscHandler.FormatFileSystemWithTemplate(
                        srcPartitionStream, destPartitionStream, destPartition.FirstSector,
                        destPartition.SectorCount, destPartitionSize
                        );

                    // Tracks all NTFS file IDs for hard link recognition
                    // <Source FS File ID, Destination FS File ID>
                    var ntfsHardlinkTracker = new Dictionary <uint, uint>();

                    // last clone each single with here
                    Action <DiscDirectoryInfo, DiscDirectoryInfo> cloneSrcFsToDestFs = null;
                    cloneSrcFsToDestFs = new Action <DiscDirectoryInfo, DiscDirectoryInfo>(
                        (sourceDir, destDir) =>
                    {
                        // recursive enumeration. save to create directory without checks for
                        // parent directories.
                        var taskId = (mTaskIdCounter++);
                        Logger.Info("[{0}/{1}] mk-dir : {2}", i, taskId, destDir.FullName);

                        destDir.Create();

                        // copy files if there are any
                        foreach (var sourceFile in sourceDir.GetFiles())
                        {
                            var skipCopying = false;
                            taskId          = (mTaskIdCounter++);

                            var sourceFileStream = sourceFile.Open(FileMode.Open,
                                                                   FileAccess.Read);

                            var destFileName = Path.Combine(destDir.FullName, sourceFile.Name);
                            Logger.Info("[{0}/{1}] mk-file: {2}", i, taskId,
                                        destFileName);

                            var destFileStream = destPartitionFS.OpenFile(
                                destFileName,
                                FileMode.Create, FileAccess.Write);
                            var destFile = new DiscFileInfo(destPartitionFS, destFileName);

                            // NTFS hard link handling
                            if (destPartitionFS is NtfsFileSystem)
                            {
                                var ntfsSourceFS = (NtfsFileSystem)srcPartitionFS;
                                var ntfsDestFS   = (NtfsFileSystem)destPartitionFS;

                                var sourceFileNtfsEntry = ntfsSourceFS.GetDirectoryEntry(
                                    sourceFile.FullName);
                                var sourceFileNtfsRef = ntfsSourceFS.GetFile(
                                    sourceFileNtfsEntry.Reference);

                                var destFileNtfsEntry = ntfsDestFS.GetDirectoryEntry(
                                    destFile.FullName);
                                var destFileNtfsRef = ntfsDestFS.GetFile(
                                    destFileNtfsEntry.Reference);

                                var sourceFileNtfsId = sourceFileNtfsRef.IndexInMft;

                                // check if this files was already processed once
                                if (ntfsHardlinkTracker.ContainsKey(sourceFileNtfsId))
                                {
                                    var trackedDestFileNtfsRef = ntfsDestFS.GetFile(
                                        ntfsHardlinkTracker[sourceFileNtfsId]);

                                    // if we have a hardlink-match, close the old stream
                                    // and delete the file
                                    destFileStream.Close();
                                    destFile.Delete();

                                    // then create the hardlink and mention that we don't
                                    // want/need to copy the content anymore
                                    Logger.Info("[{0}/{1}] mk-lnk : {2} => {3}", i, taskId,
                                                sourceFileNtfsRef.Names[0], destFile.FullName);

                                    ntfsDestFS.CreateHardLink(
                                        trackedDestFileNtfsRef.DirectoryEntry,
                                        destFile.FullName);
                                    skipCopying = true;
                                }
                                else
                                {
                                    Logger.Verbose("[{0}/{1}] rg-lnk : {2}#{3} -> {4}#{5}", i, taskId,
                                                   sourceFileNtfsRef.Names[0], sourceFileNtfsRef.IndexInMft,
                                                   destFileNtfsRef.Names[0], destFileNtfsRef.IndexInMft);

                                    // if not, track it
                                    ntfsHardlinkTracker.Add(sourceFileNtfsRef.IndexInMft,
                                                            destFileNtfsRef.IndexInMft);
                                }
                            }

                            if (!skipCopying)
                            {
                                Logger.Info("[{0}/{1}] cp-file: {2}", i, taskId, destFile.FullName);
                                CloneStream(i, taskId, sourceFileStream, destFileStream);
                            }

                            // clone basic file informationsdestFile
                            Logger.Verbose("[{0}/{1}] cp-meta: {2}", i, taskId, destFile.FullName);
                            destFile.Attributes        = sourceFile.Attributes;
                            destFile.CreationTime      = sourceFile.CreationTime;
                            destFile.CreationTimeUtc   = sourceFile.CreationTimeUtc;
                            destFile.IsReadOnly        = sourceFile.IsReadOnly;
                            destFile.LastAccessTime    = sourceFile.LastAccessTime;
                            destFile.LastAccessTimeUtc = sourceFile.LastAccessTimeUtc;
                            destFile.LastWriteTime     = sourceFile.LastWriteTime;
                            destFile.LastWriteTimeUtc  = sourceFile.LastWriteTimeUtc;

                            // file-system based cloning
                            if (destPartitionFS is NtfsFileSystem)
                            {
                                Logger.Verbose("[{0}/{1}] cp-fsec: {2}", i, taskId, destFile.FullName);
                                var ntfsSourceFS = (NtfsFileSystem)srcPartitionFS;
                                var ntfsDestFS   = (NtfsFileSystem)destPartitionFS;

                                var destFileNtfsEntry = ntfsDestFS.GetDirectoryEntry(
                                    destFile.FullName);
                                var destFileNtfsRef = ntfsDestFS.GetFile(
                                    destFileNtfsEntry.Reference);

                                // clone security descriptors
                                var sourceNtfsSecurity = ntfsSourceFS.GetSecurity(
                                    sourceFile.FullName);
                                ntfsDestFS.SetSecurity(destFile.FullName, sourceNtfsSecurity);

                                // clone short names if destination file is not a hard link
                                if (destFileNtfsEntry.Details.FileNameNamespace !=
                                    FileNameNamespace.Posix || !destFileNtfsRef.HasWin32OrDosName)
                                {
                                    Logger.Verbose("[{0}/{1}] cp-shrt: {2}", i, taskId, destFile.FullName);
                                    var sourceNtfsShortName = ntfsSourceFS.GetShortName(
                                        sourceFile.FullName);

                                    if (sourceNtfsShortName != null)
                                    {
                                        ntfsSourceFS.SetShortName(destFile.FullName,
                                                                  sourceNtfsShortName);
                                    }
                                }
                            }
                        }

                        // advance recursion into directories
                        foreach (var sourceDirectory in sourceDir.GetDirectories())
                        {
                            if (srcPartitionFS is FatFileSystem)
                            {
                                // Don't copy SYSTEM~1 on FAT. Just don't do it...
                                if (sourceDirectory.Name.Equals("SYSTEM~1"))
                                {
                                    continue;
                                }
                            }

                            cloneSrcFsToDestFs(sourceDirectory,
                                               new DiscDirectoryInfo(
                                                   destPartitionFS,
                                                   Path.Combine(destDir.FullName, sourceDirectory.Name)
                                                   )
                                               );
                        }
                    });

                    cloneSrcFsToDestFs(srcPartitionFS.Root, destPartitionFS.Root);
                }
            }

            return(SUCCESS);
        }
 internal GuidPartitionInfo(GuidPartitionTable table, GptEntry entry)
 {
     _table = table;
     _entry = entry;
 }
Example #29
0
        protected override void ProcessRecord()
        {
            PSObject    diskObject = null;
            VirtualDisk disk       = null;

            if (InputObject != null)
            {
                diskObject = InputObject;
                disk       = diskObject.BaseObject as VirtualDisk;
            }
            if (disk == null && string.IsNullOrEmpty(LiteralPath))
            {
                WriteError(new ErrorRecord(
                               new ArgumentException("No disk specified"),
                               "NoDiskSpecified",
                               ErrorCategory.InvalidArgument,
                               null));
                return;
            }

            if (disk == null)
            {
                diskObject = SessionState.InvokeProvider.Item.Get(LiteralPath)[0];
                VirtualDisk vdisk = diskObject.BaseObject as VirtualDisk;

                if (vdisk == null)
                {
                    WriteError(new ErrorRecord(
                                   new ArgumentException("Path specified is not a virtual disk"),
                                   "BadDiskSpecified",
                                   ErrorCategory.InvalidArgument,
                                   null));
                    return;
                }

                disk = vdisk;
            }

            PartitionTable pt = null;

            if (VolumeManager == VolumeManagerType.Bios)
            {
                pt = BiosPartitionTable.Initialize(disk);
            }
            else
            {
                pt = GuidPartitionTable.Initialize(disk);
            }

            if (Signature != 0)
            {
                disk.Signature = Signature;
            }
            else
            {
                disk.Signature = new Random().Next();
            }

            // Changed volume layout, force a rescan
            var drive = diskObject.Properties["PSDrive"].Value as VirtualDiskPSDriveInfo;

            if (drive != null)
            {
                drive.RescanVolumes();
            }


            WriteObject(disk);
        }
Example #30
0
        private static int StartInternal()
        {
            Info("====== Starting Partition Table Checks ======");

            DoingCheck();
            if (!GuidPartitionTable.Detect(mLeftStream))
            {
                Error("Could not find valid GUID Partition Table on left stream");
                return(ERROR);
            }
            Info("Found valid GUID Partition Table on left stream");

            DoingCheck();
            if (!GuidPartitionTable.Detect(mRightStream))
            {
                Error("Could not find valid GUID Partition Table on right stream");
                return(ERROR);
            }
            Info("Found valid GUID Partition Table on right stream");

            var leftGeometry       = GuidPartitionTable.DetectGeometry(mLeftStream);
            var leftPartitionTable = new GuidPartitionTable(mLeftStream, leftGeometry);

            var rightGeometry       = GuidPartitionTable.DetectGeometry(mRightStream);
            var rightPartitionTable = new GuidPartitionTable(mRightStream, rightGeometry);

            // adjust length
            if (FixedLengthStream.IsFixedDiskStream(mLeftStream))
            {
                mLeftStream.SetLength(leftGeometry.Capacity);
            }
            if (FixedLengthStream.IsFixedDiskStream(mRightStream))
            {
                mRightStream.SetLength(leftGeometry.Capacity);
            }

            DoingCheck();
            if (leftPartitionTable.Count != rightPartitionTable.Count)
            {
                Error("Non-equal count of partitions (Left: {0}, Right {1})",
                      leftPartitionTable.Count, rightPartitionTable.Count);
                return(ERROR);
            }
            Info("Matching count of partitions ({0})", leftPartitionTable.Count);

            Info("======    Starting Partition Checks    ======");
            for (int i = 0; i < leftPartitionTable.Count; i++)
            {
                var leftPartition  = leftPartitionTable[i];
                var rightPartition = rightPartitionTable[i];

                DoingCheck();
                if (leftPartition.BiosType != rightPartition.BiosType)
                {
                    Error("Partition{0}: Non-matching BIOS partition type " +
                          "(Left: 0x{1:X2}, Right: 0x{2:X2})", i,
                          leftPartition.BiosType, rightPartition.BiosType);
                    return(ERROR);
                }
                Info("Partition{0}: Matching BIOS partition type (0x{1:X2})", i,
                     leftPartition.BiosType);

                DoingCheck();
                if (leftPartition.GuidType != rightPartition.GuidType)
                {
                    Error("Partition{0}: Non-matching GUID partition type " +
                          "(Left: {1}, Right: {2})", i,
                          leftPartition.GuidType, rightPartition.GuidType);
                    return(ERROR);
                }
                Info("Partition{0}: Matching GUID partition type ({1})", i,
                     leftPartition.GuidType);

                DoingCheck();
                if (leftPartition.VolumeType != rightPartition.VolumeType)
                {
                    Error("Partition{0}: Non-matching physical partition type " +
                          "(Left: 0x{1:X}/{1}, Right: 0x{2:X}/{2})", i,
                          leftPartition.VolumeType, rightPartition.VolumeType);
                    return(ERROR);
                }
                Info("Partition{0}: Matching physical partition type (0x{1:X}/{1})", i,
                     leftPartition.VolumeType);
            }

            Info("======   Starting File System Checks   ======");
            for (int i = 0; i < leftPartitionTable.Count; i++)
            {
                var leftPartition        = leftPartitionTable[i];
                var leftPartitionStream  = leftPartition.Open();
                var leftPartitionValidFS =
                    DiscHandler.IsStreamSupportedFileSystem(leftPartitionStream);

                var rightPartition        = rightPartitionTable[i];
                var rightPartitionStream  = rightPartition.Open();
                var rightPartitionValidFS =
                    DiscHandler.IsStreamSupportedFileSystem(rightPartitionStream);

                if (!leftPartitionValidFS)
                {
                    Info("Partition{0}: Left stream does not contain supported file system", i);
                }

                if (!rightPartitionValidFS)
                {
                    Info("Partition{0}: Right stream does not contain supported file system", i);
                }

                if (leftPartitionValidFS && rightPartitionValidFS)
                {
                    Info("Partition{0}: Running checks on file-system layer", i);

                    var leftFileSystem  = DiscHandler.GetFileSystem(leftPartitionStream);
                    var rightFileSystem = DiscHandler.GetFileSystem(rightPartitionStream);

                    if (leftFileSystem.UsedSpace != rightFileSystem.UsedSpace)
                    {
                        Warning("Partition{0}: Non-matching amount of used space " +
                                "(Left: 0x{1:X2}/{2}, Right: 0x{3:X2}/{4})", i,
                                leftFileSystem.UsedSpace,
                                FormatBytes(leftFileSystem.UsedSpace, 3),
                                rightFileSystem.UsedSpace,
                                FormatBytes(rightFileSystem.UsedSpace, 3));
                    }

                    Action <DiscDirectoryInfo, DiscDirectoryInfo> traverseFiles = null;
                    traverseFiles = new Action <DiscDirectoryInfo, DiscDirectoryInfo>(
                        (leftBaseDir, rightBaseDir) =>
                    {
                        var leftFiles  = leftBaseDir.GetFiles();
                        var rightFiles = rightBaseDir.GetFiles();

                        DoingCheck();
                        if (leftFiles.Length != rightFiles.Length)
                        {
                            Warning("Partition{0}: Non-matching amount of files in {1} " +
                                    "(Left: {2}, Right: {3})", i, leftBaseDir.FullName,
                                    leftFiles.Length, rightFiles.Length);

                            var checkedFiles = new List <DiscFileInfo>();
                            foreach (var leftFile in leftFiles)
                            {
                                var matchingRightFiles = rightFiles.Where(f =>
                                                                          f.Name == leftFile.Name);

                                DoingCheck();
                                if (matchingRightFiles.Count() >= 2)
                                {
                                    Error("Partition{0}:{1}: Found invalid file entry in " +
                                          "file system, looked up file with {2} results",
                                          i, leftBaseDir.FullName, matchingRightFiles.Count());
                                }

                                DoingCheck();
                                if (matchingRightFiles.Count() == 1)
                                {
                                    checkedFiles.Add(leftFile);

                                    var rightFile       = matchingRightFiles.First();
                                    var leftFileStream  = leftFile.OpenRead();
                                    var rightFileStream = rightFile.OpenRead();

                                    DoByteComparison(leftFileStream, rightFileStream, i,
                                                     leftFile.FullName);

                                    leftFileStream.Close();
                                    rightFileStream.Close();

                                    DoMetaDataComparison(i, leftFile, rightFile);

                                    if (leftFileSystem is NtfsFileSystem)
                                    {
                                        DoNtfsSecurityComparison(i,
                                                                 (NtfsFileSystem)leftFileSystem, leftFile,
                                                                 (NtfsFileSystem)rightFileSystem, rightFile);
                                    }
                                }
                                else     // == 0
                                {
                                    Warning("Partition{0}:{1}: Found file in left " +
                                            "file system only", i, leftFile.FullName);
                                }
                            }

                            foreach (var rightFile in rightFiles)
                            {
                                DoingCheck();
                                if (!checkedFiles.Any(f => f.Name == rightFile.Name))
                                {
                                    Warning("Partition{0}:{1}: Found file in right " +
                                            "file system only", i, rightFile.FullName);
                                }
                            }
                        }
                        else
                        {
                            for (int j = 0; j < leftFiles.Length; j++)
                            {
                                var leftFile        = leftFiles[j];
                                var leftFileStream  = leftFile.OpenRead();
                                var rightFile       = rightFiles[j];
                                var rightFileStream = rightFile.OpenRead();

                                DoByteComparison(leftFileStream, rightFileStream, i,
                                                 leftFile.FullName);

                                leftFileStream.Close();
                                rightFileStream.Close();

                                DoMetaDataComparison(i, leftFile, rightFile);

                                if (leftFileSystem is NtfsFileSystem)
                                {
                                    DoNtfsSecurityComparison(i,
                                                             (NtfsFileSystem)leftFileSystem, leftFile,
                                                             (NtfsFileSystem)rightFileSystem, rightFile);
                                }
                            }
                        }

                        // advance recursion into directories
                        var leftDirectories  = leftBaseDir.GetDirectories();
                        var rightDirectories = rightBaseDir.GetDirectories();

                        DoingCheck();
                        if (leftDirectories.Length != rightDirectories.Length)
                        {
                            Warning("Partition{0}: Non-matching amount of directories in {1} " +
                                    "(Left: {2}, Right: {3})", i, leftBaseDir.FullName,
                                    leftDirectories.Length, rightDirectories.Length);

                            var checkedDirectories = new List <DiscFileInfo>();
                            foreach (var leftDirectory in leftDirectories)
                            {
                                var matchingRightFiles = rightDirectories.Where(f =>
                                                                                f.Name == leftDirectory.Name);

                                DoingCheck();
                                if (matchingRightFiles.Count() >= 2)
                                {
                                    Error("Partition{0}:{1}: Found invalid directory entry in " +
                                          "file system, looked up file with {2} results",
                                          i, leftBaseDir.FullName, matchingRightFiles.Count());
                                }

                                DoingCheck();
                                if (matchingRightFiles.Count() == 1)
                                {
                                    var rightDirectory = matchingRightFiles.First();
                                    traverseFiles(leftDirectory, rightDirectory);
                                }
                                else     // == 0
                                {
                                    Warning("Partition{0}:{1}: Found directory in left " +
                                            "file system only", i, leftDirectory.FullName);
                                }
                            }

                            foreach (var rightFile in rightDirectories)
                            {
                                DoingCheck();
                                if (!checkedDirectories.Any(f => f.Name == rightFile.Name))
                                {
                                    Warning("Partition{0}:{1}: Found file in right " +
                                            "file system only", i, rightFile.FullName);
                                }
                            }
                        }
                        else
                        {
                            for (int j = 0; j < leftDirectories.Length; j++)
                            {
                                var leftDirectory  = leftDirectories[j];
                                var rightDirectory = leftDirectories[j];

                                traverseFiles(leftDirectory, rightDirectory);
                            }
                        }
                    });

                    traverseFiles(leftFileSystem.Root, rightFileSystem.Root);
                }
                else
                {
                    Info("Partition{0}: Running checks on data layer", i);

                    // This can can be run because of the fact that we don't resize
                    // unsupported partitions but run byte-for-byte copy with them
                    DoByteComparison(leftPartitionStream, rightPartitionStream, i, null);
                }
            }

            return(SUCCESS);
        }