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