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