Example #1
0
        public static List <DiskExtent> GetUnallocatedExtents(Disk disk)
        {
            MasterBootRecord  mbr    = MasterBootRecord.ReadFromDisk(disk);
            List <DiskExtent> result = new List <DiskExtent>();

            if (mbr == null)
            {
                result.Add(new DiskExtent(disk, 0, disk.Size));
                return(result);
            }
            else
            {
                long dataRegionStartSector;
                long dataRegionSize;
                if (!mbr.IsGPTBasedDisk)
                {
                    dataRegionStartSector = MBRDiskFirstUsableSector;
                    dataRegionSize        = Math.Min(disk.Size, UInt32.MaxValue * disk.BytesPerSector) - dataRegionStartSector;
                }
                else
                {
                    GuidPartitionTableHeader gptHeader = GuidPartitionTableHeader.ReadFromDisk(disk);
                    dataRegionStartSector = (long)gptHeader.FirstUsableLBA;
                    dataRegionSize        = (long)(gptHeader.LastUsableLBA - gptHeader.FirstUsableLBA + 1) * disk.BytesPerSector;
                }

                List <Partition>  partitions  = GetPartitions(disk);
                List <DiskExtent> usedExtents = new List <DiskExtent>();
                foreach (Partition partition in partitions)
                {
                    usedExtents.Add(partition.Extent);
                }
                return(DiskExtentsHelper.GetUnallocatedExtents(disk, dataRegionStartSector, dataRegionSize, usedExtents));
            }
        }
        private static List <GuidPartitionEntry> ReadEntriesFromDisk(Disk disk, GuidPartitionTableHeader header)
        {
            int bufferLength  = (int)(header.NumberOfPartitionEntries * header.SizeOfPartitionEntry);
            int sectorsToRead = (int)Math.Ceiling((double)bufferLength / disk.BytesPerSector);

            byte[] buffer = disk.ReadSectors((long)header.PartitionEntriesLBA, sectorsToRead);
            if (buffer.Length > bufferLength)
            {
                buffer = ByteReader.ReadBytes(buffer, 0, bufferLength);
            }
            uint expectedCRC32 = CRC32.Compute(buffer);

            if (header.PartitionArrayCRC32 != expectedCRC32)
            {
                return(null);
            }

            int offset = 0;
            List <GuidPartitionEntry> result = new List <GuidPartitionEntry>();

            for (int index = 0; index < header.NumberOfPartitionEntries; index++)
            {
                GuidPartitionEntry entry = new GuidPartitionEntry(buffer, offset);
                entry.EntryIndex = index;
                // Unused entries use Guid.Empty as PartitionTypeGuid
                if (entry.PartitionTypeGuid != Guid.Empty)
                {
                    result.Add(entry);
                }
                offset += (int)header.SizeOfPartitionEntry;
            }
            return(result);
        }
