Reactos
1/*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Partition list functions
5 * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net)
6 * Copyright 2018-2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7 */
8
9#include "precomp.h"
10#include <ntddscsi.h>
11
12#include "partlist.h"
13#include "volutil.h"
14#include "fsrec.h" // For FileSystemToMBRPartitionType()
15
16#include "registry.h"
17
18#define NDEBUG
19#include <debug.h>
20
21// #define DUMP_PARTITION_TABLE
22
23#include <pshpack1.h>
24typedef struct _REG_DISK_MOUNT_INFO
25{
26 ULONG Signature;
27 ULONGLONG StartingOffset;
28} REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO;
29#include <poppack.h>
30
31
32/* FUNCTIONS ****************************************************************/
33
34#ifdef DUMP_PARTITION_TABLE
35static
36VOID
37DumpPartitionTable(
38 PDISKENTRY DiskEntry)
39{
40 PPARTITION_INFORMATION PartitionInfo;
41 ULONG i;
42
43 DbgPrint("\n");
44 DbgPrint("Index Start Length Hidden Nr Type Boot RW\n");
45 DbgPrint("----- ------------ ------------ ---------- -- ---- ---- --\n");
46
47 for (i = 0; i < DiskEntry->LayoutBuffer->PartitionCount; i++)
48 {
49 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[i];
50 DbgPrint(" %3lu %12I64u %12I64u %10lu %2lu %2x %c %c\n",
51 i,
52 PartitionInfo->StartingOffset.QuadPart / DiskEntry->BytesPerSector,
53 PartitionInfo->PartitionLength.QuadPart / DiskEntry->BytesPerSector,
54 PartitionInfo->HiddenSectors,
55 PartitionInfo->PartitionNumber,
56 PartitionInfo->PartitionType,
57 PartitionInfo->BootIndicator ? '*': ' ',
58 PartitionInfo->RewritePartition ? 'Y': 'N');
59 }
60
61 DbgPrint("\n");
62}
63#endif
64
65
66ULONGLONG
67AlignDown(
68 IN ULONGLONG Value,
69 IN ULONG Alignment)
70{
71 ULONGLONG Temp;
72
73 Temp = Value / Alignment;
74
75 return Temp * Alignment;
76}
77
78ULONGLONG
79AlignUp(
80 IN ULONGLONG Value,
81 IN ULONG Alignment)
82{
83 ULONGLONG Temp, Result;
84
85 Temp = Value / Alignment;
86
87 Result = Temp * Alignment;
88 if (Value % Alignment)
89 Result += Alignment;
90
91 return Result;
92}
93
94ULONGLONG
95RoundingDivide(
96 IN ULONGLONG Dividend,
97 IN ULONGLONG Divisor)
98{
99 return (Dividend + Divisor / 2) / Divisor;
100}
101
102
103static
104VOID
105GetDriverName(
106 IN PDISKENTRY DiskEntry)
107{
108 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
109 WCHAR KeyName[32];
110 NTSTATUS Status;
111
112 RtlInitUnicodeString(&DiskEntry->DriverName, NULL);
113
114 RtlStringCchPrintfW(KeyName, ARRAYSIZE(KeyName),
115 L"\\Scsi\\Scsi Port %hu",
116 DiskEntry->Port);
117
118 RtlZeroMemory(&QueryTable, sizeof(QueryTable));
119
120 QueryTable[0].Name = L"Driver";
121 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
122 QueryTable[0].EntryContext = &DiskEntry->DriverName;
123
124 /* This will allocate DiskEntry->DriverName if needed */
125 Status = RtlQueryRegistryValues(RTL_REGISTRY_DEVICEMAP,
126 KeyName,
127 QueryTable,
128 NULL,
129 NULL);
130 if (!NT_SUCCESS(Status))
131 {
132 DPRINT1("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
133 }
134}
135
136static
137VOID
138AssignDriveLetters(
139 IN PPARTLIST List)
140{
141 PDISKENTRY DiskEntry;
142 PPARTENTRY PartEntry;
143 PLIST_ENTRY Entry1;
144 PLIST_ENTRY Entry2;
145 WCHAR Letter;
146
147 Letter = L'C';
148
149 /* Assign drive letters to primary partitions */
150 for (Entry1 = List->DiskListHead.Flink;
151 Entry1 != &List->DiskListHead;
152 Entry1 = Entry1->Flink)
153 {
154 DiskEntry = CONTAINING_RECORD(Entry1, DISKENTRY, ListEntry);
155
156 for (Entry2 = DiskEntry->PrimaryPartListHead.Flink;
157 Entry2 != &DiskEntry->PrimaryPartListHead;
158 Entry2 = Entry2->Flink)
159 {
160 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
161
162 if (!PartEntry->Volume)
163 continue;
164 PartEntry->Volume->Info.DriveLetter = UNICODE_NULL;
165
166 if (PartEntry->IsPartitioned &&
167 !IsContainerPartition(PartEntry->PartitionType) &&
168 (IsRecognizedPartition(PartEntry->PartitionType) ||
169 PartEntry->SectorCount.QuadPart != 0LL))
170 {
171 if (Letter <= L'Z')
172 PartEntry->Volume->Info.DriveLetter = Letter++;
173 }
174 }
175 }
176
177 /* Assign drive letters to logical drives */
178 for (Entry1 = List->DiskListHead.Flink;
179 Entry1 != &List->DiskListHead;
180 Entry1 = Entry1->Flink)
181 {
182 DiskEntry = CONTAINING_RECORD(Entry1, DISKENTRY, ListEntry);
183
184 for (Entry2 = DiskEntry->LogicalPartListHead.Flink;
185 Entry2 != &DiskEntry->LogicalPartListHead;
186 Entry2 = Entry2->Flink)
187 {
188 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
189
190 if (!PartEntry->Volume)
191 continue;
192 PartEntry->Volume->Info.DriveLetter = UNICODE_NULL;
193
194 if (PartEntry->IsPartitioned &&
195 (IsRecognizedPartition(PartEntry->PartitionType) ||
196 PartEntry->SectorCount.QuadPart != 0LL))
197 {
198 if (Letter <= L'Z')
199 PartEntry->Volume->Info.DriveLetter = Letter++;
200 }
201 }
202 }
203}
204
205static NTSTATUS
206NTAPI
207DiskIdentifierQueryRoutine(
208 PWSTR ValueName,
209 ULONG ValueType,
210 PVOID ValueData,
211 ULONG ValueLength,
212 PVOID Context,
213 PVOID EntryContext)
214{
215 PBIOSDISKENTRY BiosDiskEntry = (PBIOSDISKENTRY)Context;
216 UNICODE_STRING NameU;
217
218 if (ValueType == REG_SZ &&
219 ValueLength == 20 * sizeof(WCHAR) &&
220 ((PWCHAR)ValueData)[8] == L'-')
221 {
222 NameU.Buffer = (PWCHAR)ValueData;
223 NameU.Length = NameU.MaximumLength = 8 * sizeof(WCHAR);
224 RtlUnicodeStringToInteger(&NameU, 16, &BiosDiskEntry->Checksum);
225
226 NameU.Buffer = (PWCHAR)ValueData + 9;
227 RtlUnicodeStringToInteger(&NameU, 16, &BiosDiskEntry->Signature);
228
229 return STATUS_SUCCESS;
230 }
231
232 return STATUS_UNSUCCESSFUL;
233}
234
235static NTSTATUS
236NTAPI
237DiskConfigurationDataQueryRoutine(
238 PWSTR ValueName,
239 ULONG ValueType,
240 PVOID ValueData,
241 ULONG ValueLength,
242 PVOID Context,
243 PVOID EntryContext)
244{
245 PBIOSDISKENTRY BiosDiskEntry = (PBIOSDISKENTRY)Context;
246 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
247 PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry;
248 ULONG i;
249
250 if (ValueType != REG_FULL_RESOURCE_DESCRIPTOR ||
251 ValueLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
252 return STATUS_UNSUCCESSFUL;
253
254 FullResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)ValueData;
255
256 /* Hm. Version and Revision are not set on Microsoft Windows XP... */
257#if 0
258 if (FullResourceDescriptor->PartialResourceList.Version != 1 ||
259 FullResourceDescriptor->PartialResourceList.Revision != 1)
260 return STATUS_UNSUCCESSFUL;
261#endif
262
263 for (i = 0; i < FullResourceDescriptor->PartialResourceList.Count; i++)
264 {
265 if (FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].Type != CmResourceTypeDeviceSpecific ||
266 FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize != sizeof(CM_DISK_GEOMETRY_DEVICE_DATA))
267 continue;
268
269 DiskGeometry = (PCM_DISK_GEOMETRY_DEVICE_DATA)&FullResourceDescriptor->PartialResourceList.PartialDescriptors[i + 1];
270 BiosDiskEntry->DiskGeometry = *DiskGeometry;
271
272 return STATUS_SUCCESS;
273 }
274
275 return STATUS_UNSUCCESSFUL;
276}
277
278static NTSTATUS
279NTAPI
280SystemConfigurationDataQueryRoutine(
281 PWSTR ValueName,
282 ULONG ValueType,
283 PVOID ValueData,
284 ULONG ValueLength,
285 PVOID Context,
286 PVOID EntryContext)
287{
288 PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
289 PCM_INT13_DRIVE_PARAMETER* Int13Drives = (PCM_INT13_DRIVE_PARAMETER*)Context;
290 ULONG i;
291
292 if (ValueType != REG_FULL_RESOURCE_DESCRIPTOR ||
293 ValueLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR))
294 return STATUS_UNSUCCESSFUL;
295
296 FullResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)ValueData;
297
298 /* Hm. Version and Revision are not set on Microsoft Windows XP... */
299#if 0
300 if (FullResourceDescriptor->PartialResourceList.Version != 1 ||
301 FullResourceDescriptor->PartialResourceList.Revision != 1)
302 return STATUS_UNSUCCESSFUL;
303#endif
304
305 for (i = 0; i < FullResourceDescriptor->PartialResourceList.Count; i++)
306 {
307 if (FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].Type != CmResourceTypeDeviceSpecific ||
308 FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize % sizeof(CM_INT13_DRIVE_PARAMETER) != 0)
309 continue;
310
311 *Int13Drives = (CM_INT13_DRIVE_PARAMETER*)RtlAllocateHeap(ProcessHeap, 0,
312 FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize);
313 if (*Int13Drives == NULL)
314 return STATUS_NO_MEMORY;
315
316 memcpy(*Int13Drives,
317 &FullResourceDescriptor->PartialResourceList.PartialDescriptors[i + 1],
318 FullResourceDescriptor->PartialResourceList.PartialDescriptors[i].u.DeviceSpecificData.DataSize);
319 return STATUS_SUCCESS;
320 }
321
322 return STATUS_UNSUCCESSFUL;
323}
324
325
326static VOID
327EnumerateBiosDiskEntries(
328 IN PPARTLIST PartList)
329{
330 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
331 WCHAR Name[120];
332 ULONG AdapterCount;
333 ULONG ControllerCount;
334 ULONG DiskCount;
335 NTSTATUS Status;
336 PCM_INT13_DRIVE_PARAMETER Int13Drives;
337 PBIOSDISKENTRY BiosDiskEntry;
338
339#define ROOT_NAME L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter"
340
341 memset(QueryTable, 0, sizeof(QueryTable));
342
343 QueryTable[1].Name = L"Configuration Data";
344 QueryTable[1].QueryRoutine = SystemConfigurationDataQueryRoutine;
345 Int13Drives = NULL;
346 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
347 L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System",
348 &QueryTable[1],
349 (PVOID)&Int13Drives,
350 NULL);
351 if (!NT_SUCCESS(Status))
352 {
353 DPRINT1("Unable to query the 'Configuration Data' key in '\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System', status=%lx\n", Status);
354 return;
355 }
356
357 for (AdapterCount = 0; ; ++AdapterCount)
358 {
359 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
360 L"%s\\%lu",
361 ROOT_NAME, AdapterCount);
362 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
363 Name,
364 &QueryTable[2],
365 NULL,
366 NULL);
367 if (!NT_SUCCESS(Status))
368 {
369 break;
370 }
371
372 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
373 L"%s\\%lu\\DiskController",
374 ROOT_NAME, AdapterCount);
375 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
376 Name,
377 &QueryTable[2],
378 NULL,
379 NULL);
380 if (NT_SUCCESS(Status))
381 {
382 for (ControllerCount = 0; ; ++ControllerCount)
383 {
384 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
385 L"%s\\%lu\\DiskController\\%lu",
386 ROOT_NAME, AdapterCount, ControllerCount);
387 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
388 Name,
389 &QueryTable[2],
390 NULL,
391 NULL);
392 if (!NT_SUCCESS(Status))
393 {
394 RtlFreeHeap(ProcessHeap, 0, Int13Drives);
395 return;
396 }
397
398 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
399 L"%s\\%lu\\DiskController\\%lu\\DiskPeripheral",
400 ROOT_NAME, AdapterCount, ControllerCount);
401 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
402 Name,
403 &QueryTable[2],
404 NULL,
405 NULL);
406 if (NT_SUCCESS(Status))
407 {
408 QueryTable[0].Name = L"Identifier";
409 QueryTable[0].QueryRoutine = DiskIdentifierQueryRoutine;
410 QueryTable[1].Name = L"Configuration Data";
411 QueryTable[1].QueryRoutine = DiskConfigurationDataQueryRoutine;
412
413 for (DiskCount = 0; ; ++DiskCount)
414 {
415 BiosDiskEntry = (BIOSDISKENTRY*)RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(BIOSDISKENTRY));
416 if (BiosDiskEntry == NULL)
417 {
418 RtlFreeHeap(ProcessHeap, 0, Int13Drives);
419 return;
420 }
421
422 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
423 L"%s\\%lu\\DiskController\\%lu\\DiskPeripheral\\%lu",
424 ROOT_NAME, AdapterCount, ControllerCount, DiskCount);
425 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
426 Name,
427 QueryTable,
428 (PVOID)BiosDiskEntry,
429 NULL);
430 if (!NT_SUCCESS(Status))
431 {
432 RtlFreeHeap(ProcessHeap, 0, BiosDiskEntry);
433 RtlFreeHeap(ProcessHeap, 0, Int13Drives);
434 return;
435 }
436
437 BiosDiskEntry->AdapterNumber = 0; // And NOT "AdapterCount" as it needs to be hardcoded for BIOS!
438 BiosDiskEntry->ControllerNumber = ControllerCount;
439 BiosDiskEntry->DiskNumber = DiskCount;
440 BiosDiskEntry->DiskEntry = NULL;
441
442 if (DiskCount < Int13Drives[0].NumberDrives)
443 {
444 BiosDiskEntry->Int13DiskData = Int13Drives[DiskCount];
445 }
446 else
447 {
448 DPRINT1("Didn't find Int13 drive data for disk %u\n", DiskCount);
449 }
450
451 InsertTailList(&PartList->BiosDiskListHead, &BiosDiskEntry->ListEntry);
452
453 DPRINT("--->\n");
454 DPRINT("AdapterNumber: %lu\n", BiosDiskEntry->AdapterNumber);
455 DPRINT("ControllerNumber: %lu\n", BiosDiskEntry->ControllerNumber);
456 DPRINT("DiskNumber: %lu\n", BiosDiskEntry->DiskNumber);
457 DPRINT("Signature: %08lx\n", BiosDiskEntry->Signature);
458 DPRINT("Checksum: %08lx\n", BiosDiskEntry->Checksum);
459 DPRINT("BytesPerSector: %lu\n", BiosDiskEntry->DiskGeometry.BytesPerSector);
460 DPRINT("NumberOfCylinders: %lu\n", BiosDiskEntry->DiskGeometry.NumberOfCylinders);
461 DPRINT("NumberOfHeads: %lu\n", BiosDiskEntry->DiskGeometry.NumberOfHeads);
462 DPRINT("DriveSelect: %02x\n", BiosDiskEntry->Int13DiskData.DriveSelect);
463 DPRINT("MaxCylinders: %lu\n", BiosDiskEntry->Int13DiskData.MaxCylinders);
464 DPRINT("SectorsPerTrack: %d\n", BiosDiskEntry->Int13DiskData.SectorsPerTrack);
465 DPRINT("MaxHeads: %d\n", BiosDiskEntry->Int13DiskData.MaxHeads);
466 DPRINT("NumberDrives: %d\n", BiosDiskEntry->Int13DiskData.NumberDrives);
467 DPRINT("<---\n");
468 }
469 }
470 }
471 }
472 }
473
474 RtlFreeHeap(ProcessHeap, 0, Int13Drives);
475
476#undef ROOT_NAME
477}
478
479
480/*
481 * Detects whether a disk is a "super-floppy", i.e. an unpartitioned
482 * disk with only a valid VBR, as reported by IoReadPartitionTable()
483 * and IoWritePartitionTable():
484 * only one single partition starting at offset zero and spanning the
485 * whole disk, without hidden sectors, whose type is FAT16 non-bootable.
486 *
487 * Accessing \Device\HarddiskN\Partition0 or Partition1 on such disks
488 * returns the same data.
489 */
490BOOLEAN
491IsDiskSuperFloppy2(
492 _In_ const DISK_PARTITION_INFO* DiskInfo,
493 _In_opt_ const ULONGLONG* DiskSize,
494 _In_ const PARTITION_INFORMATION* PartitionInfo)
495{
496 /* Structure size must be valid */
497 if (DiskInfo->SizeOfPartitionInfo < RTL_SIZEOF_THROUGH_FIELD(DISK_PARTITION_INFO, Mbr))
498 return FALSE;
499
500 /* The layout must be MBR */
501 if (DiskInfo->PartitionStyle != PARTITION_STYLE_MBR)
502 return FALSE;
503
504 /* The single partition must start at the beginning of the disk */
505 if (!(PartitionInfo->StartingOffset.QuadPart == 0 &&
506 PartitionInfo->HiddenSectors == 0))
507 {
508 return FALSE;
509 }
510
511 /* The disk signature is usually set to 1; warn in case it's not */
512 if (DiskInfo->Mbr.Signature != 1)
513 {
514 DPRINT1("Super-Floppy signature %08x != 1\n", DiskInfo->Mbr.Signature);
515 }
516
517 /* The partition must be recognized and report as FAT16 non-bootable */
518 if ((PartitionInfo->RecognizedPartition != TRUE) ||
519 (PartitionInfo->PartitionType != PARTITION_FAT_16) ||
520 (PartitionInfo->BootIndicator != FALSE))
521 {
522 DPRINT1("Super-Floppy does not return default settings:\n"
523 " RecognizedPartition = %s, expected TRUE\n"
524 " PartitionType = 0x%02x, expected 0x04 (PARTITION_FAT_16)\n"
525 " BootIndicator = %s, expected FALSE\n",
526 PartitionInfo->RecognizedPartition ? "TRUE" : "FALSE",
527 PartitionInfo->PartitionType,
528 PartitionInfo->BootIndicator ? "TRUE" : "FALSE");
529 }
530
531 /* The partition and disk sizes should agree */
532 if (DiskSize && (PartitionInfo->PartitionLength.QuadPart != *DiskSize))
533 {
534 DPRINT1("PartitionLength = %I64u is different from DiskSize = %I64u\n",
535 PartitionInfo->PartitionLength.QuadPart, *DiskSize);
536 }
537
538 return TRUE;
539}
540
541BOOLEAN
542IsDiskSuperFloppy(
543 _In_ const DRIVE_LAYOUT_INFORMATION* Layout,
544 _In_opt_ const ULONGLONG* DiskSize)
545{
546 DISK_PARTITION_INFO DiskInfo;
547
548 /* The layout must contain only one partition */
549 if (Layout->PartitionCount != 1)
550 return FALSE;
551
552 /* Build the disk partition info */
553 DiskInfo.SizeOfPartitionInfo = RTL_SIZEOF_THROUGH_FIELD(DISK_PARTITION_INFO, Mbr);
554 DiskInfo.PartitionStyle = PARTITION_STYLE_MBR;
555 DiskInfo.Mbr.Signature = Layout->Signature;
556 DiskInfo.Mbr.CheckSum = 0; // Dummy value
557
558 /* Call the helper on the single partition entry */
559 return IsDiskSuperFloppy2(&DiskInfo, DiskSize, Layout->PartitionEntry);
560}
561
562BOOLEAN
563IsDiskSuperFloppyEx(
564 _In_ const DRIVE_LAYOUT_INFORMATION_EX* LayoutEx,
565 _In_opt_ const ULONGLONG* DiskSize)
566{
567 DISK_PARTITION_INFO DiskInfo;
568 const PARTITION_INFORMATION_EX* PartitionInfoEx;
569 PARTITION_INFORMATION PartitionInfo;
570
571 /* The layout must be MBR and contain only one partition */
572 if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR)
573 return FALSE;
574 if (LayoutEx->PartitionCount != 1)
575 return FALSE;
576
577 /* Build the disk partition info */
578 DiskInfo.SizeOfPartitionInfo = RTL_SIZEOF_THROUGH_FIELD(DISK_PARTITION_INFO, Mbr);
579 DiskInfo.PartitionStyle = PARTITION_STYLE_MBR; // LayoutEx->PartitionStyle;
580 DiskInfo.Mbr.Signature = LayoutEx->Mbr.Signature;
581 DiskInfo.Mbr.CheckSum = 0; // Dummy value
582
583 /* Convert the single partition entry */
584 PartitionInfoEx = LayoutEx->PartitionEntry;
585
586 PartitionInfo.StartingOffset = PartitionInfoEx->StartingOffset;
587 PartitionInfo.PartitionLength = PartitionInfoEx->PartitionLength;
588 PartitionInfo.HiddenSectors = PartitionInfoEx->Mbr.HiddenSectors;
589 PartitionInfo.PartitionNumber = PartitionInfoEx->PartitionNumber;
590 PartitionInfo.PartitionType = PartitionInfoEx->Mbr.PartitionType;
591 PartitionInfo.BootIndicator = PartitionInfoEx->Mbr.BootIndicator;
592 PartitionInfo.RecognizedPartition = PartitionInfoEx->Mbr.RecognizedPartition;
593 PartitionInfo.RewritePartition = PartitionInfoEx->RewritePartition;
594
595 /* Call the helper on the single partition entry */
596 return IsDiskSuperFloppy2(&DiskInfo, DiskSize, &PartitionInfo);
597}
598
599BOOLEAN
600IsSuperFloppy(
601 _In_ PDISKENTRY DiskEntry)
602{
603 ULONGLONG DiskSize;
604
605 /* No layout buffer: we cannot say anything yet */
606 if (!DiskEntry->LayoutBuffer)
607 return FALSE;
608
609 /* The disk must be MBR */
610 if (DiskEntry->DiskStyle != PARTITION_STYLE_MBR)
611 return FALSE;
612
613 DiskSize = GetDiskSizeInBytes(DiskEntry);
614 return IsDiskSuperFloppy(DiskEntry->LayoutBuffer, &DiskSize);
615}
616
617
618/*
619 * Inserts the disk region represented by PartEntry into either
620 * the primary or the logical partition list of the given disk.
621 * The lists are kept sorted by increasing order of start sectors.
622 * Of course no disk region should overlap at all with one another.
623 */
624static
625BOOLEAN
626InsertDiskRegion(
627 IN PDISKENTRY DiskEntry,
628 IN PPARTENTRY PartEntry,
629 IN BOOLEAN LogicalPartition)
630{
631 PLIST_ENTRY List;
632 PLIST_ENTRY Entry;
633 PPARTENTRY PartEntry2;
634
635 /* Use the correct partition list */
636 if (LogicalPartition)
637 List = &DiskEntry->LogicalPartListHead;
638 else
639 List = &DiskEntry->PrimaryPartListHead;
640
641 /* Find the first disk region before which we need to insert the new one */
642 for (Entry = List->Flink; Entry != List; Entry = Entry->Flink)
643 {
644 PartEntry2 = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
645
646 /* Ignore any unused empty region */
647 if ((PartEntry2->PartitionType == PARTITION_ENTRY_UNUSED &&
648 PartEntry2->StartSector.QuadPart == 0) || PartEntry2->SectorCount.QuadPart == 0)
649 {
650 continue;
651 }
652
653 /* If the current region ends before the one to be inserted, try again */
654 if (PartEntry2->StartSector.QuadPart + PartEntry2->SectorCount.QuadPart - 1 < PartEntry->StartSector.QuadPart)
655 continue;
656
657 /*
658 * One of the disk region boundaries crosses the desired region
659 * (it starts after the desired region, or ends before the end
660 * of the desired region): this is an impossible situation because
661 * disk regions (partitions) cannot overlap!
662 * Throw an error and bail out.
663 */
664 if (max(PartEntry->StartSector.QuadPart, PartEntry2->StartSector.QuadPart)
665 <=
666 min( PartEntry->StartSector.QuadPart + PartEntry->SectorCount.QuadPart - 1,
667 PartEntry2->StartSector.QuadPart + PartEntry2->SectorCount.QuadPart - 1))
668 {
669 DPRINT1("Disk region overlap problem, stopping there!\n"
670 "Partition to be inserted:\n"
671 " StartSector = %I64u ; EndSector = %I64u\n"
672 "Existing disk region:\n"
673 " StartSector = %I64u ; EndSector = %I64u\n",
674 PartEntry->StartSector.QuadPart,
675 PartEntry->StartSector.QuadPart + PartEntry->SectorCount.QuadPart - 1,
676 PartEntry2->StartSector.QuadPart,
677 PartEntry2->StartSector.QuadPart + PartEntry2->SectorCount.QuadPart - 1);
678 return FALSE;
679 }
680
681 /* We have found the first region before which the new one has to be inserted */
682 break;
683 }
684
685 /* Insert the disk region */
686 InsertTailList(Entry, &PartEntry->ListEntry);
687 return TRUE;
688}
689
690static
691PPARTENTRY
692CreateInsertBlankRegion(
693 IN PDISKENTRY DiskEntry,
694 IN OUT PLIST_ENTRY ListHead,
695 IN ULONGLONG StartSector,
696 IN ULONGLONG SectorCount,
697 IN BOOLEAN LogicalSpace)
698{
699 PPARTENTRY NewPartEntry;
700
701 NewPartEntry = RtlAllocateHeap(ProcessHeap,
702 HEAP_ZERO_MEMORY,
703 sizeof(PARTENTRY));
704 if (!NewPartEntry)
705 return NULL;
706
707 NewPartEntry->DiskEntry = DiskEntry;
708
709 NewPartEntry->StartSector.QuadPart = StartSector;
710 NewPartEntry->SectorCount.QuadPart = SectorCount;
711
712 NewPartEntry->LogicalPartition = LogicalSpace;
713 NewPartEntry->IsPartitioned = FALSE;
714 NewPartEntry->PartitionType = PARTITION_ENTRY_UNUSED;
715 NewPartEntry->Volume = NULL;
716
717 DPRINT1("First Sector : %I64u\n", NewPartEntry->StartSector.QuadPart);
718 DPRINT1("Last Sector : %I64u\n", NewPartEntry->StartSector.QuadPart + NewPartEntry->SectorCount.QuadPart - 1);
719 DPRINT1("Total Sectors: %I64u\n", NewPartEntry->SectorCount.QuadPart);
720
721 /* Insert the new entry into the list */
722 InsertTailList(ListHead, &NewPartEntry->ListEntry);
723
724 return NewPartEntry;
725}
726
727static
728VOID
729DestroyRegion(
730 _Inout_ PPARTENTRY PartEntry)
731{
732 // RemoveEntryList(&PartEntry->Volume->ListEntry);
733 if (PartEntry->Volume)
734 RtlFreeHeap(ProcessHeap, 0, PartEntry->Volume);
735 RtlFreeHeap(ProcessHeap, 0, PartEntry);
736}
737
738static
739VOID
740AddLogicalDiskSpace(
741 _In_ PDISKENTRY DiskEntry)
742{
743 ULONGLONG StartSector;
744 ULONGLONG SectorCount;
745 PPARTENTRY NewPartEntry;
746
747 DPRINT("AddLogicalDiskSpace()\n");
748
749 /* Create a partition entry that represents the empty space in the container partition */
750
751 StartSector = DiskEntry->ExtendedPartition->StartSector.QuadPart + (ULONGLONG)DiskEntry->SectorAlignment;
752 SectorCount = DiskEntry->ExtendedPartition->SectorCount.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment;
753
754 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
755 &DiskEntry->LogicalPartListHead,
756 StartSector,
757 SectorCount,
758 TRUE);
759 if (!NewPartEntry)
760 DPRINT1("Failed to create a new empty region for full extended partition space!\n");
761}
762
763// TODO: Improve upon the PartitionInfo parameter later
764// (see VDS::CREATE_PARTITION_PARAMETERS and PPARTITION_INFORMATION_MBR/GPT for example)
765// So far we only use it as the optional type of the partition to create.
766//
767// See also CreatePartition().
768static
769BOOLEAN
770InitializePartitionEntry(
771 _Inout_ PPARTENTRY PartEntry,
772 _In_opt_ ULONGLONG SizeBytes,
773 _In_opt_ ULONG_PTR PartitionInfo)
774{
775 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
776 ULONGLONG SectorCount;
777 BOOLEAN isContainer = IsContainerPartition((UCHAR)PartitionInfo);
778
779 DPRINT1("Current entry sector count: %I64u\n", PartEntry->SectorCount.QuadPart);
780
781 /* The entry must not be already partitioned and not be void */
782 ASSERT(!PartEntry->IsPartitioned);
783 ASSERT(PartEntry->SectorCount.QuadPart);
784 ASSERT(!PartEntry->Volume);
785
786 /* Either we create a primary/logical partition, or we create an
787 * extended partition but the entry must not be logical space */
788 ASSERT(!isContainer || !PartEntry->LogicalPartition);
789
790 /* Convert the size in bytes to sector count. SizeBytes being
791 * zero means the caller wants to use all the empty space. */
792 if ((SizeBytes == 0) || (SizeBytes == GetPartEntrySizeInBytes(PartEntry)))
793 {
794 /* Use all of the unpartitioned disk space */
795 SectorCount = PartEntry->SectorCount.QuadPart;
796 }
797 else
798 {
799 SectorCount = SizeBytes / DiskEntry->BytesPerSector;
800 if (SectorCount == 0)
801 {
802 /* SizeBytes was certainly less than the minimal size, so fail */
803 DPRINT1("Partition size %I64u too small\n", SizeBytes);
804 return FALSE;
805 }
806 }
807 DPRINT1(" New sector count: %I64u\n", SectorCount);
808
809 /* Fail if we request more sectors than what the entry actually contains */
810 if (SectorCount > PartEntry->SectorCount.QuadPart)
811 return FALSE;
812
813 if ((SectorCount == 0) ||
814 (AlignDown(PartEntry->StartSector.QuadPart + SectorCount, DiskEntry->SectorAlignment) -
815 PartEntry->StartSector.QuadPart == PartEntry->SectorCount.QuadPart))
816 {
817 /* Reuse the whole current entry */
818 }
819 else
820 {
821 ULONGLONG StartSector;
822 ULONGLONG SectorCount2;
823 PPARTENTRY NewPartEntry;
824
825 /* Create a partition entry that represents the remaining space
826 * after the partition to be initialized */
827
828 StartSector = AlignDown(PartEntry->StartSector.QuadPart + SectorCount, DiskEntry->SectorAlignment);
829 SectorCount2 = PartEntry->StartSector.QuadPart + PartEntry->SectorCount.QuadPart - StartSector;
830
831 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
832 PartEntry->ListEntry.Flink,
833 StartSector,
834 SectorCount2,
835 PartEntry->LogicalPartition);
836 if (!NewPartEntry)
837 {
838 DPRINT1("Failed to create a new empty region for disk space!\n");
839 return FALSE;
840 }
841
842 /* Resize down the partition entry; its StartSector remains the same */
843 PartEntry->SectorCount.QuadPart = StartSector - PartEntry->StartSector.QuadPart;
844 }
845
846 /* Convert to a new partition entry */
847 PartEntry->New = TRUE;
848 PartEntry->IsPartitioned = TRUE;
849
850 PartEntry->BootIndicator = FALSE;
851 if (PartitionInfo)
852 {
853 if (!isContainer)
854 {
855 PartEntry->PartitionType = (UCHAR)PartitionInfo;
856 }
857 else
858 {
859 /* Set the correct extended container partition type,
860 * depending on whether it is contained below or above
861 * the 1024-cylinder (usually 8.4GB/7.8GiB) boundary:
862 * - below: INT13h CHS partition;
863 * - above: Extended INT13h LBA partition. */
864 if ((PartEntry->StartSector.QuadPart + PartEntry->SectorCount.QuadPart - 1)
865 / (DiskEntry->TracksPerCylinder * DiskEntry->SectorsPerTrack) < 1024)
866 {
867 PartEntry->PartitionType = PARTITION_EXTENDED;
868 }
869 else
870 {
871 PartEntry->PartitionType = PARTITION_XINT13_EXTENDED;
872 }
873 }
874 }
875 else
876 {
877// FIXME: Use FileSystemToMBRPartitionType() only for MBR, otherwise use PARTITION_BASIC_DATA_GUID.
878 ASSERT(!isContainer);
879 PartEntry->PartitionType = FileSystemToMBRPartitionType(L"RAW",
880 PartEntry->StartSector.QuadPart,
881 PartEntry->SectorCount.QuadPart);
882 }
883 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
884
885 if (isContainer)
886 {
887 DiskEntry->ExtendedPartition = PartEntry;
888 AddLogicalDiskSpace(DiskEntry);
889 }
890
891 DPRINT1("First Sector : %I64u\n", PartEntry->StartSector.QuadPart);
892 DPRINT1("Last Sector : %I64u\n", PartEntry->StartSector.QuadPart + PartEntry->SectorCount.QuadPart - 1);
893 DPRINT1("Total Sectors: %I64u\n", PartEntry->SectorCount.QuadPart);
894
895 return TRUE;
896}
897
898static
899VOID
900InitPartitionDeviceName(
901 _Inout_ PPARTENTRY PartEntry)
902{
903 NTSTATUS Status;
904
905 /* Ignore if this is a container partition */
906 if (IsContainerPartition(PartEntry->PartitionType))
907 return;
908 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
909
910 /* Make a device name for the partition */
911 Status = RtlStringCchPrintfW(PartEntry->DeviceName,
912 _countof(PartEntry->DeviceName),
913 L"\\Device\\Harddisk%lu\\Partition%lu",
914 PartEntry->DiskEntry->DiskNumber,
915 PartEntry->PartitionNumber);
916 ASSERT(NT_SUCCESS(Status));
917}
918
919static
920VOID
921InitVolumeDeviceName(
922 _Inout_ PVOLENTRY Volume)
923{
924 NTSTATUS Status;
925 PPARTENTRY PartEntry;
926
927 /* If we already have a volume device name, do nothing more */
928 if (*Volume->Info.DeviceName)
929 return;
930
931 /* Use the partition device name as a temporary volume device name */
932 // TODO: Ask instead the MOUNTMGR for the name.
933 PartEntry = Volume->PartEntry;
934 ASSERT(PartEntry);
935 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
936
937 /* Copy the volume device name */
938 Status = RtlStringCchCopyW(Volume->Info.DeviceName,
939 _countof(Volume->Info.DeviceName),
940 PartEntry->DeviceName);
941 ASSERT(NT_SUCCESS(Status));
942}
943
944static
945PVOLENTRY
946InitVolume(
947 _In_ PPARTLIST List,
948 _In_opt_ PPARTENTRY PartEntry)
949{
950 PVOLENTRY Volume;
951
952 Volume = RtlAllocateHeap(ProcessHeap,
953 HEAP_ZERO_MEMORY,
954 sizeof(VOLENTRY));
955 if (!Volume)
956 return NULL;
957
958 /* Reset some volume information */
959
960 /* No device name for now */
961 Volume->Info.DeviceName[0] = UNICODE_NULL;
962 // Volume->Info.VolumeName[0] = UNICODE_NULL;
963
964 /* Initialize the volume letter and label */
965 Volume->Info.DriveLetter = UNICODE_NULL;
966 Volume->Info.VolumeLabel[0] = UNICODE_NULL;
967
968 /* Specify the volume as initially unformatted */
969 Volume->Info.FileSystem[0] = UNICODE_NULL;
970 Volume->FormatState = Unformatted;
971 Volume->NeedsCheck = FALSE;
972 Volume->New = TRUE;
973
974 if (PartEntry)
975 {
976 ASSERT(PartEntry->DiskEntry->PartList == List);
977 Volume->PartEntry = PartEntry;
978 }
979 InsertTailList(&List->VolumesList, &Volume->ListEntry);
980 return Volume;
981}
982
983static
984VOID
985AddPartitionToDisk(
986 IN ULONG DiskNumber,
987 IN PDISKENTRY DiskEntry,
988 IN ULONG PartitionIndex,
989 IN BOOLEAN LogicalPartition)
990{
991 PPARTITION_INFORMATION PartitionInfo;
992 PPARTENTRY PartEntry;
993
994 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartitionIndex];
995
996 /* Ignore empty partitions */
997 if (PartitionInfo->PartitionType == PARTITION_ENTRY_UNUSED)
998 return;
999 /* Request must be consistent, though! */
1000 ASSERT(!(LogicalPartition && IsContainerPartition(PartitionInfo->PartitionType)));
1001
1002 PartEntry = RtlAllocateHeap(ProcessHeap,
1003 HEAP_ZERO_MEMORY,
1004 sizeof(PARTENTRY));
1005 if (!PartEntry)
1006 return;
1007
1008 PartEntry->DiskEntry = DiskEntry;
1009
1010 PartEntry->StartSector.QuadPart = (ULONGLONG)PartitionInfo->StartingOffset.QuadPart / DiskEntry->BytesPerSector;
1011 PartEntry->SectorCount.QuadPart = (ULONGLONG)PartitionInfo->PartitionLength.QuadPart / DiskEntry->BytesPerSector;
1012
1013 PartEntry->BootIndicator = PartitionInfo->BootIndicator;
1014 PartEntry->PartitionType = PartitionInfo->PartitionType;
1015
1016 PartEntry->LogicalPartition = LogicalPartition;
1017 PartEntry->IsPartitioned = TRUE;
1018 PartEntry->OnDiskPartitionNumber = PartitionInfo->PartitionNumber;
1019 PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
1020 PartEntry->PartitionIndex = PartitionIndex;
1021 InitPartitionDeviceName(PartEntry);
1022
1023 /* No volume initially */
1024 PartEntry->Volume = NULL;
1025
1026 if (IsContainerPartition(PartEntry->PartitionType))
1027 {
1028 if (!LogicalPartition && DiskEntry->ExtendedPartition == NULL)
1029 DiskEntry->ExtendedPartition = PartEntry;
1030 }
1031 else if (IsRecognizedPartition(PartEntry->PartitionType) || // PartitionInfo->RecognizedPartition
1032 IsOEMPartition(PartEntry->PartitionType))
1033 {
1034 PVOLENTRY Volume;
1035 NTSTATUS Status;
1036
1037 ASSERT(PartEntry->PartitionNumber != 0);
1038
1039 /* The PARTMGR should have notified the MOUNTMGR that a volume
1040 * associated with this partition had to be created */
1041 Volume = InitVolume(DiskEntry->PartList, PartEntry);
1042 if (!Volume)
1043 {
1044 DPRINT1("Couldn't allocate a volume for device '%S'\n",
1045 PartEntry->DeviceName);
1046 goto SkipVolume;
1047 }
1048 PartEntry->Volume = Volume;
1049 InitVolumeDeviceName(Volume);
1050 Volume->New = FALSE;
1051
1052 /* Attach and mount the volume */
1053 Status = MountVolume(&Volume->Info, PartEntry->PartitionType);
1054 if (!NT_SUCCESS(Status))
1055 {
1056 DPRINT1("Failed to mount volume '%S', Status 0x%08lx\n",
1057 Volume->Info.DeviceName, Status);
1058 }
1059
1060 //
1061 // FIXME: TEMP Backward-compatibility: Set the FormatState
1062 // flag in accordance with the FileSystem volume value.
1063 //
1064 /*
1065 * MountVolume() determines whether the given volume is actually
1066 * unformatted, if it was mounted with RawFS and the partition
1067 * type has specific values for FAT volumes. If so, the volume
1068 * stays mounted with RawFS (the FileSystem is "RAW"). However,
1069 * if the partition type has different values, the volume is
1070 * considered as having an unknown format (it may or may not be
1071 * formatted) and the FileSystem value has been emptied.
1072 */
1073 if (IsUnknown(&Volume->Info))
1074 Volume->FormatState = UnknownFormat;
1075 else if (IsUnformatted(&Volume->Info)) // FileSystem is "RAW"
1076 Volume->FormatState = Unformatted;
1077 else // !IsUnknown && !IsUnformatted == IsFormatted
1078 Volume->FormatState = Formatted;
1079SkipVolume:;
1080 }
1081 else
1082 {
1083 /* Unknown partition (may or may not be actually formatted):
1084 * the partition is hidden, hence no volume */
1085 DPRINT1("Disk %lu Partition %lu is not recognized (Type 0x%02x)\n",
1086 DiskEntry->DiskNumber, PartEntry->PartitionNumber,
1087 PartEntry->PartitionType);
1088 }
1089
1090 InsertDiskRegion(DiskEntry, PartEntry, LogicalPartition);
1091}
1092
1093static
1094VOID
1095ScanForUnpartitionedDiskSpace(
1096 IN PDISKENTRY DiskEntry)
1097{
1098 ULONGLONG StartSector;
1099 ULONGLONG SectorCount;
1100 ULONGLONG LastStartSector;
1101 ULONGLONG LastSectorCount;
1102 ULONGLONG LastUnusedSectorCount;
1103 PPARTENTRY PartEntry;
1104 PPARTENTRY NewPartEntry;
1105 PLIST_ENTRY Entry;
1106
1107 DPRINT("ScanForUnpartitionedDiskSpace()\n");
1108
1109 if (IsListEmpty(&DiskEntry->PrimaryPartListHead))
1110 {
1111 DPRINT1("No primary partition!\n");
1112
1113 /* Create a partition entry that represents the empty disk */
1114
1115 if (DiskEntry->SectorAlignment < 2048)
1116 StartSector = 2048ULL;
1117 else
1118 StartSector = (ULONGLONG)DiskEntry->SectorAlignment;
1119 SectorCount = AlignDown(DiskEntry->SectorCount.QuadPart, DiskEntry->SectorAlignment) - StartSector;
1120
1121 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
1122 &DiskEntry->PrimaryPartListHead,
1123 StartSector,
1124 SectorCount,
1125 FALSE);
1126 if (!NewPartEntry)
1127 DPRINT1("Failed to create a new empty region for full disk space!\n");
1128
1129 return;
1130 }
1131
1132 /* Start partition at head 1, cylinder 0 */
1133 if (DiskEntry->SectorAlignment < 2048)
1134 LastStartSector = 2048ULL;
1135 else
1136 LastStartSector = (ULONGLONG)DiskEntry->SectorAlignment;
1137 LastSectorCount = 0ULL;
1138 LastUnusedSectorCount = 0ULL;
1139
1140 for (Entry = DiskEntry->PrimaryPartListHead.Flink;
1141 Entry != &DiskEntry->PrimaryPartListHead;
1142 Entry = Entry->Flink)
1143 {
1144 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
1145
1146 if (PartEntry->PartitionType != PARTITION_ENTRY_UNUSED ||
1147 PartEntry->SectorCount.QuadPart != 0ULL)
1148 {
1149 LastUnusedSectorCount =
1150 PartEntry->StartSector.QuadPart - (LastStartSector + LastSectorCount);
1151
1152 if (PartEntry->StartSector.QuadPart > (LastStartSector + LastSectorCount) &&
1153 LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
1154 {
1155 DPRINT("Unpartitioned disk space %I64u sectors\n", LastUnusedSectorCount);
1156
1157 StartSector = LastStartSector + LastSectorCount;
1158 SectorCount = AlignDown(StartSector + LastUnusedSectorCount, DiskEntry->SectorAlignment) - StartSector;
1159
1160 /* Insert the table into the list */
1161 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
1162 &PartEntry->ListEntry,
1163 StartSector,
1164 SectorCount,
1165 FALSE);
1166 if (!NewPartEntry)
1167 {
1168 DPRINT1("Failed to create a new empty region for disk space!\n");
1169 return;
1170 }
1171 }
1172
1173 LastStartSector = PartEntry->StartSector.QuadPart;
1174 LastSectorCount = PartEntry->SectorCount.QuadPart;
1175 }
1176 }
1177
1178 /* Check for trailing unpartitioned disk space */
1179 if ((LastStartSector + LastSectorCount) < DiskEntry->SectorCount.QuadPart)
1180 {
1181 LastUnusedSectorCount = AlignDown(DiskEntry->SectorCount.QuadPart - (LastStartSector + LastSectorCount), DiskEntry->SectorAlignment);
1182
1183 if (LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
1184 {
1185 DPRINT("Unpartitioned disk space: %I64u sectors\n", LastUnusedSectorCount);
1186
1187 StartSector = LastStartSector + LastSectorCount;
1188 SectorCount = AlignDown(StartSector + LastUnusedSectorCount, DiskEntry->SectorAlignment) - StartSector;
1189
1190 /* Append the table to the list */
1191 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
1192 &DiskEntry->PrimaryPartListHead,
1193 StartSector,
1194 SectorCount,
1195 FALSE);
1196 if (!NewPartEntry)
1197 {
1198 DPRINT1("Failed to create a new empty region for trailing disk space!\n");
1199 return;
1200 }
1201 }
1202 }
1203
1204 if (DiskEntry->ExtendedPartition != NULL)
1205 {
1206 if (IsListEmpty(&DiskEntry->LogicalPartListHead))
1207 {
1208 DPRINT1("No logical partition!\n");
1209
1210 /* Create a partition entry that represents the empty extended partition */
1211 AddLogicalDiskSpace(DiskEntry);
1212 return;
1213 }
1214
1215 /* Start partition at head 1, cylinder 0 */
1216 LastStartSector = DiskEntry->ExtendedPartition->StartSector.QuadPart + (ULONGLONG)DiskEntry->SectorAlignment;
1217 LastSectorCount = 0ULL;
1218 LastUnusedSectorCount = 0ULL;
1219
1220 for (Entry = DiskEntry->LogicalPartListHead.Flink;
1221 Entry != &DiskEntry->LogicalPartListHead;
1222 Entry = Entry->Flink)
1223 {
1224 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
1225
1226 if (PartEntry->PartitionType != PARTITION_ENTRY_UNUSED ||
1227 PartEntry->SectorCount.QuadPart != 0ULL)
1228 {
1229 LastUnusedSectorCount =
1230 PartEntry->StartSector.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment - (LastStartSector + LastSectorCount);
1231
1232 if ((PartEntry->StartSector.QuadPart - (ULONGLONG)DiskEntry->SectorAlignment) > (LastStartSector + LastSectorCount) &&
1233 LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
1234 {
1235 DPRINT("Unpartitioned disk space %I64u sectors\n", LastUnusedSectorCount);
1236
1237 StartSector = LastStartSector + LastSectorCount;
1238 SectorCount = AlignDown(StartSector + LastUnusedSectorCount, DiskEntry->SectorAlignment) - StartSector;
1239
1240 /* Insert the table into the list */
1241 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
1242 &PartEntry->ListEntry,
1243 StartSector,
1244 SectorCount,
1245 TRUE);
1246 if (!NewPartEntry)
1247 {
1248 DPRINT1("Failed to create a new empty region for extended partition space!\n");
1249 return;
1250 }
1251 }
1252
1253 LastStartSector = PartEntry->StartSector.QuadPart;
1254 LastSectorCount = PartEntry->SectorCount.QuadPart;
1255 }
1256 }
1257
1258 /* Check for trailing unpartitioned disk space */
1259 if ((LastStartSector + LastSectorCount) < DiskEntry->ExtendedPartition->StartSector.QuadPart + DiskEntry->ExtendedPartition->SectorCount.QuadPart)
1260 {
1261 LastUnusedSectorCount = AlignDown(DiskEntry->ExtendedPartition->StartSector.QuadPart +
1262 DiskEntry->ExtendedPartition->SectorCount.QuadPart - (LastStartSector + LastSectorCount),
1263 DiskEntry->SectorAlignment);
1264
1265 if (LastUnusedSectorCount >= (ULONGLONG)DiskEntry->SectorAlignment)
1266 {
1267 DPRINT("Unpartitioned disk space: %I64u sectors\n", LastUnusedSectorCount);
1268
1269 StartSector = LastStartSector + LastSectorCount;
1270 SectorCount = AlignDown(StartSector + LastUnusedSectorCount, DiskEntry->SectorAlignment) - StartSector;
1271
1272 /* Append the table to the list */
1273 NewPartEntry = CreateInsertBlankRegion(DiskEntry,
1274 &DiskEntry->LogicalPartListHead,
1275 StartSector,
1276 SectorCount,
1277 TRUE);
1278 if (!NewPartEntry)
1279 {
1280 DPRINT1("Failed to create a new empty region for extended partition space!\n");
1281 return;
1282 }
1283 }
1284 }
1285 }
1286
1287 DPRINT("ScanForUnpartitionedDiskSpace() done\n");
1288}
1289
1290static
1291VOID
1292SetDiskSignature(
1293 IN PPARTLIST List,
1294 IN PDISKENTRY DiskEntry)
1295{
1296 LARGE_INTEGER SystemTime;
1297 TIME_FIELDS TimeFields;
1298 PLIST_ENTRY Entry2;
1299 PDISKENTRY DiskEntry2;
1300 PUCHAR Buffer;
1301
1302 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
1303 {
1304 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
1305 return;
1306 }
1307
1308 Buffer = (PUCHAR)&DiskEntry->LayoutBuffer->Signature;
1309
1310 while (TRUE)
1311 {
1312 NtQuerySystemTime(&SystemTime);
1313 RtlTimeToTimeFields(&SystemTime, &TimeFields);
1314
1315 Buffer[0] = (UCHAR)(TimeFields.Year & 0xFF) + (UCHAR)(TimeFields.Hour & 0xFF);
1316 Buffer[1] = (UCHAR)(TimeFields.Year >> 8) + (UCHAR)(TimeFields.Minute & 0xFF);
1317 Buffer[2] = (UCHAR)(TimeFields.Month & 0xFF) + (UCHAR)(TimeFields.Second & 0xFF);
1318 Buffer[3] = (UCHAR)(TimeFields.Day & 0xFF) + (UCHAR)(TimeFields.Milliseconds & 0xFF);
1319
1320 if (DiskEntry->LayoutBuffer->Signature == 0)
1321 {
1322 continue;
1323 }
1324
1325 /* Check if the signature already exist */
1326 /* FIXME:
1327 * Check also signatures from disks, which are
1328 * not visible (bootable) by the BIOS.
1329 */
1330 for (Entry2 = List->DiskListHead.Flink;
1331 Entry2 != &List->DiskListHead;
1332 Entry2 = Entry2->Flink)
1333 {
1334 DiskEntry2 = CONTAINING_RECORD(Entry2, DISKENTRY, ListEntry);
1335
1336 if (DiskEntry2->DiskStyle == PARTITION_STYLE_GPT)
1337 {
1338 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
1339 continue;
1340 }
1341
1342 if (DiskEntry != DiskEntry2 &&
1343 DiskEntry->LayoutBuffer->Signature == DiskEntry2->LayoutBuffer->Signature)
1344 break;
1345 }
1346
1347 if (Entry2 == &List->DiskListHead)
1348 break;
1349 }
1350}
1351
1352static
1353VOID
1354UpdateDiskSignatures(
1355 IN PPARTLIST List)
1356{
1357 PLIST_ENTRY Entry;
1358 PDISKENTRY DiskEntry;
1359
1360 /* Update each disk */
1361 for (Entry = List->DiskListHead.Flink;
1362 Entry != &List->DiskListHead;
1363 Entry = Entry->Flink)
1364 {
1365 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
1366
1367 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
1368 {
1369 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
1370 continue;
1371 }
1372
1373 if (DiskEntry->LayoutBuffer &&
1374 DiskEntry->LayoutBuffer->Signature == 0)
1375 {
1376 SetDiskSignature(List, DiskEntry);
1377 DiskEntry->LayoutBuffer->PartitionEntry[0].RewritePartition = TRUE;
1378 }
1379 }
1380}
1381
1382static
1383VOID
1384UpdateHwDiskNumbers(
1385 IN PPARTLIST List)
1386{
1387 PLIST_ENTRY ListEntry;
1388 PBIOSDISKENTRY BiosDiskEntry;
1389 PDISKENTRY DiskEntry;
1390 ULONG HwAdapterNumber = 0;
1391 ULONG HwControllerNumber = 0;
1392 ULONG RemovableDiskCount = 0;
1393
1394 /*
1395 * Enumerate the disks recognized by the BIOS and recompute the disk
1396 * numbers on the system when *ALL* removable disks are not connected.
1397 * The entries are inserted in increasing order of AdapterNumber,
1398 * ControllerNumber and DiskNumber.
1399 */
1400 for (ListEntry = List->BiosDiskListHead.Flink;
1401 ListEntry != &List->BiosDiskListHead;
1402 ListEntry = ListEntry->Flink)
1403 {
1404 BiosDiskEntry = CONTAINING_RECORD(ListEntry, BIOSDISKENTRY, ListEntry);
1405 DiskEntry = BiosDiskEntry->DiskEntry;
1406
1407 /*
1408 * If the adapter or controller numbers change, update them and reset
1409 * the number of removable disks on this adapter/controller.
1410 */
1411 if (HwAdapterNumber != BiosDiskEntry->AdapterNumber ||
1412 HwControllerNumber != BiosDiskEntry->ControllerNumber)
1413 {
1414 HwAdapterNumber = BiosDiskEntry->AdapterNumber;
1415 HwControllerNumber = BiosDiskEntry->ControllerNumber;
1416 RemovableDiskCount = 0;
1417 }
1418
1419 /* Adjust the actual hardware disk number */
1420 if (DiskEntry)
1421 {
1422 ASSERT(DiskEntry->HwDiskNumber == BiosDiskEntry->DiskNumber);
1423
1424 if (DiskEntry->MediaType == RemovableMedia)
1425 {
1426 /* Increase the number of removable disks and set the disk number to zero */
1427 ++RemovableDiskCount;
1428 DiskEntry->HwFixedDiskNumber = 0;
1429 }
1430 else // if (DiskEntry->MediaType == FixedMedia)
1431 {
1432 /* Adjust the fixed disk number, offset by the number of removable disks found before this one */
1433 DiskEntry->HwFixedDiskNumber = BiosDiskEntry->DiskNumber - RemovableDiskCount;
1434 }
1435 }
1436 else
1437 {
1438 DPRINT1("BIOS disk %lu is not recognized by NTOS!\n", BiosDiskEntry->DiskNumber);
1439 }
1440 }
1441}
1442
1443static
1444VOID
1445AddDiskToList(
1446 IN HANDLE FileHandle,
1447 IN ULONG DiskNumber,
1448 IN PPARTLIST List)
1449{
1450 DISK_GEOMETRY DiskGeometry;
1451 SCSI_ADDRESS ScsiAddress;
1452 PDISKENTRY DiskEntry;
1453 IO_STATUS_BLOCK Iosb;
1454 NTSTATUS Status;
1455 PPARTITION_SECTOR Mbr;
1456 PULONG Buffer;
1457 LARGE_INTEGER FileOffset;
1458 WCHAR Identifier[20];
1459 ULONG Checksum;
1460 ULONG Signature;
1461 ULONG i;
1462 PLIST_ENTRY ListEntry;
1463 PBIOSDISKENTRY BiosDiskEntry;
1464 ULONG LayoutBufferSize;
1465 PDRIVE_LAYOUT_INFORMATION NewLayoutBuffer;
1466
1467 /* Retrieve the drive geometry */
1468 Status = NtDeviceIoControlFile(FileHandle,
1469 NULL,
1470 NULL,
1471 NULL,
1472 &Iosb,
1473 IOCTL_DISK_GET_DRIVE_GEOMETRY,
1474 NULL,
1475 0,
1476 &DiskGeometry,
1477 sizeof(DiskGeometry));
1478 if (!NT_SUCCESS(Status))
1479 return;
1480
1481 if (DiskGeometry.MediaType != FixedMedia &&
1482 DiskGeometry.MediaType != RemovableMedia)
1483 {
1484 return;
1485 }
1486
1487 /*
1488 * FIXME: Here we suppose the disk is always SCSI. What if it is
1489 * of another type? To check this we need to retrieve the name of
1490 * the driver the disk device belongs to.
1491 */
1492 Status = NtDeviceIoControlFile(FileHandle,
1493 NULL,
1494 NULL,
1495 NULL,
1496 &Iosb,
1497 IOCTL_SCSI_GET_ADDRESS,
1498 NULL,
1499 0,
1500 &ScsiAddress,
1501 sizeof(ScsiAddress));
1502 if (!NT_SUCCESS(Status))
1503 return;
1504
1505 /*
1506 * Check whether the disk is initialized, by looking at its MBR.
1507 * NOTE that this must be generalized to GPT disks as well!
1508 */
1509
1510 Mbr = (PARTITION_SECTOR*)RtlAllocateHeap(ProcessHeap,
1511 0,
1512 DiskGeometry.BytesPerSector);
1513 if (Mbr == NULL)
1514 return;
1515
1516 FileOffset.QuadPart = 0;
1517 Status = NtReadFile(FileHandle,
1518 NULL,
1519 NULL,
1520 NULL,
1521 &Iosb,
1522 (PVOID)Mbr,
1523 DiskGeometry.BytesPerSector,
1524 &FileOffset,
1525 NULL);
1526 if (!NT_SUCCESS(Status))
1527 {
1528 RtlFreeHeap(ProcessHeap, 0, Mbr);
1529 DPRINT1("NtReadFile failed, status=%x\n", Status);
1530 return;
1531 }
1532 Signature = Mbr->Signature;
1533
1534 /* Calculate the MBR checksum */
1535 Checksum = 0;
1536 Buffer = (PULONG)Mbr;
1537 for (i = 0; i < 128; i++)
1538 {
1539 Checksum += Buffer[i];
1540 }
1541 Checksum = ~Checksum + 1;
1542
1543 RtlStringCchPrintfW(Identifier, ARRAYSIZE(Identifier),
1544 L"%08x-%08x-%c",
1545 Checksum, Signature,
1546 (Mbr->Magic == PARTITION_MAGIC) ? L'A' : L'X');
1547 DPRINT("Identifier: %S\n", Identifier);
1548
1549 DiskEntry = RtlAllocateHeap(ProcessHeap,
1550 HEAP_ZERO_MEMORY,
1551 sizeof(DISKENTRY));
1552 if (DiskEntry == NULL)
1553 {
1554 RtlFreeHeap(ProcessHeap, 0, Mbr);
1555 DPRINT1("Failed to allocate a new disk entry.\n");
1556 return;
1557 }
1558
1559 DiskEntry->PartList = List;
1560
1561#if 0
1562 {
1563 FILE_FS_DEVICE_INFORMATION FileFsDevice;
1564
1565 /* Query the device for its type */
1566 Status = NtQueryVolumeInformationFile(FileHandle,
1567 &Iosb,
1568 &FileFsDevice,
1569 sizeof(FileFsDevice),
1570 FileFsDeviceInformation);
1571 if (!NT_SUCCESS(Status))
1572 {
1573 DPRINT1("Couldn't detect device type for disk %lu of identifier '%S'...\n", DiskNumber, Identifier);
1574 }
1575 else
1576 {
1577 DPRINT1("Disk %lu : DeviceType: 0x%08x ; Characteristics: 0x%08x\n", DiskNumber, FileFsDevice.DeviceType, FileFsDevice.Characteristics);
1578 }
1579 }
1580 // NOTE: We may also use NtQueryVolumeInformationFile(FileFsDeviceInformation).
1581#endif
1582 DiskEntry->MediaType = DiskGeometry.MediaType;
1583 if (DiskEntry->MediaType == RemovableMedia)
1584 {
1585 DPRINT1("Disk %lu of identifier '%S' is removable\n", DiskNumber, Identifier);
1586 }
1587 else // if (DiskEntry->MediaType == FixedMedia)
1588 {
1589 DPRINT1("Disk %lu of identifier '%S' is fixed\n", DiskNumber, Identifier);
1590 }
1591
1592// DiskEntry->Checksum = Checksum;
1593// DiskEntry->Signature = Signature;
1594 DiskEntry->BiosFound = FALSE;
1595
1596 /*
1597 * Check if this disk has a valid MBR: verify its signature,
1598 * and whether its two first bytes are a valid instruction
1599 * (related to this, see IsThereAValidBootSector() in partlist.c).
1600 *
1601 * See also ntoskrnl/fstub/fstubex.c!FstubDetectPartitionStyle().
1602 */
1603
1604 // DiskEntry->NoMbr = (Mbr->Magic != PARTITION_MAGIC || (*(PUSHORT)Mbr->BootCode) == 0x0000);
1605
1606 /* If we have not the 0xAA55 then it's raw partition */
1607 if (Mbr->Magic != PARTITION_MAGIC)
1608 {
1609 DiskEntry->DiskStyle = PARTITION_STYLE_RAW;
1610 }
1611 /* Check partitions types: if first is 0xEE and all the others 0, we have GPT */
1612 else if (Mbr->Partition[0].PartitionType == EFI_PMBR_OSTYPE_EFI &&
1613 Mbr->Partition[1].PartitionType == 0 &&
1614 Mbr->Partition[2].PartitionType == 0 &&
1615 Mbr->Partition[3].PartitionType == 0)
1616 {
1617 DiskEntry->DiskStyle = PARTITION_STYLE_GPT;
1618 }
1619 /* Otherwise, partition table is in MBR */
1620 else
1621 {
1622 DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
1623 }
1624
1625 /* Free the MBR sector buffer */
1626 RtlFreeHeap(ProcessHeap, 0, Mbr);
1627
1628
1629 for (ListEntry = List->BiosDiskListHead.Flink;
1630 ListEntry != &List->BiosDiskListHead;
1631 ListEntry = ListEntry->Flink)
1632 {
1633 BiosDiskEntry = CONTAINING_RECORD(ListEntry, BIOSDISKENTRY, ListEntry);
1634 /* FIXME:
1635 * Compare the size from BIOS and the reported size from driver.
1636 * If we have more than one disk with a zero or with the same signature
1637 * we must create new signatures and reboot. After the reboot,
1638 * it is possible to identify the disks.
1639 */
1640 if (BiosDiskEntry->Signature == Signature &&
1641 BiosDiskEntry->Checksum == Checksum &&
1642 BiosDiskEntry->DiskEntry == NULL)
1643 {
1644 if (!DiskEntry->BiosFound)
1645 {
1646 DiskEntry->HwAdapterNumber = BiosDiskEntry->AdapterNumber;
1647 DiskEntry->HwControllerNumber = BiosDiskEntry->ControllerNumber;
1648 DiskEntry->HwDiskNumber = BiosDiskEntry->DiskNumber;
1649
1650 if (DiskEntry->MediaType == RemovableMedia)
1651 {
1652 /* Set the removable disk number to zero */
1653 DiskEntry->HwFixedDiskNumber = 0;
1654 }
1655 else // if (DiskEntry->MediaType == FixedMedia)
1656 {
1657 /* The fixed disk number will later be adjusted using the number of removable disks */
1658 DiskEntry->HwFixedDiskNumber = BiosDiskEntry->DiskNumber;
1659 }
1660
1661 DiskEntry->BiosFound = TRUE;
1662 BiosDiskEntry->DiskEntry = DiskEntry;
1663 break;
1664 }
1665 else
1666 {
1667 // FIXME: What to do?
1668 DPRINT1("Disk %lu of identifier '%S' has already been found?!\n", DiskNumber, Identifier);
1669 }
1670 }
1671 }
1672
1673 if (!DiskEntry->BiosFound)
1674 {
1675 DPRINT1("WARNING: Setup could not find a matching BIOS disk entry. Disk %lu may not be bootable by the BIOS!\n", DiskNumber);
1676 }
1677
1678 DiskEntry->Cylinders = DiskGeometry.Cylinders.QuadPart;
1679 DiskEntry->TracksPerCylinder = DiskGeometry.TracksPerCylinder;
1680 DiskEntry->SectorsPerTrack = DiskGeometry.SectorsPerTrack;
1681 DiskEntry->BytesPerSector = DiskGeometry.BytesPerSector;
1682
1683 DPRINT("Cylinders %I64u\n", DiskEntry->Cylinders);
1684 DPRINT("TracksPerCylinder %lu\n", DiskEntry->TracksPerCylinder);
1685 DPRINT("SectorsPerTrack %lu\n", DiskEntry->SectorsPerTrack);
1686 DPRINT("BytesPerSector %lu\n", DiskEntry->BytesPerSector);
1687
1688 DiskEntry->SectorCount.QuadPart = DiskGeometry.Cylinders.QuadPart *
1689 (ULONGLONG)DiskGeometry.TracksPerCylinder *
1690 (ULONGLONG)DiskGeometry.SectorsPerTrack;
1691
1692 DiskEntry->SectorAlignment = DiskGeometry.SectorsPerTrack;
1693 DiskEntry->CylinderAlignment = DiskGeometry.TracksPerCylinder *
1694 DiskGeometry.SectorsPerTrack;
1695
1696 DPRINT("SectorCount %I64u\n", DiskEntry->SectorCount.QuadPart);
1697 DPRINT("SectorAlignment %lu\n", DiskEntry->SectorAlignment);
1698
1699 DiskEntry->DiskNumber = DiskNumber;
1700 DiskEntry->Port = ScsiAddress.PortNumber;
1701 DiskEntry->Bus = ScsiAddress.PathId;
1702 DiskEntry->Id = ScsiAddress.TargetId;
1703
1704 GetDriverName(DiskEntry);
1705 /*
1706 * Actually it would be more correct somehow to use:
1707 *
1708 * OBJECT_NAME_INFORMATION NameInfo; // ObjectNameInfo;
1709 * ULONG ReturnedLength;
1710 *
1711 * Status = NtQueryObject(SomeHandleToTheDisk,
1712 * ObjectNameInformation,
1713 * &NameInfo,
1714 * sizeof(NameInfo),
1715 * &ReturnedLength);
1716 * etc...
1717 *
1718 * See examples in https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/ntoskrnl/io/iomgr/error.c;hb=2f3a93ee9cec8322a86bf74b356f1ad83fc912dc#l267
1719 */
1720
1721 InitializeListHead(&DiskEntry->PrimaryPartListHead);
1722 InitializeListHead(&DiskEntry->LogicalPartListHead);
1723
1724 InsertAscendingList(&List->DiskListHead, DiskEntry, DISKENTRY, ListEntry, DiskNumber);
1725
1726
1727 /*
1728 * We now retrieve the disk partition layout
1729 */
1730
1731 /*
1732 * Stop there now if the disk is GPT-partitioned,
1733 * since we currently do not support such disks.
1734 */
1735 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
1736 {
1737 DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
1738 return;
1739 }
1740
1741 /* Allocate a layout buffer with 4 partition entries first */
1742 LayoutBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
1743 ((4 - ANYSIZE_ARRAY) * sizeof(PARTITION_INFORMATION));
1744 DiskEntry->LayoutBuffer = RtlAllocateHeap(ProcessHeap,
1745 HEAP_ZERO_MEMORY,
1746 LayoutBufferSize);
1747 if (DiskEntry->LayoutBuffer == NULL)
1748 {
1749 DPRINT1("Failed to allocate the disk layout buffer!\n");
1750 return;
1751 }
1752
1753 /* Keep looping while the drive layout buffer is too small */
1754 for (;;)
1755 {
1756 DPRINT1("Buffer size: %lu\n", LayoutBufferSize);
1757 Status = NtDeviceIoControlFile(FileHandle,
1758 NULL,
1759 NULL,
1760 NULL,
1761 &Iosb,
1762 IOCTL_DISK_GET_DRIVE_LAYOUT,
1763 NULL,
1764 0,
1765 DiskEntry->LayoutBuffer,
1766 LayoutBufferSize);
1767 if (NT_SUCCESS(Status))
1768 break;
1769
1770 if (Status != STATUS_BUFFER_TOO_SMALL)
1771 {
1772 DPRINT1("NtDeviceIoControlFile() failed (Status: 0x%08lx)\n", Status);
1773 return;
1774 }
1775
1776 LayoutBufferSize += 4 * sizeof(PARTITION_INFORMATION);
1777 NewLayoutBuffer = RtlReAllocateHeap(ProcessHeap,
1778 HEAP_ZERO_MEMORY,
1779 DiskEntry->LayoutBuffer,
1780 LayoutBufferSize);
1781 if (NewLayoutBuffer == NULL)
1782 {
1783 DPRINT1("Failed to reallocate the disk layout buffer!\n");
1784 return;
1785 }
1786
1787 DiskEntry->LayoutBuffer = NewLayoutBuffer;
1788 }
1789
1790 DPRINT1("PartitionCount: %lu\n", DiskEntry->LayoutBuffer->PartitionCount);
1791
1792#ifdef DUMP_PARTITION_TABLE
1793 DumpPartitionTable(DiskEntry);
1794#endif
1795
1796 if (IsSuperFloppy(DiskEntry))
1797 DPRINT1("Disk %lu is a super-floppy\n", DiskNumber);
1798
1799 if (DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart != 0 &&
1800 DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionLength.QuadPart != 0 &&
1801 DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionType != PARTITION_ENTRY_UNUSED)
1802 {
1803 if ((DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart / DiskEntry->BytesPerSector) % DiskEntry->SectorsPerTrack == 0)
1804 {
1805 DPRINT("Use %lu Sector alignment!\n", DiskEntry->SectorsPerTrack);
1806 }
1807 else if (DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart % (1024 * 1024) == 0)
1808 {
1809 DPRINT1("Use megabyte (%lu Sectors) alignment!\n", (1024 * 1024) / DiskEntry->BytesPerSector);
1810 }
1811 else
1812 {
1813 DPRINT1("No matching alignment found! Partition 1 starts at %I64u\n", DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart);
1814 }
1815 }
1816 else
1817 {
1818 DPRINT1("No valid partition table found! Use megabyte (%lu Sectors) alignment!\n", (1024 * 1024) / DiskEntry->BytesPerSector);
1819 }
1820
1821 if (DiskEntry->LayoutBuffer->PartitionCount == 0)
1822 {
1823 DiskEntry->NewDisk = TRUE;
1824 DiskEntry->LayoutBuffer->PartitionCount = 4;
1825
1826 for (i = 0; i < 4; i++)
1827 {
1828 DiskEntry->LayoutBuffer->PartitionEntry[i].RewritePartition = TRUE;
1829 }
1830 }
1831 else
1832 {
1833 /* Enumerate and add the first four primary partitions */
1834 for (i = 0; i < 4; i++)
1835 {
1836 AddPartitionToDisk(DiskNumber, DiskEntry, i, FALSE);
1837 }
1838
1839 /* Enumerate and add the remaining partitions as logical ones */
1840 for (i = 4; i < DiskEntry->LayoutBuffer->PartitionCount; i += 4)
1841 {
1842 AddPartitionToDisk(DiskNumber, DiskEntry, i, TRUE);
1843 }
1844 }
1845
1846 ScanForUnpartitionedDiskSpace(DiskEntry);
1847}
1848
1849/*
1850 * Retrieve the system disk, i.e. the fixed disk that is accessible by the
1851 * firmware during boot time and where the system partition resides.
1852 * If no system partition has been determined, we retrieve the first disk
1853 * that verifies the mentioned criteria above.
1854 */
1855static
1856PDISKENTRY
1857GetSystemDisk(
1858 IN PPARTLIST List)
1859{
1860 PLIST_ENTRY Entry;
1861 PDISKENTRY DiskEntry;
1862
1863 /* Check for empty disk list */
1864 if (IsListEmpty(&List->DiskListHead))
1865 return NULL;
1866
1867 /*
1868 * If we already have a system partition, the system disk
1869 * is the one on which the system partition resides.
1870 */
1871 if (List->SystemPartition)
1872 return List->SystemPartition->DiskEntry;
1873
1874 /* Loop over the disks and find the correct one */
1875 for (Entry = List->DiskListHead.Flink;
1876 Entry != &List->DiskListHead;
1877 Entry = Entry->Flink)
1878 {
1879 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
1880
1881 /* The disk must be a fixed disk and be found by the firmware */
1882 if (DiskEntry->MediaType == FixedMedia && DiskEntry->BiosFound)
1883 {
1884 break;
1885 }
1886 }
1887 if (Entry == &List->DiskListHead)
1888 {
1889 /* We haven't encountered any suitable disk */
1890 return NULL;
1891 }
1892
1893 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
1894 {
1895 DPRINT1("System disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
1896 }
1897
1898 return DiskEntry;
1899}
1900
1901/*
1902 * Retrieve the actual "active" partition of the given disk.
1903 * On MBR disks, partition with the Active/Boot flag set;
1904 * on GPT disks, partition with the correct GUID.
1905 */
1906BOOLEAN
1907IsPartitionActive(
1908 IN PPARTENTRY PartEntry)
1909{
1910 // TODO: Support for GPT disks!
1911
1912 if (IsContainerPartition(PartEntry->PartitionType))
1913 return FALSE;
1914
1915 /* Check if the partition is partitioned, used and active */
1916 if (PartEntry->IsPartitioned &&
1917 // !IsContainerPartition(PartEntry->PartitionType) &&
1918 PartEntry->BootIndicator)
1919 {
1920 /* Yes it is */
1921 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
1922 return TRUE;
1923 }
1924
1925 return FALSE;
1926}
1927
1928static
1929PPARTENTRY
1930GetActiveDiskPartition(
1931 IN PDISKENTRY DiskEntry)
1932{
1933 PLIST_ENTRY ListEntry;
1934 PPARTENTRY PartEntry;
1935 PPARTENTRY ActivePartition = NULL;
1936
1937 /* Check for empty disk list */
1938 // ASSERT(DiskEntry);
1939 if (!DiskEntry)
1940 return NULL;
1941
1942 /* Check for empty partition list */
1943 if (IsListEmpty(&DiskEntry->PrimaryPartListHead))
1944 return NULL;
1945
1946 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
1947 {
1948 DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
1949 return NULL;
1950 }
1951
1952 /* Scan all (primary) partitions to find the active disk partition */
1953 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
1954 ListEntry != &DiskEntry->PrimaryPartListHead;
1955 ListEntry = ListEntry->Flink)
1956 {
1957 /* Retrieve the partition */
1958 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
1959 if (IsPartitionActive(PartEntry))
1960 {
1961 /* Yes, we've found it */
1962 ASSERT(DiskEntry == PartEntry->DiskEntry);
1963 ASSERT(PartEntry->IsPartitioned);
1964 ASSERT(PartEntry->Volume);
1965
1966 ActivePartition = PartEntry;
1967
1968 DPRINT1("Found active system partition %lu in disk %lu, drive letter %C\n",
1969 PartEntry->PartitionNumber, DiskEntry->DiskNumber,
1970 !PartEntry->Volume->Info.DriveLetter ? L'-' : PartEntry->Volume->Info.DriveLetter);
1971 break;
1972 }
1973 }
1974
1975 /* Check if the disk is new and if so, use its first partition as the active system partition */
1976 if (DiskEntry->NewDisk && ActivePartition != NULL)
1977 {
1978 // FIXME: What to do??
1979 DPRINT1("NewDisk TRUE but already existing active partition?\n");
1980 }
1981
1982 /* Return the active partition found (or none) */
1983 return ActivePartition;
1984}
1985
1986PPARTLIST
1987NTAPI
1988CreatePartitionList(VOID)
1989{
1990 PPARTLIST List;
1991 PDISKENTRY SystemDisk;
1992 OBJECT_ATTRIBUTES ObjectAttributes;
1993 SYSTEM_DEVICE_INFORMATION Sdi;
1994 IO_STATUS_BLOCK Iosb;
1995 ULONG ReturnSize;
1996 NTSTATUS Status;
1997 ULONG DiskNumber;
1998 HANDLE FileHandle;
1999 UNICODE_STRING Name;
2000 WCHAR Buffer[MAX_PATH];
2001
2002 List = (PPARTLIST)RtlAllocateHeap(ProcessHeap,
2003 0,
2004 sizeof(PARTLIST));
2005 if (!List)
2006 return NULL;
2007
2008 List->SystemPartition = NULL;
2009
2010 InitializeListHead(&List->DiskListHead);
2011 InitializeListHead(&List->BiosDiskListHead);
2012 InitializeListHead(&List->VolumesList);
2013
2014 /*
2015 * Enumerate the disks seen by the BIOS; this will be used later
2016 * to map drives seen by NTOS with their corresponding BIOS names.
2017 */
2018 EnumerateBiosDiskEntries(List);
2019
2020 /* Enumerate disks seen by NTOS */
2021 Status = NtQuerySystemInformation(SystemDeviceInformation,
2022 &Sdi,
2023 sizeof(Sdi),
2024 &ReturnSize);
2025 if (!NT_SUCCESS(Status))
2026 {
2027 DPRINT1("NtQuerySystemInformation() failed, Status 0x%08lx\n", Status);
2028 RtlFreeHeap(ProcessHeap, 0, List);
2029 return NULL;
2030 }
2031
2032 for (DiskNumber = 0; DiskNumber < Sdi.NumberOfDisks; DiskNumber++)
2033 {
2034 RtlStringCchPrintfW(Buffer, ARRAYSIZE(Buffer),
2035 L"\\Device\\Harddisk%lu\\Partition0",
2036 DiskNumber);
2037 RtlInitUnicodeString(&Name, Buffer);
2038
2039 InitializeObjectAttributes(&ObjectAttributes,
2040 &Name,
2041 OBJ_CASE_INSENSITIVE,
2042 NULL,
2043 NULL);
2044
2045 Status = NtOpenFile(&FileHandle,
2046 FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
2047 &ObjectAttributes,
2048 &Iosb,
2049 FILE_SHARE_READ | FILE_SHARE_WRITE,
2050 FILE_SYNCHRONOUS_IO_NONALERT);
2051 if (NT_SUCCESS(Status))
2052 {
2053 AddDiskToList(FileHandle, DiskNumber, List);
2054 NtClose(FileHandle);
2055 }
2056 }
2057
2058 UpdateDiskSignatures(List);
2059 UpdateHwDiskNumbers(List);
2060 AssignDriveLetters(List);
2061
2062 /*
2063 * Retrieve the system partition: the active partition on the system
2064 * disk (the one that will be booted by default by the hardware).
2065 */
2066 SystemDisk = GetSystemDisk(List);
2067 List->SystemPartition = (SystemDisk ? GetActiveDiskPartition(SystemDisk) : NULL);
2068
2069 return List;
2070}
2071
2072VOID
2073NTAPI
2074DestroyPartitionList(
2075 IN PPARTLIST List)
2076{
2077 PDISKENTRY DiskEntry;
2078 PBIOSDISKENTRY BiosDiskEntry;
2079 PPARTENTRY PartEntry;
2080 PLIST_ENTRY Entry;
2081
2082 /* Release disk and partition info */
2083 while (!IsListEmpty(&List->DiskListHead))
2084 {
2085 Entry = RemoveHeadList(&List->DiskListHead);
2086 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2087
2088 /* Release driver name */
2089 RtlFreeUnicodeString(&DiskEntry->DriverName);
2090
2091 /* Release primary partition list */
2092 while (!IsListEmpty(&DiskEntry->PrimaryPartListHead))
2093 {
2094 Entry = RemoveHeadList(&DiskEntry->PrimaryPartListHead);
2095 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2096 DestroyRegion(PartEntry);
2097 }
2098
2099 /* Release logical partition list */
2100 while (!IsListEmpty(&DiskEntry->LogicalPartListHead))
2101 {
2102 Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead);
2103 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2104 DestroyRegion(PartEntry);
2105 }
2106
2107 /* Release layout buffer */
2108 if (DiskEntry->LayoutBuffer != NULL)
2109 RtlFreeHeap(ProcessHeap, 0, DiskEntry->LayoutBuffer);
2110
2111 /* Release disk entry */
2112 RtlFreeHeap(ProcessHeap, 0, DiskEntry);
2113 }
2114
2115 /* Release the BIOS disk info */
2116 while (!IsListEmpty(&List->BiosDiskListHead))
2117 {
2118 Entry = RemoveHeadList(&List->BiosDiskListHead);
2119 BiosDiskEntry = CONTAINING_RECORD(Entry, BIOSDISKENTRY, ListEntry);
2120 RtlFreeHeap(ProcessHeap, 0, BiosDiskEntry);
2121 }
2122
2123 /* Release list head */
2124 RtlFreeHeap(ProcessHeap, 0, List);
2125}
2126
2127PDISKENTRY
2128GetDiskByBiosNumber(
2129 _In_ PPARTLIST List,
2130 _In_ ULONG HwDiskNumber)
2131{
2132 PDISKENTRY DiskEntry;
2133 PLIST_ENTRY Entry;
2134
2135 /* Loop over the disks and find the correct one */
2136 for (Entry = List->DiskListHead.Flink;
2137 Entry != &List->DiskListHead;
2138 Entry = Entry->Flink)
2139 {
2140 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2141
2142 if (DiskEntry->HwDiskNumber == HwDiskNumber)
2143 return DiskEntry; /* Disk found, return it */
2144 }
2145
2146 /* Disk not found, stop there */
2147 return NULL;
2148}
2149
2150PDISKENTRY
2151GetDiskByNumber(
2152 _In_ PPARTLIST List,
2153 _In_ ULONG DiskNumber)
2154{
2155 PDISKENTRY DiskEntry;
2156 PLIST_ENTRY Entry;
2157
2158 /* Loop over the disks and find the correct one */
2159 for (Entry = List->DiskListHead.Flink;
2160 Entry != &List->DiskListHead;
2161 Entry = Entry->Flink)
2162 {
2163 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2164
2165 if (DiskEntry->DiskNumber == DiskNumber)
2166 return DiskEntry; /* Disk found, return it */
2167 }
2168
2169 /* Disk not found, stop there */
2170 return NULL;
2171}
2172
2173PDISKENTRY
2174GetDiskBySCSI(
2175 _In_ PPARTLIST List,
2176 _In_ USHORT Port,
2177 _In_ USHORT Bus,
2178 _In_ USHORT Id)
2179{
2180 PDISKENTRY DiskEntry;
2181 PLIST_ENTRY Entry;
2182
2183 /* Loop over the disks and find the correct one */
2184 for (Entry = List->DiskListHead.Flink;
2185 Entry != &List->DiskListHead;
2186 Entry = Entry->Flink)
2187 {
2188 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2189
2190 if (DiskEntry->Port == Port &&
2191 DiskEntry->Bus == Bus &&
2192 DiskEntry->Id == Id)
2193 {
2194 /* Disk found, return it */
2195 return DiskEntry;
2196 }
2197 }
2198
2199 /* Disk not found, stop there */
2200 return NULL;
2201}
2202
2203PDISKENTRY
2204GetDiskBySignature(
2205 _In_ PPARTLIST List,
2206 _In_ ULONG Signature)
2207{
2208 PDISKENTRY DiskEntry;
2209 PLIST_ENTRY Entry;
2210
2211 /* Loop over the disks and find the correct one */
2212 for (Entry = List->DiskListHead.Flink;
2213 Entry != &List->DiskListHead;
2214 Entry = Entry->Flink)
2215 {
2216 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2217
2218 if (DiskEntry->LayoutBuffer->Signature == Signature)
2219 return DiskEntry; /* Disk found, return it */
2220 }
2221
2222 /* Disk not found, stop there */
2223 return NULL;
2224}
2225
2226PPARTENTRY
2227GetPartition(
2228 _In_ PDISKENTRY DiskEntry,
2229 _In_ ULONG PartitionNumber)
2230{
2231 PPARTENTRY PartEntry;
2232 PLIST_ENTRY Entry;
2233
2234 /* Forbid whole-disk or extended container partition access */
2235 if (PartitionNumber == 0)
2236 return NULL;
2237
2238 /* Loop over the primary partitions first... */
2239 for (Entry = DiskEntry->PrimaryPartListHead.Flink;
2240 Entry != &DiskEntry->PrimaryPartListHead;
2241 Entry = Entry->Flink)
2242 {
2243 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2244
2245 if (PartEntry->PartitionNumber == PartitionNumber)
2246 return PartEntry; /* Partition found, return it */
2247 }
2248
2249 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2250 return NULL;
2251
2252 /* ... then over the logical partitions if needed */
2253 for (Entry = DiskEntry->LogicalPartListHead.Flink;
2254 Entry != &DiskEntry->LogicalPartListHead;
2255 Entry = Entry->Flink)
2256 {
2257 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2258
2259 if (PartEntry->PartitionNumber == PartitionNumber)
2260 return PartEntry; /* Partition found, return it */
2261 }
2262
2263 /* The partition was not found on the disk, stop there */
2264 return NULL;
2265}
2266
2267PPARTENTRY
2268SelectPartition(
2269 _In_ PPARTLIST List,
2270 _In_ ULONG DiskNumber,
2271 _In_ ULONG PartitionNumber)
2272{
2273 PDISKENTRY DiskEntry;
2274 PPARTENTRY PartEntry;
2275
2276 /* Find the disk */
2277 DiskEntry = GetDiskByNumber(List, DiskNumber);
2278 if (!DiskEntry)
2279 return NULL;
2280 ASSERT(DiskEntry->DiskNumber == DiskNumber);
2281
2282 /* Find the partition */
2283 PartEntry = GetPartition(DiskEntry, PartitionNumber);
2284 if (!PartEntry)
2285 return NULL;
2286 ASSERT(PartEntry->DiskEntry == DiskEntry);
2287 ASSERT(PartEntry->PartitionNumber == PartitionNumber);
2288
2289 return PartEntry;
2290}
2291
2292PPARTENTRY
2293NTAPI
2294GetNextPartition(
2295 IN PPARTLIST List,
2296 IN PPARTENTRY CurrentPart OPTIONAL)
2297{
2298 PLIST_ENTRY DiskListEntry;
2299 PLIST_ENTRY PartListEntry;
2300 PDISKENTRY CurrentDisk;
2301
2302 /* Fail if no disks are available */
2303 if (IsListEmpty(&List->DiskListHead))
2304 return NULL;
2305
2306 /* Check for the next usable entry on the current partition's disk */
2307 if (CurrentPart != NULL)
2308 {
2309 CurrentDisk = CurrentPart->DiskEntry;
2310
2311 if (CurrentPart->LogicalPartition)
2312 {
2313 /* Logical partition */
2314
2315 PartListEntry = CurrentPart->ListEntry.Flink;
2316 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2317 {
2318 /* Next logical partition */
2319 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2320 return CurrentPart;
2321 }
2322 else
2323 {
2324 PartListEntry = CurrentDisk->ExtendedPartition->ListEntry.Flink;
2325 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2326 {
2327 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2328 return CurrentPart;
2329 }
2330 }
2331 }
2332 else
2333 {
2334 /* Primary or extended partition */
2335
2336 if (CurrentPart->IsPartitioned &&
2337 IsContainerPartition(CurrentPart->PartitionType))
2338 {
2339 /* First logical partition */
2340 PartListEntry = CurrentDisk->LogicalPartListHead.Flink;
2341 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2342 {
2343 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2344 return CurrentPart;
2345 }
2346 }
2347 else
2348 {
2349 /* Next primary partition */
2350 PartListEntry = CurrentPart->ListEntry.Flink;
2351 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2352 {
2353 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2354 return CurrentPart;
2355 }
2356 }
2357 }
2358 }
2359
2360 /* Search for the first partition entry on the next disk */
2361 for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Flink
2362 : List->DiskListHead.Flink);
2363 DiskListEntry != &List->DiskListHead;
2364 DiskListEntry = DiskListEntry->Flink)
2365 {
2366 CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
2367
2368 if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
2369 {
2370 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2371 continue;
2372 }
2373
2374 PartListEntry = CurrentDisk->PrimaryPartListHead.Flink;
2375 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2376 {
2377 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2378 return CurrentPart;
2379 }
2380 }
2381
2382 return NULL;
2383}
2384
2385PPARTENTRY
2386NTAPI
2387GetPrevPartition(
2388 IN PPARTLIST List,
2389 IN PPARTENTRY CurrentPart OPTIONAL)
2390{
2391 PLIST_ENTRY DiskListEntry;
2392 PLIST_ENTRY PartListEntry;
2393 PDISKENTRY CurrentDisk;
2394
2395 /* Fail if no disks are available */
2396 if (IsListEmpty(&List->DiskListHead))
2397 return NULL;
2398
2399 /* Check for the previous usable entry on the current partition's disk */
2400 if (CurrentPart != NULL)
2401 {
2402 CurrentDisk = CurrentPart->DiskEntry;
2403
2404 if (CurrentPart->LogicalPartition)
2405 {
2406 /* Logical partition */
2407
2408 PartListEntry = CurrentPart->ListEntry.Blink;
2409 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2410 {
2411 /* Previous logical partition */
2412 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2413 }
2414 else
2415 {
2416 /* Extended partition */
2417 CurrentPart = CurrentDisk->ExtendedPartition;
2418 }
2419 return CurrentPart;
2420 }
2421 else
2422 {
2423 /* Primary or extended partition */
2424
2425 PartListEntry = CurrentPart->ListEntry.Blink;
2426 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2427 {
2428 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2429
2430 if (CurrentPart->IsPartitioned &&
2431 IsContainerPartition(CurrentPart->PartitionType))
2432 {
2433 PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
2434 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2435 }
2436
2437 return CurrentPart;
2438 }
2439 }
2440 }
2441
2442 /* Search for the last partition entry on the previous disk */
2443 for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Blink
2444 : List->DiskListHead.Blink);
2445 DiskListEntry != &List->DiskListHead;
2446 DiskListEntry = DiskListEntry->Blink)
2447 {
2448 CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
2449
2450 if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
2451 {
2452 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2453 continue;
2454 }
2455
2456 PartListEntry = CurrentDisk->PrimaryPartListHead.Blink;
2457 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2458 {
2459 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2460
2461 if (CurrentPart->IsPartitioned &&
2462 IsContainerPartition(CurrentPart->PartitionType))
2463 {
2464 PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
2465 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2466 {
2467 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2468 return CurrentPart;
2469 }
2470 }
2471 else
2472 {
2473 return CurrentPart;
2474 }
2475 }
2476 }
2477
2478 return NULL;
2479}
2480
2481static inline
2482BOOLEAN
2483IsEmptyLayoutEntry(
2484 _In_ PPARTITION_INFORMATION PartitionInfo)
2485{
2486 return (PartitionInfo->StartingOffset.QuadPart == 0 &&
2487 PartitionInfo->PartitionLength.QuadPart == 0);
2488}
2489
2490static inline
2491BOOLEAN
2492IsSamePrimaryLayoutEntry(
2493 _In_ PPARTITION_INFORMATION PartitionInfo,
2494 _In_ PPARTENTRY PartEntry)
2495{
2496 return ((PartitionInfo->StartingOffset.QuadPart == GetPartEntryOffsetInBytes(PartEntry)) &&
2497 (PartitionInfo->PartitionLength.QuadPart == GetPartEntrySizeInBytes(PartEntry)));
2498// PartitionInfo->PartitionType == PartEntry->PartitionType
2499}
2500
2501
2502/**
2503 * @brief
2504 * Counts the number of partitioned disk regions in a given partition list.
2505 **/
2506static
2507ULONG
2508GetPartitionCount(
2509 _In_ PLIST_ENTRY PartListHead)
2510{
2511 PLIST_ENTRY Entry;
2512 PPARTENTRY PartEntry;
2513 ULONG Count = 0;
2514
2515 for (Entry = PartListHead->Flink;
2516 Entry != PartListHead;
2517 Entry = Entry->Flink)
2518 {
2519 PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2520 if (PartEntry->IsPartitioned)
2521 ++Count;
2522 }
2523
2524 return Count;
2525}
2526
2527#define GetPrimaryPartitionCount(DiskEntry) \
2528 GetPartitionCount(&(DiskEntry)->PrimaryPartListHead)
2529
2530#define GetLogicalPartitionCount(DiskEntry) \
2531 (((DiskEntry)->DiskStyle == PARTITION_STYLE_MBR) \
2532 ? GetPartitionCount(&(DiskEntry)->LogicalPartListHead) : 0)
2533
2534
2535static
2536BOOLEAN
2537ReAllocateLayoutBuffer(
2538 IN PDISKENTRY DiskEntry)
2539{
2540 PDRIVE_LAYOUT_INFORMATION NewLayoutBuffer;
2541 ULONG NewPartitionCount;
2542 ULONG CurrentPartitionCount = 0;
2543 ULONG LayoutBufferSize;
2544 ULONG i;
2545
2546 DPRINT1("ReAllocateLayoutBuffer()\n");
2547
2548 NewPartitionCount = 4 + GetLogicalPartitionCount(DiskEntry) * 4;
2549
2550 if (DiskEntry->LayoutBuffer)
2551 CurrentPartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
2552
2553 DPRINT1("CurrentPartitionCount: %lu ; NewPartitionCount: %lu\n",
2554 CurrentPartitionCount, NewPartitionCount);
2555
2556 if (CurrentPartitionCount == NewPartitionCount)
2557 return TRUE;
2558
2559 LayoutBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
2560 ((NewPartitionCount - ANYSIZE_ARRAY) * sizeof(PARTITION_INFORMATION));
2561 NewLayoutBuffer = RtlReAllocateHeap(ProcessHeap,
2562 HEAP_ZERO_MEMORY,
2563 DiskEntry->LayoutBuffer,
2564 LayoutBufferSize);
2565 if (NewLayoutBuffer == NULL)
2566 {
2567 DPRINT1("Failed to allocate the new layout buffer (size: %lu)\n", LayoutBufferSize);
2568 return FALSE;
2569 }
2570
2571 NewLayoutBuffer->PartitionCount = NewPartitionCount;
2572
2573 /* If the layout buffer grows, make sure the new (empty) entries are written to the disk */
2574 if (NewPartitionCount > CurrentPartitionCount)
2575 {
2576 for (i = CurrentPartitionCount; i < NewPartitionCount; i++)
2577 {
2578 NewLayoutBuffer->PartitionEntry[i].RewritePartition = TRUE;
2579 }
2580 }
2581
2582 DiskEntry->LayoutBuffer = NewLayoutBuffer;
2583
2584 return TRUE;
2585}
2586
2587static
2588VOID
2589UpdateDiskLayout(
2590 IN PDISKENTRY DiskEntry)
2591{
2592 PPARTITION_INFORMATION PartitionInfo;
2593 PPARTITION_INFORMATION LinkInfo;
2594 PLIST_ENTRY ListEntry;
2595 PPARTENTRY PartEntry;
2596 LARGE_INTEGER HiddenSectors64;
2597 ULONG Index;
2598 ULONG PartitionNumber = 1;
2599
2600 DPRINT1("UpdateDiskLayout()\n");
2601
2602 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2603 {
2604 DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2605 return;
2606 }
2607
2608 /* Resize the layout buffer if necessary */
2609 if (!ReAllocateLayoutBuffer(DiskEntry))
2610 {
2611 DPRINT("ReAllocateLayoutBuffer() failed.\n");
2612 return;
2613 }
2614
2615 /* Update the primary partition table */
2616 Index = 0;
2617 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
2618 ListEntry != &DiskEntry->PrimaryPartListHead;
2619 ListEntry = ListEntry->Flink)
2620 {
2621 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
2622
2623 if (PartEntry->IsPartitioned)
2624 {
2625 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2626
2627 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2628 PartEntry->PartitionIndex = Index;
2629
2630 /* Reset the current partition number only for not-yet written partitions */
2631 if (PartEntry->New)
2632 PartEntry->PartitionNumber = 0;
2633
2634 PartEntry->OnDiskPartitionNumber = (!IsContainerPartition(PartEntry->PartitionType) ? PartitionNumber : 0);
2635
2636 if (!IsSamePrimaryLayoutEntry(PartitionInfo, PartEntry))
2637 {
2638 DPRINT1("Updating primary partition entry %lu\n", Index);
2639
2640 PartitionInfo->StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry);
2641 PartitionInfo->PartitionLength.QuadPart = GetPartEntrySizeInBytes(PartEntry);
2642 PartitionInfo->HiddenSectors = PartEntry->StartSector.LowPart;
2643 PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
2644 PartitionInfo->PartitionType = PartEntry->PartitionType;
2645 PartitionInfo->BootIndicator = PartEntry->BootIndicator;
2646 PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
2647 PartitionInfo->RewritePartition = TRUE;
2648 }
2649
2650 if (!IsContainerPartition(PartEntry->PartitionType))
2651 PartitionNumber++;
2652
2653 Index++;
2654 }
2655 }
2656
2657 ASSERT(Index <= 4);
2658
2659 /* Update the logical partition table */
2660 LinkInfo = NULL;
2661 Index = 4;
2662 for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
2663 ListEntry != &DiskEntry->LogicalPartListHead;
2664 ListEntry = ListEntry->Flink)
2665 {
2666 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
2667
2668 if (PartEntry->IsPartitioned)
2669 {
2670 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2671
2672 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2673 PartEntry->PartitionIndex = Index;
2674
2675 /* Reset the current partition number only for not-yet written partitions */
2676 if (PartEntry->New)
2677 PartEntry->PartitionNumber = 0;
2678
2679 PartEntry->OnDiskPartitionNumber = PartitionNumber;
2680
2681 DPRINT1("Updating logical partition entry %lu\n", Index);
2682
2683 PartitionInfo->StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry);
2684 PartitionInfo->PartitionLength.QuadPart = GetPartEntrySizeInBytes(PartEntry);
2685 PartitionInfo->HiddenSectors = DiskEntry->SectorAlignment;
2686 PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
2687 PartitionInfo->PartitionType = PartEntry->PartitionType;
2688 PartitionInfo->BootIndicator = FALSE;
2689 PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
2690 PartitionInfo->RewritePartition = TRUE;
2691
2692 /* Fill the link entry of the previous partition entry */
2693 if (LinkInfo)
2694 {
2695 LinkInfo->StartingOffset.QuadPart = (PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
2696 LinkInfo->PartitionLength.QuadPart = (PartEntry->StartSector.QuadPart + DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
2697 HiddenSectors64.QuadPart = PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment - DiskEntry->ExtendedPartition->StartSector.QuadPart;
2698 LinkInfo->HiddenSectors = HiddenSectors64.LowPart;
2699 LinkInfo->PartitionNumber = 0;
2700
2701 /* Extended partition links only use type 0x05, as observed
2702 * on Windows NT. Alternatively they could inherit the type
2703 * of the main extended container. */
2704 LinkInfo->PartitionType = PARTITION_EXTENDED; // DiskEntry->ExtendedPartition->PartitionType;
2705
2706 LinkInfo->BootIndicator = FALSE;
2707 LinkInfo->RecognizedPartition = FALSE;
2708 LinkInfo->RewritePartition = TRUE;
2709 }
2710
2711 /* Save a pointer to the link entry of the current partition entry */
2712 LinkInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index + 1];
2713
2714 PartitionNumber++;
2715 Index += 4;
2716 }
2717 }
2718
2719 /* Wipe unused primary partition entries */
2720 for (Index = GetPrimaryPartitionCount(DiskEntry); Index < 4; Index++)
2721 {
2722 DPRINT1("Primary partition entry %lu\n", Index);
2723
2724 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2725
2726 if (!IsEmptyLayoutEntry(PartitionInfo))
2727 {
2728 DPRINT1("Wiping primary partition entry %lu\n", Index);
2729
2730 PartitionInfo->StartingOffset.QuadPart = 0;
2731 PartitionInfo->PartitionLength.QuadPart = 0;
2732 PartitionInfo->HiddenSectors = 0;
2733 PartitionInfo->PartitionNumber = 0;
2734 PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
2735 PartitionInfo->BootIndicator = FALSE;
2736 PartitionInfo->RecognizedPartition = FALSE;
2737 PartitionInfo->RewritePartition = TRUE;
2738 }
2739 }
2740
2741 /* Wipe unused logical partition entries */
2742 for (Index = 4; Index < DiskEntry->LayoutBuffer->PartitionCount; Index++)
2743 {
2744 if (Index % 4 >= 2)
2745 {
2746 DPRINT1("Logical partition entry %lu\n", Index);
2747
2748 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2749
2750 if (!IsEmptyLayoutEntry(PartitionInfo))
2751 {
2752 DPRINT1("Wiping partition entry %lu\n", Index);
2753
2754 PartitionInfo->StartingOffset.QuadPart = 0;
2755 PartitionInfo->PartitionLength.QuadPart = 0;
2756 PartitionInfo->HiddenSectors = 0;
2757 PartitionInfo->PartitionNumber = 0;
2758 PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
2759 PartitionInfo->BootIndicator = FALSE;
2760 PartitionInfo->RecognizedPartition = FALSE;
2761 PartitionInfo->RewritePartition = TRUE;
2762 }
2763 }
2764 }
2765
2766 // HACK: See the FIXMEs in WritePartitions(): (Re)set the PartitionStyle to MBR.
2767 DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
2768
2769 DiskEntry->Dirty = TRUE;
2770
2771#ifdef DUMP_PARTITION_TABLE
2772 DumpPartitionTable(DiskEntry);
2773#endif
2774}
2775
2776/**
2777 * @brief
2778 * Retrieves, if any, the unpartitioned disk region that is adjacent
2779 * (next or previous) to the specified partition.
2780 *
2781 * @param[in] PartEntry
2782 * Partition from where to find the adjacent unpartitioned region.
2783 *
2784 * @param[in] Direction
2785 * TRUE or FALSE to search the next or previous region, respectively.
2786 *
2787 * @return The adjacent unpartitioned region, if it exists, or NULL.
2788 **/
2789PPARTENTRY
2790NTAPI
2791GetAdjUnpartitionedEntry(
2792 _In_ PPARTENTRY PartEntry,
2793 _In_ BOOLEAN Direction)
2794{
2795 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2796 PLIST_ENTRY ListHead, AdjEntry;
2797
2798 /* In case of MBR disks only, check the logical partitions if necessary */
2799 if ((DiskEntry->DiskStyle == PARTITION_STYLE_MBR) &&
2800 PartEntry->LogicalPartition)
2801 {
2802 ListHead = &DiskEntry->LogicalPartListHead;
2803 }
2804 else
2805 {
2806 ListHead = &DiskEntry->PrimaryPartListHead;
2807 }
2808
2809 if (Direction)
2810 AdjEntry = PartEntry->ListEntry.Flink; // Next region.
2811 else
2812 AdjEntry = PartEntry->ListEntry.Blink; // Previous region.
2813
2814 if (AdjEntry != ListHead)
2815 {
2816 PartEntry = CONTAINING_RECORD(AdjEntry, PARTENTRY, ListEntry);
2817 if (!PartEntry->IsPartitioned)
2818 {
2819 ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
2820 return PartEntry;
2821 }
2822 }
2823 return NULL;
2824}
2825
2826static ERROR_NUMBER
2827MBRPartitionCreateChecks(
2828 _In_ PPARTENTRY PartEntry,
2829 _In_opt_ ULONGLONG SizeBytes,
2830 _In_opt_ ULONG_PTR PartitionInfo)
2831{
2832 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2833 BOOLEAN isContainer = IsContainerPartition((UCHAR)PartitionInfo);
2834
2835 // TODO: Re-enable once we initialize unpartitioned disks before using them.
2836 // ASSERT(DiskEntry->DiskStyle == PARTITION_STYLE_MBR);
2837 ASSERT(!PartEntry->IsPartitioned);
2838
2839 if (isContainer)
2840 {
2841 /* Cannot create an extended partition within logical partition space */
2842 if (PartEntry->LogicalPartition)
2843 return ERROR_ONLY_ONE_EXTENDED;
2844
2845 /* Fail if there is another extended partition in the list */
2846 if (DiskEntry->ExtendedPartition)
2847 return ERROR_ONLY_ONE_EXTENDED;
2848 }
2849
2850 /*
2851 * Primary or Extended partitions
2852 */
2853 if (!PartEntry->LogicalPartition || isContainer)
2854 {
2855 /* Only one primary partition is allowed on super-floppy */
2856 if (IsSuperFloppy(DiskEntry))
2857 return ERROR_PARTITION_TABLE_FULL;
2858
2859 /* Fail if there are too many primary partitions */
2860 if (GetPrimaryPartitionCount(DiskEntry) >= 4)
2861 return ERROR_PARTITION_TABLE_FULL;
2862 }
2863 /*
2864 * Logical partitions
2865 */
2866 else
2867 {
2868 // TODO: Check that we are inside an extended partition!
2869 // Then the following check will be useless.
2870
2871 /* Only one (primary) partition is allowed on super-floppy */
2872 if (IsSuperFloppy(DiskEntry))
2873 return ERROR_PARTITION_TABLE_FULL;
2874 }
2875
2876 return ERROR_SUCCESS;
2877}
2878
2879ERROR_NUMBER
2880NTAPI
2881PartitionCreateChecks(
2882 _In_ PPARTENTRY PartEntry,
2883 _In_opt_ ULONGLONG SizeBytes,
2884 _In_opt_ ULONG_PTR PartitionInfo)
2885{
2886 // PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2887
2888 /* Fail if the partition is already in use */
2889 if (PartEntry->IsPartitioned)
2890 return ERROR_NEW_PARTITION;
2891
2892 // TODO: Re-enable once we initialize unpartitioned disks before
2893 // using them; because such disks would be mistook as GPT otherwise.
2894 // if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR)
2895 return MBRPartitionCreateChecks(PartEntry, SizeBytes, PartitionInfo);
2896#if 0
2897 else // if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2898 {
2899 DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2900 return ERROR_WARN_PARTITION;
2901 }
2902#endif
2903}
2904
2905// TODO: Improve upon the PartitionInfo parameter later
2906// (see VDS::CREATE_PARTITION_PARAMETERS and PPARTITION_INFORMATION_MBR/GPT for example)
2907// So far we only use it as the optional type of the partition to create.
2908BOOLEAN
2909NTAPI
2910CreatePartition(
2911 _In_ PPARTLIST List,
2912 _Inout_ PPARTENTRY PartEntry,
2913 _In_opt_ ULONGLONG SizeBytes,
2914 _In_opt_ ULONG_PTR PartitionInfo)
2915{
2916 ERROR_NUMBER Error;
2917 BOOLEAN isContainer = IsContainerPartition((UCHAR)PartitionInfo);
2918 PDISKENTRY DiskEntry;
2919 PCSTR mainType = "Primary";
2920
2921 if (isContainer)
2922 mainType = "Extended";
2923 else if (PartEntry && PartEntry->LogicalPartition)
2924 mainType = "Logical";
2925
2926 DPRINT1("CreatePartition(%s, %I64u bytes)\n", mainType, SizeBytes);
2927
2928 if (!List || !PartEntry ||
2929 !PartEntry->DiskEntry || PartEntry->IsPartitioned)
2930 {
2931 return FALSE;
2932 }
2933
2934 Error = PartitionCreateChecks(PartEntry, SizeBytes, PartitionInfo);
2935 if (Error != NOT_AN_ERROR)
2936 {
2937 DPRINT1("PartitionCreateChecks(%s) failed with error %lu\n", mainType, Error);
2938 return FALSE;
2939 }
2940
2941 /* Initialize the partition entry, inserting a new blank region if needed */
2942 if (!InitializePartitionEntry(PartEntry, SizeBytes, PartitionInfo))
2943 return FALSE;
2944
2945 DiskEntry = PartEntry->DiskEntry;
2946 UpdateDiskLayout(DiskEntry);
2947
2948 ASSERT(!PartEntry->Volume);
2949 if (!isContainer)
2950 {
2951 /* We create a primary/logical partition: initialize a new basic
2952 * volume entry. When the partition will actually be written onto
2953 * the disk, the PARTMGR will notify the MOUNTMGR that a volume
2954 * associated with this partition has to be created. */
2955 PartEntry->Volume = InitVolume(DiskEntry->PartList, PartEntry);
2956 ASSERT(PartEntry->Volume);
2957 }
2958
2959 AssignDriveLetters(List);
2960
2961 return TRUE;
2962}
2963
2964static NTSTATUS
2965DismountPartition(
2966 _In_ PPARTLIST List,
2967 _In_ PPARTENTRY PartEntry)
2968{
2969 NTSTATUS Status;
2970 PVOLENTRY Volume = PartEntry->Volume;
2971
2972 ASSERT(PartEntry->DiskEntry->PartList == List);
2973
2974 /* Check whether the partition is valid and was mounted by the system */
2975 if (!PartEntry->IsPartitioned ||
2976 IsContainerPartition(PartEntry->PartitionType) ||
2977 !IsRecognizedPartition(PartEntry->PartitionType) ||
2978 !Volume || Volume->FormatState == UnknownFormat ||
2979 // NOTE: If FormatState == Unformatted but *FileSystem != 0 this means
2980 // it has been usually mounted with RawFS and thus needs to be dismounted.
2981 PartEntry->PartitionNumber == 0)
2982 {
2983 /* The partition is not mounted, so just return success */
2984 return STATUS_SUCCESS;
2985 }
2986
2987 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2988 ASSERT(Volume->PartEntry == PartEntry);
2989
2990 /* Unlink the basic volume from the volumes list and dismount it */
2991 PartEntry->Volume = NULL;
2992 Volume->PartEntry = NULL;
2993 RemoveEntryList(&Volume->ListEntry);
2994 Status = DismountVolume(&Volume->Info, TRUE);
2995 RtlFreeHeap(ProcessHeap, 0, Volume);
2996 return Status;
2997}
2998
2999BOOLEAN
3000NTAPI
3001DeletePartition(
3002 _In_ PPARTLIST List,
3003 _In_ PPARTENTRY PartEntry,
3004 _Out_opt_ PPARTENTRY* FreeRegion)
3005{
3006 PDISKENTRY DiskEntry;
3007 PPARTENTRY PrevPartEntry;
3008 PPARTENTRY NextPartEntry;
3009 PPARTENTRY LogicalPartEntry;
3010 PLIST_ENTRY Entry;
3011
3012 if (!List || !PartEntry ||
3013 !PartEntry->DiskEntry || !PartEntry->IsPartitioned)
3014 {
3015 return FALSE;
3016 }
3017
3018 ASSERT(PartEntry->DiskEntry->PartList == List);
3019 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3020
3021 /* Clear the system partition if it is being deleted */
3022 if (List->SystemPartition == PartEntry)
3023 {
3024 ASSERT(List->SystemPartition);
3025 List->SystemPartition = NULL;
3026 }
3027
3028 DiskEntry = PartEntry->DiskEntry;
3029
3030 /* Check which type of partition (primary/logical or extended) is being deleted */
3031 if (DiskEntry->ExtendedPartition == PartEntry)
3032 {
3033 /* An extended partition is being deleted: delete all logical partition entries */
3034 while (!IsListEmpty(&DiskEntry->LogicalPartListHead))
3035 {
3036 Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead);
3037 LogicalPartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
3038
3039 /* Dismount the logical partition and delete it */
3040 DismountPartition(List, LogicalPartEntry);
3041 DestroyRegion(LogicalPartEntry);
3042 }
3043
3044 DiskEntry->ExtendedPartition = NULL;
3045 }
3046 else
3047 {
3048 /* A primary/logical partition is being deleted: dismount it */
3049 DismountPartition(List, PartEntry);
3050 }
3051
3052 /* Adjust the unpartitioned disk space entries */
3053
3054 /* Get pointer to previous and next unpartitioned entries */
3055 PrevPartEntry = GetAdjUnpartitionedEntry(PartEntry, FALSE);
3056 NextPartEntry = GetAdjUnpartitionedEntry(PartEntry, TRUE);
3057
3058 if (PrevPartEntry != NULL && NextPartEntry != NULL)
3059 {
3060 /* Merge the previous, current and next unpartitioned entries */
3061
3062 /* Adjust the previous entry length */
3063 PrevPartEntry->SectorCount.QuadPart += (PartEntry->SectorCount.QuadPart + NextPartEntry->SectorCount.QuadPart);
3064
3065 /* Remove the current and next entries */
3066 RemoveEntryList(&PartEntry->ListEntry);
3067 DestroyRegion(PartEntry);
3068 RemoveEntryList(&NextPartEntry->ListEntry);
3069 DestroyRegion(NextPartEntry);
3070
3071 /* Optionally return the freed region */
3072 if (FreeRegion)
3073 *FreeRegion = PrevPartEntry;
3074 }
3075 else if (PrevPartEntry != NULL && NextPartEntry == NULL)
3076 {
3077 /* Merge the current and the previous unpartitioned entries */
3078
3079 /* Adjust the previous entry length */
3080 PrevPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
3081
3082 /* Remove the current entry */
3083 RemoveEntryList(&PartEntry->ListEntry);
3084 DestroyRegion(PartEntry);
3085
3086 /* Optionally return the freed region */
3087 if (FreeRegion)
3088 *FreeRegion = PrevPartEntry;
3089 }
3090 else if (PrevPartEntry == NULL && NextPartEntry != NULL)
3091 {
3092 /* Merge the current and the next unpartitioned entries */
3093
3094 /* Adjust the next entry offset and length */
3095 NextPartEntry->StartSector.QuadPart = PartEntry->StartSector.QuadPart;
3096 NextPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
3097
3098 /* Remove the current entry */
3099 RemoveEntryList(&PartEntry->ListEntry);
3100 DestroyRegion(PartEntry);
3101
3102 /* Optionally return the freed region */
3103 if (FreeRegion)
3104 *FreeRegion = NextPartEntry;
3105 }
3106 else
3107 {
3108 /* Nothing to merge but change the current entry */
3109 PartEntry->New = FALSE;
3110 PartEntry->IsPartitioned = FALSE;
3111 PartEntry->PartitionType = PARTITION_ENTRY_UNUSED;
3112 PartEntry->OnDiskPartitionNumber = 0;
3113 PartEntry->PartitionNumber = 0;
3114 // PartEntry->PartitionIndex = 0;
3115 PartEntry->BootIndicator = FALSE;
3116 PartEntry->DeviceName[0] = UNICODE_NULL;
3117
3118 if (PartEntry->Volume)
3119 {
3120 RemoveEntryList(&PartEntry->Volume->ListEntry);
3121 RtlFreeHeap(ProcessHeap, 0, PartEntry->Volume);
3122 }
3123 PartEntry->Volume = NULL;
3124
3125 /* Optionally return the freed region */
3126 if (FreeRegion)
3127 *FreeRegion = PartEntry;
3128 }
3129
3130 UpdateDiskLayout(DiskEntry);
3131 AssignDriveLetters(List);
3132
3133 return TRUE;
3134}
3135
3136static
3137BOOLEAN
3138IsSupportedActivePartition(
3139 IN PPARTENTRY PartEntry)
3140{
3141 PVOLENTRY Volume;
3142
3143 /* Check the type and the file system of this partition */
3144
3145 /*
3146 * We do not support extended partition containers (on MBR disks) marked
3147 * as active, and containing code inside their extended boot records.
3148 */
3149 if (IsContainerPartition(PartEntry->PartitionType))
3150 {
3151 DPRINT1("System partition %lu in disk %lu is an extended partition container?!\n",
3152 PartEntry->PartitionNumber, PartEntry->DiskEntry->DiskNumber);
3153 return FALSE;
3154 }
3155
3156 Volume = PartEntry->Volume;
3157 if (!Volume)
3158 {
3159 /* Still no recognizable volume mounted: partition not supported */
3160 return FALSE;
3161 }
3162
3163 /*
3164 * ADDITIONAL CHECKS / BIG HACK:
3165 *
3166 * Retrieve its file system and check whether we have
3167 * write support for it. If that is the case we are fine
3168 * and we can use it directly. However if we don't have
3169 * write support we will need to change the active system
3170 * partition.
3171 *
3172 * NOTE that this is completely useless on architectures
3173 * where a real system partition is required, as on these
3174 * architectures the partition uses the FAT FS, for which
3175 * we do have write support.
3176 * NOTE also that for those architectures looking for a
3177 * partition boot indicator is insufficient.
3178 */
3179 if (Volume->FormatState == Unformatted)
3180 {
3181 /* If this partition is mounted, it would use RawFS ("RAW") */
3182 return TRUE;
3183 }
3184 else if (Volume->FormatState == Formatted)
3185 {
3186 ASSERT(*Volume->Info.FileSystem);
3187
3188 /* NOTE: Please keep in sync with the RegisteredFileSystems list! */
3189 if (_wcsicmp(Volume->Info.FileSystem, L"FAT") == 0 ||
3190 _wcsicmp(Volume->Info.FileSystem, L"FAT32") == 0 ||
3191 // _wcsicmp(Volume->Info.FileSystem, L"NTFS") == 0 ||
3192 _wcsicmp(Volume->Info.FileSystem, L"BTRFS") == 0)
3193 {
3194 return TRUE;
3195 }
3196 else
3197 {
3198 // WARNING: We cannot write on this FS yet!
3199 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
3200 Volume->Info.FileSystem);
3201 return FALSE;
3202 }
3203 }
3204 else // if (Volume->FormatState == UnknownFormat)
3205 {
3206 ASSERT(!*Volume->Info.FileSystem);
3207
3208 DPRINT1("System partition %lu in disk %lu with no or unknown FS?!\n",
3209 PartEntry->PartitionNumber, PartEntry->DiskEntry->DiskNumber);
3210 return FALSE;
3211 }
3212
3213 // HACK: WARNING: We cannot write on this FS yet!
3214 // See fsutil.c:InferFileSystem()
3215 if (PartEntry->PartitionType == PARTITION_IFS)
3216 {
3217 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
3218 Volume->Info.FileSystem);
3219 return FALSE;
3220 }
3221
3222 return TRUE;
3223}
3224
3225PPARTENTRY
3226FindSupportedSystemPartition(
3227 IN PPARTLIST List,
3228 IN BOOLEAN ForceSelect,
3229 IN PDISKENTRY AlternativeDisk OPTIONAL,
3230 IN PPARTENTRY AlternativePart OPTIONAL)
3231{
3232 PLIST_ENTRY ListEntry;
3233 PDISKENTRY DiskEntry;
3234 PPARTENTRY PartEntry;
3235 PPARTENTRY ActivePartition;
3236 PPARTENTRY CandidatePartition = NULL;
3237
3238 /* Check for empty disk list */
3239 if (IsListEmpty(&List->DiskListHead))
3240 {
3241 /* No system partition! */
3242 ASSERT(List->SystemPartition == NULL);
3243 goto NoSystemPartition;
3244 }
3245
3246 /* Adjust the optional alternative disk if needed */
3247 if (!AlternativeDisk && AlternativePart)
3248 AlternativeDisk = AlternativePart->DiskEntry;
3249
3250 /* Ensure that the alternative partition is on the alternative disk */
3251 if (AlternativePart)
3252 ASSERT(AlternativeDisk && (AlternativePart->DiskEntry == AlternativeDisk));
3253
3254 /* Ensure that the alternative disk is in the list */
3255 if (AlternativeDisk)
3256 ASSERT(AlternativeDisk->PartList == List);
3257
3258 /* Start fresh */
3259 CandidatePartition = NULL;
3260
3261//
3262// Step 1 : Check the system disk.
3263//
3264
3265 /*
3266 * First, check whether the system disk, i.e. the one that will be booted
3267 * by default by the hardware, contains an active partition. If so this
3268 * should be our system partition.
3269 */
3270 DiskEntry = GetSystemDisk(List);
3271 if (!DiskEntry)
3272 {
3273 /* No system disk found, directly go check the alternative disk */
3274 goto UseAlternativeDisk;
3275 }
3276
3277 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
3278 {
3279 DPRINT1("System disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
3280 goto UseAlternativeDisk;
3281 }
3282
3283 /* If we have a system partition (in the system disk), validate it */
3284 ActivePartition = List->SystemPartition;
3285 if (ActivePartition && IsSupportedActivePartition(ActivePartition))
3286 {
3287 CandidatePartition = ActivePartition;
3288
3289 DPRINT1("Use the current system partition %lu in disk %lu, drive letter %C\n",
3290 CandidatePartition->PartitionNumber,
3291 CandidatePartition->DiskEntry->DiskNumber,
3292 !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3293
3294 /* Return the candidate system partition */
3295 return CandidatePartition;
3296 }
3297
3298 /* If the system disk is not the optional alternative disk, perform the minimal checks */
3299 if (DiskEntry != AlternativeDisk)
3300 {
3301 /*
3302 * No active partition has been recognized. Enumerate all the (primary)
3303 * partitions in the system disk, excluding the possible current active
3304 * partition, to find a new candidate.
3305 */
3306 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3307 ListEntry != &DiskEntry->PrimaryPartListHead;
3308 ListEntry = ListEntry->Flink)
3309 {
3310 /* Retrieve the partition */
3311 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3312
3313 /* Skip the current active partition */
3314 if (PartEntry == ActivePartition)
3315 continue;
3316
3317 /* Check if the partition is partitioned and used */
3318 if (PartEntry->IsPartitioned &&
3319 !IsContainerPartition(PartEntry->PartitionType))
3320 {
3321 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3322
3323 /* If we get a candidate active partition in the disk, validate it */
3324 if (IsSupportedActivePartition(PartEntry))
3325 {
3326 CandidatePartition = PartEntry;
3327 goto UseAlternativePartition;
3328 }
3329 }
3330
3331#if 0
3332 /* Check if the partition is partitioned and used */
3333 if (!PartEntry->IsPartitioned)
3334 {
3335 ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
3336
3337 // TODO: Check for minimal size!!
3338 CandidatePartition = PartEntry;
3339 goto UseAlternativePartition;
3340 }
3341#endif
3342 }
3343
3344 /*
3345 * Still nothing, look whether there is some free space that we can use
3346 * for the new system partition. We must be sure that the total number
3347 * of partition is less than the maximum allowed, and that the minimal
3348 * size is fine.
3349 */
3350//
3351// TODO: Fix the handling of system partition being created in unpartitioned space!!
3352// --> When to partition it? etc...
3353//
3354 if (GetPrimaryPartitionCount(DiskEntry) < 4)
3355 {
3356 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3357 ListEntry != &DiskEntry->PrimaryPartListHead;
3358 ListEntry = ListEntry->Flink)
3359 {
3360 /* Retrieve the partition */
3361 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3362
3363 /* Skip the current active partition */
3364 if (PartEntry == ActivePartition)
3365 continue;
3366
3367 /* Check for unpartitioned space */
3368 if (!PartEntry->IsPartitioned)
3369 {
3370 ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
3371
3372 // TODO: Check for minimal size!!
3373 CandidatePartition = PartEntry;
3374 goto UseAlternativePartition;
3375 }
3376 }
3377 }
3378 }
3379
3380
3381//
3382// Step 2 : No active partition found: Check the alternative disk if specified.
3383//
3384
3385UseAlternativeDisk:
3386 if (!AlternativeDisk || (!ForceSelect && (DiskEntry != AlternativeDisk)))
3387 goto NoSystemPartition;
3388
3389 if (AlternativeDisk->DiskStyle == PARTITION_STYLE_GPT)
3390 {
3391 DPRINT1("Alternative disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
3392 goto NoSystemPartition;
3393 }
3394
3395 if (DiskEntry != AlternativeDisk)
3396 {
3397 /* Choose the alternative disk */
3398 DiskEntry = AlternativeDisk;
3399
3400 /* If we get a candidate active partition, validate it */
3401 ActivePartition = GetActiveDiskPartition(DiskEntry);
3402 if (ActivePartition && IsSupportedActivePartition(ActivePartition))
3403 {
3404 CandidatePartition = ActivePartition;
3405 goto UseAlternativePartition;
3406 }
3407 }
3408
3409 /* We now may have an unsupported active partition, or none */
3410
3411/***
3412 *** TODO: Improve the selection:
3413 *** - If we want a really separate system partition from the partition where
3414 *** we install, do something similar to what's done below in the code.
3415 *** - Otherwise if we allow for the system partition to be also the partition
3416 *** where we install, just directly fall down to using AlternativePart.
3417 ***/
3418
3419 /* Retrieve the first partition of the disk */
3420 PartEntry = CONTAINING_RECORD(DiskEntry->PrimaryPartListHead.Flink,
3421 PARTENTRY, ListEntry);
3422 ASSERT(DiskEntry == PartEntry->DiskEntry);
3423
3424 CandidatePartition = PartEntry;
3425
3426 //
3427 // See: https://svn.reactos.org/svn/reactos/trunk/reactos/base/setup/usetup/partlist.c?r1=63355&r2=63354&pathrev=63355#l2318
3428 //
3429
3430 /* Check if the disk is new and if so, use its first partition as the active system partition */
3431 if (DiskEntry->NewDisk)
3432 {
3433 // !IsContainerPartition(PartEntry->PartitionType);
3434 if (!CandidatePartition->IsPartitioned || !CandidatePartition->BootIndicator) /* CandidatePartition != ActivePartition */
3435 {
3436 ASSERT(DiskEntry == CandidatePartition->DiskEntry);
3437
3438 DPRINT1("Use new first active system partition %lu in disk %lu, drive letter %C\n",
3439 CandidatePartition->PartitionNumber,
3440 CandidatePartition->DiskEntry->DiskNumber,
3441 (!CandidatePartition->Volume || !CandidatePartition->Volume->Info.DriveLetter) ?
3442 L'-' : CandidatePartition->Volume->Info.DriveLetter);
3443
3444 /* Return the candidate system partition */
3445 return CandidatePartition;
3446 }
3447
3448 // FIXME: What to do??
3449 DPRINT1("NewDisk TRUE but first partition is used?\n");
3450 }
3451
3452 /*
3453 * The disk is not new, check if any partition is initialized;
3454 * if not, the first one becomes the system partition.
3455 */
3456 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3457 ListEntry != &DiskEntry->PrimaryPartListHead;
3458 ListEntry = ListEntry->Flink)
3459 {
3460 /* Retrieve the partition */
3461 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3462
3463 /* Check if the partition is partitioned and is used */
3464 // !IsContainerPartition(PartEntry->PartitionType);
3465 if (/* PartEntry->IsPartitioned && */
3466 PartEntry->PartitionType != PARTITION_ENTRY_UNUSED || PartEntry->BootIndicator)
3467 {
3468 break;
3469 }
3470 }
3471 if (ListEntry == &DiskEntry->PrimaryPartListHead)
3472 {
3473 /*
3474 * OK we haven't encountered any used and active partition,
3475 * so use the first one as the system partition.
3476 */
3477 ASSERT(DiskEntry == CandidatePartition->DiskEntry);
3478
3479 DPRINT1("Use first active system partition %lu in disk %lu, drive letter %C\n",
3480 CandidatePartition->PartitionNumber,
3481 CandidatePartition->DiskEntry->DiskNumber,
3482 (!CandidatePartition->Volume || !CandidatePartition->Volume->Info.DriveLetter) ?
3483 L'-' : CandidatePartition->Volume->Info.DriveLetter);
3484
3485 /* Return the candidate system partition */
3486 return CandidatePartition;
3487 }
3488
3489 /*
3490 * The disk is not new, we did not find any actual active partition,
3491 * or the one we found was not supported, or any possible other candidate
3492 * is not supported. We then use the alternative partition if specified.
3493 */
3494 if (AlternativePart)
3495 {
3496 DPRINT1("No valid or supported system partition has been found, use the alternative partition!\n");
3497 CandidatePartition = AlternativePart;
3498 goto UseAlternativePartition;
3499 }
3500 else
3501 {
3502NoSystemPartition:
3503 DPRINT1("No valid or supported system partition has been found on this system!\n");
3504 return NULL;
3505 }
3506
3507UseAlternativePartition:
3508 /*
3509 * We are here because we did not find any (active) candidate system
3510 * partition that we know how to support. What we are going to do is
3511 * to change the existing system partition and use the alternative partition
3512 * (e.g. on which we install ReactOS) as the new system partition.
3513 * Then we will need to add in FreeLdr's boot menu an entry for booting
3514 * from the original system partition.
3515 */
3516 ASSERT(CandidatePartition);
3517
3518 DPRINT1("Use alternative active system partition %lu in disk %lu, drive letter %C\n",
3519 CandidatePartition->PartitionNumber,
3520 CandidatePartition->DiskEntry->DiskNumber,
3521 (!CandidatePartition->Volume || !CandidatePartition->Volume->Info.DriveLetter) ?
3522 L'-' : CandidatePartition->Volume->Info.DriveLetter);
3523
3524 /* Return the candidate system partition */
3525 return CandidatePartition;
3526}
3527
3528BOOLEAN
3529SetActivePartition(
3530 IN PPARTLIST List,
3531 IN PPARTENTRY PartEntry,
3532 IN PPARTENTRY OldActivePart OPTIONAL)
3533{
3534 /* Check for empty disk list */
3535 if (IsListEmpty(&List->DiskListHead))
3536 return FALSE;
3537
3538 /* Validate the partition entry */
3539 if (!PartEntry)
3540 return FALSE;
3541
3542 /*
3543 * If the partition entry is already the system partition, or if it is
3544 * the same as the old active partition hint the user provided (and if
3545 * it is already active), just return success.
3546 */
3547 if ((PartEntry == List->SystemPartition) ||
3548 ((PartEntry == OldActivePart) && IsPartitionActive(OldActivePart)))
3549 {
3550 return TRUE;
3551 }
3552
3553 ASSERT(PartEntry->DiskEntry);
3554
3555 /* Ensure that the partition's disk is in the list */
3556 ASSERT(PartEntry->DiskEntry->PartList == List);
3557
3558 /*
3559 * If the user provided an old active partition hint, verify that it is
3560 * indeed active and belongs to the same disk where the new partition
3561 * belongs. Otherwise determine the current active partition on the disk
3562 * where the new partition belongs.
3563 */
3564 if (!(OldActivePart && IsPartitionActive(OldActivePart) && (OldActivePart->DiskEntry == PartEntry->DiskEntry)))
3565 {
3566 /* It's not, determine the current active partition for the disk */
3567 OldActivePart = GetActiveDiskPartition(PartEntry->DiskEntry);
3568 }
3569
3570 /* Unset the old active partition if it exists */
3571 if (OldActivePart)
3572 {
3573 OldActivePart->BootIndicator = FALSE;
3574 OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].BootIndicator = FALSE;
3575 OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].RewritePartition = TRUE;
3576 OldActivePart->DiskEntry->Dirty = TRUE;
3577 }
3578
3579 /* Modify the system partition if the new partition is on the system disk */
3580 if (PartEntry->DiskEntry == GetSystemDisk(List))
3581 List->SystemPartition = PartEntry;
3582
3583 /* Set the new active partition */
3584 PartEntry->BootIndicator = TRUE;
3585 PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].BootIndicator = TRUE;
3586 PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3587 PartEntry->DiskEntry->Dirty = TRUE;
3588
3589 return TRUE;
3590}
3591
3592NTSTATUS
3593WritePartitions(
3594 IN PDISKENTRY DiskEntry)
3595{
3596 NTSTATUS Status;
3597 OBJECT_ATTRIBUTES ObjectAttributes;
3598 UNICODE_STRING Name;
3599 HANDLE FileHandle;
3600 IO_STATUS_BLOCK Iosb;
3601 ULONG BufferSize;
3602 PPARTITION_INFORMATION PartitionInfo;
3603 ULONG PartitionCount;
3604 PLIST_ENTRY ListEntry;
3605 PPARTENTRY PartEntry;
3606 WCHAR DstPath[MAX_PATH];
3607
3608 DPRINT("WritePartitions() Disk: %lu\n", DiskEntry->DiskNumber);
3609
3610 /* If the disk is not dirty, there is nothing to do */
3611 if (!DiskEntry->Dirty)
3612 return STATUS_SUCCESS;
3613
3614 RtlStringCchPrintfW(DstPath, ARRAYSIZE(DstPath),
3615 L"\\Device\\Harddisk%lu\\Partition0",
3616 DiskEntry->DiskNumber);
3617 RtlInitUnicodeString(&Name, DstPath);
3618
3619 InitializeObjectAttributes(&ObjectAttributes,
3620 &Name,
3621 OBJ_CASE_INSENSITIVE,
3622 NULL,
3623 NULL);
3624
3625 Status = NtOpenFile(&FileHandle,
3626 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
3627 &ObjectAttributes,
3628 &Iosb,
3629 0,
3630 FILE_SYNCHRONOUS_IO_NONALERT);
3631 if (!NT_SUCCESS(Status))
3632 {
3633 DPRINT1("NtOpenFile() failed (Status %lx)\n", Status);
3634 return Status;
3635 }
3636
3637#ifdef DUMP_PARTITION_TABLE
3638 DumpPartitionTable(DiskEntry);
3639#endif
3640
3641 //
3642 // FIXME: We first *MUST* use IOCTL_DISK_CREATE_DISK to initialize
3643 // the disk in MBR or GPT format in case the disk was not initialized!!
3644 // For this we must ask the user which format to use.
3645 //
3646
3647 /* Save the original partition count to be restored later (see comment below) */
3648 PartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
3649
3650 /* Set the new disk layout and retrieve its updated version with
3651 * new partition numbers for the new partitions. The PARTMGR will
3652 * automatically notify the MOUNTMGR of new or deleted volumes. */
3653 BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
3654 ((PartitionCount - 1) * sizeof(PARTITION_INFORMATION));
3655 Status = NtDeviceIoControlFile(FileHandle,
3656 NULL,
3657 NULL,
3658 NULL,
3659 &Iosb,
3660 IOCTL_DISK_SET_DRIVE_LAYOUT,
3661 DiskEntry->LayoutBuffer,
3662 BufferSize,
3663 DiskEntry->LayoutBuffer,
3664 BufferSize);
3665 NtClose(FileHandle);
3666
3667 /*
3668 * IOCTL_DISK_SET_DRIVE_LAYOUT calls IoWritePartitionTable(), which converts
3669 * DiskEntry->LayoutBuffer->PartitionCount into a partition *table* count,
3670 * where such a table is expected to enumerate up to 4 partitions:
3671 * partition *table* count == ROUND_UP(PartitionCount, 4) / 4 .
3672 * Due to this we need to restore the original PartitionCount number.
3673 */
3674 DiskEntry->LayoutBuffer->PartitionCount = PartitionCount;
3675
3676 /* Check whether the IOCTL_DISK_SET_DRIVE_LAYOUT call succeeded */
3677 if (!NT_SUCCESS(Status))
3678 {
3679 DPRINT1("IOCTL_DISK_SET_DRIVE_LAYOUT failed (Status 0x%08lx)\n", Status);
3680 return Status;
3681 }
3682
3683#ifdef DUMP_PARTITION_TABLE
3684 DumpPartitionTable(DiskEntry);
3685#endif
3686
3687 /* Update the partition numbers and device names */
3688
3689 /* Update the primary partition table */
3690 for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3691 ListEntry != &DiskEntry->PrimaryPartListHead;
3692 ListEntry = ListEntry->Flink)
3693 {
3694 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3695 if (!PartEntry->IsPartitioned)
3696 continue;
3697 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3698
3699 /*
3700 * Initialize the partition's number and its device name only
3701 * if the partition was new. Note that the partition number
3702 * should not change if this partition has not been deleted
3703 * during repartitioning.
3704 */
3705 // FIXME: Our PartMgr currently returns modified numbers
3706 // in the layout, this needs to be investigated and fixed.
3707 if (PartEntry->New)
3708 {
3709 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3710 PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3711 InitPartitionDeviceName(PartEntry);
3712 }
3713 PartEntry->New = FALSE;
3714 }
3715
3716 /* Update the logical partition table */
3717 for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
3718 ListEntry != &DiskEntry->LogicalPartListHead;
3719 ListEntry = ListEntry->Flink)
3720 {
3721 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3722 if (!PartEntry->IsPartitioned)
3723 continue;
3724 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3725
3726 /* See comment above */
3727 if (PartEntry->New)
3728 {
3729 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3730 PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3731 InitPartitionDeviceName(PartEntry);
3732 }
3733 PartEntry->New = FALSE;
3734 }
3735
3736 //
3737 // NOTE: Originally (see r40437), we used to install here also a new MBR
3738 // for this disk (by calling InstallMbrBootCodeToDisk), only if:
3739 // DiskEntry->NewDisk == TRUE and DiskEntry->HwDiskNumber == 0.
3740 // Then after that, both DiskEntry->NewDisk and DiskEntry->NoMbr were set
3741 // to FALSE. In the other place (in usetup.c) where InstallMbrBootCodeToDisk
3742 // was called too, the installation test was modified by checking whether
3743 // DiskEntry->NoMbr was TRUE (instead of NewDisk).
3744 //
3745
3746 // HACK: Parts of FIXMEs described above: (Re)set the PartitionStyle to MBR.
3747 DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
3748
3749 /* The layout has been successfully updated, the disk is not dirty anymore */
3750 DiskEntry->Dirty = FALSE;
3751
3752 return Status;
3753}
3754
3755BOOLEAN
3756WritePartitionsToDisk(
3757 IN PPARTLIST List)
3758{
3759 NTSTATUS Status;
3760 PLIST_ENTRY Entry;
3761 PDISKENTRY DiskEntry;
3762 PVOLENTRY Volume;
3763
3764 if (!List)
3765 return TRUE;
3766
3767 /* Write all the partitions to all the disks */
3768 for (Entry = List->DiskListHead.Flink;
3769 Entry != &List->DiskListHead;
3770 Entry = Entry->Flink)
3771 {
3772 DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
3773
3774 if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
3775 {
3776 DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
3777 continue;
3778 }
3779
3780 if (DiskEntry->Dirty != FALSE)
3781 {
3782 Status = WritePartitions(DiskEntry);
3783 if (!NT_SUCCESS(Status))
3784 {
3785 DPRINT1("WritePartitionsToDisk() failed to update disk %lu, Status 0x%08lx\n",
3786 DiskEntry->DiskNumber, Status);
3787 }
3788 }
3789 }
3790
3791 /* The PARTMGR should have notified the MOUNTMGR that new volumes
3792 * associated with the new partitions had to be created */
3793
3794 /* Assign valid device names to new volumes */
3795 for (Entry = List->VolumesList.Flink;
3796 Entry != &List->VolumesList;
3797 Entry = Entry->Flink)
3798 {
3799 Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3800 InitVolumeDeviceName(Volume);
3801 }
3802
3803 return TRUE;
3804}
3805
3806
3807/**
3808 * @brief
3809 * Assign a "\DosDevices\#:" mount point drive letter to a disk partition or
3810 * volume, specified by a given disk signature and starting partition offset.
3811 **/
3812static BOOLEAN
3813SetMountedDeviceValue(
3814 _In_ PVOLENTRY Volume)
3815{
3816 PPARTENTRY PartEntry = Volume->PartEntry;
3817 WCHAR Letter = Volume->Info.DriveLetter;
3818 NTSTATUS Status;
3819 OBJECT_ATTRIBUTES ObjectAttributes;
3820 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\MountedDevices");
3821 UNICODE_STRING ValueName;
3822 WCHAR Buffer[16];
3823 HANDLE KeyHandle;
3824 REG_DISK_MOUNT_INFO MountInfo;
3825
3826 /* Ignore no letter */
3827 if (!Letter)
3828 return TRUE;
3829
3830 RtlStringCchPrintfW(Buffer, _countof(Buffer),
3831 L"\\DosDevices\\%c:", Letter);
3832 RtlInitUnicodeString(&ValueName, Buffer);
3833
3834 InitializeObjectAttributes(&ObjectAttributes,
3835 &KeyName,
3836 OBJ_CASE_INSENSITIVE,
3837 GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
3838 NULL);
3839
3840 Status = NtOpenKey(&KeyHandle,
3841 KEY_ALL_ACCESS,
3842 &ObjectAttributes);
3843 if (!NT_SUCCESS(Status))
3844 {
3845 Status = NtCreateKey(&KeyHandle,
3846 KEY_ALL_ACCESS,
3847 &ObjectAttributes,
3848 0,
3849 NULL,
3850 REG_OPTION_NON_VOLATILE,
3851 NULL);
3852 }
3853 if (!NT_SUCCESS(Status))
3854 {
3855 DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
3856 return FALSE;
3857 }
3858
3859 MountInfo.Signature = PartEntry->DiskEntry->LayoutBuffer->Signature;
3860 MountInfo.StartingOffset = GetPartEntryOffsetInBytes(PartEntry);
3861 Status = NtSetValueKey(KeyHandle,
3862 &ValueName,
3863 0,
3864 REG_BINARY,
3865 (PVOID)&MountInfo,
3866 sizeof(MountInfo));
3867 NtClose(KeyHandle);
3868 if (!NT_SUCCESS(Status))
3869 {
3870 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
3871 return FALSE;
3872 }
3873
3874 return TRUE;
3875}
3876
3877BOOLEAN
3878SetMountedDeviceValues(
3879 _In_ PPARTLIST List)
3880{
3881 PLIST_ENTRY Entry;
3882 PVOLENTRY Volume;
3883
3884 if (!List)
3885 return FALSE;
3886
3887 for (Entry = List->VolumesList.Flink;
3888 Entry != &List->VolumesList;
3889 Entry = Entry->Flink)
3890 {
3891 Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3892
3893 /* Assign a "\DosDevices\#:" mount point to this volume */
3894 if (!SetMountedDeviceValue(Volume))
3895 return FALSE;
3896 }
3897
3898 return TRUE;
3899}
3900
3901VOID
3902SetMBRPartitionType(
3903 IN PPARTENTRY PartEntry,
3904 IN UCHAR PartitionType)
3905{
3906 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
3907
3908 ASSERT(DiskEntry->DiskStyle == PARTITION_STYLE_MBR);
3909
3910 /* Nothing to do if we assign the same type */
3911 if (PartitionType == PartEntry->PartitionType)
3912 return;
3913
3914 // TODO: We might need to remount the associated basic volume...
3915
3916 PartEntry->PartitionType = PartitionType;
3917
3918 DiskEntry->Dirty = TRUE;
3919 DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].PartitionType = PartitionType;
3920 DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RecognizedPartition = IsRecognizedPartition(PartitionType);
3921 DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3922}
3923
3924/* EOF */