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