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