public static bool TryGetMbrInfo(DiskImage hdd, out MbrPartitioningInfo info)
        {
            info = default;

            Debug.Assert(Unsafe.SizeOf <MbrHeader>() == MBR_SIZE);

            var       headerBytes = hdd.ReadBytes(0, MBR_SIZE);
            MbrHeader header      = MemoryMarshal.Cast <byte, MbrHeader>(new Span <byte>(headerBytes))[0];

            if (header.BootSig != MbrHeader.MbrMagic)
            {
                return(false);
            }

            bool hasGpt = false;

            for (int i = 0; i < 4; i++)
            {
                var part = header.GetPartition(i);
                hasGpt |= part.Type == MbrPartitionType.GptProtective;
            }

            //MBR paritioned disks often contain boot loaders in non-paritioned space.
            //So to be safe copy all the data.
            List <FileExtent> partitions = new List <FileExtent>()
            {
                new FileExtent(MBR_SIZE, hdd.Length - MBR_SIZE),
            };

            info = new MbrPartitioningInfo(headerBytes, partitions, hasGpt);
            return(true);
        }
        static bool getParitionInfo(DiskImage image, out IPartitioningInfo info)
        {
            GptPartitioningInfo gpt;

            if (GptPartitioningInfo.TryGetGptInfo(image, out gpt))
            {
                info = gpt;
                return(true);
            }
            MbrPartitioningInfo mbr;

            if (MbrPartitioningInfo.TryGetMbrInfo(image, out mbr))
            {
                info = mbr;
                return(true);
            }
            info = null;
            return(false);
        }
        public static bool TryGetGptInfo(DiskImage hdd, out GptPartitioningInfo info)
        {
            Debug.Assert(Unsafe.SizeOf <GptHeader>() == CurrentHeaderSize);
            Debug.Assert(Unsafe.SizeOf <PartitionEntry>() == PartitionEntrySize);

            info = null;

            MbrPartitioningInfo mbr;

            if (!MbrPartitioningInfo.TryGetMbrInfo(hdd, out mbr))
            {
                return(false);
            }
            if (!mbr.HasGpt)
            {
                return(false);
            }

            var primaryHeader = LoadHeader(hdd, SectorSize);
            var backupHeader  = LoadHeader(hdd, hdd.Length - SectorSize);

            //Make sure the headers agree on each other's locations
            if (primaryHeader.BackupLba != (hdd.Length / SectorSize) - 1)
            {
                throw new Exception("Location of backup LBA in primary header is wrong.");
            }
            if (backupHeader.BackupLba != 1)
            {
                throw new Exception("Location of primary LBA in backup header is wrong.");
            }

            //make sure the headers match
            if (primaryHeader.NumberOfPartitions != backupHeader.NumberOfPartitions)
            {
                throw new Exception("Primary and backup GPT headers do not agree on the number of partitions.");
            }
            if (primaryHeader.FirstUsableLba != backupHeader.FirstUsableLba || primaryHeader.LastUsableLba != backupHeader.LastUsableLba)
            {
                throw new Exception("Primary and backup GPT headers do not agree on the usable area of the disk.");
            }
            if (primaryHeader.DiskGuid != backupHeader.DiskGuid)
            {
                throw new Exception("Primary and backup GPT headers do not agree on the disk GUID.");
            }

            //TODO: Make sure the partition entries do not intersect with the usable area of the disk or the GptHeader.

            //at this point we have varified that the header for both the primary and backup
            //GPT are correct. We are only going to load paritions from the primary header.

            //Make sure the minimum space for partition entries was reserved
            if (primaryHeader.FirstUsableLba < (Program.RoundUp(MinimumSizeForParitionEntriesArea, SectorSize) / SectorSize + 2)) //extra two sectors for protective MBR and GPT header
            {
                throw new Exception("Not enough space for primary parition entries was reserved.");
            }
            if (primaryHeader.LastUsableLba >= Program.RoundDown(hdd.Length - MinimumSizeForParitionEntriesArea - SectorSize, SectorSize) / SectorSize)
            {
                throw new Exception("Not enough space for backup parition entries was reserved.");
            }


            byte[] partitionEntriesBytes = new byte[primaryHeader.NumberOfPartitions * PartitionEntrySize];
            hdd.ReadBytes(new Span <byte>(partitionEntriesBytes), primaryHeader.StartingLbaOfPartitionEntries * SectorSize);
            uint actualEntriesCrc = Crc32Algorithm.Compute(partitionEntriesBytes);

            if (primaryHeader.CrcOfPartitionEntry != actualEntriesCrc)
            {
                throw new Exception("CRC of partition entries not correct.");
            }

            var partitions = new List <PartitionEntry>();

            foreach (PartitionEntry part in MemoryMarshal.Cast <byte, PartitionEntry>(new ReadOnlySpan <byte>(partitionEntriesBytes)))
            {
                if (part.Type == Guid.Empty)
                {
                    continue;
                }
                if (part.FirstLba < primaryHeader.FirstUsableLba || part.LastLba > primaryHeader.LastUsableLba)
                {
                    throw new Exception($"Parition '{part.Name}' is outside the usable space.");
                }
                partitions.Add(part);
            }

            partitions.Sort((a, b) => a.FirstLba.CompareTo(b.FirstLba));

            //TODO: make sure the paritions do not intersect with each other

            info = new GptPartitioningInfo(mbr, partitions);
            return(true);
        }
 private GptPartitioningInfo(MbrPartitioningInfo protectiveMbr, List <PartitionEntry> partitions)
 {
     this.mProtectiveMbr = protectiveMbr;
     this.mPartitions    = partitions;
 }