Example #3
0
        public static void ExtendGPTPartition(GPTPartition partition, long numberOfAdditionalExtentSectors)
        {
            Disk disk = partition.Disk;
            GuidPartitionTableHeader primaryHeader   = GuidPartitionTableHeader.ReadPrimaryFromDisk(disk);
            GuidPartitionTableHeader secondaryHeader = GuidPartitionTableHeader.ReadSecondaryFromDisk(disk, primaryHeader);

            if (primaryHeader == null || secondaryHeader == null)
            {
                throw new NotImplementedException("Cannot extend GPT disk with corrupted header");
            }

            if (primaryHeader.PartitionArrayCRC32 != secondaryHeader.PartitionArrayCRC32)
            {
                throw new NotImplementedException("Cannot extend GPT disk with mismatched partition arrays");
            }

            List <GuidPartitionEntry> entries = GuidPartitionTable.ReadEntriesFromDisk(disk);

            foreach (GuidPartitionEntry entry in entries)
            {
                if ((long)entry.FirstLBA == partition.FirstSector)
                {
                    entry.LastLBA += (ulong)numberOfAdditionalExtentSectors;
                    GuidPartitionEntry.WriteToDisk(disk, primaryHeader, entry);
                    GuidPartitionEntry.WriteToDisk(disk, secondaryHeader, entry);
                    break;
                }
            }

            primaryHeader.PartitionArrayCRC32 = GuidPartitionTable.ComputePartitionArrayCRC32(disk, primaryHeader);
            GuidPartitionTableHeader.WriteToDisk(disk, primaryHeader);
            secondaryHeader.PartitionArrayCRC32 = GuidPartitionTable.ComputePartitionArrayCRC32(disk, secondaryHeader);
            GuidPartitionTableHeader.WriteToDisk(disk, secondaryHeader);
        }
        public static uint ComputePartitionArrayCRC32(Disk disk, GuidPartitionTableHeader header)
        {
            int sectorsToRead = (int)Math.Ceiling((double)header.NumberOfPartitionEntries * header.SizeOfPartitionEntry / disk.BytesPerSector);

            byte[] buffer = disk.ReadSectors((long)header.PartitionEntriesLBA, sectorsToRead);
            return(CRC32.Compute(buffer));
        }
        public static void WriteToDisk(Disk disk, GuidPartitionTableHeader header, GuidPartitionEntry entry)
        {
            long sectorIndex      = (long)header.PartitionEntriesLBA + entry.EntryIndex * header.SizeOfPartitionEntry / disk.BytesPerSector;
            int  entriesPerSector = (int)(disk.BytesPerSector / header.SizeOfPartitionEntry);
            int  indexInSector    = (int)(entry.EntryIndex % entriesPerSector);

            byte[] buffer = disk.ReadSector(sectorIndex);
            entry.WriteBytes(buffer, indexInSector * (int)header.SizeOfPartitionEntry);
            disk.WriteSectors(sectorIndex, buffer);
        }
        public static byte[] GetBytes(GuidPartitionTableHeader header, List <GuidPartitionEntry> entries)
        {
            byte[] buffer = new byte[header.NumberOfPartitionEntries * header.SizeOfPartitionEntry];
            int    count  = (int)Math.Min(header.NumberOfPartitionEntries, entries.Count);

            for (int index = 0; index < count; index++)
            {
                entries[index].WriteBytes(buffer, index * (int)header.SizeOfPartitionEntry);
            }
            return(buffer);
        }
        public static GuidPartitionTableHeader ReadFromDisk(Disk disk)
        {
            GuidPartitionTableHeader header = ReadPrimaryFromDisk(disk);

            if (header == null)
            {
                // try reading secondary GPT header:
                header = ReadSecondaryFromDisk(disk, header);
            }
            return(header);
        }
 /// <summary>
 /// primaryHeader can be NULL
 /// </summary>
 public static GuidPartitionTableHeader ReadSecondaryFromDisk(Disk disk, GuidPartitionTableHeader primaryHeader)
 {
     if (primaryHeader == null)
     {
         return(ReadFromDisk(disk, disk.TotalSectors - 1));
     }
     else
     {
         // The secondary GPT header is supposed to be located in the last sector of the disk,
         // however, this is not always the case, the disk could have been extended or the backup GPT scheme was written in an uncommon way
         return(ReadFromDisk(disk, (long)primaryHeader.BackupLBA));
     }
 }
        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);
        }
Example #10
0
        /// <returns>Number of bytes</returns>
        public static long GetMaximumSizeToExtendGPTPartition(GPTPartition partition)
        {
            GuidPartitionTableHeader header = GuidPartitionTableHeader.ReadFromDisk(partition.Disk);
            long partitonEndSector          = partition.FirstSector + partition.Size / partition.BytesPerSector;
            // Prevent from extending beyond the secondary GPT header / partition array
            long max = ((long)header.LastUsableLBA + 1) * partition.BytesPerSector - (partition.FirstSector * partition.BytesPerSector + partition.Size);

            List <GuidPartitionEntry> entries = GuidPartitionTable.ReadEntriesFromDisk(partition.Disk);

            foreach (GuidPartitionEntry entry in entries)
            {
                if ((long)entry.FirstLBA > partition.FirstSector)
                {
                    long available = ((long)entry.FirstLBA - partition.FirstSector) * partition.BytesPerSector - partition.Size;
                    max = Math.Min(max, available);
                }
            }

            return(max);
        }
        public static List <GuidPartitionEntry> ReadEntriesFromDisk(Disk disk)
        {
            GuidPartitionTableHeader primaryHeader = GuidPartitionTableHeader.ReadPrimaryFromDisk(disk);

            if (primaryHeader != null)
            {
                List <GuidPartitionEntry> result = ReadEntriesFromDisk(disk, primaryHeader);
                if (result != null)
                {
                    return(result);
                }
            }

            GuidPartitionTableHeader secondaryHeader = GuidPartitionTableHeader.ReadSecondaryFromDisk(disk, primaryHeader);

            if (secondaryHeader != null)
            {
                return(ReadEntriesFromDisk(disk, secondaryHeader));
            }
            return(null);
        }
        /// <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);
            }
        }
        public GuidPartitionTableHeader Clone()
        {
            GuidPartitionTableHeader clone = (GuidPartitionTableHeader)this.MemberwiseClone();

            return(clone);
        }
 /// <summary>
 /// GuidPartitionTableHeader.CurrentLBA Will be used as the writing location,
 /// This will help preserve cases where the backup GPT scheme was written in an uncommon way.
 /// </summary>
 public static void WriteToDisk(Disk disk, GuidPartitionTableHeader header)
 {
     disk.WriteSectors((long)header.CurrentLBA, header.GetBytes(disk.BytesPerSector));
 }