public static void InitializeDisk(Disk disk, long firstUsableLBA, List <GuidPartitionEntry> partitionEntries)
        {
            MasterBootRecord mbr = new MasterBootRecord();

            mbr.DiskSignature = (uint)new Random().Next(Int32.MaxValue);
            mbr.PartitionTable[0].PartitionTypeName = PartitionTypeName.EFIGPT;
            mbr.PartitionTable[0].FirstSectorLBA    = 1;
            mbr.PartitionTable[0].SectorCountLBA    = (uint)Math.Min(disk.TotalSectors - firstUsableLBA, UInt32.MaxValue);
            mbr.MBRSignature = 0xAA55;
            MasterBootRecord.WriteToDisk(disk, mbr);

            const int DefaultNumberOfEntries       = 128;
            const int DefaultSizeOfEntry           = 128;
            int       partitionEntriesLength       = DefaultNumberOfEntries * DefaultSizeOfEntry;
            long      partitionEntriesPrimaryLBA   = 2;
            long      partitionEntriesSecondaryLBA = disk.TotalSectors - 1 - partitionEntriesLength / disk.BytesPerSector;

            GuidPartitionTableHeader primaryHeader = new GuidPartitionTableHeader();

            primaryHeader.HeaderSize               = 92;
            primaryHeader.CurrentLBA               = 1;
            primaryHeader.BackupLBA                = (ulong)(disk.TotalSectors - 1);
            primaryHeader.DiskGuid                 = Guid.NewGuid();
            primaryHeader.FirstUsableLBA           = (ulong)firstUsableLBA;
            primaryHeader.LastUsableLBA            = (ulong)(partitionEntriesSecondaryLBA - 1);
            primaryHeader.PartitionEntriesLBA      = (ulong)partitionEntriesPrimaryLBA;
            primaryHeader.NumberOfPartitionEntries = DefaultNumberOfEntries;
            primaryHeader.SizeOfPartitionEntry     = DefaultSizeOfEntry;
            byte[] partitionTableEntries = new byte[partitionEntriesLength];

            for (int index = 0; index < partitionEntries.Count; index++)
            {
                partitionEntries[index].WriteBytes(partitionTableEntries, index * DefaultSizeOfEntry);
            }
            primaryHeader.PartitionArrayCRC32 = CRC32.Compute(partitionTableEntries);

            GuidPartitionTableHeader secondaryHeader = primaryHeader.Clone();

            secondaryHeader.CurrentLBA          = (ulong)(disk.TotalSectors - 1);
            secondaryHeader.BackupLBA           = 1;
            secondaryHeader.PartitionEntriesLBA = (ulong)partitionEntriesSecondaryLBA;

            GuidPartitionTableHeader.WriteToDisk(disk, primaryHeader);
            disk.WriteSectors(partitionEntriesPrimaryLBA, partitionTableEntries);
            GuidPartitionTableHeader.WriteToDisk(disk, secondaryHeader);
            disk.WriteSectors(partitionEntriesSecondaryLBA, partitionTableEntries);
        }
        /// <summary>
        /// Read valid GPT (header and partition table), and write it to the correct locations at the beginning and end of the disk.
        /// The protective MBR partition size will be updated as well.
        /// </summary>
        public static void RebaseDisk(Disk disk, MasterBootRecord mbr)
        {
            GuidPartitionTableHeader  primaryHeader   = GuidPartitionTableHeader.ReadPrimaryFromDisk(disk);
            GuidPartitionTableHeader  secondaryHeader = null;
            List <GuidPartitionEntry> entries         = null;

            if (primaryHeader != null)
            {
                entries = ReadEntriesFromDisk(disk, primaryHeader);
            }

            if (primaryHeader == null || entries == null)
            {
                secondaryHeader = GuidPartitionTableHeader.ReadSecondaryFromDisk(disk, primaryHeader);
                if (secondaryHeader != null)
                {
                    entries = ReadEntriesFromDisk(disk, secondaryHeader);
                }
            }

            if (entries == null)
            {
                throw new InvalidDataException("Both the primary and secondary GPT are corrupted");
            }

            if (secondaryHeader != null)
            {
                primaryHeader = secondaryHeader.Clone();
            }
            else
            {
                secondaryHeader = primaryHeader.Clone();
            }

            byte[] partitionTableEntries = GuidPartitionEntryCollection.GetBytes(primaryHeader, entries);

            int  partitionEntriesLength       = (int)(primaryHeader.NumberOfPartitionEntries * primaryHeader.SizeOfPartitionEntry);
            long partitionEntriesPrimaryLBA   = 2;
            long partitionEntriesSecondaryLBA = disk.TotalSectors - 1 - partitionEntriesLength / disk.BytesPerSector;

            // If the disk was trimmed or converted to GPT without a secondary header, we don't want to overwrite partition data
            bool writeSecondaryGPT = primaryHeader.LastUsableLBA <= (ulong)(partitionEntriesSecondaryLBA - 1);

            primaryHeader.CurrentLBA          = 1;
            primaryHeader.BackupLBA           = (ulong)(disk.TotalSectors - 1);
            primaryHeader.PartitionEntriesLBA = (ulong)partitionEntriesPrimaryLBA;
            primaryHeader.LastUsableLBA       = (ulong)(partitionEntriesSecondaryLBA - 1);

            secondaryHeader.CurrentLBA          = (ulong)(disk.TotalSectors - 1);
            secondaryHeader.BackupLBA           = 1;
            secondaryHeader.PartitionEntriesLBA = (ulong)partitionEntriesSecondaryLBA;
            secondaryHeader.LastUsableLBA       = (ulong)(partitionEntriesSecondaryLBA - 1);

            // Update protective MBR partition size
            uint firstUsableLBA = mbr.PartitionTable[0].FirstSectorLBA;

            mbr.PartitionTable[0].SectorCountLBA = (uint)Math.Min(disk.TotalSectors - firstUsableLBA, UInt32.MaxValue);
            MasterBootRecord.WriteToDisk(disk, mbr);

            // Write primary and secondary GPT
            GuidPartitionTableHeader.WriteToDisk(disk, primaryHeader);
            disk.WriteSectors(partitionEntriesPrimaryLBA, partitionTableEntries);
            if (writeSecondaryGPT)
            {
                GuidPartitionTableHeader.WriteToDisk(disk, secondaryHeader);
                disk.WriteSectors(partitionEntriesSecondaryLBA, partitionTableEntries);
            }
        }