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