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