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