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