partlib.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. partlib.c
  5. Abstract:
  6. This module implements the partition support library.
  7. Author:
  8. Evan Green 30-Jan-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. PartpWriteMbrPartitionLayout (
  27. PPARTITION_CONTEXT Context,
  28. PPARTITION_INFORMATION Partitions,
  29. ULONG PartitionCount,
  30. BOOL CleanMbr
  31. );
  32. KSTATUS
  33. PartpParseMbrPartitionEntry (
  34. PPARTITION_CONTEXT Context,
  35. PPARTITION_TABLE_ENTRY TableEntry,
  36. BOOL Primary,
  37. ULONG Parent,
  38. ULONGLONG ExtendedEnd,
  39. ULONGLONG ExtendedRecordStart,
  40. PPARTITION_INFORMATION Information
  41. );
  42. VOID
  43. PartpConvertToMbrPartitionEntry (
  44. PPARTITION_CONTEXT Context,
  45. PPARTITION_INFORMATION Partition,
  46. ULONG StartOffset,
  47. ULONG Length,
  48. PPARTITION_TABLE_ENTRY TableEntry
  49. );
  50. PPARTITION_INFORMATION
  51. PartpReallocateArray (
  52. PPARTITION_CONTEXT Context,
  53. PPARTITION_INFORMATION Information,
  54. PULONG Capacity
  55. );
  56. PARTITION_TYPE
  57. PartpConvertSystemIdToPartitionType (
  58. UCHAR SystemId
  59. );
  60. UCHAR
  61. PartpConvertPartitionTypeToSystemId (
  62. PARTITION_TYPE Type
  63. );
  64. VOID
  65. PartpConvertLbaToChs (
  66. PPARTITION_CONTEXT Context,
  67. ULONG Lba,
  68. PULONG Cylinder,
  69. PULONG Head,
  70. PULONG Sector
  71. );
  72. //
  73. // -------------------------------------------------------------------- Globals
  74. //
  75. //
  76. // Define the mapping between partition system ID bytes and the partition type
  77. // enum.
  78. //
  79. PARTITION_SYSTEM_ID_MAPPING PartSystemIdToPartitionTypeTable[] = {
  80. {PARTITION_ID_EMPTY, PartitionTypeEmpty},
  81. {PARTITION_ID_MINOCA, PartitionTypeMinoca},
  82. {PARTITION_ID_DOS_FAT12, PartitionTypeDosFat12},
  83. {PARTITION_ID_DOS_PRIMARY_FAT16, PartitionTypeDosPrimaryFat16},
  84. {PARTITION_ID_DOS_EXTENDED, PartitionTypeDosExtended},
  85. {PARTITION_ID_NTFS, PartitionTypeNtfs},
  86. {PARTITION_ID_WINDOWS95_FAT32, PartitionTypeWindows95Fat32},
  87. {PARTITION_ID_WINDOWS95_FAT32_LBA, PartitionTypeWindows95Fat32Lba},
  88. {PARTITION_ID_DOS_EXTENDED_FAT16, PartitionTypeDosExtendedFat16},
  89. {PARTITION_ID_DOS_EXTENDED_LBA, PartitionTypeDosExtendedLba},
  90. {PARTITION_ID_WINDOWS_RE, PartitionTypeWindowsRecovery},
  91. {PARTITION_ID_PLAN9, PartitionTypePlan9},
  92. {PARTITION_ID_SYSTEMV_MACH_HURD, PartitionTypeSystemVMachHurd},
  93. {PARTITION_ID_MINIX_13, PartitionTypeMinix13},
  94. {PARTITION_ID_MINIX_14, PartitionTypeMinix14},
  95. {PARTITION_ID_LINUX_SWAP, PartitionTypeLinuxSwap},
  96. {PARTITION_ID_LINUX, PartitionTypeLinux},
  97. {PARTITION_ID_LINUX_EXTENDED, PartitionTypeLinuxExtended},
  98. {PARTITION_ID_LINUX_LVM, PartitionTypeLinuxLvm},
  99. {PARTITION_ID_BSD, PartitionTypeBsd},
  100. {PARTITION_ID_FREEBSD, PartitionTypeFreeBsd},
  101. {PARTITION_ID_OPENBSD, PartitionTypeOpenBsd},
  102. {PARTITION_ID_NEXTSTEP, PartitionTypeNextStep},
  103. {PARTITION_ID_MAC_OS_X, PartitionTypeMacOsX},
  104. {PARTITION_ID_NETBSD, PartitionTypeNetBsd},
  105. {PARTITION_ID_MAC_OS_X_BOOT, PartitionTypeMaxOsXBoot},
  106. {PARTITION_ID_MAX_OS_X_HFS, PartitionTypeMaxOsXHfs},
  107. {PARTITION_ID_EFI_GPT, PartitionTypeEfiGpt},
  108. {PARTITION_ID_EFI_SYSTEM, PartitionTypeEfiSystem},
  109. };
  110. //
  111. // ------------------------------------------------------------------ Functions
  112. //
  113. KSTATUS
  114. PartInitialize (
  115. PPARTITION_CONTEXT Context
  116. )
  117. /*++
  118. Routine Description:
  119. This routine initializes a partition context. The caller is expected to
  120. have filled in pointers to the allocate, free, and read sector functions.
  121. The caller is also expected to have filled in the block size, disk
  122. geometry information, and alignment (if needed).
  123. Arguments:
  124. Context - Supplies a pointer to the context, partially initialized by the
  125. caller.
  126. Return Value:
  127. STATUS_SUCCESS on success.
  128. STATUS_INVALID_PARAMETER if the context was not initialized properly by
  129. the caller.
  130. --*/
  131. {
  132. if (Context->Alignment == 1) {
  133. Context->Alignment = 0;
  134. }
  135. if ((Context->AllocateFunction == NULL) ||
  136. (Context->FreeFunction == NULL) ||
  137. (Context->ReadFunction == NULL) ||
  138. (Context->BlockSize < MINIMUM_BLOCK_SIZE) ||
  139. (!POWER_OF_2(Context->BlockSize)) ||
  140. ((Context->Alignment != 0) && (!POWER_OF_2(Context->Alignment)))) {
  141. return STATUS_INVALID_PARAMETER;
  142. }
  143. Context->BlockShift = RtlCountTrailingZeros32(Context->BlockSize);
  144. RtlZeroMemory(&(Context->DiskIdentifier), sizeof(Context->DiskIdentifier));
  145. Context->PartitionCount = 0;
  146. Context->Partitions = NULL;
  147. return STATUS_SUCCESS;
  148. }
  149. VOID
  150. PartDestroy (
  151. PPARTITION_CONTEXT Context
  152. )
  153. /*++
  154. Routine Description:
  155. This routine destroys a partition context.
  156. Arguments:
  157. Context - Supplies a pointer to the context.
  158. Return Value:
  159. None.
  160. --*/
  161. {
  162. if (Context->Partitions != NULL) {
  163. ASSERT(Context->FreeFunction != NULL);
  164. Context->FreeFunction(Context->Partitions);
  165. Context->Partitions = NULL;
  166. }
  167. Context->PartitionCount = 0;
  168. return;
  169. }
  170. KSTATUS
  171. PartEnumeratePartitions (
  172. PPARTITION_CONTEXT Context
  173. )
  174. /*++
  175. Routine Description:
  176. This routine is called to read the partition information from the disk
  177. and enumerate the list of partitions. The caller must have just called
  178. the initialize context function.
  179. Arguments:
  180. Context - Supplies a pointer to the initialized context.
  181. Return Value:
  182. STATUS_SUCCESS if the partition information could be determined. There
  183. could still be zero partitions in this case.
  184. STATUS_NO_ELIGIBLE_DEVICES if the partition table is invalid.
  185. Error codes on device read or allocation failure.
  186. --*/
  187. {
  188. PUCHAR Block;
  189. PVOID BlockAllocation;
  190. ULONG Capacity;
  191. PPARTITION_TABLE_ENTRY Entry;
  192. ULONG EntryIndex;
  193. ULONGLONG ExtendedEnd;
  194. ULONGLONG ExtendedRecordOffset;
  195. ULONGLONG ExtendedStart;
  196. PPARTITION_INFORMATION Information;
  197. PPARTITION_INFORMATION NewInformation;
  198. ULONG NextExtendedRecord;
  199. PUSHORT Pointer16;
  200. ULONG PrimaryCount;
  201. KSTATUS Status;
  202. ASSERT((Context->BlockSize != 0) &&
  203. ((1 << Context->BlockShift) == Context->BlockSize) &&
  204. (Context->AllocateFunction != NULL) &&
  205. (Context->FreeFunction != NULL) &&
  206. (Context->ReadFunction != NULL) &&
  207. (Context->PartitionCount == 0) &&
  208. (Context->Partitions == NULL));
  209. Capacity = 0;
  210. Information = NULL;
  211. //
  212. // Allocate a block for reading.
  213. //
  214. BlockAllocation = PartpAllocateIo(Context,
  215. Context->BlockSize,
  216. (PVOID *)&Block);
  217. if (BlockAllocation == NULL) {
  218. Status = STATUS_INSUFFICIENT_RESOURCES;
  219. goto EnumeratePartitionsEnd;
  220. }
  221. //
  222. // Read the first block.
  223. //
  224. Status = Context->ReadFunction(Context, 0, Block);
  225. if (!KSUCCESS(Status)) {
  226. goto EnumeratePartitionsEnd;
  227. }
  228. Context->Format = PartitionFormatNone;
  229. //
  230. // Check the MBR signature.
  231. //
  232. Pointer16 = (PUSHORT)(Block + PARTITION_SIGNATURE_OFFSET);
  233. if (*Pointer16 != PARTITION_SIGNATURE) {
  234. Status = STATUS_NO_ELIGIBLE_DEVICES;
  235. goto EnumeratePartitionsEnd;
  236. }
  237. //
  238. // Parse differently if this is a GPT disk.
  239. //
  240. Entry = (PPARTITION_TABLE_ENTRY)(Block + PARTITION_TABLE_OFFSET);
  241. if (PartpGptIsProtectiveMbr(Entry) != FALSE) {
  242. Status = PartpGptEnumeratePartitions(Context);
  243. if (!KSUCCESS(Status)) {
  244. goto EnumeratePartitionsEnd;
  245. }
  246. Context->Format = PartitionFormatGpt;
  247. Information = Context->Partitions;
  248. goto EnumeratePartitionsEnd;
  249. }
  250. //
  251. // This is an MBR disk. Save the disk ID.
  252. //
  253. RtlCopyMemory(&(Context->DiskIdentifier),
  254. Block + MBR_DISK_ID_OFFSET,
  255. MBR_DISK_ID_SIZE);
  256. //
  257. // Loop over each entry and create the partition information.
  258. //
  259. for (EntryIndex = 0;
  260. EntryIndex < PARTITION_TABLE_SIZE;
  261. EntryIndex += 1) {
  262. //
  263. // Expand the array if needed.
  264. //
  265. if (Context->PartitionCount == Capacity) {
  266. NewInformation = PartpReallocateArray(Context,
  267. Information,
  268. &Capacity);
  269. if (NewInformation == NULL) {
  270. Status = STATUS_INSUFFICIENT_RESOURCES;
  271. goto EnumeratePartitionsEnd;
  272. }
  273. Information = NewInformation;
  274. }
  275. ASSERT(Context->PartitionCount < Capacity);
  276. Status = PartpParseMbrPartitionEntry(
  277. Context,
  278. &(Entry[EntryIndex]),
  279. TRUE,
  280. 0,
  281. 0,
  282. 0,
  283. &(Information[Context->PartitionCount]));
  284. if (!KSUCCESS(Status)) {
  285. Status = STATUS_NO_ELIGIBLE_DEVICES;
  286. goto EnumeratePartitionsEnd;
  287. }
  288. }
  289. //
  290. // Now go through each of the primary partitions and parse any logical
  291. // partitions out of any extended partitions.
  292. //
  293. PrimaryCount = Context->PartitionCount;
  294. for (EntryIndex = 0; EntryIndex < PrimaryCount; EntryIndex += 1) {
  295. if ((Information[EntryIndex].Flags & PARTITION_FLAG_EXTENDED) == 0) {
  296. continue;
  297. }
  298. ExtendedStart = Information[EntryIndex].StartOffset;
  299. ExtendedEnd = Information[EntryIndex].EndOffset;
  300. ExtendedRecordOffset = ExtendedStart;
  301. //
  302. // Loop over the singly linked list of logical partitions within the
  303. // extended partition.
  304. //
  305. while (TRUE) {
  306. //
  307. // Read the extended boot record.
  308. //
  309. Status = Context->ReadFunction(Context,
  310. ExtendedRecordOffset,
  311. Block);
  312. if (!KSUCCESS(Status)) {
  313. goto EnumeratePartitionsEnd;
  314. }
  315. //
  316. // Check the signature.
  317. //
  318. Pointer16 = (PUSHORT)(Block + PARTITION_SIGNATURE_OFFSET);
  319. if (*Pointer16 != PARTITION_SIGNATURE) {
  320. continue;
  321. }
  322. //
  323. // Expand the array if needed.
  324. //
  325. if (Context->PartitionCount == Capacity) {
  326. NewInformation = PartpReallocateArray(Context,
  327. Information,
  328. &Capacity);
  329. if (NewInformation == NULL) {
  330. Status = STATUS_INSUFFICIENT_RESOURCES;
  331. goto EnumeratePartitionsEnd;
  332. }
  333. Information = NewInformation;
  334. }
  335. ASSERT(Context->PartitionCount < Capacity);
  336. //
  337. // The first entry has information about the logical partition, the
  338. // second entry has a pointer to the next EBR. Parse the first
  339. // entry.
  340. //
  341. Entry = (PPARTITION_TABLE_ENTRY)(Block + PARTITION_TABLE_OFFSET);
  342. PartpParseMbrPartitionEntry(
  343. Context,
  344. &(Entry[0]),
  345. FALSE,
  346. Information[EntryIndex].Number,
  347. ExtendedEnd,
  348. ExtendedRecordOffset,
  349. &(Information[Context->PartitionCount]));
  350. //
  351. // The offset for the second entry is relative to the start of the
  352. // extended partition as a whole (which is different than the first
  353. // entry, whose offset was relative to this EBR's offset). If it's
  354. // zero, take that to mean the end.
  355. //
  356. NextExtendedRecord = Entry[1].StartingLba;
  357. if (NextExtendedRecord == 0) {
  358. break;
  359. }
  360. //
  361. // Also quietly stop if the next record tries to go off the end of
  362. // the extended partition.
  363. //
  364. if (NextExtendedRecord + ExtendedStart >= ExtendedEnd) {
  365. break;
  366. }
  367. ExtendedRecordOffset = NextExtendedRecord + ExtendedStart;
  368. }
  369. }
  370. //
  371. // Trim off any empty partitions on the end.
  372. //
  373. while ((Context->PartitionCount != 0) &&
  374. (Information[Context->PartitionCount - 1].PartitionType ==
  375. PartitionTypeEmpty)) {
  376. Context->PartitionCount -= 1;
  377. }
  378. Context->Format = PartitionFormatMbr;
  379. Status = STATUS_SUCCESS;
  380. EnumeratePartitionsEnd:
  381. if (!KSUCCESS(Status)) {
  382. if (Information != NULL) {
  383. Context->FreeFunction(Information);
  384. Information = NULL;
  385. }
  386. Context->PartitionCount = 0;
  387. }
  388. Context->Partitions = Information;
  389. if (BlockAllocation != NULL) {
  390. Context->FreeFunction(BlockAllocation);
  391. }
  392. return Status;
  393. }
  394. KSTATUS
  395. PartWritePartitionLayout (
  396. PPARTITION_CONTEXT Context,
  397. PARTITION_FORMAT Format,
  398. PPARTITION_INFORMATION Partitions,
  399. ULONG PartitionCount,
  400. BOOL CleanMbr
  401. )
  402. /*++
  403. Routine Description:
  404. This routine writes a partition layout to the disk. This usually wipes out
  405. all data on the disk.
  406. Arguments:
  407. Context - Supplies a pointer to the partition context.
  408. Format - Supplies the partition format to use.
  409. Partitions - Supplies a pointer to the new partition layout.
  410. PartitionCount - Supplies the number of partitions in the new layout.
  411. CleanMbr - Supplies a boolean indicating if only the partition entries of
  412. the MBR should be modified (FALSE) or if the whole MBR should be
  413. zeroed before being written (TRUE).
  414. Return Value:
  415. STATUS_SUCCESS if the valid block count is non-zero.
  416. STATUS_OUT_OF_BOUNDS if the block address is beyond the end of the
  417. partition.
  418. --*/
  419. {
  420. KSTATUS Status;
  421. if (Format == PartitionFormatMbr) {
  422. Status = PartpWriteMbrPartitionLayout(Context,
  423. Partitions,
  424. PartitionCount,
  425. CleanMbr);
  426. } else if (Format == PartitionFormatGpt) {
  427. Status = PartpGptWritePartitionLayout(Context,
  428. Partitions,
  429. PartitionCount,
  430. CleanMbr);
  431. } else {
  432. ASSERT(FALSE);
  433. Status = STATUS_INVALID_PARAMETER;
  434. }
  435. return Status;
  436. }
  437. KSTATUS
  438. PartTranslateIo (
  439. PPARTITION_INFORMATION Partition,
  440. PULONGLONG BlockAddress,
  441. PULONGLONG BlockCount
  442. )
  443. /*++
  444. Routine Description:
  445. This routine performs a translation from a partition-relative offset to a
  446. global disk offset.
  447. Arguments:
  448. Partition - Supplies a pointer to the partition to translate for.
  449. BlockAddress - Supplies a pointer that on input contains the
  450. partition-relative block address. On successful output, this will
  451. contain the global address.
  452. BlockCount - Supplies a pointer that on input contains the number of blocks
  453. to read or write. On output, the number of valid blocks will be
  454. returned. This number may be reduced on output if the caller tried to
  455. do I/O off the end of the partition.
  456. Return Value:
  457. STATUS_SUCCESS if the valid block count is non-zero.
  458. STATUS_OUT_OF_BOUNDS if the block address is beyond the end of the
  459. partition.
  460. --*/
  461. {
  462. ULONGLONG Length;
  463. Length = Partition->EndOffset - Partition->StartOffset;
  464. if (*BlockAddress >= Length) {
  465. return STATUS_OUT_OF_BOUNDS;
  466. }
  467. if (BlockCount != NULL) {
  468. if (*BlockAddress + *BlockCount < *BlockAddress) {
  469. return STATUS_OUT_OF_BOUNDS;
  470. }
  471. if (*BlockAddress + *BlockCount > Length) {
  472. *BlockCount = Length - *BlockAddress;
  473. }
  474. }
  475. *BlockAddress += Partition->StartOffset;
  476. return STATUS_SUCCESS;
  477. }
  478. PARTITION_TYPE
  479. PartConvertToPartitionType (
  480. PARTITION_FORMAT Format,
  481. UCHAR PartitionTypeId[PARTITION_TYPE_SIZE]
  482. )
  483. /*++
  484. Routine Description:
  485. This routine converts a partition type ID into a known partition type.
  486. Arguments:
  487. Format - Supplies the format. Valid values are MBR and GPT.
  488. PartitionTypeId - Supplies the partition type ID bytes.
  489. Return Value:
  490. Returns the partition type that corresponds with the given partition
  491. type ID.
  492. PartitionTypeInvalid if the format is invalid.
  493. PartitionTypeUnknown if the partition type ID is unknown.
  494. --*/
  495. {
  496. PARTITION_TYPE PartitionType;
  497. if (Format == PartitionFormatMbr) {
  498. PartitionType = PartpConvertSystemIdToPartitionType(PartitionTypeId[0]);
  499. } else if (Format == PartitionFormatGpt) {
  500. PartitionType = PartpGptConvertTypeGuidToPartitionType(PartitionTypeId);
  501. } else {
  502. PartitionType = PartitionTypeInvalid;
  503. }
  504. return PartitionType;
  505. }
  506. PVOID
  507. PartpAllocateIo (
  508. PPARTITION_CONTEXT Context,
  509. UINTN Size,
  510. PVOID *AlignedAllocation
  511. )
  512. /*++
  513. Routine Description:
  514. This routine allocates a region that will be used for I/O.
  515. Arguments:
  516. Context - Supplies a pointer to the initialized partition context.
  517. Size - Supplies the required size of the allocation.
  518. AlignedAllocation - Supplies a pointer where the aligned buffer will be
  519. returned.
  520. Return Value:
  521. Returns the actual buffer to be passed to the free function on success.
  522. NULL on failure.
  523. --*/
  524. {
  525. PVOID Allocation;
  526. Allocation = Context->AllocateFunction(Size + Context->Alignment);
  527. if (Allocation == NULL) {
  528. return NULL;
  529. }
  530. if (Context->Alignment == 0) {
  531. *AlignedAllocation = Allocation;
  532. } else {
  533. *AlignedAllocation = (PVOID)(UINTN)ALIGN_RANGE_UP((UINTN)Allocation,
  534. Context->Alignment);
  535. }
  536. return Allocation;
  537. }
  538. //
  539. // --------------------------------------------------------- Internal Functions
  540. //
  541. KSTATUS
  542. PartpWriteMbrPartitionLayout (
  543. PPARTITION_CONTEXT Context,
  544. PPARTITION_INFORMATION Partitions,
  545. ULONG PartitionCount,
  546. BOOL CleanMbr
  547. )
  548. /*++
  549. Routine Description:
  550. This routine writes an MBR partition layout to the disk. This usually wipes
  551. out all data on the disk.
  552. Arguments:
  553. Context - Supplies a pointer to the partition context.
  554. Partitions - Supplies a pointer to the new partition layout.
  555. PartitionCount - Supplies the number of partitions in the new layout.
  556. CleanMbr - Supplies a boolean indicating if only the partition entries of
  557. the MBR should be modified (FALSE) or if the whole MBR should be
  558. zeroed before being written (TRUE).
  559. Return Value:
  560. Status code.
  561. --*/
  562. {
  563. PVOID Block;
  564. PVOID BlockAllocation;
  565. ULONG BlockIndex;
  566. ULONG FirstPartition;
  567. PARTITION_TABLE_ENTRY MbrEntries[PARTITION_TABLE_SIZE];
  568. ULONG MbrEntryCount;
  569. PPARTITION_INFORMATION Partition;
  570. ULONG PartitionIndex;
  571. KSTATUS Status;
  572. BlockAllocation = NULL;
  573. RtlZeroMemory(MbrEntries, sizeof(MbrEntries));
  574. //
  575. // Loop over the partitions to fill in the primary MBR entries.
  576. //
  577. FirstPartition = 0;
  578. MbrEntryCount = 0;
  579. for (PartitionIndex = 0;
  580. PartitionIndex < PartitionCount;
  581. PartitionIndex += 1) {
  582. Partition = &(Partitions[PartitionIndex]);
  583. if (PartitionIndex == 0) {
  584. FirstPartition = Partition->StartOffset;
  585. }
  586. ASSERT(Partition->EndOffset >= Partition->StartOffset);
  587. //
  588. // Find a slot in the MBR if this is a primary or extended partition.
  589. //
  590. if ((Partition->Flags &
  591. (PARTITION_FLAG_PRIMARY | PARTITION_FLAG_EXTENDED)) != 0) {
  592. if (MbrEntryCount == PARTITION_TABLE_SIZE) {
  593. Status = STATUS_INVALID_CONFIGURATION;
  594. goto WriteMbrPartitionLayoutEnd;
  595. }
  596. ASSERT((Partition->Number == 0) ||
  597. (Partition->Number == MbrEntryCount + 1));
  598. ASSERT(((Partition->Flags & PARTITION_FLAG_EXTENDED) == 0) ||
  599. (Partition->TypeIdentifier[0] ==
  600. PARTITION_ID_DOS_EXTENDED) ||
  601. (Partition->TypeIdentifier[0] ==
  602. PARTITION_ID_DOS_EXTENDED_LBA));
  603. PartpConvertToMbrPartitionEntry(
  604. Context,
  605. Partition,
  606. Partition->StartOffset,
  607. Partition->EndOffset - Partition->StartOffset,
  608. &(MbrEntries[MbrEntryCount]));
  609. MbrEntryCount += 1;
  610. }
  611. //
  612. // Logical partitions are currently not supported.
  613. //
  614. if ((Partition->Flags & PARTITION_FLAG_LOGICAL) != 0) {
  615. Status = STATUS_NOT_SUPPORTED;
  616. goto WriteMbrPartitionLayoutEnd;
  617. }
  618. }
  619. //
  620. // Allocate space for the MBR block to be read in.
  621. //
  622. if (Context->BlockSize < MINIMUM_BLOCK_SIZE) {
  623. Status = STATUS_INVALID_CONFIGURATION;
  624. goto WriteMbrPartitionLayoutEnd;
  625. }
  626. BlockAllocation = PartpAllocateIo(Context, Context->BlockSize, &Block);
  627. if (BlockAllocation == NULL) {
  628. Status = STATUS_INSUFFICIENT_RESOURCES;
  629. goto WriteMbrPartitionLayoutEnd;
  630. }
  631. //
  632. // Read in the MBR, or zero out the buffer.
  633. //
  634. if (CleanMbr != FALSE) {
  635. RtlZeroMemory(Block, Context->BlockSize);
  636. } else {
  637. Status = Context->ReadFunction(Context, 0, Block);
  638. if (!KSUCCESS(Status)) {
  639. goto WriteMbrPartitionLayoutEnd;
  640. }
  641. }
  642. //
  643. // Copy the new partition tables over.
  644. //
  645. RtlCopyMemory(Block + PARTITION_TABLE_OFFSET,
  646. MbrEntries,
  647. sizeof(MbrEntries));
  648. //
  649. // If there's a random function, create a random disk ID.
  650. //
  651. if (Context->FillRandomFunction != NULL) {
  652. Context->FillRandomFunction(Context,
  653. Block + MBR_DISK_ID_OFFSET,
  654. MBR_DISK_ID_SIZE);
  655. }
  656. ASSERT(Context->WriteFunction != NULL);
  657. //
  658. // Apply the signature.
  659. //
  660. *((PUSHORT)((PUCHAR)Block + PARTITION_SIGNATURE_OFFSET)) =
  661. PARTITION_SIGNATURE;
  662. //
  663. // Write the MBR back out.
  664. //
  665. Status = Context->WriteFunction(Context, 0, Block);
  666. if (!KSUCCESS(Status)) {
  667. goto WriteMbrPartitionLayoutEnd;
  668. }
  669. //
  670. // Zero out the space between the MBR and the first partition.
  671. //
  672. RtlZeroMemory(Block, Context->BlockSize);
  673. BlockIndex = 1;
  674. while (BlockIndex < FirstPartition) {
  675. Status = Context->WriteFunction(Context, BlockIndex, Block);
  676. if (!KSUCCESS(Status)) {
  677. goto WriteMbrPartitionLayoutEnd;
  678. }
  679. BlockIndex += 1;
  680. }
  681. WriteMbrPartitionLayoutEnd:
  682. if (BlockAllocation != NULL) {
  683. Context->FreeFunction(BlockAllocation);
  684. }
  685. return Status;
  686. }
  687. KSTATUS
  688. PartpParseMbrPartitionEntry (
  689. PPARTITION_CONTEXT Context,
  690. PPARTITION_TABLE_ENTRY TableEntry,
  691. BOOL Primary,
  692. ULONG Parent,
  693. ULONGLONG ExtendedEnd,
  694. ULONGLONG ExtendedRecordStart,
  695. PPARTITION_INFORMATION Information
  696. )
  697. /*++
  698. Routine Description:
  699. This routine parses an MBR-style partition table entry and converts it to
  700. a partition information structure.
  701. Arguments:
  702. Context - Supplies a pointer to the partition context.
  703. TableEntry - Supplies a pointer to the MBR-style partition table entry.
  704. Primary - Supplies a boolean indicating if this is a primary partition
  705. (TRUE) or a logical partition (FALSE).
  706. Parent - Supplies the parent partition number (used only for logical
  707. partitions).
  708. ExtendedEnd - Supplies the end block address (exclusive) for the extended
  709. partition this entry resides in. This is the ending sector for the
  710. entire extended partition, not just this EBR. This value is ignored for
  711. primary partitions.
  712. ExtendedRecordStart - Supplies the block address that this extended boot
  713. record resides in. This is the offset of this EBR. This is ignored for
  714. primary partitions.
  715. Information - Supplies a pointer where the partition information will be
  716. returned on success.
  717. Return Value:
  718. STATUS_SUCCESS if a partition was parsed out.
  719. STATUS_INVALID_CONFIGURATION if the partition table entry is not valid.
  720. --*/
  721. {
  722. if ((TableEntry->BootIndicator != 0) &&
  723. (TableEntry->BootIndicator != MBR_PARTITION_BOOT)) {
  724. return STATUS_INVALID_CONFIGURATION;
  725. }
  726. if (Primary != FALSE) {
  727. ExtendedEnd = 0;
  728. ExtendedRecordStart = 0;
  729. }
  730. //
  731. // Fail if the logical partition goes outside of its parent extended
  732. // partition.
  733. //
  734. if ((Primary == FALSE) &&
  735. (TableEntry->StartingLba + ExtendedRecordStart > ExtendedEnd)) {
  736. return STATUS_BUFFER_OVERRUN;
  737. }
  738. //
  739. // The starting offset for the first entry in the extended boot record
  740. // is the relative offset from this EBR. For primary partitions, this value
  741. // is 0. The second entry is a link, and isn't handled by this routine.
  742. //
  743. Information->StartOffset = TableEntry->StartingLba + ExtendedRecordStart;
  744. Information->EndOffset = Information->StartOffset + TableEntry->SectorCount;
  745. Information->Number = Context->PartitionCount + 1;
  746. Information->ParentNumber = Parent;
  747. Context->PartitionCount += 1;
  748. Information->Flags = 0;
  749. if (TableEntry->BootIndicator == MBR_PARTITION_BOOT) {
  750. Information->Flags |= PARTITION_FLAG_BOOT;
  751. }
  752. Information->TypeIdentifier[0] = TableEntry->SystemId;
  753. Information->PartitionType =
  754. PartpConvertSystemIdToPartitionType(TableEntry->SystemId);
  755. if (Primary != FALSE) {
  756. if ((Information->PartitionType == PartitionTypeDosExtended) ||
  757. (Information->PartitionType == PartitionTypeDosExtendedLba)) {
  758. Information->Flags |= PARTITION_FLAG_EXTENDED;
  759. } else {
  760. Information->Flags |= PARTITION_FLAG_PRIMARY;
  761. }
  762. } else {
  763. Information->Flags |= PARTITION_FLAG_LOGICAL;
  764. }
  765. //
  766. // Create a partition signature by cobbling together the partition number
  767. // and the disk ID.
  768. //
  769. RtlCopyMemory(&(Information->Identifier[0]),
  770. &(Context->DiskIdentifier),
  771. MBR_DISK_ID_SIZE);
  772. RtlCopyMemory(&(Information->Identifier[MBR_DISK_ID_SIZE]),
  773. &(Information->Number),
  774. sizeof(Information->Number));
  775. return STATUS_SUCCESS;
  776. }
  777. VOID
  778. PartpConvertToMbrPartitionEntry (
  779. PPARTITION_CONTEXT Context,
  780. PPARTITION_INFORMATION Partition,
  781. ULONG StartOffset,
  782. ULONG Length,
  783. PPARTITION_TABLE_ENTRY TableEntry
  784. )
  785. /*++
  786. Routine Description:
  787. This routine initializes an MBR-style partition entry from a partition
  788. information structure.
  789. Arguments:
  790. Context - Supplies a pointer to the partition context.
  791. Partition - Supplies a pointer to the partition information.
  792. StartOffset - Supplies the start offset to set in the partition table.
  793. Length - Supplies the length to use in the partition table.
  794. TableEntry - Supplies a pointer where table entry is returned.
  795. Return Value:
  796. None.
  797. --*/
  798. {
  799. ULONG Cylinder;
  800. ULONG Head;
  801. ULONG Sector;
  802. RtlZeroMemory(TableEntry, sizeof(PARTITION_TABLE_ENTRY));
  803. if (Length == 0) {
  804. return;
  805. }
  806. if ((Partition->Flags & PARTITION_FLAG_BOOT) != 0) {
  807. TableEntry->BootIndicator = MBR_PARTITION_BOOT;
  808. }
  809. PartpConvertLbaToChs(Context, StartOffset, &Cylinder, &Head, &Sector);
  810. TableEntry->StartingHead = Head;
  811. TableEntry->StartingSector = Sector | ((Cylinder >> 2) & 0xC0);
  812. TableEntry->StartingCylinder = Cylinder & 0xFF;
  813. PartpConvertLbaToChs(Context,
  814. StartOffset + Length - 1,
  815. &Cylinder,
  816. &Head,
  817. &Sector);
  818. TableEntry->EndingHead = Head;
  819. TableEntry->EndingSector = Sector | ((Cylinder >> 2) & 0xC0);
  820. TableEntry->EndingCylinder = Cylinder & 0xFF;
  821. if (Partition->PartitionType != PartitionTypeInvalid) {
  822. TableEntry->SystemId =
  823. PartpConvertPartitionTypeToSystemId(Partition->PartitionType);
  824. } else {
  825. TableEntry->SystemId = Partition->TypeIdentifier[0];
  826. }
  827. TableEntry->StartingLba = StartOffset;
  828. TableEntry->SectorCount = Length;
  829. return;
  830. }
  831. PPARTITION_INFORMATION
  832. PartpReallocateArray (
  833. PPARTITION_CONTEXT Context,
  834. PPARTITION_INFORMATION Information,
  835. PULONG Capacity
  836. )
  837. /*++
  838. Routine Description:
  839. This routine allocates or reallocates the partition information array.
  840. Arguments:
  841. Context - Supplies a pointer to the partition context.
  842. Information - Supplies an optional pointer to the current array.
  843. Capacity - Supplies a pointer that on input contains the capacity of the
  844. current array. On output, this will be updated to the new capacity.
  845. Return Value:
  846. Returns a pointer to the new array on success.
  847. NULL on allocation failure, and the old array will not be freed.
  848. --*/
  849. {
  850. PPARTITION_INFORMATION NewBuffer;
  851. ULONG NewCapacity;
  852. if (*Capacity == 0) {
  853. NewCapacity = INITIAL_PARTITION_INFORMATION_CAPACITY;
  854. } else {
  855. NewCapacity = *Capacity * 2;
  856. }
  857. if (NewCapacity <= *Capacity) {
  858. return NULL;
  859. }
  860. NewBuffer = Context->AllocateFunction(
  861. NewCapacity * sizeof(PARTITION_INFORMATION));
  862. if (NewBuffer == NULL) {
  863. return NULL;
  864. }
  865. //
  866. // Copy the old buffer over.
  867. //
  868. if (*Capacity != 0) {
  869. RtlCopyMemory(NewBuffer,
  870. Information,
  871. *Capacity * sizeof(PARTITION_INFORMATION));
  872. }
  873. //
  874. // Zero out the new stuff.
  875. //
  876. RtlZeroMemory(NewBuffer + *Capacity,
  877. (NewCapacity - *Capacity) * sizeof(PARTITION_INFORMATION));
  878. //
  879. // Free the old buffer and return the new.
  880. //
  881. if (Information != NULL) {
  882. Context->FreeFunction(Information);
  883. }
  884. *Capacity = NewCapacity;
  885. return NewBuffer;
  886. }
  887. PARTITION_TYPE
  888. PartpConvertSystemIdToPartitionType (
  889. UCHAR SystemId
  890. )
  891. /*++
  892. Routine Description:
  893. This routine converts a system ID byte into a partition type to the best of
  894. its abilities.
  895. Arguments:
  896. SystemId - Supplies the system ID byte.
  897. Return Value:
  898. Returns a partition type for this system ID byte.
  899. --*/
  900. {
  901. ULONG EntryCount;
  902. ULONG EntryIndex;
  903. EntryCount = sizeof(PartSystemIdToPartitionTypeTable) /
  904. sizeof(PartSystemIdToPartitionTypeTable[0]);
  905. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  906. if (PartSystemIdToPartitionTypeTable[EntryIndex].SystemId == SystemId) {
  907. return PartSystemIdToPartitionTypeTable[EntryIndex].PartitionType;
  908. }
  909. }
  910. return PartitionTypeUnknown;
  911. }
  912. UCHAR
  913. PartpConvertPartitionTypeToSystemId (
  914. PARTITION_TYPE Type
  915. )
  916. /*++
  917. Routine Description:
  918. This routine converts a partition type value into a system ID byte.
  919. Arguments:
  920. Type - Supplies the partition type.
  921. Return Value:
  922. Returns a partition type for this system ID byte.
  923. --*/
  924. {
  925. ULONG EntryCount;
  926. ULONG EntryIndex;
  927. EntryCount = sizeof(PartSystemIdToPartitionTypeTable) /
  928. sizeof(PartSystemIdToPartitionTypeTable[0]);
  929. for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex += 1) {
  930. if (PartSystemIdToPartitionTypeTable[EntryIndex].PartitionType ==
  931. Type) {
  932. return PartSystemIdToPartitionTypeTable[EntryIndex].SystemId;
  933. }
  934. }
  935. ASSERT(FALSE);
  936. return PARTITION_ID_DOS_FAT12;
  937. }
  938. VOID
  939. PartpConvertLbaToChs (
  940. PPARTITION_CONTEXT Context,
  941. ULONG Lba,
  942. PULONG Cylinder,
  943. PULONG Head,
  944. PULONG Sector
  945. )
  946. /*++
  947. Routine Description:
  948. This routine converts a LBA address (linear offset) into a Cylinder-Head-
  949. Sector geometry address. If the LBA address is too high, then the maximum
  950. CHS values will be set.
  951. Arguments:
  952. Context - Supplies a pointer to the initialized partition context.
  953. Lba - Supplies the LBA to convert.
  954. Cylinder - Supplies a pointer where the cylinder will be returned.
  955. Head - Supplies a pointer where the head will be returned.
  956. Sector - Supplies a pointer where the sector will be returned.
  957. Return Value:
  958. None.
  959. --*/
  960. {
  961. ULONG TotalHead;
  962. if ((Context->SectorsPerHead == 0) || (Context->HeadsPerCylinder == 0)) {
  963. *Cylinder = 0xFF;
  964. *Head = 0xFE;
  965. *Sector = 0xFF;
  966. return;
  967. }
  968. TotalHead = Lba / Context->SectorsPerHead;
  969. *Sector = (Lba % Context->SectorsPerHead) + 1;
  970. *Cylinder = TotalHead / Context->HeadsPerCylinder;
  971. *Head = TotalHead % Context->HeadsPerCylinder;
  972. if (*Cylinder > MBR_MAX_CYLINDER) {
  973. *Cylinder = MBR_MAX_CYLINDER;
  974. *Head = Context->HeadsPerCylinder - 1;
  975. *Sector = Context->SectorsPerHead;
  976. }
  977. return;
  978. }