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