xref: /reactos/base/setup/lib/utils/partlist.c (revision b09b5584)
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>
24 typedef 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
35 static
36 VOID
37 DumpPartitionTable(
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 
66 ULONGLONG
67 AlignDown(
68     IN ULONGLONG Value,
69     IN ULONG Alignment)
70 {
71     ULONGLONG Temp;
72 
73     Temp = Value / Alignment;
74 
75     return Temp * Alignment;
76 }
77 
78 ULONGLONG
79 AlignUp(
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 
94 ULONGLONG
95 RoundingDivide(
96    IN ULONGLONG Dividend,
97    IN ULONGLONG Divisor)
98 {
99     return (Dividend + Divisor / 2) / Divisor;
100 }
101 
102 
103 static
104 VOID
105 GetDriverName(
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 
136 static
137 VOID
138 AssignDriveLetters(
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 
205 static NTSTATUS
206 NTAPI
207 DiskIdentifierQueryRoutine(
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 
235 static NTSTATUS
236 NTAPI
237 DiskConfigurationDataQueryRoutine(
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 
278 static NTSTATUS
279 NTAPI
280 SystemConfigurationDataQueryRoutine(
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 
326 static VOID
327 EnumerateBiosDiskEntries(
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  */
490 BOOLEAN
491 IsDiskSuperFloppy2(
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 
541 BOOLEAN
542 IsDiskSuperFloppy(
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 
562 BOOLEAN
563 IsDiskSuperFloppyEx(
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 
599 BOOLEAN
600 IsSuperFloppy(
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  */
624 static
625 BOOLEAN
626 InsertDiskRegion(
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 
690 static
691 PPARTENTRY
692 CreateInsertBlankRegion(
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 
727 static
728 VOID
729 DestroyRegion(
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 
738 static
739 VOID
740 AddLogicalDiskSpace(
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().
768 static
769 BOOLEAN
770 InitializePartitionEntry(
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 
898 static
899 VOID
900 InitPartitionDeviceName(
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 
919 static
920 VOID
921 InitVolumeDeviceName(
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 
944 static
945 PVOLENTRY
946 InitVolume(
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 
983 static
984 VOID
985 AddPartitionToDisk(
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;
1079 SkipVolume:;
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 
1093 static
1094 VOID
1095 ScanForUnpartitionedDiskSpace(
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 
1290 static
1291 VOID
1292 SetDiskSignature(
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 
1352 static
1353 VOID
1354 UpdateDiskSignatures(
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 
1382 static
1383 VOID
1384 UpdateHwDiskNumbers(
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 
1443 static
1444 VOID
1445 AddDiskToList(
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  */
1855 static
1856 PDISKENTRY
1857 GetSystemDisk(
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  */
1906 BOOLEAN
1907 IsPartitionActive(
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 
1928 static
1929 PPARTENTRY
1930 GetActiveDiskPartition(
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 
1986 PPARTLIST
1987 NTAPI
1988 CreatePartitionList(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 
2072 VOID
2073 NTAPI
2074 DestroyPartitionList(
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 
2127 PDISKENTRY
2128 GetDiskByBiosNumber(
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 
2150 PDISKENTRY
2151 GetDiskByNumber(
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 
2173 PDISKENTRY
2174 GetDiskBySCSI(
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 
2203 PDISKENTRY
2204 GetDiskBySignature(
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 
2226 PPARTENTRY
2227 GetPartition(
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 
2267 PPARTENTRY
2268 SelectPartition(
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 
2292 PPARTENTRY
2293 NTAPI
2294 GetNextPartition(
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 
2385 PPARTENTRY
2386 NTAPI
2387 GetPrevPartition(
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 
2481 static inline
2482 BOOLEAN
2483 IsEmptyLayoutEntry(
2484     _In_ PPARTITION_INFORMATION PartitionInfo)
2485 {
2486     return (PartitionInfo->StartingOffset.QuadPart == 0 &&
2487             PartitionInfo->PartitionLength.QuadPart == 0);
2488 }
2489 
2490 static inline
2491 BOOLEAN
2492 IsSamePrimaryLayoutEntry(
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  **/
2506 static
2507 ULONG
2508 GetPartitionCount(
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 
2535 static
2536 BOOLEAN
2537 ReAllocateLayoutBuffer(
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 
2587 static
2588 VOID
2589 UpdateDiskLayout(
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  **/
2789 PPARTENTRY
2790 NTAPI
2791 GetAdjUnpartitionedEntry(
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 
2826 static ERROR_NUMBER
2827 MBRPartitionCreateChecks(
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 
2879 ERROR_NUMBER
2880 NTAPI
2881 PartitionCreateChecks(
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.
2908 BOOLEAN
2909 NTAPI
2910 CreatePartition(
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 
2964 static NTSTATUS
2965 DismountPartition(
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 
2999 BOOLEAN
3000 NTAPI
3001 DeletePartition(
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 
3136 static
3137 BOOLEAN
3138 IsSupportedActivePartition(
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 
3225 PPARTENTRY
3226 FindSupportedSystemPartition(
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 
3385 UseAlternativeDisk:
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->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3442 
3443             /* Return the candidate system partition */
3444             return CandidatePartition;
3445         }
3446 
3447         // FIXME: What to do??
3448         DPRINT1("NewDisk TRUE but first partition is used?\n");
3449     }
3450 
3451     /*
3452      * The disk is not new, check if any partition is initialized;
3453      * if not, the first one becomes the system partition.
3454      */
3455     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3456          ListEntry != &DiskEntry->PrimaryPartListHead;
3457          ListEntry = ListEntry->Flink)
3458     {
3459         /* Retrieve the partition */
3460         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3461 
3462         /* Check if the partition is partitioned and is used */
3463         // !IsContainerPartition(PartEntry->PartitionType);
3464         if (/* PartEntry->IsPartitioned && */
3465             PartEntry->PartitionType != PARTITION_ENTRY_UNUSED || PartEntry->BootIndicator)
3466         {
3467             break;
3468         }
3469     }
3470     if (ListEntry == &DiskEntry->PrimaryPartListHead)
3471     {
3472         /*
3473          * OK we haven't encountered any used and active partition,
3474          * so use the first one as the system partition.
3475          */
3476         ASSERT(DiskEntry == CandidatePartition->DiskEntry);
3477 
3478         DPRINT1("Use first active system partition %lu in disk %lu, drive letter %C\n",
3479                 CandidatePartition->PartitionNumber,
3480                 CandidatePartition->DiskEntry->DiskNumber,
3481                 !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3482 
3483         /* Return the candidate system partition */
3484         return CandidatePartition;
3485     }
3486 
3487     /*
3488      * The disk is not new, we did not find any actual active partition,
3489      * or the one we found was not supported, or any possible other candidate
3490      * is not supported. We then use the alternative partition if specified.
3491      */
3492     if (AlternativePart)
3493     {
3494         DPRINT1("No valid or supported system partition has been found, use the alternative partition!\n");
3495         CandidatePartition = AlternativePart;
3496         goto UseAlternativePartition;
3497     }
3498     else
3499     {
3500 NoSystemPartition:
3501         DPRINT1("No valid or supported system partition has been found on this system!\n");
3502         return NULL;
3503     }
3504 
3505 UseAlternativePartition:
3506     /*
3507      * We are here because we did not find any (active) candidate system
3508      * partition that we know how to support. What we are going to do is
3509      * to change the existing system partition and use the alternative partition
3510      * (e.g. on which we install ReactOS) as the new system partition.
3511      * Then we will need to add in FreeLdr's boot menu an entry for booting
3512      * from the original system partition.
3513      */
3514     ASSERT(CandidatePartition);
3515 
3516     DPRINT1("Use alternative active system partition %lu in disk %lu, drive letter %C\n",
3517             CandidatePartition->PartitionNumber,
3518             CandidatePartition->DiskEntry->DiskNumber,
3519             !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3520 
3521     /* Return the candidate system partition */
3522     return CandidatePartition;
3523 }
3524 
3525 BOOLEAN
3526 SetActivePartition(
3527     IN PPARTLIST List,
3528     IN PPARTENTRY PartEntry,
3529     IN PPARTENTRY OldActivePart OPTIONAL)
3530 {
3531     /* Check for empty disk list */
3532     if (IsListEmpty(&List->DiskListHead))
3533         return FALSE;
3534 
3535     /* Validate the partition entry */
3536     if (!PartEntry)
3537         return FALSE;
3538 
3539     /*
3540      * If the partition entry is already the system partition, or if it is
3541      * the same as the old active partition hint the user provided (and if
3542      * it is already active), just return success.
3543      */
3544     if ((PartEntry == List->SystemPartition) ||
3545         ((PartEntry == OldActivePart) && IsPartitionActive(OldActivePart)))
3546     {
3547         return TRUE;
3548     }
3549 
3550     ASSERT(PartEntry->DiskEntry);
3551 
3552     /* Ensure that the partition's disk is in the list */
3553     ASSERT(PartEntry->DiskEntry->PartList == List);
3554 
3555     /*
3556      * If the user provided an old active partition hint, verify that it is
3557      * indeed active and belongs to the same disk where the new partition
3558      * belongs. Otherwise determine the current active partition on the disk
3559      * where the new partition belongs.
3560      */
3561     if (!(OldActivePart && IsPartitionActive(OldActivePart) && (OldActivePart->DiskEntry == PartEntry->DiskEntry)))
3562     {
3563         /* It's not, determine the current active partition for the disk */
3564         OldActivePart = GetActiveDiskPartition(PartEntry->DiskEntry);
3565     }
3566 
3567     /* Unset the old active partition if it exists */
3568     if (OldActivePart)
3569     {
3570         OldActivePart->BootIndicator = FALSE;
3571         OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].BootIndicator = FALSE;
3572         OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].RewritePartition = TRUE;
3573         OldActivePart->DiskEntry->Dirty = TRUE;
3574     }
3575 
3576     /* Modify the system partition if the new partition is on the system disk */
3577     if (PartEntry->DiskEntry == GetSystemDisk(List))
3578         List->SystemPartition = PartEntry;
3579 
3580     /* Set the new active partition */
3581     PartEntry->BootIndicator = TRUE;
3582     PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].BootIndicator = TRUE;
3583     PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3584     PartEntry->DiskEntry->Dirty = TRUE;
3585 
3586     return TRUE;
3587 }
3588 
3589 NTSTATUS
3590 WritePartitions(
3591     IN PDISKENTRY DiskEntry)
3592 {
3593     NTSTATUS Status;
3594     OBJECT_ATTRIBUTES ObjectAttributes;
3595     UNICODE_STRING Name;
3596     HANDLE FileHandle;
3597     IO_STATUS_BLOCK Iosb;
3598     ULONG BufferSize;
3599     PPARTITION_INFORMATION PartitionInfo;
3600     ULONG PartitionCount;
3601     PLIST_ENTRY ListEntry;
3602     PPARTENTRY PartEntry;
3603     WCHAR DstPath[MAX_PATH];
3604 
3605     DPRINT("WritePartitions() Disk: %lu\n", DiskEntry->DiskNumber);
3606 
3607     /* If the disk is not dirty, there is nothing to do */
3608     if (!DiskEntry->Dirty)
3609         return STATUS_SUCCESS;
3610 
3611     RtlStringCchPrintfW(DstPath, ARRAYSIZE(DstPath),
3612                         L"\\Device\\Harddisk%lu\\Partition0",
3613                         DiskEntry->DiskNumber);
3614     RtlInitUnicodeString(&Name, DstPath);
3615 
3616     InitializeObjectAttributes(&ObjectAttributes,
3617                                &Name,
3618                                OBJ_CASE_INSENSITIVE,
3619                                NULL,
3620                                NULL);
3621 
3622     Status = NtOpenFile(&FileHandle,
3623                         GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
3624                         &ObjectAttributes,
3625                         &Iosb,
3626                         0,
3627                         FILE_SYNCHRONOUS_IO_NONALERT);
3628     if (!NT_SUCCESS(Status))
3629     {
3630         DPRINT1("NtOpenFile() failed (Status %lx)\n", Status);
3631         return Status;
3632     }
3633 
3634 #ifdef DUMP_PARTITION_TABLE
3635     DumpPartitionTable(DiskEntry);
3636 #endif
3637 
3638     //
3639     // FIXME: We first *MUST* use IOCTL_DISK_CREATE_DISK to initialize
3640     // the disk in MBR or GPT format in case the disk was not initialized!!
3641     // For this we must ask the user which format to use.
3642     //
3643 
3644     /* Save the original partition count to be restored later (see comment below) */
3645     PartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
3646 
3647     /* Set the new disk layout and retrieve its updated version with
3648      * new partition numbers for the new partitions. The PARTMGR will
3649      * automatically notify the MOUNTMGR of new or deleted volumes. */
3650     BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
3651                  ((PartitionCount - 1) * sizeof(PARTITION_INFORMATION));
3652     Status = NtDeviceIoControlFile(FileHandle,
3653                                    NULL,
3654                                    NULL,
3655                                    NULL,
3656                                    &Iosb,
3657                                    IOCTL_DISK_SET_DRIVE_LAYOUT,
3658                                    DiskEntry->LayoutBuffer,
3659                                    BufferSize,
3660                                    DiskEntry->LayoutBuffer,
3661                                    BufferSize);
3662     NtClose(FileHandle);
3663 
3664     /*
3665      * IOCTL_DISK_SET_DRIVE_LAYOUT calls IoWritePartitionTable(), which converts
3666      * DiskEntry->LayoutBuffer->PartitionCount into a partition *table* count,
3667      * where such a table is expected to enumerate up to 4 partitions:
3668      * partition *table* count == ROUND_UP(PartitionCount, 4) / 4 .
3669      * Due to this we need to restore the original PartitionCount number.
3670      */
3671     DiskEntry->LayoutBuffer->PartitionCount = PartitionCount;
3672 
3673     /* Check whether the IOCTL_DISK_SET_DRIVE_LAYOUT call succeeded */
3674     if (!NT_SUCCESS(Status))
3675     {
3676         DPRINT1("IOCTL_DISK_SET_DRIVE_LAYOUT failed (Status 0x%08lx)\n", Status);
3677         return Status;
3678     }
3679 
3680 #ifdef DUMP_PARTITION_TABLE
3681     DumpPartitionTable(DiskEntry);
3682 #endif
3683 
3684     /* Update the partition numbers and device names */
3685 
3686     /* Update the primary partition table */
3687     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3688          ListEntry != &DiskEntry->PrimaryPartListHead;
3689          ListEntry = ListEntry->Flink)
3690     {
3691         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3692         if (!PartEntry->IsPartitioned)
3693             continue;
3694         ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3695 
3696         /*
3697          * Initialize the partition's number and its device name only
3698          * if the partition was new. Note that the partition number
3699          * should not change if this partition has not been deleted
3700          * during repartitioning.
3701          */
3702         // FIXME: Our PartMgr currently returns modified numbers
3703         // in the layout, this needs to be investigated and fixed.
3704         if (PartEntry->New)
3705         {
3706             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3707             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3708             InitPartitionDeviceName(PartEntry);
3709         }
3710         PartEntry->New = FALSE;
3711     }
3712 
3713     /* Update the logical partition table */
3714     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
3715          ListEntry != &DiskEntry->LogicalPartListHead;
3716          ListEntry = ListEntry->Flink)
3717     {
3718         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3719         if (!PartEntry->IsPartitioned)
3720             continue;
3721         ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3722 
3723         /* See comment above */
3724         if (PartEntry->New)
3725         {
3726             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3727             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3728             InitPartitionDeviceName(PartEntry);
3729         }
3730         PartEntry->New = FALSE;
3731     }
3732 
3733     //
3734     // NOTE: Originally (see r40437), we used to install here also a new MBR
3735     // for this disk (by calling InstallMbrBootCodeToDisk), only if:
3736     // DiskEntry->NewDisk == TRUE and DiskEntry->HwDiskNumber == 0.
3737     // Then after that, both DiskEntry->NewDisk and DiskEntry->NoMbr were set
3738     // to FALSE. In the other place (in usetup.c) where InstallMbrBootCodeToDisk
3739     // was called too, the installation test was modified by checking whether
3740     // DiskEntry->NoMbr was TRUE (instead of NewDisk).
3741     //
3742 
3743     // HACK: Parts of FIXMEs described above: (Re)set the PartitionStyle to MBR.
3744     DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
3745 
3746     /* The layout has been successfully updated, the disk is not dirty anymore */
3747     DiskEntry->Dirty = FALSE;
3748 
3749     return Status;
3750 }
3751 
3752 BOOLEAN
3753 WritePartitionsToDisk(
3754     IN PPARTLIST List)
3755 {
3756     NTSTATUS Status;
3757     PLIST_ENTRY Entry;
3758     PDISKENTRY DiskEntry;
3759     PVOLENTRY Volume;
3760 
3761     if (!List)
3762         return TRUE;
3763 
3764     /* Write all the partitions to all the disks */
3765     for (Entry = List->DiskListHead.Flink;
3766          Entry != &List->DiskListHead;
3767          Entry = Entry->Flink)
3768     {
3769         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
3770 
3771         if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
3772         {
3773             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
3774             continue;
3775         }
3776 
3777         if (DiskEntry->Dirty != FALSE)
3778         {
3779             Status = WritePartitions(DiskEntry);
3780             if (!NT_SUCCESS(Status))
3781             {
3782                 DPRINT1("WritePartitionsToDisk() failed to update disk %lu, Status 0x%08lx\n",
3783                         DiskEntry->DiskNumber, Status);
3784             }
3785         }
3786     }
3787 
3788     /* The PARTMGR should have notified the MOUNTMGR that new volumes
3789      * associated with the new partitions had to be created */
3790 
3791     /* Assign valid device names to new volumes */
3792     for (Entry = List->VolumesList.Flink;
3793          Entry != &List->VolumesList;
3794          Entry = Entry->Flink)
3795     {
3796         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3797         InitVolumeDeviceName(Volume);
3798     }
3799 
3800     return TRUE;
3801 }
3802 
3803 
3804 /**
3805  * @brief
3806  * Assign a "\DosDevices\#:" mount point drive letter to a disk partition or
3807  * volume, specified by a given disk signature and starting partition offset.
3808  **/
3809 static BOOLEAN
3810 SetMountedDeviceValue(
3811     _In_ PVOLENTRY Volume)
3812 {
3813     PPARTENTRY PartEntry = Volume->PartEntry;
3814     WCHAR Letter = Volume->Info.DriveLetter;
3815     NTSTATUS Status;
3816     OBJECT_ATTRIBUTES ObjectAttributes;
3817     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\MountedDevices");
3818     UNICODE_STRING ValueName;
3819     WCHAR Buffer[16];
3820     HANDLE KeyHandle;
3821     REG_DISK_MOUNT_INFO MountInfo;
3822 
3823     /* Ignore no letter */
3824     if (!Letter)
3825         return TRUE;
3826 
3827     RtlStringCchPrintfW(Buffer, _countof(Buffer),
3828                         L"\\DosDevices\\%c:", Letter);
3829     RtlInitUnicodeString(&ValueName, Buffer);
3830 
3831     InitializeObjectAttributes(&ObjectAttributes,
3832                                &KeyName,
3833                                OBJ_CASE_INSENSITIVE,
3834                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
3835                                NULL);
3836 
3837     Status = NtOpenKey(&KeyHandle,
3838                        KEY_ALL_ACCESS,
3839                        &ObjectAttributes);
3840     if (!NT_SUCCESS(Status))
3841     {
3842         Status = NtCreateKey(&KeyHandle,
3843                              KEY_ALL_ACCESS,
3844                              &ObjectAttributes,
3845                              0,
3846                              NULL,
3847                              REG_OPTION_NON_VOLATILE,
3848                              NULL);
3849     }
3850     if (!NT_SUCCESS(Status))
3851     {
3852         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
3853         return FALSE;
3854     }
3855 
3856     MountInfo.Signature = PartEntry->DiskEntry->LayoutBuffer->Signature;
3857     MountInfo.StartingOffset = GetPartEntryOffsetInBytes(PartEntry);
3858     Status = NtSetValueKey(KeyHandle,
3859                            &ValueName,
3860                            0,
3861                            REG_BINARY,
3862                            (PVOID)&MountInfo,
3863                            sizeof(MountInfo));
3864     NtClose(KeyHandle);
3865     if (!NT_SUCCESS(Status))
3866     {
3867         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
3868         return FALSE;
3869     }
3870 
3871     return TRUE;
3872 }
3873 
3874 BOOLEAN
3875 SetMountedDeviceValues(
3876     _In_ PPARTLIST List)
3877 {
3878     PLIST_ENTRY Entry;
3879     PVOLENTRY Volume;
3880 
3881     if (!List)
3882         return FALSE;
3883 
3884     for (Entry = List->VolumesList.Flink;
3885          Entry != &List->VolumesList;
3886          Entry = Entry->Flink)
3887     {
3888         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3889 
3890         /* Assign a "\DosDevices\#:" mount point to this volume */
3891         if (!SetMountedDeviceValue(Volume))
3892             return FALSE;
3893     }
3894 
3895     return TRUE;
3896 }
3897 
3898 VOID
3899 SetMBRPartitionType(
3900     IN PPARTENTRY PartEntry,
3901     IN UCHAR PartitionType)
3902 {
3903     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
3904 
3905     ASSERT(DiskEntry->DiskStyle == PARTITION_STYLE_MBR);
3906 
3907     /* Nothing to do if we assign the same type */
3908     if (PartitionType == PartEntry->PartitionType)
3909         return;
3910 
3911     // TODO: We might need to remount the associated basic volume...
3912 
3913     PartEntry->PartitionType = PartitionType;
3914 
3915     DiskEntry->Dirty = TRUE;
3916     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].PartitionType = PartitionType;
3917     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RecognizedPartition = IsRecognizedPartition(PartitionType);
3918     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3919 }
3920 
3921 /* EOF */
3922