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