gpt.c 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. gpt.c
  5. Abstract:
  6. This module contains GPT support for the partition library.
  7. Author:
  8. Evan Green 6-Feb-2014
  9. Environment:
  10. Any
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "partlibp.h"
  16. //
  17. // ---------------------------------------------------------------- Definitions
  18. //
  19. //
  20. // ------------------------------------------------------ Data Type Definitions
  21. //
  22. //
  23. // ----------------------------------------------- Internal Function Prototypes
  24. //
  25. KSTATUS
  26. PartpGptReadEntries (
  27. PPARTITION_CONTEXT Context,
  28. PVOID Block,
  29. PGPT_PARTITION_ENTRY *Entries,
  30. PVOID *EntriesAllocation,
  31. PULONG EntryCount,
  32. PULONG ValidCount
  33. );
  34. KSTATUS
  35. PartpGptWriteBlocks (
  36. PPARTITION_CONTEXT Context,
  37. ULONGLONG BlockAddress,
  38. ULONG BlockCount,
  39. PVOID Buffer
  40. );
  41. KSTATUS
  42. PartpGptConvertPartitionTypeToGuid (
  43. PARTITION_TYPE PartitionType,
  44. UCHAR TypeGuid[GPT_GUID_SIZE]
  45. );
  46. BOOL
  47. PartpGptIsGuidEmpty (
  48. UCHAR Guid[GPT_GUID_SIZE]
  49. );
  50. BOOL
  51. PartpGptAreGuidsEqual (
  52. UCHAR FirstGuid[GPT_GUID_SIZE],
  53. UCHAR SecondGuid[GPT_GUID_SIZE]
  54. );
  55. //
  56. // -------------------------------------------------------------------- Globals
  57. //
  58. //
  59. // Define the mapping of known partition types. The first entry here is
  60. // assumed to be the empty GUID.
  61. //
  62. PARTITION_TYPE_GUID_MAPPING PartTypeGuidToPartitionTypeTable[] = {
  63. {GPT_PARTITION_TYPE_EMPTY, PartitionTypeEmpty},
  64. {GPT_PARTITION_TYPE_EFI_SYSTEM, PartitionTypeEfiSystem},
  65. {GPT_PARTITION_TYPE_MINOCA, PartitionTypeMinoca},
  66. };
  67. //
  68. // ------------------------------------------------------------------ Functions
  69. //
  70. BOOL
  71. PartpGptIsProtectiveMbr (
  72. PARTITION_TABLE_ENTRY Entry[PARTITION_TABLE_SIZE]
  73. )
  74. /*++
  75. Routine Description:
  76. This routine determines if the given partition table is a protective MBR
  77. for a GPT disk.
  78. Arguments:
  79. Entry - Supplies the array of MBR partition table entries.
  80. Return Value:
  81. TRUE if this is a GPT disk.
  82. FALSE if this is not a GPT disk.
  83. --*/
  84. {
  85. BOOL FoundEfiEntry;
  86. ULONG Index;
  87. FoundEfiEntry = FALSE;
  88. for (Index = 0; Index < PARTITION_TABLE_SIZE; Index += 1) {
  89. if (Entry->SystemId == PARTITION_ID_EMPTY) {
  90. continue;
  91. }
  92. if ((Entry->SystemId == PARTITION_ID_EFI_GPT) &&
  93. (Entry->StartingLba == 1)) {
  94. FoundEfiEntry = TRUE;
  95. //
  96. // Anything other than empty, GPT, or EFI system partitions mean it's
  97. // not a GPT disk.
  98. //
  99. } else {
  100. return FALSE;
  101. }
  102. }
  103. return FoundEfiEntry;
  104. }
  105. KSTATUS
  106. PartpGptEnumeratePartitions (
  107. PPARTITION_CONTEXT Context
  108. )
  109. /*++
  110. Routine Description:
  111. This routine is called to read the partition information from the
  112. GPT-formatted disk and enumerate the list of partitions.
  113. Arguments:
  114. Context - Supplies a pointer to the initialized context.
  115. Return Value:
  116. STATUS_SUCCESS if the partition information could be determined. There
  117. could still be zero partitions in this case.
  118. STATUS_NO_ELIGIBLE_DEVICES if the partition table is invalid.
  119. Error codes on device read or allocation failure.
  120. --*/
  121. {
  122. ULONG AllocationSize;
  123. PVOID Block;
  124. PVOID BlockAllocation;
  125. ULONG BlockSize;
  126. ULONG EntryCount;
  127. ULONG EntryIndex;
  128. PGPT_PARTITION_ENTRY GptEntry;
  129. ULONGLONG GptHeaderLba;
  130. PGPT_HEADER Header;
  131. PPARTITION_INFORMATION Information;
  132. ULONG InformationIndex;
  133. PPARTITION_INFORMATION Partition;
  134. PGPT_PARTITION_ENTRY PartitionEntries;
  135. PVOID PartitionEntriesAllocation;
  136. KSTATUS Status;
  137. ULONG ValidCount;
  138. BlockSize = Context->BlockSize;
  139. Information = NULL;
  140. PartitionEntriesAllocation = NULL;
  141. BlockAllocation = PartpAllocateIo(Context, BlockSize, &Block);
  142. if (BlockAllocation == NULL) {
  143. Status = STATUS_INSUFFICIENT_RESOURCES;
  144. goto GptEnumeratePartitionsEnd;
  145. }
  146. //
  147. // Read LBA 1 to get the GPT header.
  148. //
  149. GptHeaderLba = 1;
  150. Status = Context->ReadFunction(Context, GptHeaderLba, Block);
  151. if (!KSUCCESS(Status)) {
  152. goto GptEnumeratePartitionsEnd;
  153. }
  154. //
  155. // Validate the header here. If it wasn't valid, try the header that's at
  156. // the last sector of the disk.
  157. //
  158. Status = PartpGptReadEntries(Context,
  159. Block,
  160. &PartitionEntries,
  161. &PartitionEntriesAllocation,
  162. &EntryCount,
  163. &ValidCount);
  164. if ((!KSUCCESS(Status)) && (Context->BlockCount != 0)) {
  165. GptHeaderLba = Context->BlockCount - 1;
  166. Status = Context->ReadFunction(Context, GptHeaderLba, Block);
  167. if (!KSUCCESS(Status)) {
  168. goto GptEnumeratePartitionsEnd;
  169. }
  170. Status = PartpGptReadEntries(Context,
  171. Block,
  172. &PartitionEntries,
  173. &PartitionEntriesAllocation,
  174. &EntryCount,
  175. &ValidCount);
  176. }
  177. //
  178. // If neither header is valid, abort.
  179. //
  180. if (!KSUCCESS(Status)) {
  181. goto GptEnumeratePartitionsEnd;
  182. }
  183. Header = (PGPT_HEADER)Block;
  184. ASSERT(sizeof(Context->DiskIdentifier) >= sizeof(Header->DiskGuid));
  185. RtlCopyMemory(Context->DiskIdentifier,
  186. Header->DiskGuid,
  187. sizeof(Context->DiskIdentifier));
  188. if (ValidCount == 0) {
  189. Status = STATUS_SUCCESS;
  190. goto GptEnumeratePartitionsEnd;
  191. }
  192. //
  193. // Allocate space for the partition information structures.
  194. //
  195. AllocationSize = sizeof(PARTITION_INFORMATION) * ValidCount;
  196. Information = Context->AllocateFunction(AllocationSize);
  197. if (Information == NULL) {
  198. Status = STATUS_INSUFFICIENT_RESOURCES;
  199. goto GptEnumeratePartitionsEnd;
  200. }
  201. RtlZeroMemory(Information, AllocationSize);
  202. //
  203. // Loop through and convert all the GPT entries to partition information
  204. // structures.
  205. //
  206. InformationIndex = 0;
  207. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  208. GptEntry = &(PartitionEntries[EntryIndex]);
  209. if ((GptEntry->FirstLba == 0) ||
  210. (GptEntry->LastLba == 0)) {
  211. continue;
  212. }
  213. if (PartpGptIsGuidEmpty(GptEntry->TypeGuid) != FALSE) {
  214. continue;
  215. }
  216. ASSERT(InformationIndex < ValidCount);
  217. Partition = &(Information[InformationIndex]);
  218. Partition->StartOffset = GptEntry->FirstLba;
  219. Partition->EndOffset = GptEntry->LastLba + 1;
  220. Partition->Number = EntryIndex + 1;
  221. ASSERT(PARTITION_IDENTIFIER_SIZE >= GPT_GUID_SIZE);
  222. RtlCopyMemory(Partition->TypeIdentifier,
  223. GptEntry->TypeGuid,
  224. sizeof(Partition->TypeIdentifier));
  225. RtlCopyMemory(Partition->Identifier,
  226. GptEntry->Guid,
  227. sizeof(Partition->TypeIdentifier));
  228. Partition->PartitionType =
  229. PartpGptConvertTypeGuidToPartitionType(GptEntry->TypeGuid);
  230. InformationIndex += 1;
  231. }
  232. ASSERT(InformationIndex == ValidCount);
  233. Context->Partitions = Information;
  234. Context->PartitionCount = InformationIndex;
  235. Information = NULL;
  236. Status = STATUS_SUCCESS;
  237. GptEnumeratePartitionsEnd:
  238. if (PartitionEntriesAllocation != NULL) {
  239. Context->FreeFunction(PartitionEntriesAllocation);
  240. }
  241. if (Information != NULL) {
  242. Context->FreeFunction(Information);
  243. }
  244. if (BlockAllocation != NULL) {
  245. Context->FreeFunction(BlockAllocation);
  246. }
  247. return Status;
  248. }
  249. KSTATUS
  250. PartpGptWritePartitionLayout (
  251. PPARTITION_CONTEXT Context,
  252. PPARTITION_INFORMATION Partitions,
  253. ULONG PartitionCount,
  254. BOOL CleanMbr
  255. )
  256. /*++
  257. Routine Description:
  258. This routine writes a GPT partition layout to the disk. This usually wipes
  259. out all data on the disk.
  260. Arguments:
  261. Context - Supplies a pointer to the partition context.
  262. Partitions - Supplies a pointer to the new partition layout.
  263. PartitionCount - Supplies the number of partitions in the new layout.
  264. CleanMbr - Supplies a boolean indicating if only the partition entries of
  265. the MBR should be modified (FALSE) or if the whole MBR should be
  266. zeroed before being written (TRUE).
  267. Return Value:
  268. Status code.
  269. --*/
  270. {
  271. ULONG BlockSize;
  272. ULONG EntriesBlockCount;
  273. ULONG EntriesSize;
  274. ULONG EntryCount;
  275. ULONGLONG FirstUsableBlock;
  276. PGPT_PARTITION_ENTRY GptEntries;
  277. PGPT_PARTITION_ENTRY GptEntry;
  278. PGPT_HEADER Header;
  279. ULONGLONG LastUsableBlock;
  280. PPARTITION_TABLE_ENTRY MbrEntry;
  281. PPARTITION_INFORMATION Partition;
  282. ULONG PartitionIndex;
  283. PUSHORT Pointer16;
  284. KSTATUS Status;
  285. PVOID Table;
  286. PVOID TableAllocation;
  287. TableAllocation = NULL;
  288. if (Context->BlockCount < 12) {
  289. Status = STATUS_INVALID_CONFIGURATION;
  290. goto GptWritePartitionLayoutEnd;
  291. }
  292. if (Context->FillRandomFunction == NULL) {
  293. Status = STATUS_NOT_INITIALIZED;
  294. goto GptWritePartitionLayoutEnd;
  295. }
  296. BlockSize = Context->BlockSize;
  297. ASSERT(POWER_OF_2(BlockSize));
  298. EntriesSize = PartitionCount * sizeof(GPT_PARTITION_ENTRY);
  299. if (EntriesSize < GPT_MINIMUM_PARTITION_ENTRIES_SIZE) {
  300. EntriesSize = GPT_MINIMUM_PARTITION_ENTRIES_SIZE;
  301. }
  302. EntriesSize = ALIGN_RANGE_UP(EntriesSize, BlockSize);
  303. EntriesBlockCount = (EntriesSize / BlockSize);
  304. EntryCount = EntriesSize / sizeof(GPT_PARTITION_ENTRY);
  305. //
  306. // Make sure the first usable block is aligned to 4KB so that disks that
  307. // internally use 4KB sectors but report 512 byte sectors don't have
  308. // performance issues doing read/modify/writes all the time within the
  309. // partition. Remember that the first two blocks are reserved for the
  310. // protective MBR and the GPT header.
  311. //
  312. FirstUsableBlock = 2 + EntriesBlockCount;
  313. FirstUsableBlock = ALIGN_RANGE_UP(FirstUsableBlock * BlockSize,
  314. GPT_PARTITION_ALIGNMENT);
  315. FirstUsableBlock /= BlockSize;
  316. //
  317. // Allocate space for the entire table.
  318. //
  319. TableAllocation = PartpAllocateIo(Context,
  320. FirstUsableBlock * BlockSize,
  321. &Table);
  322. if (TableAllocation == NULL) {
  323. Status = STATUS_INSUFFICIENT_RESOURCES;
  324. goto GptWritePartitionLayoutEnd;
  325. }
  326. RtlZeroMemory(Table, FirstUsableBlock * BlockSize);
  327. Header = (PGPT_HEADER)(Table + BlockSize);
  328. LastUsableBlock = (Context->BlockCount - 1) - (EntriesBlockCount + 1);
  329. //
  330. // First fill out the partition entries array.
  331. //
  332. GptEntries = (PGPT_PARTITION_ENTRY)(Table + (2 * BlockSize));
  333. for (PartitionIndex = 0;
  334. PartitionIndex < PartitionCount;
  335. PartitionIndex += 1) {
  336. GptEntry = &(GptEntries[PartitionIndex]);
  337. Partition = &(Partitions[PartitionIndex]);
  338. //
  339. // Convert the type enum if it's set, otherwise, just copy the type
  340. // identifier GUID.
  341. //
  342. if ((Partition->PartitionType != PartitionTypeInvalid) &&
  343. (Partition->PartitionType != PartitionTypeUnknown)) {
  344. Status = PartpGptConvertPartitionTypeToGuid(
  345. Partition->PartitionType,
  346. GptEntry->TypeGuid);
  347. if (!KSUCCESS(Status)) {
  348. goto GptWritePartitionLayoutEnd;
  349. }
  350. } else {
  351. ASSERT(sizeof(GptEntry->TypeGuid) <=
  352. sizeof(Partition->TypeIdentifier));
  353. RtlCopyMemory(GptEntry->TypeGuid,
  354. Partition->TypeIdentifier,
  355. sizeof(GptEntry->TypeGuid));
  356. }
  357. //
  358. // Copy the GUID if it's not the empty one. If it is, create a random
  359. // one.
  360. //
  361. if (PartpGptIsGuidEmpty(Partition->Identifier) != FALSE) {
  362. Context->FillRandomFunction(Context,
  363. GptEntry->Guid,
  364. sizeof(GptEntry->Guid));
  365. } else {
  366. ASSERT(sizeof(GptEntry->Guid) <= sizeof(Partition->Identifier));
  367. RtlCopyMemory(GptEntry->Guid,
  368. Partition->Identifier,
  369. sizeof(GptEntry->Guid));
  370. }
  371. GptEntry->Attributes = Partition->Attributes;
  372. GptEntry->FirstLba = Partition->StartOffset;
  373. if (Partition->EndOffset != 0) {
  374. GptEntry->LastLba = Partition->EndOffset - 1;
  375. }
  376. if ((GptEntry->FirstLba != GptEntry->LastLba) &&
  377. ((GptEntry->FirstLba < FirstUsableBlock) ||
  378. (GptEntry->FirstLba > LastUsableBlock) ||
  379. (GptEntry->LastLba < FirstUsableBlock) ||
  380. (GptEntry->LastLba > LastUsableBlock))) {
  381. Status = STATUS_INVALID_CONFIGURATION;
  382. goto GptWritePartitionLayoutEnd;
  383. }
  384. }
  385. //
  386. // Create the backup copy first as requested by the specification.
  387. //
  388. Header->Signature = GPT_HEADER_SIGNATURE;
  389. Header->Revision = GPT_HEADER_REVISION_1;
  390. Header->HeaderSize = sizeof(GPT_HEADER);
  391. Header->CurrentLba = Context->BlockCount - 1;
  392. Header->BackupLba = 1;
  393. Header->FirstUsableLba = FirstUsableBlock;
  394. Header->LastUsableLba = LastUsableBlock;
  395. if (PartpGptIsGuidEmpty(Context->DiskIdentifier) != FALSE) {
  396. Context->FillRandomFunction(Context,
  397. Header->DiskGuid,
  398. sizeof(Header->DiskGuid));
  399. } else {
  400. RtlCopyMemory(Header->DiskGuid,
  401. Context->DiskIdentifier,
  402. sizeof(Header->DiskGuid));
  403. }
  404. Header->PartitionEntriesLba = Header->CurrentLba - EntriesBlockCount;
  405. Header->PartitionEntryCount = EntryCount;
  406. Header->PartitionEntrySize = sizeof(GPT_PARTITION_ENTRY);
  407. Header->PartitionArrayCrc32 = RtlComputeCrc32(
  408. 0,
  409. GptEntries,
  410. EntryCount * sizeof(GPT_PARTITION_ENTRY));
  411. Header->HeaderCrc32 = RtlComputeCrc32(0, Header, Header->HeaderSize);
  412. //
  413. // Write out the header, then the partition entries array.
  414. //
  415. Status = PartpGptWriteBlocks(Context, Header->CurrentLba, 1, Header);
  416. if (!KSUCCESS(Status)) {
  417. goto GptWritePartitionLayoutEnd;
  418. }
  419. Status = PartpGptWriteBlocks(Context,
  420. Header->PartitionEntriesLba,
  421. EntriesBlockCount,
  422. GptEntries);
  423. if (!KSUCCESS(Status)) {
  424. goto GptWritePartitionLayoutEnd;
  425. }
  426. //
  427. // Create the protective MBR.
  428. //
  429. Pointer16 = (PUSHORT)(Table + PARTITION_SIGNATURE_OFFSET);
  430. *Pointer16 = PARTITION_SIGNATURE;
  431. MbrEntry = (PPARTITION_TABLE_ENTRY)(Table + PARTITION_TABLE_OFFSET);
  432. MbrEntry->StartingSector = 1;
  433. MbrEntry->SystemId = PARTITION_ID_EFI_GPT;
  434. MbrEntry->EndingHead = 0xFE;
  435. MbrEntry->EndingSector = 0xFF;
  436. MbrEntry->EndingCylinder = 0xFF;
  437. MbrEntry->StartingLba = 1;
  438. if (Context->BlockCount - 1 > MAX_ULONG) {
  439. MbrEntry->SectorCount = MAX_ULONG;
  440. } else {
  441. MbrEntry->SectorCount = Context->BlockCount - 1;
  442. }
  443. //
  444. // Fix up the GPT header for the beginning of the disk.
  445. //
  446. Header->HeaderCrc32 = 0;
  447. Header->BackupLba = Header->CurrentLba;
  448. Header->CurrentLba = 1;
  449. Header->PartitionEntriesLba = 2;
  450. Header->HeaderCrc32 = RtlComputeCrc32(0, Header, Header->HeaderSize);
  451. //
  452. // Finally, write out the MBR, GPT header, and entries all at once.
  453. //
  454. Status = PartpGptWriteBlocks(Context, 0, FirstUsableBlock, Table);
  455. if (!KSUCCESS(Status)) {
  456. goto GptWritePartitionLayoutEnd;
  457. }
  458. GptWritePartitionLayoutEnd:
  459. if (TableAllocation != NULL) {
  460. Context->FreeFunction(TableAllocation);
  461. }
  462. return Status;
  463. }
  464. PARTITION_TYPE
  465. PartpGptConvertTypeGuidToPartitionType (
  466. UCHAR TypeGuid[GPT_GUID_SIZE]
  467. )
  468. /*++
  469. Routine Description:
  470. This routine converts a partition type GUID into a partition type to the
  471. best of its abilities.
  472. Arguments:
  473. TypeGuid - Supplies a pointer to the partition type GUID.
  474. Return Value:
  475. Returns a partition type for this type GUID.
  476. --*/
  477. {
  478. ULONG EntryCount;
  479. ULONG EntryIndex;
  480. BOOL Match;
  481. EntryCount = sizeof(PartTypeGuidToPartitionTypeTable) /
  482. sizeof(PartTypeGuidToPartitionTypeTable[0]);
  483. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  484. Match = PartpGptAreGuidsEqual(
  485. PartTypeGuidToPartitionTypeTable[EntryIndex].TypeGuid,
  486. TypeGuid);
  487. if (Match != FALSE) {
  488. return PartTypeGuidToPartitionTypeTable[EntryIndex].PartitionType;
  489. }
  490. }
  491. return PartitionTypeUnknown;
  492. }
  493. //
  494. // --------------------------------------------------------- Internal Functions
  495. //
  496. KSTATUS
  497. PartpGptReadEntries (
  498. PPARTITION_CONTEXT Context,
  499. PVOID Block,
  500. PGPT_PARTITION_ENTRY *Entries,
  501. PVOID *EntriesAllocation,
  502. PULONG EntryCount,
  503. PULONG ValidCount
  504. )
  505. /*++
  506. Routine Description:
  507. This routine determines if the GPT header is valid, and reads the
  508. partition entries if so.
  509. Arguments:
  510. Context - Supplies a pointer to the initialized context.
  511. Block - Supplies a pointer to the block containing the GPT header.
  512. Entries - Supplies a pointer where a pointer will be returned to the
  513. GPT partition entries on success.
  514. EntriesAllocation - Supplies a pointer where a pointer to the actual
  515. entries allocation will be returned. This is what should be passed to
  516. the free function.
  517. EntryCount - Supplies a pointer where the number of GPT entries in the
  518. array will be returned.
  519. ValidCount - Supplies a pointer where the number of non-empty partitions
  520. will be returned on success.
  521. Return Value:
  522. STATUS_SUCCESS if the partition information could be determined. There
  523. could still be zero partitions in this case.
  524. STATUS_NO_ELIGIBLE_DEVICES if the header is invalid.
  525. Other errors on read or allocation failures.
  526. --*/
  527. {
  528. ULONG AllocationSize;
  529. ULONG BlockCount;
  530. ULONG BlockIndex;
  531. ULONG BlockSize;
  532. ULONG ComputedCrc;
  533. PGPT_PARTITION_ENTRY Entry;
  534. ULONG EntryIndex;
  535. PGPT_HEADER Header;
  536. ULONG HeaderCrc;
  537. PGPT_PARTITION_ENTRY PartitionEntries;
  538. PVOID PartitionEntriesAllocation;
  539. ULONG PartitionEntryCount;
  540. KSTATUS Status;
  541. ULONG ValidPartitionCount;
  542. Status = STATUS_NO_ELIGIBLE_DEVICES;
  543. BlockSize = Context->BlockSize;
  544. Header = (PGPT_HEADER)Block;
  545. PartitionEntriesAllocation = NULL;
  546. PartitionEntryCount = 0;
  547. ValidPartitionCount = 0;
  548. if (Header->Signature != GPT_HEADER_SIGNATURE) {
  549. goto GptValidateHeaderEnd;
  550. }
  551. if (Header->Revision < GPT_HEADER_REVISION_1) {
  552. goto GptValidateHeaderEnd;
  553. }
  554. //
  555. // Validate that the reported sizes of the header and partition entry are
  556. // reasonable.
  557. //
  558. if ((Header->HeaderSize < sizeof(GPT_HEADER)) ||
  559. (Header->HeaderSize > Context->BlockSize)) {
  560. goto GptValidateHeaderEnd;
  561. }
  562. if ((Header->PartitionEntrySize < sizeof(GPT_PARTITION_ENTRY)) ||
  563. (Header->PartitionEntrySize > Context->BlockSize)) {
  564. goto GptValidateHeaderEnd;
  565. }
  566. //
  567. // Validate that the partition entries live outside the usable data.
  568. //
  569. if ((Header->PartitionEntriesLba >= Header->FirstUsableLba) &&
  570. (Header->PartitionEntriesLba <= Header->LastUsableLba)) {
  571. goto GptValidateHeaderEnd;
  572. }
  573. HeaderCrc = Header->HeaderCrc32;
  574. Header->HeaderCrc32 = 0;
  575. ComputedCrc = RtlComputeCrc32(0, Header, Header->HeaderSize);
  576. Header->HeaderCrc32 = HeaderCrc;
  577. if (ComputedCrc != HeaderCrc) {
  578. goto GptValidateHeaderEnd;
  579. }
  580. //
  581. // Allocate space for the partition entries.
  582. //
  583. PartitionEntryCount = Header->PartitionEntryCount;
  584. AllocationSize = PartitionEntryCount * Header->PartitionEntrySize;
  585. if (AllocationSize == 0) {
  586. Status = STATUS_SUCCESS;
  587. goto GptValidateHeaderEnd;
  588. }
  589. AllocationSize = ALIGN_RANGE_UP(AllocationSize, BlockSize);
  590. PartitionEntriesAllocation = PartpAllocateIo(Context,
  591. AllocationSize,
  592. (PVOID *)&PartitionEntries);
  593. if (PartitionEntriesAllocation == NULL) {
  594. Status = STATUS_INSUFFICIENT_RESOURCES;
  595. goto GptValidateHeaderEnd;
  596. }
  597. BlockCount = AllocationSize / BlockSize;
  598. for (BlockIndex = 0; BlockIndex < BlockCount; BlockIndex += 1) {
  599. Status = Context->ReadFunction(
  600. Context,
  601. Header->PartitionEntriesLba + BlockIndex,
  602. (PUCHAR)PartitionEntries + (BlockIndex * BlockSize));
  603. if (!KSUCCESS(Status)) {
  604. goto GptValidateHeaderEnd;
  605. }
  606. }
  607. //
  608. // Validate the CRC for the partition entries.
  609. //
  610. ComputedCrc = RtlComputeCrc32(
  611. 0,
  612. PartitionEntries,
  613. Header->PartitionEntryCount * Header->PartitionEntrySize);
  614. if (ComputedCrc != Header->PartitionArrayCrc32) {
  615. Status = STATUS_NO_ELIGIBLE_DEVICES;
  616. goto GptValidateHeaderEnd;
  617. }
  618. //
  619. // Loop through and count the valid entries.
  620. //
  621. for (EntryIndex = 0; EntryIndex < PartitionEntryCount; EntryIndex += 1) {
  622. Entry = &(PartitionEntries[EntryIndex]);
  623. if ((Entry->FirstLba == 0) || (Entry->LastLba == 0)) {
  624. continue;
  625. }
  626. if (PartpGptIsGuidEmpty(Entry->TypeGuid) != FALSE) {
  627. continue;
  628. }
  629. ValidPartitionCount += 1;
  630. }
  631. Status = STATUS_SUCCESS;
  632. GptValidateHeaderEnd:
  633. if (!KSUCCESS(Status)) {
  634. if (PartitionEntriesAllocation != NULL) {
  635. Context->FreeFunction(PartitionEntriesAllocation);
  636. PartitionEntries = NULL;
  637. PartitionEntriesAllocation = NULL;
  638. }
  639. PartitionEntryCount = 0;
  640. ValidPartitionCount = 0;
  641. }
  642. *Entries = PartitionEntries;
  643. *EntriesAllocation = PartitionEntriesAllocation;
  644. *EntryCount = PartitionEntryCount;
  645. *ValidCount = ValidPartitionCount;
  646. return Status;
  647. }
  648. KSTATUS
  649. PartpGptWriteBlocks (
  650. PPARTITION_CONTEXT Context,
  651. ULONGLONG BlockAddress,
  652. ULONG BlockCount,
  653. PVOID Buffer
  654. )
  655. /*++
  656. Routine Description:
  657. This routine writes multiple blocks to the disk.
  658. Arguments:
  659. Context - Supplies a pointer to the partition context.
  660. BlockAddress - Supplies the block address to write to.
  661. BlockCount - Supplies the number of blocks to write.
  662. Buffer - Supplies a pointer to the buffer containing the data to write.
  663. Return Value:
  664. Status code.
  665. --*/
  666. {
  667. ULONG BlockIndex;
  668. KSTATUS Status;
  669. Status = STATUS_SUCCESS;
  670. for (BlockIndex = 0; BlockIndex < BlockCount; BlockIndex += 1) {
  671. Status = Context->WriteFunction(Context, BlockAddress, Buffer);
  672. if (!KSUCCESS(Status)) {
  673. goto GptWriteBlocksEnd;
  674. }
  675. Buffer += Context->BlockSize;
  676. BlockAddress += 1;
  677. }
  678. GptWriteBlocksEnd:
  679. return Status;
  680. }
  681. KSTATUS
  682. PartpGptConvertPartitionTypeToGuid (
  683. PARTITION_TYPE PartitionType,
  684. UCHAR TypeGuid[GPT_GUID_SIZE]
  685. )
  686. /*++
  687. Routine Description:
  688. This routine converts a partition type enum into its corresponding GPT GUID.
  689. Arguments:
  690. PartitionType - Supplies the partition type.
  691. TypeGuid - Supplies a pointer where the GUID will be returned.
  692. Return Value:
  693. STATUS_SUCCESS on success.
  694. STATUS_INVALID_PARAMETER if the partition type could not be converted.
  695. --*/
  696. {
  697. ULONG EntryCount;
  698. ULONG EntryIndex;
  699. EntryCount = sizeof(PartTypeGuidToPartitionTypeTable) /
  700. sizeof(PartTypeGuidToPartitionTypeTable[0]);
  701. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  702. if (PartTypeGuidToPartitionTypeTable[EntryIndex].PartitionType ==
  703. PartitionType) {
  704. RtlCopyMemory(TypeGuid,
  705. PartTypeGuidToPartitionTypeTable[EntryIndex].TypeGuid,
  706. GPT_GUID_SIZE);
  707. return STATUS_SUCCESS;
  708. }
  709. }
  710. return STATUS_INVALID_PARAMETER;
  711. }
  712. BOOL
  713. PartpGptIsGuidEmpty (
  714. UCHAR Guid[GPT_GUID_SIZE]
  715. )
  716. /*++
  717. Routine Description:
  718. This routine determines if the given GUID is all zero.
  719. Arguments:
  720. Guid - Supplies the guid to compare.
  721. Return Value:
  722. TRUE if the GUID is all zero.
  723. FALSE if the GUID has something in there.
  724. --*/
  725. {
  726. BOOL Match;
  727. //
  728. // The first entry is known to be the empty entry.
  729. //
  730. Match = PartpGptAreGuidsEqual(Guid,
  731. PartTypeGuidToPartitionTypeTable[0].TypeGuid);
  732. return Match;
  733. }
  734. BOOL
  735. PartpGptAreGuidsEqual (
  736. UCHAR FirstGuid[GPT_GUID_SIZE],
  737. UCHAR SecondGuid[GPT_GUID_SIZE]
  738. )
  739. /*++
  740. Routine Description:
  741. This routine compares two GPT GUIDs.
  742. Arguments:
  743. FirstGuid - Supplies the first GUID to compare.
  744. SecondGuid - Supplies the second GUID to compare.
  745. Return Value:
  746. TRUE if the GUIDs are equal.
  747. FALSE if the GUIDs are not equal.
  748. --*/
  749. {
  750. ULONG Index;
  751. for (Index = 0; Index < GPT_GUID_SIZE; Index += 1) {
  752. if (FirstGuid[Index] != SecondGuid[Index]) {
  753. return FALSE;
  754. }
  755. }
  756. return TRUE;
  757. }