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