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