GuidPartitionTableHeader.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using Utilities;
  11. namespace DiskAccessLibrary
  12. {
  13. public class GuidPartitionTableHeader
  14. {
  15. public const string GuidPartitionTableSignature = "EFI PART";
  16. public const uint GuidPartitionTableRevision = 0x10000;
  17. public const long GuidPartitionTableHeaderLBA = 1;
  18. private string Signature;
  19. public uint Revision;
  20. public uint HeaderSize;
  21. private uint CRC32;
  22. // public uint Reserved
  23. public ulong CurrentLBA;
  24. public ulong BackupLBA;
  25. public ulong FirstUsableLBA; // for partitions
  26. public ulong LastUsableLBA; // for partitions
  27. public Guid DiskGuid;
  28. public ulong PartitionEntriesLBA;
  29. public uint NumberOfPartitionEntries;
  30. public uint SizeOfPartitionEntry;
  31. public uint PartitionArrayCRC32;
  32. public GuidPartitionTableHeader()
  33. {
  34. Signature = GuidPartitionTableSignature;
  35. Revision = GuidPartitionTableRevision;
  36. }
  37. public GuidPartitionTableHeader(byte[] buffer)
  38. {
  39. Signature = ByteReader.ReadAnsiString(buffer, 0, 8);
  40. Revision = LittleEndianConverter.ToUInt32(buffer, 8);
  41. HeaderSize = LittleEndianConverter.ToUInt32(buffer, 12);
  42. CRC32 = LittleEndianConverter.ToUInt32(buffer, 16);
  43. CurrentLBA = LittleEndianConverter.ToUInt64(buffer, 24);
  44. BackupLBA = LittleEndianConverter.ToUInt64(buffer, 32);
  45. FirstUsableLBA = LittleEndianConverter.ToUInt64(buffer, 40);
  46. LastUsableLBA = LittleEndianConverter.ToUInt64(buffer, 48);
  47. DiskGuid = LittleEndianConverter.ToGuid(buffer, 56);
  48. PartitionEntriesLBA = LittleEndianConverter.ToUInt64(buffer, 72);
  49. NumberOfPartitionEntries = LittleEndianConverter.ToUInt32(buffer, 80);
  50. SizeOfPartitionEntry = LittleEndianConverter.ToUInt32(buffer, 84);
  51. PartitionArrayCRC32 = LittleEndianConverter.ToUInt32(buffer, 88);
  52. }
  53. public byte[] GetBytes(int bytesPerSector)
  54. {
  55. byte[] buffer = new byte[bytesPerSector];
  56. ByteWriter.WriteAnsiString(buffer, 0, Signature, 8);
  57. LittleEndianWriter.WriteUInt32(buffer, 8, Revision);
  58. LittleEndianWriter.WriteUInt32(buffer, 12, HeaderSize);
  59. LittleEndianWriter.WriteUInt64(buffer, 24, CurrentLBA);
  60. LittleEndianWriter.WriteUInt64(buffer, 32, BackupLBA);
  61. LittleEndianWriter.WriteUInt64(buffer, 40, FirstUsableLBA);
  62. LittleEndianWriter.WriteUInt64(buffer, 48, LastUsableLBA);
  63. LittleEndianWriter.WriteGuidBytes(buffer, 56, DiskGuid);
  64. LittleEndianWriter.WriteUInt64(buffer, 72, PartitionEntriesLBA);
  65. LittleEndianWriter.WriteUInt32(buffer, 80, NumberOfPartitionEntries);
  66. LittleEndianWriter.WriteUInt32(buffer, 84, SizeOfPartitionEntry);
  67. LittleEndianWriter.WriteUInt32(buffer, 88, PartitionArrayCRC32);
  68. CRC32 = ComputeCRC32(buffer);
  69. LittleEndianWriter.WriteUInt32(buffer, 16, CRC32);
  70. return buffer;
  71. }
  72. public GuidPartitionTableHeader Clone()
  73. {
  74. GuidPartitionTableHeader clone = (GuidPartitionTableHeader)this.MemberwiseClone();
  75. return clone;
  76. }
  77. public static uint ComputeCRC32(byte[] buffer)
  78. {
  79. uint headerSize = LittleEndianConverter.ToUInt32(buffer, 12);
  80. byte[] temp = new byte[headerSize];
  81. Array.Copy(buffer, temp, headerSize);
  82. uint crc32 = 0;
  83. LittleEndianWriter.WriteUInt32(temp, 16, crc32);
  84. return Utilities.CRC32.Compute(temp);
  85. }
  86. public static GuidPartitionTableHeader ReadFromDisk(Disk disk)
  87. {
  88. GuidPartitionTableHeader header = ReadPrimaryFromDisk(disk);
  89. if (header == null)
  90. {
  91. // try reading secondary GPT header:
  92. header = ReadSecondaryFromDisk(disk, header);
  93. }
  94. return header;
  95. }
  96. public static GuidPartitionTableHeader ReadPrimaryFromDisk(Disk disk)
  97. {
  98. return ReadFromDisk(disk, GuidPartitionTableHeaderLBA);
  99. }
  100. /// <summary>
  101. /// primaryHeader can be NULL
  102. /// </summary>
  103. public static GuidPartitionTableHeader ReadSecondaryFromDisk(Disk disk, GuidPartitionTableHeader primaryHeader)
  104. {
  105. if (primaryHeader == null)
  106. {
  107. return ReadFromDisk(disk, disk.TotalSectors - 1);
  108. }
  109. else
  110. {
  111. // The secondary GPT header is supposed to be located in the last sector of the disk,
  112. // however, this is not always the case, the disk could have been extended or the backup GPT scheme was written in an uncommon way
  113. return ReadFromDisk(disk, (long)primaryHeader.BackupLBA);
  114. }
  115. }
  116. public static GuidPartitionTableHeader ReadFromDisk(Disk disk, long sectorIndex)
  117. {
  118. byte[] buffer = disk.ReadSector(sectorIndex);
  119. string signature = ByteReader.ReadAnsiString(buffer, 0, 8);
  120. uint revision = LittleEndianConverter.ToUInt32(buffer, 8);
  121. uint crc32 = LittleEndianConverter.ToUInt32(buffer, 16);
  122. if (signature == GuidPartitionTableSignature &&
  123. revision == GuidPartitionTableRevision &&
  124. crc32 == ComputeCRC32(buffer))
  125. {
  126. return new GuidPartitionTableHeader(buffer);
  127. }
  128. return null;
  129. }
  130. /// <summary>
  131. /// GuidPartitionTableHeader.CurrentLBA Will be used as the writing location,
  132. /// This will help preserve cases where the backup GPT scheme was written in an uncommon way.
  133. /// </summary>
  134. public static void WriteToDisk(Disk disk, GuidPartitionTableHeader header)
  135. {
  136. disk.WriteSectors((long)header.CurrentLBA, header.GetBytes(disk.BytesPerSector));
  137. }
  138. }
  139. }