xref: /reactos/base/setup/lib/utils/partlist.c (revision d6d3d0ea)
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
CreatePartitionList(VOID)1987 CreatePartitionList(VOID)
1988 {
1989     PPARTLIST List;
1990     PDISKENTRY SystemDisk;
1991     OBJECT_ATTRIBUTES ObjectAttributes;
1992     SYSTEM_DEVICE_INFORMATION Sdi;
1993     IO_STATUS_BLOCK Iosb;
1994     ULONG ReturnSize;
1995     NTSTATUS Status;
1996     ULONG DiskNumber;
1997     HANDLE FileHandle;
1998     UNICODE_STRING Name;
1999     WCHAR Buffer[MAX_PATH];
2000 
2001     List = (PPARTLIST)RtlAllocateHeap(ProcessHeap,
2002                                       0,
2003                                       sizeof(PARTLIST));
2004     if (!List)
2005         return NULL;
2006 
2007     List->SystemPartition = NULL;
2008 
2009     InitializeListHead(&List->DiskListHead);
2010     InitializeListHead(&List->BiosDiskListHead);
2011     InitializeListHead(&List->VolumesList);
2012 
2013     /*
2014      * Enumerate the disks seen by the BIOS; this will be used later
2015      * to map drives seen by NTOS with their corresponding BIOS names.
2016      */
2017     EnumerateBiosDiskEntries(List);
2018 
2019     /* Enumerate disks seen by NTOS */
2020     Status = NtQuerySystemInformation(SystemDeviceInformation,
2021                                       &Sdi,
2022                                       sizeof(Sdi),
2023                                       &ReturnSize);
2024     if (!NT_SUCCESS(Status))
2025     {
2026         DPRINT1("NtQuerySystemInformation() failed, Status 0x%08lx\n", Status);
2027         RtlFreeHeap(ProcessHeap, 0, List);
2028         return NULL;
2029     }
2030 
2031     for (DiskNumber = 0; DiskNumber < Sdi.NumberOfDisks; DiskNumber++)
2032     {
2033         RtlStringCchPrintfW(Buffer, ARRAYSIZE(Buffer),
2034                             L"\\Device\\Harddisk%lu\\Partition0",
2035                             DiskNumber);
2036         RtlInitUnicodeString(&Name, Buffer);
2037 
2038         InitializeObjectAttributes(&ObjectAttributes,
2039                                    &Name,
2040                                    OBJ_CASE_INSENSITIVE,
2041                                    NULL,
2042                                    NULL);
2043 
2044         Status = NtOpenFile(&FileHandle,
2045                             FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
2046                             &ObjectAttributes,
2047                             &Iosb,
2048                             FILE_SHARE_READ | FILE_SHARE_WRITE,
2049                             FILE_SYNCHRONOUS_IO_NONALERT);
2050         if (NT_SUCCESS(Status))
2051         {
2052             AddDiskToList(FileHandle, DiskNumber, List);
2053             NtClose(FileHandle);
2054         }
2055     }
2056 
2057     UpdateDiskSignatures(List);
2058     UpdateHwDiskNumbers(List);
2059     AssignDriveLetters(List);
2060 
2061     /*
2062      * Retrieve the system partition: the active partition on the system
2063      * disk (the one that will be booted by default by the hardware).
2064      */
2065     SystemDisk = GetSystemDisk(List);
2066     List->SystemPartition = (SystemDisk ? GetActiveDiskPartition(SystemDisk) : NULL);
2067 
2068     return List;
2069 }
2070 
2071 VOID
DestroyPartitionList(IN PPARTLIST List)2072 DestroyPartitionList(
2073     IN PPARTLIST List)
2074 {
2075     PDISKENTRY DiskEntry;
2076     PBIOSDISKENTRY BiosDiskEntry;
2077     PPARTENTRY PartEntry;
2078     PLIST_ENTRY Entry;
2079 
2080     /* Release disk and partition info */
2081     while (!IsListEmpty(&List->DiskListHead))
2082     {
2083         Entry = RemoveHeadList(&List->DiskListHead);
2084         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2085 
2086         /* Release driver name */
2087         RtlFreeUnicodeString(&DiskEntry->DriverName);
2088 
2089         /* Release primary partition list */
2090         while (!IsListEmpty(&DiskEntry->PrimaryPartListHead))
2091         {
2092             Entry = RemoveHeadList(&DiskEntry->PrimaryPartListHead);
2093             PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2094             DestroyRegion(PartEntry);
2095         }
2096 
2097         /* Release logical partition list */
2098         while (!IsListEmpty(&DiskEntry->LogicalPartListHead))
2099         {
2100             Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead);
2101             PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2102             DestroyRegion(PartEntry);
2103         }
2104 
2105         /* Release layout buffer */
2106         if (DiskEntry->LayoutBuffer != NULL)
2107             RtlFreeHeap(ProcessHeap, 0, DiskEntry->LayoutBuffer);
2108 
2109         /* Release disk entry */
2110         RtlFreeHeap(ProcessHeap, 0, DiskEntry);
2111     }
2112 
2113     /* Release the BIOS disk info */
2114     while (!IsListEmpty(&List->BiosDiskListHead))
2115     {
2116         Entry = RemoveHeadList(&List->BiosDiskListHead);
2117         BiosDiskEntry = CONTAINING_RECORD(Entry, BIOSDISKENTRY, ListEntry);
2118         RtlFreeHeap(ProcessHeap, 0, BiosDiskEntry);
2119     }
2120 
2121     /* Release list head */
2122     RtlFreeHeap(ProcessHeap, 0, List);
2123 }
2124 
2125 PDISKENTRY
GetDiskByBiosNumber(_In_ PPARTLIST List,_In_ ULONG HwDiskNumber)2126 GetDiskByBiosNumber(
2127     _In_ PPARTLIST List,
2128     _In_ ULONG HwDiskNumber)
2129 {
2130     PDISKENTRY DiskEntry;
2131     PLIST_ENTRY Entry;
2132 
2133     /* Loop over the disks and find the correct one */
2134     for (Entry = List->DiskListHead.Flink;
2135          Entry != &List->DiskListHead;
2136          Entry = Entry->Flink)
2137     {
2138         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2139 
2140         if (DiskEntry->HwDiskNumber == HwDiskNumber)
2141             return DiskEntry; /* Disk found, return it */
2142     }
2143 
2144     /* Disk not found, stop there */
2145     return NULL;
2146 }
2147 
2148 PDISKENTRY
GetDiskByNumber(_In_ PPARTLIST List,_In_ ULONG DiskNumber)2149 GetDiskByNumber(
2150     _In_ PPARTLIST List,
2151     _In_ ULONG DiskNumber)
2152 {
2153     PDISKENTRY DiskEntry;
2154     PLIST_ENTRY Entry;
2155 
2156     /* Loop over the disks and find the correct one */
2157     for (Entry = List->DiskListHead.Flink;
2158          Entry != &List->DiskListHead;
2159          Entry = Entry->Flink)
2160     {
2161         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2162 
2163         if (DiskEntry->DiskNumber == DiskNumber)
2164             return DiskEntry; /* Disk found, return it */
2165     }
2166 
2167     /* Disk not found, stop there */
2168     return NULL;
2169 }
2170 
2171 PDISKENTRY
GetDiskBySCSI(_In_ PPARTLIST List,_In_ USHORT Port,_In_ USHORT Bus,_In_ USHORT Id)2172 GetDiskBySCSI(
2173     _In_ PPARTLIST List,
2174     _In_ USHORT Port,
2175     _In_ USHORT Bus,
2176     _In_ USHORT Id)
2177 {
2178     PDISKENTRY DiskEntry;
2179     PLIST_ENTRY Entry;
2180 
2181     /* Loop over the disks and find the correct one */
2182     for (Entry = List->DiskListHead.Flink;
2183          Entry != &List->DiskListHead;
2184          Entry = Entry->Flink)
2185     {
2186         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2187 
2188         if (DiskEntry->Port == Port &&
2189             DiskEntry->Bus  == Bus  &&
2190             DiskEntry->Id   == Id)
2191         {
2192             /* Disk found, return it */
2193             return DiskEntry;
2194         }
2195     }
2196 
2197     /* Disk not found, stop there */
2198     return NULL;
2199 }
2200 
2201 PDISKENTRY
GetDiskBySignature(_In_ PPARTLIST List,_In_ ULONG Signature)2202 GetDiskBySignature(
2203     _In_ PPARTLIST List,
2204     _In_ ULONG Signature)
2205 {
2206     PDISKENTRY DiskEntry;
2207     PLIST_ENTRY Entry;
2208 
2209     /* Loop over the disks and find the correct one */
2210     for (Entry = List->DiskListHead.Flink;
2211          Entry != &List->DiskListHead;
2212          Entry = Entry->Flink)
2213     {
2214         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
2215 
2216         if (DiskEntry->LayoutBuffer->Signature == Signature)
2217             return DiskEntry; /* Disk found, return it */
2218     }
2219 
2220     /* Disk not found, stop there */
2221     return NULL;
2222 }
2223 
2224 PPARTENTRY
GetPartition(_In_ PDISKENTRY DiskEntry,_In_ ULONG PartitionNumber)2225 GetPartition(
2226     _In_ PDISKENTRY DiskEntry,
2227     _In_ ULONG PartitionNumber)
2228 {
2229     PPARTENTRY PartEntry;
2230     PLIST_ENTRY Entry;
2231 
2232     /* Forbid whole-disk or extended container partition access */
2233     if (PartitionNumber == 0)
2234         return NULL;
2235 
2236     /* Loop over the primary partitions first... */
2237     for (Entry = DiskEntry->PrimaryPartListHead.Flink;
2238          Entry != &DiskEntry->PrimaryPartListHead;
2239          Entry = Entry->Flink)
2240     {
2241         PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2242 
2243         if (PartEntry->PartitionNumber == PartitionNumber)
2244             return PartEntry; /* Partition found, return it */
2245     }
2246 
2247     if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2248         return NULL;
2249 
2250     /* ... then over the logical partitions if needed */
2251     for (Entry = DiskEntry->LogicalPartListHead.Flink;
2252          Entry != &DiskEntry->LogicalPartListHead;
2253          Entry = Entry->Flink)
2254     {
2255         PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2256 
2257         if (PartEntry->PartitionNumber == PartitionNumber)
2258             return PartEntry; /* Partition found, return it */
2259     }
2260 
2261     /* The partition was not found on the disk, stop there */
2262     return NULL;
2263 }
2264 
2265 PPARTENTRY
SelectPartition(_In_ PPARTLIST List,_In_ ULONG DiskNumber,_In_ ULONG PartitionNumber)2266 SelectPartition(
2267     _In_ PPARTLIST List,
2268     _In_ ULONG DiskNumber,
2269     _In_ ULONG PartitionNumber)
2270 {
2271     PDISKENTRY DiskEntry;
2272     PPARTENTRY PartEntry;
2273 
2274     /* Find the disk */
2275     DiskEntry = GetDiskByNumber(List, DiskNumber);
2276     if (!DiskEntry)
2277         return NULL;
2278     ASSERT(DiskEntry->DiskNumber == DiskNumber);
2279 
2280     /* Find the partition */
2281     PartEntry = GetPartition(DiskEntry, PartitionNumber);
2282     if (!PartEntry)
2283         return NULL;
2284     ASSERT(PartEntry->DiskEntry == DiskEntry);
2285     ASSERT(PartEntry->PartitionNumber == PartitionNumber);
2286 
2287     return PartEntry;
2288 }
2289 
2290 PPARTENTRY
GetNextPartition(IN PPARTLIST List,IN PPARTENTRY CurrentPart OPTIONAL)2291 GetNextPartition(
2292     IN PPARTLIST List,
2293     IN PPARTENTRY CurrentPart OPTIONAL)
2294 {
2295     PLIST_ENTRY DiskListEntry;
2296     PLIST_ENTRY PartListEntry;
2297     PDISKENTRY CurrentDisk;
2298 
2299     /* Fail if no disks are available */
2300     if (IsListEmpty(&List->DiskListHead))
2301         return NULL;
2302 
2303     /* Check for the next usable entry on the current partition's disk */
2304     if (CurrentPart != NULL)
2305     {
2306         CurrentDisk = CurrentPart->DiskEntry;
2307 
2308         if (CurrentPart->LogicalPartition)
2309         {
2310             /* Logical partition */
2311 
2312             PartListEntry = CurrentPart->ListEntry.Flink;
2313             if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2314             {
2315                 /* Next logical partition */
2316                 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2317                 return CurrentPart;
2318             }
2319             else
2320             {
2321                 PartListEntry = CurrentDisk->ExtendedPartition->ListEntry.Flink;
2322                 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2323                 {
2324                     CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2325                     return CurrentPart;
2326                 }
2327             }
2328         }
2329         else
2330         {
2331             /* Primary or extended partition */
2332 
2333             if (CurrentPart->IsPartitioned &&
2334                 IsContainerPartition(CurrentPart->PartitionType))
2335             {
2336                 /* First logical partition */
2337                 PartListEntry = CurrentDisk->LogicalPartListHead.Flink;
2338                 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2339                 {
2340                     CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2341                     return CurrentPart;
2342                 }
2343             }
2344             else
2345             {
2346                 /* Next primary partition */
2347                 PartListEntry = CurrentPart->ListEntry.Flink;
2348                 if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2349                 {
2350                     CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2351                     return CurrentPart;
2352                 }
2353             }
2354         }
2355     }
2356 
2357     /* Search for the first partition entry on the next disk */
2358     for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Flink
2359                                       : List->DiskListHead.Flink);
2360          DiskListEntry != &List->DiskListHead;
2361          DiskListEntry = DiskListEntry->Flink)
2362     {
2363         CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
2364 
2365         if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
2366         {
2367             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2368             continue;
2369         }
2370 
2371         PartListEntry = CurrentDisk->PrimaryPartListHead.Flink;
2372         if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2373         {
2374             CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2375             return CurrentPart;
2376         }
2377     }
2378 
2379     return NULL;
2380 }
2381 
2382 PPARTENTRY
GetPrevPartition(IN PPARTLIST List,IN PPARTENTRY CurrentPart OPTIONAL)2383 GetPrevPartition(
2384     IN PPARTLIST List,
2385     IN PPARTENTRY CurrentPart OPTIONAL)
2386 {
2387     PLIST_ENTRY DiskListEntry;
2388     PLIST_ENTRY PartListEntry;
2389     PDISKENTRY CurrentDisk;
2390 
2391     /* Fail if no disks are available */
2392     if (IsListEmpty(&List->DiskListHead))
2393         return NULL;
2394 
2395     /* Check for the previous usable entry on the current partition's disk */
2396     if (CurrentPart != NULL)
2397     {
2398         CurrentDisk = CurrentPart->DiskEntry;
2399 
2400         if (CurrentPart->LogicalPartition)
2401         {
2402             /* Logical partition */
2403 
2404             PartListEntry = CurrentPart->ListEntry.Blink;
2405             if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2406             {
2407                 /* Previous logical partition */
2408                 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2409             }
2410             else
2411             {
2412                 /* Extended partition */
2413                 CurrentPart = CurrentDisk->ExtendedPartition;
2414             }
2415             return CurrentPart;
2416         }
2417         else
2418         {
2419             /* Primary or extended partition */
2420 
2421             PartListEntry = CurrentPart->ListEntry.Blink;
2422             if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2423             {
2424                 CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2425 
2426                 if (CurrentPart->IsPartitioned &&
2427                     IsContainerPartition(CurrentPart->PartitionType))
2428                 {
2429                     PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
2430                     CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2431                 }
2432 
2433                 return CurrentPart;
2434             }
2435         }
2436     }
2437 
2438     /* Search for the last partition entry on the previous disk */
2439     for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Blink
2440                                       : List->DiskListHead.Blink);
2441          DiskListEntry != &List->DiskListHead;
2442          DiskListEntry = DiskListEntry->Blink)
2443     {
2444         CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
2445 
2446         if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
2447         {
2448             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2449             continue;
2450         }
2451 
2452         PartListEntry = CurrentDisk->PrimaryPartListHead.Blink;
2453         if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
2454         {
2455             CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2456 
2457             if (CurrentPart->IsPartitioned &&
2458                 IsContainerPartition(CurrentPart->PartitionType))
2459             {
2460                 PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
2461                 if (PartListEntry != &CurrentDisk->LogicalPartListHead)
2462                 {
2463                     CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
2464                     return CurrentPart;
2465                 }
2466             }
2467             else
2468             {
2469                 return CurrentPart;
2470             }
2471         }
2472     }
2473 
2474     return NULL;
2475 }
2476 
2477 static inline
2478 BOOLEAN
IsEmptyLayoutEntry(_In_ PPARTITION_INFORMATION PartitionInfo)2479 IsEmptyLayoutEntry(
2480     _In_ PPARTITION_INFORMATION PartitionInfo)
2481 {
2482     return (PartitionInfo->StartingOffset.QuadPart == 0 &&
2483             PartitionInfo->PartitionLength.QuadPart == 0);
2484 }
2485 
2486 static inline
2487 BOOLEAN
IsSamePrimaryLayoutEntry(_In_ PPARTITION_INFORMATION PartitionInfo,_In_ PPARTENTRY PartEntry)2488 IsSamePrimaryLayoutEntry(
2489     _In_ PPARTITION_INFORMATION PartitionInfo,
2490     _In_ PPARTENTRY PartEntry)
2491 {
2492     return ((PartitionInfo->StartingOffset.QuadPart == GetPartEntryOffsetInBytes(PartEntry)) &&
2493             (PartitionInfo->PartitionLength.QuadPart == GetPartEntrySizeInBytes(PartEntry)));
2494 //        PartitionInfo->PartitionType == PartEntry->PartitionType
2495 }
2496 
2497 
2498 /**
2499  * @brief
2500  * Counts the number of partitioned disk regions in a given partition list.
2501  **/
2502 static
2503 ULONG
GetPartitionCount(_In_ PLIST_ENTRY PartListHead)2504 GetPartitionCount(
2505     _In_ PLIST_ENTRY PartListHead)
2506 {
2507     PLIST_ENTRY Entry;
2508     PPARTENTRY PartEntry;
2509     ULONG Count = 0;
2510 
2511     for (Entry = PartListHead->Flink;
2512          Entry != PartListHead;
2513          Entry = Entry->Flink)
2514     {
2515         PartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
2516         if (PartEntry->IsPartitioned)
2517             ++Count;
2518     }
2519 
2520     return Count;
2521 }
2522 
2523 #define GetPrimaryPartitionCount(DiskEntry) \
2524     GetPartitionCount(&(DiskEntry)->PrimaryPartListHead)
2525 
2526 #define GetLogicalPartitionCount(DiskEntry) \
2527     (((DiskEntry)->DiskStyle == PARTITION_STYLE_MBR) \
2528         ? GetPartitionCount(&(DiskEntry)->LogicalPartListHead) : 0)
2529 
2530 
2531 static
2532 BOOLEAN
ReAllocateLayoutBuffer(IN PDISKENTRY DiskEntry)2533 ReAllocateLayoutBuffer(
2534     IN PDISKENTRY DiskEntry)
2535 {
2536     PDRIVE_LAYOUT_INFORMATION NewLayoutBuffer;
2537     ULONG NewPartitionCount;
2538     ULONG CurrentPartitionCount = 0;
2539     ULONG LayoutBufferSize;
2540     ULONG i;
2541 
2542     DPRINT1("ReAllocateLayoutBuffer()\n");
2543 
2544     NewPartitionCount = 4 + GetLogicalPartitionCount(DiskEntry) * 4;
2545 
2546     if (DiskEntry->LayoutBuffer)
2547         CurrentPartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
2548 
2549     DPRINT1("CurrentPartitionCount: %lu ; NewPartitionCount: %lu\n",
2550             CurrentPartitionCount, NewPartitionCount);
2551 
2552     if (CurrentPartitionCount == NewPartitionCount)
2553         return TRUE;
2554 
2555     LayoutBufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
2556                        ((NewPartitionCount - ANYSIZE_ARRAY) * sizeof(PARTITION_INFORMATION));
2557     NewLayoutBuffer = RtlReAllocateHeap(ProcessHeap,
2558                                         HEAP_ZERO_MEMORY,
2559                                         DiskEntry->LayoutBuffer,
2560                                         LayoutBufferSize);
2561     if (NewLayoutBuffer == NULL)
2562     {
2563         DPRINT1("Failed to allocate the new layout buffer (size: %lu)\n", LayoutBufferSize);
2564         return FALSE;
2565     }
2566 
2567     NewLayoutBuffer->PartitionCount = NewPartitionCount;
2568 
2569     /* If the layout buffer grows, make sure the new (empty) entries are written to the disk */
2570     if (NewPartitionCount > CurrentPartitionCount)
2571     {
2572         for (i = CurrentPartitionCount; i < NewPartitionCount; i++)
2573         {
2574             NewLayoutBuffer->PartitionEntry[i].RewritePartition = TRUE;
2575         }
2576     }
2577 
2578     DiskEntry->LayoutBuffer = NewLayoutBuffer;
2579 
2580     return TRUE;
2581 }
2582 
2583 static
2584 VOID
UpdateDiskLayout(IN PDISKENTRY DiskEntry)2585 UpdateDiskLayout(
2586     IN PDISKENTRY DiskEntry)
2587 {
2588     PPARTITION_INFORMATION PartitionInfo;
2589     PPARTITION_INFORMATION LinkInfo;
2590     PLIST_ENTRY ListEntry;
2591     PPARTENTRY PartEntry;
2592     LARGE_INTEGER HiddenSectors64;
2593     ULONG Index;
2594     ULONG PartitionNumber = 1;
2595 
2596     DPRINT1("UpdateDiskLayout()\n");
2597 
2598     if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2599     {
2600         DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2601         return;
2602     }
2603 
2604     /* Resize the layout buffer if necessary */
2605     if (!ReAllocateLayoutBuffer(DiskEntry))
2606     {
2607         DPRINT("ReAllocateLayoutBuffer() failed.\n");
2608         return;
2609     }
2610 
2611     /* Update the primary partition table */
2612     Index = 0;
2613     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
2614          ListEntry != &DiskEntry->PrimaryPartListHead;
2615          ListEntry = ListEntry->Flink)
2616     {
2617         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
2618 
2619         if (PartEntry->IsPartitioned)
2620         {
2621             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2622 
2623             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2624             PartEntry->PartitionIndex = Index;
2625 
2626             /* Reset the current partition number only for not-yet written partitions */
2627             if (PartEntry->New)
2628                 PartEntry->PartitionNumber = 0;
2629 
2630             PartEntry->OnDiskPartitionNumber = (!IsContainerPartition(PartEntry->PartitionType) ? PartitionNumber : 0);
2631 
2632             if (!IsSamePrimaryLayoutEntry(PartitionInfo, PartEntry))
2633             {
2634                 DPRINT1("Updating primary partition entry %lu\n", Index);
2635 
2636                 PartitionInfo->StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry);
2637                 PartitionInfo->PartitionLength.QuadPart = GetPartEntrySizeInBytes(PartEntry);
2638                 PartitionInfo->HiddenSectors = PartEntry->StartSector.LowPart;
2639                 PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
2640                 PartitionInfo->PartitionType = PartEntry->PartitionType;
2641                 PartitionInfo->BootIndicator = PartEntry->BootIndicator;
2642                 PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
2643                 PartitionInfo->RewritePartition = TRUE;
2644             }
2645 
2646             if (!IsContainerPartition(PartEntry->PartitionType))
2647                 PartitionNumber++;
2648 
2649             Index++;
2650         }
2651     }
2652 
2653     ASSERT(Index <= 4);
2654 
2655     /* Update the logical partition table */
2656     LinkInfo = NULL;
2657     Index = 4;
2658     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
2659          ListEntry != &DiskEntry->LogicalPartListHead;
2660          ListEntry = ListEntry->Flink)
2661     {
2662         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
2663 
2664         if (PartEntry->IsPartitioned)
2665         {
2666             ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2667 
2668             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2669             PartEntry->PartitionIndex = Index;
2670 
2671             /* Reset the current partition number only for not-yet written partitions */
2672             if (PartEntry->New)
2673                 PartEntry->PartitionNumber = 0;
2674 
2675             PartEntry->OnDiskPartitionNumber = PartitionNumber;
2676 
2677             DPRINT1("Updating logical partition entry %lu\n", Index);
2678 
2679             PartitionInfo->StartingOffset.QuadPart = GetPartEntryOffsetInBytes(PartEntry);
2680             PartitionInfo->PartitionLength.QuadPart = GetPartEntrySizeInBytes(PartEntry);
2681             PartitionInfo->HiddenSectors = DiskEntry->SectorAlignment;
2682             PartitionInfo->PartitionNumber = PartEntry->PartitionNumber;
2683             PartitionInfo->PartitionType = PartEntry->PartitionType;
2684             PartitionInfo->BootIndicator = FALSE;
2685             PartitionInfo->RecognizedPartition = IsRecognizedPartition(PartEntry->PartitionType);
2686             PartitionInfo->RewritePartition = TRUE;
2687 
2688             /* Fill the link entry of the previous partition entry */
2689             if (LinkInfo)
2690             {
2691                 LinkInfo->StartingOffset.QuadPart = (PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
2692                 LinkInfo->PartitionLength.QuadPart = (PartEntry->StartSector.QuadPart + DiskEntry->SectorAlignment) * DiskEntry->BytesPerSector;
2693                 HiddenSectors64.QuadPart = PartEntry->StartSector.QuadPart - DiskEntry->SectorAlignment - DiskEntry->ExtendedPartition->StartSector.QuadPart;
2694                 LinkInfo->HiddenSectors = HiddenSectors64.LowPart;
2695                 LinkInfo->PartitionNumber = 0;
2696 
2697                 /* Extended partition links only use type 0x05, as observed
2698                  * on Windows NT. Alternatively they could inherit the type
2699                  * of the main extended container. */
2700                 LinkInfo->PartitionType = PARTITION_EXTENDED; // DiskEntry->ExtendedPartition->PartitionType;
2701 
2702                 LinkInfo->BootIndicator = FALSE;
2703                 LinkInfo->RecognizedPartition = FALSE;
2704                 LinkInfo->RewritePartition = TRUE;
2705             }
2706 
2707             /* Save a pointer to the link entry of the current partition entry */
2708             LinkInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index + 1];
2709 
2710             PartitionNumber++;
2711             Index += 4;
2712         }
2713     }
2714 
2715     /* Wipe unused primary partition entries */
2716     for (Index = GetPrimaryPartitionCount(DiskEntry); Index < 4; Index++)
2717     {
2718         DPRINT1("Primary partition entry %lu\n", Index);
2719 
2720         PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2721 
2722         if (!IsEmptyLayoutEntry(PartitionInfo))
2723         {
2724             DPRINT1("Wiping primary partition entry %lu\n", Index);
2725 
2726             PartitionInfo->StartingOffset.QuadPart = 0;
2727             PartitionInfo->PartitionLength.QuadPart = 0;
2728             PartitionInfo->HiddenSectors = 0;
2729             PartitionInfo->PartitionNumber = 0;
2730             PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
2731             PartitionInfo->BootIndicator = FALSE;
2732             PartitionInfo->RecognizedPartition = FALSE;
2733             PartitionInfo->RewritePartition = TRUE;
2734         }
2735     }
2736 
2737     /* Wipe unused logical partition entries */
2738     for (Index = 4; Index < DiskEntry->LayoutBuffer->PartitionCount; Index++)
2739     {
2740         if (Index % 4 >= 2)
2741         {
2742             DPRINT1("Logical partition entry %lu\n", Index);
2743 
2744             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[Index];
2745 
2746             if (!IsEmptyLayoutEntry(PartitionInfo))
2747             {
2748                 DPRINT1("Wiping partition entry %lu\n", Index);
2749 
2750                 PartitionInfo->StartingOffset.QuadPart = 0;
2751                 PartitionInfo->PartitionLength.QuadPart = 0;
2752                 PartitionInfo->HiddenSectors = 0;
2753                 PartitionInfo->PartitionNumber = 0;
2754                 PartitionInfo->PartitionType = PARTITION_ENTRY_UNUSED;
2755                 PartitionInfo->BootIndicator = FALSE;
2756                 PartitionInfo->RecognizedPartition = FALSE;
2757                 PartitionInfo->RewritePartition = TRUE;
2758             }
2759         }
2760     }
2761 
2762     // HACK: See the FIXMEs in WritePartitions(): (Re)set the PartitionStyle to MBR.
2763     DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
2764 
2765     DiskEntry->Dirty = TRUE;
2766 
2767 #ifdef DUMP_PARTITION_TABLE
2768     DumpPartitionTable(DiskEntry);
2769 #endif
2770 }
2771 
2772 /**
2773  * @brief
2774  * Retrieves, if any, the unpartitioned disk region that is adjacent
2775  * (next or previous) to the specified partition.
2776  *
2777  * @param[in]   PartEntry
2778  * Partition from where to find the adjacent unpartitioned region.
2779  *
2780  * @param[in]   Direction
2781  * TRUE or FALSE to search the next or previous region, respectively.
2782  *
2783  * @return  The adjacent unpartitioned region, if it exists, or NULL.
2784  **/
2785 PPARTENTRY
GetAdjUnpartitionedEntry(_In_ PPARTENTRY PartEntry,_In_ BOOLEAN Direction)2786 GetAdjUnpartitionedEntry(
2787     _In_ PPARTENTRY PartEntry,
2788     _In_ BOOLEAN Direction)
2789 {
2790     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2791     PLIST_ENTRY ListHead, AdjEntry;
2792 
2793     /* In case of MBR disks only, check the logical partitions if necessary */
2794     if ((DiskEntry->DiskStyle == PARTITION_STYLE_MBR) &&
2795         PartEntry->LogicalPartition)
2796     {
2797         ListHead = &DiskEntry->LogicalPartListHead;
2798     }
2799     else
2800     {
2801         ListHead = &DiskEntry->PrimaryPartListHead;
2802     }
2803 
2804     if (Direction)
2805         AdjEntry = PartEntry->ListEntry.Flink; // Next region.
2806     else
2807         AdjEntry = PartEntry->ListEntry.Blink; // Previous region.
2808 
2809     if (AdjEntry != ListHead)
2810     {
2811         PartEntry = CONTAINING_RECORD(AdjEntry, PARTENTRY, ListEntry);
2812         if (!PartEntry->IsPartitioned)
2813         {
2814             ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
2815             return PartEntry;
2816         }
2817     }
2818     return NULL;
2819 }
2820 
2821 ERROR_NUMBER
PartitionCreationChecks(_In_ PPARTENTRY PartEntry)2822 PartitionCreationChecks(
2823     _In_ PPARTENTRY PartEntry)
2824 {
2825     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2826 
2827     if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2828     {
2829         DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2830         return ERROR_WARN_PARTITION;
2831     }
2832 
2833     /* Fail if the partition is already in use */
2834     if (PartEntry->IsPartitioned)
2835         return ERROR_NEW_PARTITION;
2836 
2837     /*
2838      * For primary partitions
2839      */
2840     if (!PartEntry->LogicalPartition)
2841     {
2842         /* Only one primary partition is allowed on super-floppy */
2843         if (IsSuperFloppy(DiskEntry))
2844             return ERROR_PARTITION_TABLE_FULL;
2845 
2846         /* Fail if there are already 4 primary partitions in the list */
2847         if (GetPrimaryPartitionCount(DiskEntry) >= 4)
2848             return ERROR_PARTITION_TABLE_FULL;
2849     }
2850     /*
2851      * For logical partitions
2852      */
2853     else
2854     {
2855         // TODO: Check that we are inside an extended partition!!
2856         // Then the following check will be useless.
2857 
2858         /* Only one (primary) partition is allowed on super-floppy */
2859         if (IsSuperFloppy(DiskEntry))
2860             return ERROR_PARTITION_TABLE_FULL;
2861     }
2862 
2863     return ERROR_SUCCESS;
2864 }
2865 
2866 ERROR_NUMBER
ExtendedPartitionCreationChecks(_In_ PPARTENTRY PartEntry)2867 ExtendedPartitionCreationChecks(
2868     _In_ PPARTENTRY PartEntry)
2869 {
2870     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
2871 
2872     if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
2873     {
2874         DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
2875         return ERROR_WARN_PARTITION;
2876     }
2877 
2878     /* Fail if the partition is already in use */
2879     if (PartEntry->IsPartitioned)
2880         return ERROR_NEW_PARTITION;
2881 
2882     /* Cannot create an extended partition within logical partition space */
2883     if (PartEntry->LogicalPartition)
2884         return ERROR_ONLY_ONE_EXTENDED;
2885 
2886     /* Only one primary partition is allowed on super-floppy */
2887     if (IsSuperFloppy(DiskEntry))
2888         return ERROR_PARTITION_TABLE_FULL;
2889 
2890     /* Fail if there are already 4 primary partitions in the list */
2891     if (GetPrimaryPartitionCount(DiskEntry) >= 4)
2892         return ERROR_PARTITION_TABLE_FULL;
2893 
2894     /* Fail if there is another extended partition in the list */
2895     if (DiskEntry->ExtendedPartition)
2896         return ERROR_ONLY_ONE_EXTENDED;
2897 
2898     return ERROR_SUCCESS;
2899 }
2900 
2901 // TODO: Improve upon the PartitionInfo parameter later
2902 // (see VDS::CREATE_PARTITION_PARAMETERS and PPARTITION_INFORMATION_MBR/GPT for example)
2903 // So far we only use it as the optional type of the partition to create.
2904 BOOLEAN
CreatePartition(_In_ PPARTLIST List,_Inout_ PPARTENTRY PartEntry,_In_opt_ ULONGLONG SizeBytes,_In_opt_ ULONG_PTR PartitionInfo)2905 CreatePartition(
2906     _In_ PPARTLIST List,
2907     _Inout_ PPARTENTRY PartEntry,
2908     _In_opt_ ULONGLONG SizeBytes,
2909     _In_opt_ ULONG_PTR PartitionInfo)
2910 {
2911     ERROR_NUMBER Error;
2912     BOOLEAN isContainer = IsContainerPartition((UCHAR)PartitionInfo);
2913     PDISKENTRY DiskEntry;
2914     PCSTR mainType = "Primary";
2915 
2916     if (isContainer)
2917         mainType = "Extended";
2918     else if (PartEntry && PartEntry->LogicalPartition)
2919         mainType = "Logical";
2920 
2921     DPRINT1("CreatePartition(%s, %I64u bytes)\n", mainType, SizeBytes);
2922 
2923     if (!List || !PartEntry ||
2924         !PartEntry->DiskEntry || PartEntry->IsPartitioned)
2925     {
2926         return FALSE;
2927     }
2928 
2929     if (isContainer)
2930         Error = ExtendedPartitionCreationChecks(PartEntry);
2931     else
2932         Error = PartitionCreationChecks(PartEntry);
2933     if (Error != NOT_AN_ERROR)
2934     {
2935         DPRINT1("PartitionCreationChecks(%s) failed with error %lu\n", mainType, Error);
2936         return FALSE;
2937     }
2938 
2939     /* Initialize the partition entry, inserting a new blank region if needed */
2940     if (!InitializePartitionEntry(PartEntry, SizeBytes, PartitionInfo))
2941         return FALSE;
2942 
2943     DiskEntry = PartEntry->DiskEntry;
2944     UpdateDiskLayout(DiskEntry);
2945 
2946     ASSERT(!PartEntry->Volume);
2947     if (!isContainer)
2948     {
2949         /* We create a primary/logical partition: initialize a new basic
2950          * volume entry. When the partition will actually be written onto
2951          * the disk, the PARTMGR will notify the MOUNTMGR that a volume
2952          * associated with this partition has to be created. */
2953         PartEntry->Volume = InitVolume(DiskEntry->PartList, PartEntry);
2954         ASSERT(PartEntry->Volume);
2955     }
2956 
2957     AssignDriveLetters(List);
2958 
2959     return TRUE;
2960 }
2961 
2962 static NTSTATUS
DismountPartition(_In_ PPARTLIST List,_In_ PPARTENTRY PartEntry)2963 DismountPartition(
2964     _In_ PPARTLIST List,
2965     _In_ PPARTENTRY PartEntry)
2966 {
2967     NTSTATUS Status;
2968     PVOLENTRY Volume = PartEntry->Volume;
2969 
2970     ASSERT(PartEntry->DiskEntry->PartList == List);
2971 
2972     /* Check whether the partition is valid and was mounted by the system */
2973     if (!PartEntry->IsPartitioned ||
2974         IsContainerPartition(PartEntry->PartitionType)   ||
2975         !IsRecognizedPartition(PartEntry->PartitionType) ||
2976         !Volume || Volume->FormatState == UnknownFormat  ||
2977         // NOTE: If FormatState == Unformatted but *FileSystem != 0 this means
2978         // it has been usually mounted with RawFS and thus needs to be dismounted.
2979         PartEntry->PartitionNumber == 0)
2980     {
2981         /* The partition is not mounted, so just return success */
2982         return STATUS_SUCCESS;
2983     }
2984 
2985     ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
2986     ASSERT(Volume->PartEntry == PartEntry);
2987 
2988     /* Unlink the basic volume from the volumes list and dismount it */
2989     PartEntry->Volume = NULL;
2990     Volume->PartEntry = NULL;
2991     RemoveEntryList(&Volume->ListEntry);
2992     Status = DismountVolume(&Volume->Info, TRUE);
2993     RtlFreeHeap(ProcessHeap, 0, Volume);
2994     return Status;
2995 }
2996 
2997 BOOLEAN
DeletePartition(_In_ PPARTLIST List,_In_ PPARTENTRY PartEntry,_Out_opt_ PPARTENTRY * FreeRegion)2998 DeletePartition(
2999     _In_ PPARTLIST List,
3000     _In_ PPARTENTRY PartEntry,
3001     _Out_opt_ PPARTENTRY* FreeRegion)
3002 {
3003     PDISKENTRY DiskEntry;
3004     PPARTENTRY PrevPartEntry;
3005     PPARTENTRY NextPartEntry;
3006     PPARTENTRY LogicalPartEntry;
3007     PLIST_ENTRY Entry;
3008 
3009     if (!List || !PartEntry ||
3010         !PartEntry->DiskEntry || !PartEntry->IsPartitioned)
3011     {
3012         return FALSE;
3013     }
3014 
3015     ASSERT(PartEntry->DiskEntry->PartList == List);
3016     ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3017 
3018     /* Clear the system partition if it is being deleted */
3019     if (List->SystemPartition == PartEntry)
3020     {
3021         ASSERT(List->SystemPartition);
3022         List->SystemPartition = NULL;
3023     }
3024 
3025     DiskEntry = PartEntry->DiskEntry;
3026 
3027     /* Check which type of partition (primary/logical or extended) is being deleted */
3028     if (DiskEntry->ExtendedPartition == PartEntry)
3029     {
3030         /* An extended partition is being deleted: delete all logical partition entries */
3031         while (!IsListEmpty(&DiskEntry->LogicalPartListHead))
3032         {
3033             Entry = RemoveHeadList(&DiskEntry->LogicalPartListHead);
3034             LogicalPartEntry = CONTAINING_RECORD(Entry, PARTENTRY, ListEntry);
3035 
3036             /* Dismount the logical partition and delete it */
3037             DismountPartition(List, LogicalPartEntry);
3038             DestroyRegion(LogicalPartEntry);
3039         }
3040 
3041         DiskEntry->ExtendedPartition = NULL;
3042     }
3043     else
3044     {
3045         /* A primary/logical partition is being deleted: dismount it */
3046         DismountPartition(List, PartEntry);
3047     }
3048 
3049     /* Adjust the unpartitioned disk space entries */
3050 
3051     /* Get pointer to previous and next unpartitioned entries */
3052     PrevPartEntry = GetAdjUnpartitionedEntry(PartEntry, FALSE);
3053     NextPartEntry = GetAdjUnpartitionedEntry(PartEntry, TRUE);
3054 
3055     if (PrevPartEntry != NULL && NextPartEntry != NULL)
3056     {
3057         /* Merge the previous, current and next unpartitioned entries */
3058 
3059         /* Adjust the previous entry length */
3060         PrevPartEntry->SectorCount.QuadPart += (PartEntry->SectorCount.QuadPart + NextPartEntry->SectorCount.QuadPart);
3061 
3062         /* Remove the current and next entries */
3063         RemoveEntryList(&PartEntry->ListEntry);
3064         DestroyRegion(PartEntry);
3065         RemoveEntryList(&NextPartEntry->ListEntry);
3066         DestroyRegion(NextPartEntry);
3067 
3068         /* Optionally return the freed region */
3069         if (FreeRegion)
3070             *FreeRegion = PrevPartEntry;
3071     }
3072     else if (PrevPartEntry != NULL && NextPartEntry == NULL)
3073     {
3074         /* Merge the current and the previous unpartitioned entries */
3075 
3076         /* Adjust the previous entry length */
3077         PrevPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
3078 
3079         /* Remove the current entry */
3080         RemoveEntryList(&PartEntry->ListEntry);
3081         DestroyRegion(PartEntry);
3082 
3083         /* Optionally return the freed region */
3084         if (FreeRegion)
3085             *FreeRegion = PrevPartEntry;
3086     }
3087     else if (PrevPartEntry == NULL && NextPartEntry != NULL)
3088     {
3089         /* Merge the current and the next unpartitioned entries */
3090 
3091         /* Adjust the next entry offset and length */
3092         NextPartEntry->StartSector.QuadPart = PartEntry->StartSector.QuadPart;
3093         NextPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
3094 
3095         /* Remove the current entry */
3096         RemoveEntryList(&PartEntry->ListEntry);
3097         DestroyRegion(PartEntry);
3098 
3099         /* Optionally return the freed region */
3100         if (FreeRegion)
3101             *FreeRegion = NextPartEntry;
3102     }
3103     else
3104     {
3105         /* Nothing to merge but change the current entry */
3106         PartEntry->New = FALSE;
3107         PartEntry->IsPartitioned = FALSE;
3108         PartEntry->PartitionType = PARTITION_ENTRY_UNUSED;
3109         PartEntry->OnDiskPartitionNumber = 0;
3110         PartEntry->PartitionNumber = 0;
3111         // PartEntry->PartitionIndex = 0;
3112         PartEntry->BootIndicator = FALSE;
3113         PartEntry->DeviceName[0] = UNICODE_NULL;
3114 
3115         if (PartEntry->Volume)
3116         {
3117             RemoveEntryList(&PartEntry->Volume->ListEntry);
3118             RtlFreeHeap(ProcessHeap, 0, PartEntry->Volume);
3119         }
3120         PartEntry->Volume = NULL;
3121 
3122         /* Optionally return the freed region */
3123         if (FreeRegion)
3124             *FreeRegion = PartEntry;
3125     }
3126 
3127     UpdateDiskLayout(DiskEntry);
3128     AssignDriveLetters(List);
3129 
3130     return TRUE;
3131 }
3132 
3133 static
3134 BOOLEAN
IsSupportedActivePartition(IN PPARTENTRY PartEntry)3135 IsSupportedActivePartition(
3136     IN PPARTENTRY PartEntry)
3137 {
3138     PVOLENTRY Volume;
3139 
3140     /* Check the type and the file system of this partition */
3141 
3142     /*
3143      * We do not support extended partition containers (on MBR disks) marked
3144      * as active, and containing code inside their extended boot records.
3145      */
3146     if (IsContainerPartition(PartEntry->PartitionType))
3147     {
3148         DPRINT1("System partition %lu in disk %lu is an extended partition container?!\n",
3149                 PartEntry->PartitionNumber, PartEntry->DiskEntry->DiskNumber);
3150         return FALSE;
3151     }
3152 
3153     Volume = PartEntry->Volume;
3154     if (!Volume)
3155     {
3156         /* Still no recognizable volume mounted: partition not supported */
3157         return FALSE;
3158     }
3159 
3160     /*
3161      * ADDITIONAL CHECKS / BIG HACK:
3162      *
3163      * Retrieve its file system and check whether we have
3164      * write support for it. If that is the case we are fine
3165      * and we can use it directly. However if we don't have
3166      * write support we will need to change the active system
3167      * partition.
3168      *
3169      * NOTE that this is completely useless on architectures
3170      * where a real system partition is required, as on these
3171      * architectures the partition uses the FAT FS, for which
3172      * we do have write support.
3173      * NOTE also that for those architectures looking for a
3174      * partition boot indicator is insufficient.
3175      */
3176     if (Volume->FormatState == Unformatted)
3177     {
3178         /* If this partition is mounted, it would use RawFS ("RAW") */
3179         return TRUE;
3180     }
3181     else if (Volume->FormatState == Formatted)
3182     {
3183         ASSERT(*Volume->Info.FileSystem);
3184 
3185         /* NOTE: Please keep in sync with the RegisteredFileSystems list! */
3186         if (_wcsicmp(Volume->Info.FileSystem, L"FAT")   == 0 ||
3187             _wcsicmp(Volume->Info.FileSystem, L"FAT32") == 0 ||
3188          // _wcsicmp(Volume->Info.FileSystem, L"NTFS")  == 0 ||
3189             _wcsicmp(Volume->Info.FileSystem, L"BTRFS") == 0)
3190         {
3191             return TRUE;
3192         }
3193         else
3194         {
3195             // WARNING: We cannot write on this FS yet!
3196             DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
3197                     Volume->Info.FileSystem);
3198             return FALSE;
3199         }
3200     }
3201     else // if (Volume->FormatState == UnknownFormat)
3202     {
3203         ASSERT(!*Volume->Info.FileSystem);
3204 
3205         DPRINT1("System partition %lu in disk %lu with no or unknown FS?!\n",
3206                 PartEntry->PartitionNumber, PartEntry->DiskEntry->DiskNumber);
3207         return FALSE;
3208     }
3209 
3210     // HACK: WARNING: We cannot write on this FS yet!
3211     // See fsutil.c:InferFileSystem()
3212     if (PartEntry->PartitionType == PARTITION_IFS)
3213     {
3214         DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
3215                 Volume->Info.FileSystem);
3216         return FALSE;
3217     }
3218 
3219     return TRUE;
3220 }
3221 
3222 PPARTENTRY
FindSupportedSystemPartition(IN PPARTLIST List,IN BOOLEAN ForceSelect,IN PDISKENTRY AlternativeDisk OPTIONAL,IN PPARTENTRY AlternativePart OPTIONAL)3223 FindSupportedSystemPartition(
3224     IN PPARTLIST List,
3225     IN BOOLEAN ForceSelect,
3226     IN PDISKENTRY AlternativeDisk OPTIONAL,
3227     IN PPARTENTRY AlternativePart OPTIONAL)
3228 {
3229     PLIST_ENTRY ListEntry;
3230     PDISKENTRY DiskEntry;
3231     PPARTENTRY PartEntry;
3232     PPARTENTRY ActivePartition;
3233     PPARTENTRY CandidatePartition = NULL;
3234 
3235     /* Check for empty disk list */
3236     if (IsListEmpty(&List->DiskListHead))
3237     {
3238         /* No system partition! */
3239         ASSERT(List->SystemPartition == NULL);
3240         goto NoSystemPartition;
3241     }
3242 
3243     /* Adjust the optional alternative disk if needed */
3244     if (!AlternativeDisk && AlternativePart)
3245         AlternativeDisk = AlternativePart->DiskEntry;
3246 
3247     /* Ensure that the alternative partition is on the alternative disk */
3248     if (AlternativePart)
3249         ASSERT(AlternativeDisk && (AlternativePart->DiskEntry == AlternativeDisk));
3250 
3251     /* Ensure that the alternative disk is in the list */
3252     if (AlternativeDisk)
3253         ASSERT(AlternativeDisk->PartList == List);
3254 
3255     /* Start fresh */
3256     CandidatePartition = NULL;
3257 
3258 //
3259 // Step 1 : Check the system disk.
3260 //
3261 
3262     /*
3263      * First, check whether the system disk, i.e. the one that will be booted
3264      * by default by the hardware, contains an active partition. If so this
3265      * should be our system partition.
3266      */
3267     DiskEntry = GetSystemDisk(List);
3268     if (!DiskEntry)
3269     {
3270         /* No system disk found, directly go check the alternative disk */
3271         goto UseAlternativeDisk;
3272     }
3273 
3274     if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
3275     {
3276         DPRINT1("System disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
3277         goto UseAlternativeDisk;
3278     }
3279 
3280     /* If we have a system partition (in the system disk), validate it */
3281     ActivePartition = List->SystemPartition;
3282     if (ActivePartition && IsSupportedActivePartition(ActivePartition))
3283     {
3284         CandidatePartition = ActivePartition;
3285 
3286         DPRINT1("Use the current system partition %lu in disk %lu, drive letter %C\n",
3287                 CandidatePartition->PartitionNumber,
3288                 CandidatePartition->DiskEntry->DiskNumber,
3289                 !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3290 
3291         /* Return the candidate system partition */
3292         return CandidatePartition;
3293     }
3294 
3295     /* If the system disk is not the optional alternative disk, perform the minimal checks */
3296     if (DiskEntry != AlternativeDisk)
3297     {
3298         /*
3299          * No active partition has been recognized. Enumerate all the (primary)
3300          * partitions in the system disk, excluding the possible current active
3301          * partition, to find a new candidate.
3302          */
3303         for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3304              ListEntry != &DiskEntry->PrimaryPartListHead;
3305              ListEntry = ListEntry->Flink)
3306         {
3307             /* Retrieve the partition */
3308             PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3309 
3310             /* Skip the current active partition */
3311             if (PartEntry == ActivePartition)
3312                 continue;
3313 
3314             /* Check if the partition is partitioned and used */
3315             if (PartEntry->IsPartitioned &&
3316                 !IsContainerPartition(PartEntry->PartitionType))
3317             {
3318                 ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3319 
3320                 /* If we get a candidate active partition in the disk, validate it */
3321                 if (IsSupportedActivePartition(PartEntry))
3322                 {
3323                     CandidatePartition = PartEntry;
3324                     goto UseAlternativePartition;
3325                 }
3326             }
3327 
3328 #if 0
3329             /* Check if the partition is partitioned and used */
3330             if (!PartEntry->IsPartitioned)
3331             {
3332                 ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
3333 
3334                 // TODO: Check for minimal size!!
3335                 CandidatePartition = PartEntry;
3336                 goto UseAlternativePartition;
3337             }
3338 #endif
3339         }
3340 
3341         /*
3342          * Still nothing, look whether there is some free space that we can use
3343          * for the new system partition. We must be sure that the total number
3344          * of partition is less than the maximum allowed, and that the minimal
3345          * size is fine.
3346          */
3347 //
3348 // TODO: Fix the handling of system partition being created in unpartitioned space!!
3349 // --> When to partition it? etc...
3350 //
3351         if (GetPrimaryPartitionCount(DiskEntry) < 4)
3352         {
3353             for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3354                  ListEntry != &DiskEntry->PrimaryPartListHead;
3355                  ListEntry = ListEntry->Flink)
3356             {
3357                 /* Retrieve the partition */
3358                 PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3359 
3360                 /* Skip the current active partition */
3361                 if (PartEntry == ActivePartition)
3362                     continue;
3363 
3364                 /* Check for unpartitioned space */
3365                 if (!PartEntry->IsPartitioned)
3366                 {
3367                     ASSERT(PartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
3368 
3369                     // TODO: Check for minimal size!!
3370                     CandidatePartition = PartEntry;
3371                     goto UseAlternativePartition;
3372                 }
3373             }
3374         }
3375     }
3376 
3377 
3378 //
3379 // Step 2 : No active partition found: Check the alternative disk if specified.
3380 //
3381 
3382 UseAlternativeDisk:
3383     if (!AlternativeDisk || (!ForceSelect && (DiskEntry != AlternativeDisk)))
3384         goto NoSystemPartition;
3385 
3386     if (AlternativeDisk->DiskStyle == PARTITION_STYLE_GPT)
3387     {
3388         DPRINT1("Alternative disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
3389         goto NoSystemPartition;
3390     }
3391 
3392     if (DiskEntry != AlternativeDisk)
3393     {
3394         /* Choose the alternative disk */
3395         DiskEntry = AlternativeDisk;
3396 
3397         /* If we get a candidate active partition, validate it */
3398         ActivePartition = GetActiveDiskPartition(DiskEntry);
3399         if (ActivePartition && IsSupportedActivePartition(ActivePartition))
3400         {
3401             CandidatePartition = ActivePartition;
3402             goto UseAlternativePartition;
3403         }
3404     }
3405 
3406     /* We now may have an unsupported active partition, or none */
3407 
3408 /***
3409  *** TODO: Improve the selection:
3410  *** - If we want a really separate system partition from the partition where
3411  ***   we install, do something similar to what's done below in the code.
3412  *** - Otherwise if we allow for the system partition to be also the partition
3413  ***   where we install, just directly fall down to using AlternativePart.
3414  ***/
3415 
3416     /* Retrieve the first partition of the disk */
3417     PartEntry = CONTAINING_RECORD(DiskEntry->PrimaryPartListHead.Flink,
3418                                   PARTENTRY, ListEntry);
3419     ASSERT(DiskEntry == PartEntry->DiskEntry);
3420 
3421     CandidatePartition = PartEntry;
3422 
3423     //
3424     // See: https://svn.reactos.org/svn/reactos/trunk/reactos/base/setup/usetup/partlist.c?r1=63355&r2=63354&pathrev=63355#l2318
3425     //
3426 
3427     /* Check if the disk is new and if so, use its first partition as the active system partition */
3428     if (DiskEntry->NewDisk)
3429     {
3430         // !IsContainerPartition(PartEntry->PartitionType);
3431         if (!CandidatePartition->IsPartitioned || !CandidatePartition->BootIndicator) /* CandidatePartition != ActivePartition */
3432         {
3433             ASSERT(DiskEntry == CandidatePartition->DiskEntry);
3434 
3435             DPRINT1("Use new first active system partition %lu in disk %lu, drive letter %C\n",
3436                     CandidatePartition->PartitionNumber,
3437                     CandidatePartition->DiskEntry->DiskNumber,
3438                     !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3439 
3440             /* Return the candidate system partition */
3441             return CandidatePartition;
3442         }
3443 
3444         // FIXME: What to do??
3445         DPRINT1("NewDisk TRUE but first partition is used?\n");
3446     }
3447 
3448     /*
3449      * The disk is not new, check if any partition is initialized;
3450      * if not, the first one becomes the system partition.
3451      */
3452     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3453          ListEntry != &DiskEntry->PrimaryPartListHead;
3454          ListEntry = ListEntry->Flink)
3455     {
3456         /* Retrieve the partition */
3457         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3458 
3459         /* Check if the partition is partitioned and is used */
3460         // !IsContainerPartition(PartEntry->PartitionType);
3461         if (/* PartEntry->IsPartitioned && */
3462             PartEntry->PartitionType != PARTITION_ENTRY_UNUSED || PartEntry->BootIndicator)
3463         {
3464             break;
3465         }
3466     }
3467     if (ListEntry == &DiskEntry->PrimaryPartListHead)
3468     {
3469         /*
3470          * OK we haven't encountered any used and active partition,
3471          * so use the first one as the system partition.
3472          */
3473         ASSERT(DiskEntry == CandidatePartition->DiskEntry);
3474 
3475         DPRINT1("Use first active system partition %lu in disk %lu, drive letter %C\n",
3476                 CandidatePartition->PartitionNumber,
3477                 CandidatePartition->DiskEntry->DiskNumber,
3478                 !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3479 
3480         /* Return the candidate system partition */
3481         return CandidatePartition;
3482     }
3483 
3484     /*
3485      * The disk is not new, we did not find any actual active partition,
3486      * or the one we found was not supported, or any possible other candidate
3487      * is not supported. We then use the alternative partition if specified.
3488      */
3489     if (AlternativePart)
3490     {
3491         DPRINT1("No valid or supported system partition has been found, use the alternative partition!\n");
3492         CandidatePartition = AlternativePart;
3493         goto UseAlternativePartition;
3494     }
3495     else
3496     {
3497 NoSystemPartition:
3498         DPRINT1("No valid or supported system partition has been found on this system!\n");
3499         return NULL;
3500     }
3501 
3502 UseAlternativePartition:
3503     /*
3504      * We are here because we did not find any (active) candidate system
3505      * partition that we know how to support. What we are going to do is
3506      * to change the existing system partition and use the alternative partition
3507      * (e.g. on which we install ReactOS) as the new system partition.
3508      * Then we will need to add in FreeLdr's boot menu an entry for booting
3509      * from the original system partition.
3510      */
3511     ASSERT(CandidatePartition);
3512 
3513     DPRINT1("Use alternative active system partition %lu in disk %lu, drive letter %C\n",
3514             CandidatePartition->PartitionNumber,
3515             CandidatePartition->DiskEntry->DiskNumber,
3516             !CandidatePartition->Volume->Info.DriveLetter ? L'-' : CandidatePartition->Volume->Info.DriveLetter);
3517 
3518     /* Return the candidate system partition */
3519     return CandidatePartition;
3520 }
3521 
3522 BOOLEAN
SetActivePartition(IN PPARTLIST List,IN PPARTENTRY PartEntry,IN PPARTENTRY OldActivePart OPTIONAL)3523 SetActivePartition(
3524     IN PPARTLIST List,
3525     IN PPARTENTRY PartEntry,
3526     IN PPARTENTRY OldActivePart OPTIONAL)
3527 {
3528     /* Check for empty disk list */
3529     if (IsListEmpty(&List->DiskListHead))
3530         return FALSE;
3531 
3532     /* Validate the partition entry */
3533     if (!PartEntry)
3534         return FALSE;
3535 
3536     /*
3537      * If the partition entry is already the system partition, or if it is
3538      * the same as the old active partition hint the user provided (and if
3539      * it is already active), just return success.
3540      */
3541     if ((PartEntry == List->SystemPartition) ||
3542         ((PartEntry == OldActivePart) && IsPartitionActive(OldActivePart)))
3543     {
3544         return TRUE;
3545     }
3546 
3547     ASSERT(PartEntry->DiskEntry);
3548 
3549     /* Ensure that the partition's disk is in the list */
3550     ASSERT(PartEntry->DiskEntry->PartList == List);
3551 
3552     /*
3553      * If the user provided an old active partition hint, verify that it is
3554      * indeed active and belongs to the same disk where the new partition
3555      * belongs. Otherwise determine the current active partition on the disk
3556      * where the new partition belongs.
3557      */
3558     if (!(OldActivePart && IsPartitionActive(OldActivePart) && (OldActivePart->DiskEntry == PartEntry->DiskEntry)))
3559     {
3560         /* It's not, determine the current active partition for the disk */
3561         OldActivePart = GetActiveDiskPartition(PartEntry->DiskEntry);
3562     }
3563 
3564     /* Unset the old active partition if it exists */
3565     if (OldActivePart)
3566     {
3567         OldActivePart->BootIndicator = FALSE;
3568         OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].BootIndicator = FALSE;
3569         OldActivePart->DiskEntry->LayoutBuffer->PartitionEntry[OldActivePart->PartitionIndex].RewritePartition = TRUE;
3570         OldActivePart->DiskEntry->Dirty = TRUE;
3571     }
3572 
3573     /* Modify the system partition if the new partition is on the system disk */
3574     if (PartEntry->DiskEntry == GetSystemDisk(List))
3575         List->SystemPartition = PartEntry;
3576 
3577     /* Set the new active partition */
3578     PartEntry->BootIndicator = TRUE;
3579     PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].BootIndicator = TRUE;
3580     PartEntry->DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3581     PartEntry->DiskEntry->Dirty = TRUE;
3582 
3583     return TRUE;
3584 }
3585 
3586 NTSTATUS
WritePartitions(IN PDISKENTRY DiskEntry)3587 WritePartitions(
3588     IN PDISKENTRY DiskEntry)
3589 {
3590     NTSTATUS Status;
3591     OBJECT_ATTRIBUTES ObjectAttributes;
3592     UNICODE_STRING Name;
3593     HANDLE FileHandle;
3594     IO_STATUS_BLOCK Iosb;
3595     ULONG BufferSize;
3596     PPARTITION_INFORMATION PartitionInfo;
3597     ULONG PartitionCount;
3598     PLIST_ENTRY ListEntry;
3599     PPARTENTRY PartEntry;
3600     WCHAR DstPath[MAX_PATH];
3601 
3602     DPRINT("WritePartitions() Disk: %lu\n", DiskEntry->DiskNumber);
3603 
3604     /* If the disk is not dirty, there is nothing to do */
3605     if (!DiskEntry->Dirty)
3606         return STATUS_SUCCESS;
3607 
3608     RtlStringCchPrintfW(DstPath, ARRAYSIZE(DstPath),
3609                         L"\\Device\\Harddisk%lu\\Partition0",
3610                         DiskEntry->DiskNumber);
3611     RtlInitUnicodeString(&Name, DstPath);
3612 
3613     InitializeObjectAttributes(&ObjectAttributes,
3614                                &Name,
3615                                OBJ_CASE_INSENSITIVE,
3616                                NULL,
3617                                NULL);
3618 
3619     Status = NtOpenFile(&FileHandle,
3620                         GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
3621                         &ObjectAttributes,
3622                         &Iosb,
3623                         0,
3624                         FILE_SYNCHRONOUS_IO_NONALERT);
3625     if (!NT_SUCCESS(Status))
3626     {
3627         DPRINT1("NtOpenFile() failed (Status %lx)\n", Status);
3628         return Status;
3629     }
3630 
3631 #ifdef DUMP_PARTITION_TABLE
3632     DumpPartitionTable(DiskEntry);
3633 #endif
3634 
3635     //
3636     // FIXME: We first *MUST* use IOCTL_DISK_CREATE_DISK to initialize
3637     // the disk in MBR or GPT format in case the disk was not initialized!!
3638     // For this we must ask the user which format to use.
3639     //
3640 
3641     /* Save the original partition count to be restored later (see comment below) */
3642     PartitionCount = DiskEntry->LayoutBuffer->PartitionCount;
3643 
3644     /* Set the new disk layout and retrieve its updated version with
3645      * new partition numbers for the new partitions. The PARTMGR will
3646      * automatically notify the MOUNTMGR of new or deleted volumes. */
3647     BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION) +
3648                  ((PartitionCount - 1) * sizeof(PARTITION_INFORMATION));
3649     Status = NtDeviceIoControlFile(FileHandle,
3650                                    NULL,
3651                                    NULL,
3652                                    NULL,
3653                                    &Iosb,
3654                                    IOCTL_DISK_SET_DRIVE_LAYOUT,
3655                                    DiskEntry->LayoutBuffer,
3656                                    BufferSize,
3657                                    DiskEntry->LayoutBuffer,
3658                                    BufferSize);
3659     NtClose(FileHandle);
3660 
3661     /*
3662      * IOCTL_DISK_SET_DRIVE_LAYOUT calls IoWritePartitionTable(), which converts
3663      * DiskEntry->LayoutBuffer->PartitionCount into a partition *table* count,
3664      * where such a table is expected to enumerate up to 4 partitions:
3665      * partition *table* count == ROUND_UP(PartitionCount, 4) / 4 .
3666      * Due to this we need to restore the original PartitionCount number.
3667      */
3668     DiskEntry->LayoutBuffer->PartitionCount = PartitionCount;
3669 
3670     /* Check whether the IOCTL_DISK_SET_DRIVE_LAYOUT call succeeded */
3671     if (!NT_SUCCESS(Status))
3672     {
3673         DPRINT1("IOCTL_DISK_SET_DRIVE_LAYOUT failed (Status 0x%08lx)\n", Status);
3674         return Status;
3675     }
3676 
3677 #ifdef DUMP_PARTITION_TABLE
3678     DumpPartitionTable(DiskEntry);
3679 #endif
3680 
3681     /* Update the partition numbers and device names */
3682 
3683     /* Update the primary partition table */
3684     for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
3685          ListEntry != &DiskEntry->PrimaryPartListHead;
3686          ListEntry = ListEntry->Flink)
3687     {
3688         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3689         if (!PartEntry->IsPartitioned)
3690             continue;
3691         ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3692 
3693         /*
3694          * Initialize the partition's number and its device name only
3695          * if the partition was new. Note that the partition number
3696          * should not change if this partition has not been deleted
3697          * during repartitioning.
3698          */
3699         // FIXME: Our PartMgr currently returns modified numbers
3700         // in the layout, this needs to be investigated and fixed.
3701         if (PartEntry->New)
3702         {
3703             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3704             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3705             InitPartitionDeviceName(PartEntry);
3706         }
3707         PartEntry->New = FALSE;
3708     }
3709 
3710     /* Update the logical partition table */
3711     for (ListEntry = DiskEntry->LogicalPartListHead.Flink;
3712          ListEntry != &DiskEntry->LogicalPartListHead;
3713          ListEntry = ListEntry->Flink)
3714     {
3715         PartEntry = CONTAINING_RECORD(ListEntry, PARTENTRY, ListEntry);
3716         if (!PartEntry->IsPartitioned)
3717             continue;
3718         ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
3719 
3720         /* See comment above */
3721         if (PartEntry->New)
3722         {
3723             PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex];
3724             PartEntry->PartitionNumber = PartitionInfo->PartitionNumber;
3725             InitPartitionDeviceName(PartEntry);
3726         }
3727         PartEntry->New = FALSE;
3728     }
3729 
3730     //
3731     // NOTE: Originally (see r40437), we used to install here also a new MBR
3732     // for this disk (by calling InstallMbrBootCodeToDisk), only if:
3733     // DiskEntry->NewDisk == TRUE and DiskEntry->HwDiskNumber == 0.
3734     // Then after that, both DiskEntry->NewDisk and DiskEntry->NoMbr were set
3735     // to FALSE. In the other place (in usetup.c) where InstallMbrBootCodeToDisk
3736     // was called too, the installation test was modified by checking whether
3737     // DiskEntry->NoMbr was TRUE (instead of NewDisk).
3738     //
3739 
3740     // HACK: Parts of FIXMEs described above: (Re)set the PartitionStyle to MBR.
3741     DiskEntry->DiskStyle = PARTITION_STYLE_MBR;
3742 
3743     /* The layout has been successfully updated, the disk is not dirty anymore */
3744     DiskEntry->Dirty = FALSE;
3745 
3746     return Status;
3747 }
3748 
3749 BOOLEAN
WritePartitionsToDisk(IN PPARTLIST List)3750 WritePartitionsToDisk(
3751     IN PPARTLIST List)
3752 {
3753     NTSTATUS Status;
3754     PLIST_ENTRY Entry;
3755     PDISKENTRY DiskEntry;
3756     PVOLENTRY Volume;
3757 
3758     if (!List)
3759         return TRUE;
3760 
3761     /* Write all the partitions to all the disks */
3762     for (Entry = List->DiskListHead.Flink;
3763          Entry != &List->DiskListHead;
3764          Entry = Entry->Flink)
3765     {
3766         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
3767 
3768         if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
3769         {
3770             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
3771             continue;
3772         }
3773 
3774         if (DiskEntry->Dirty != FALSE)
3775         {
3776             Status = WritePartitions(DiskEntry);
3777             if (!NT_SUCCESS(Status))
3778             {
3779                 DPRINT1("WritePartitionsToDisk() failed to update disk %lu, Status 0x%08lx\n",
3780                         DiskEntry->DiskNumber, Status);
3781             }
3782         }
3783     }
3784 
3785     /* The PARTMGR should have notified the MOUNTMGR that new volumes
3786      * associated with the new partitions had to be created */
3787 
3788     /* Assign valid device names to new volumes */
3789     for (Entry = List->VolumesList.Flink;
3790          Entry != &List->VolumesList;
3791          Entry = Entry->Flink)
3792     {
3793         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3794         InitVolumeDeviceName(Volume);
3795     }
3796 
3797     return TRUE;
3798 }
3799 
3800 
3801 /**
3802  * @brief
3803  * Assign a "\DosDevices\#:" mount point drive letter to a disk partition or
3804  * volume, specified by a given disk signature and starting partition offset.
3805  **/
3806 static BOOLEAN
SetMountedDeviceValue(_In_ PVOLENTRY Volume)3807 SetMountedDeviceValue(
3808     _In_ PVOLENTRY Volume)
3809 {
3810     PPARTENTRY PartEntry = Volume->PartEntry;
3811     WCHAR Letter = Volume->Info.DriveLetter;
3812     NTSTATUS Status;
3813     OBJECT_ATTRIBUTES ObjectAttributes;
3814     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\MountedDevices");
3815     UNICODE_STRING ValueName;
3816     WCHAR Buffer[16];
3817     HANDLE KeyHandle;
3818     REG_DISK_MOUNT_INFO MountInfo;
3819 
3820     /* Ignore no letter */
3821     if (!Letter)
3822         return TRUE;
3823 
3824     RtlStringCchPrintfW(Buffer, _countof(Buffer),
3825                         L"\\DosDevices\\%c:", Letter);
3826     RtlInitUnicodeString(&ValueName, Buffer);
3827 
3828     InitializeObjectAttributes(&ObjectAttributes,
3829                                &KeyName,
3830                                OBJ_CASE_INSENSITIVE,
3831                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
3832                                NULL);
3833 
3834     Status = NtOpenKey(&KeyHandle,
3835                        KEY_ALL_ACCESS,
3836                        &ObjectAttributes);
3837     if (!NT_SUCCESS(Status))
3838     {
3839         Status = NtCreateKey(&KeyHandle,
3840                              KEY_ALL_ACCESS,
3841                              &ObjectAttributes,
3842                              0,
3843                              NULL,
3844                              REG_OPTION_NON_VOLATILE,
3845                              NULL);
3846     }
3847     if (!NT_SUCCESS(Status))
3848     {
3849         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
3850         return FALSE;
3851     }
3852 
3853     MountInfo.Signature = PartEntry->DiskEntry->LayoutBuffer->Signature;
3854     MountInfo.StartingOffset = GetPartEntryOffsetInBytes(PartEntry);
3855     Status = NtSetValueKey(KeyHandle,
3856                            &ValueName,
3857                            0,
3858                            REG_BINARY,
3859                            (PVOID)&MountInfo,
3860                            sizeof(MountInfo));
3861     NtClose(KeyHandle);
3862     if (!NT_SUCCESS(Status))
3863     {
3864         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
3865         return FALSE;
3866     }
3867 
3868     return TRUE;
3869 }
3870 
3871 BOOLEAN
SetMountedDeviceValues(_In_ PPARTLIST List)3872 SetMountedDeviceValues(
3873     _In_ PPARTLIST List)
3874 {
3875     PLIST_ENTRY Entry;
3876     PVOLENTRY Volume;
3877 
3878     if (!List)
3879         return FALSE;
3880 
3881     for (Entry = List->VolumesList.Flink;
3882          Entry != &List->VolumesList;
3883          Entry = Entry->Flink)
3884     {
3885         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
3886 
3887         /* Assign a "\DosDevices\#:" mount point to this volume */
3888         if (!SetMountedDeviceValue(Volume))
3889             return FALSE;
3890     }
3891 
3892     return TRUE;
3893 }
3894 
3895 VOID
SetMBRPartitionType(IN PPARTENTRY PartEntry,IN UCHAR PartitionType)3896 SetMBRPartitionType(
3897     IN PPARTENTRY PartEntry,
3898     IN UCHAR PartitionType)
3899 {
3900     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
3901 
3902     ASSERT(DiskEntry->DiskStyle == PARTITION_STYLE_MBR);
3903 
3904     /* Nothing to do if we assign the same type */
3905     if (PartitionType == PartEntry->PartitionType)
3906         return;
3907 
3908     // TODO: We might need to remount the associated basic volume...
3909 
3910     PartEntry->PartitionType = PartitionType;
3911 
3912     DiskEntry->Dirty = TRUE;
3913     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].PartitionType = PartitionType;
3914     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RecognizedPartition = IsRecognizedPartition(PartitionType);
3915     DiskEntry->LayoutBuffer->PartitionEntry[PartEntry->PartitionIndex].RewritePartition = TRUE;
3916 }
3917 
3918 /* EOF */
3919