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