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