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