1 /* 2 * PROJECT: ReactOS Setup Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Boot Stores Management functionality, with support for 5 * NT 5.x family (MS Windows <= 2003, and ReactOS) bootloaders. 6 * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito 7 */ 8 9 // TODO: Add support for NT 6.x family! (detection + BCD manipulation). 10 11 /* INCLUDES *****************************************************************/ 12 13 #include "precomp.h" 14 15 #include "bldrsup.h" 16 #include "filesup.h" 17 #include "inicache.h" 18 19 #define NDEBUG 20 #include <debug.h> 21 22 23 /* GLOBALS ******************************************************************/ 24 25 typedef NTSTATUS 26 (*POPEN_BOOT_STORE)( 27 _Out_ PVOID* Handle, 28 _In_ HANDLE PartitionDirectoryHandle, // _In_opt_ 29 _In_ BOOT_STORE_TYPE Type, 30 _In_ BOOT_STORE_OPENMODE OpenMode, 31 _In_ BOOT_STORE_ACCESS Access); 32 33 typedef NTSTATUS 34 (*PCLOSE_BOOT_STORE)( 35 _In_ PVOID Handle); 36 37 typedef NTSTATUS 38 (*PENUM_BOOT_STORE_ENTRIES)( 39 IN PVOID Handle, 40 // IN ULONG Flags, // Determine which data to retrieve 41 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 42 IN PVOID Parameter OPTIONAL); 43 44 typedef struct _NTOS_BOOT_LOADER_FILES 45 { 46 BOOT_STORE_TYPE Type; 47 PCZZWSTR LoaderExecutables; 48 PCWSTR LoaderConfigurationFile; 49 POPEN_BOOT_STORE OpenBootStore; 50 PCLOSE_BOOT_STORE CloseBootStore; 51 PENUM_BOOT_STORE_ENTRIES EnumBootStoreEntries; 52 } NTOS_BOOT_LOADER_FILES, *PNTOS_BOOT_LOADER_FILES; 53 54 55 /* 56 * Header for particular store contexts 57 */ 58 typedef struct _BOOT_STORE_CONTEXT 59 { 60 BOOT_STORE_TYPE Type; 61 BOOLEAN ReadOnly; 62 // PNTOS_BOOT_LOADER_FILES ?? 63 /* 64 PVOID PrivateData; 65 */ 66 } BOOT_STORE_CONTEXT, *PBOOT_STORE_CONTEXT; 67 68 typedef struct _BOOT_STORE_INI_CONTEXT 69 { 70 BOOT_STORE_CONTEXT Header; 71 72 /* 73 * If all these members are NULL, we know that the store is freshly created 74 * and is cached in memory only. At file closure we will therefore need to 75 * create the file proper and save its contents. 76 */ 77 HANDLE FileHandle; 78 HANDLE SectionHandle; 79 // SIZE_T ViewSize; 80 ULONG FileSize; 81 PVOID ViewBase; 82 83 PINICACHE IniCache; 84 PINI_SECTION OptionsIniSection; 85 PINI_SECTION OsIniSection; 86 } BOOT_STORE_INI_CONTEXT, *PBOOT_STORE_INI_CONTEXT; 87 88 // TODO! 89 typedef struct _BOOT_STORE_BCDREG_CONTEXT 90 { 91 BOOT_STORE_CONTEXT Header; 92 ULONG PlaceHolder; 93 } BOOT_STORE_BCDREG_CONTEXT, *PBOOT_STORE_BCDREG_CONTEXT; 94 95 96 static NTSTATUS 97 OpenIniBootLoaderStore( 98 _Out_ PVOID* Handle, 99 _In_ HANDLE PartitionDirectoryHandle, // _In_opt_ 100 _In_ BOOT_STORE_TYPE Type, 101 _In_ BOOT_STORE_OPENMODE OpenMode, 102 _In_ BOOT_STORE_ACCESS Access); 103 104 static NTSTATUS 105 CloseIniBootLoaderStore( 106 _In_ PVOID Handle); 107 108 static NTSTATUS 109 FreeLdrEnumerateBootEntries( 110 IN PBOOT_STORE_INI_CONTEXT BootStore, 111 // IN ULONG Flags, // Determine which data to retrieve 112 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 113 IN PVOID Parameter OPTIONAL); 114 115 static NTSTATUS 116 NtLdrEnumerateBootEntries( 117 IN PBOOT_STORE_INI_CONTEXT BootStore, 118 // IN ULONG Flags, // Determine which data to retrieve 119 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 120 IN PVOID Parameter OPTIONAL); 121 122 123 // Question 1: What if config file is optional? 124 // Question 2: What if many config files are possible? 125 NTOS_BOOT_LOADER_FILES NtosBootLoaders[] = 126 { 127 {FreeLdr, L"freeldr.sys\0", L"freeldr.ini", 128 OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)FreeLdrEnumerateBootEntries}, 129 {NtLdr , L"ntldr\0" L"osloader.exe\0", L"boot.ini", 130 OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)NtLdrEnumerateBootEntries }, 131 // {SetupLdr, L"setupldr\0" L"setupldr.bin\0" L"setupldr.exe\0", L"txtsetup.sif", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED} 132 // {BootMgr , L"bootmgr", L"BCD", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED} 133 }; 134 C_ASSERT(_countof(NtosBootLoaders) == BldrTypeMax); 135 136 enum BOOT_OPTION 137 { 138 BO_TimeOut, 139 BO_DefaultOS, 140 }; 141 static const PCWSTR BootOptionNames[][2] = 142 { 143 {L"TimeOut", L"DefaultOS"}, // FreeLdr 144 {L"timeout", L"default" } // NtLdr 145 }; 146 147 148 /* FUNCTIONS ****************************************************************/ 149 150 NTSTATUS 151 FindBootStore( // By handle 152 IN HANDLE PartitionDirectoryHandle, // OPTIONAL 153 IN BOOT_STORE_TYPE Type, 154 OUT PULONG VersionNumber OPTIONAL) 155 // OUT PHANDLE ConfigFileHande OPTIONAL ???? 156 { 157 PCWSTR LoaderExecutable; 158 // UINT i; 159 160 if (Type >= BldrTypeMax) 161 return STATUS_INVALID_PARAMETER; 162 163 if (VersionNumber) 164 *VersionNumber = 0; 165 166 /* Check whether any of the loader executables exist */ 167 LoaderExecutable = NtosBootLoaders[Type].LoaderExecutables; 168 while (*LoaderExecutable) 169 { 170 if (DoesFileExist(PartitionDirectoryHandle, LoaderExecutable)) 171 { 172 /* A loader was found, stop there */ 173 DPRINT("Found loader executable '%S'\n", LoaderExecutable); 174 break; 175 } 176 177 /* The loader does not exist, continue with another one */ 178 DPRINT("Loader executable '%S' does not exist, continue with another one...\n", LoaderExecutable); 179 LoaderExecutable += wcslen(LoaderExecutable) + 1; 180 } 181 if (!*LoaderExecutable) 182 { 183 /* No loader was found */ 184 DPRINT("No loader executable was found\n"); 185 return STATUS_NOT_FOUND; 186 } 187 188 /* Check for loader version if needed */ 189 if (VersionNumber) 190 { 191 *VersionNumber = 0; 192 // TODO: Check for BLDR version! 193 } 194 195 /* Check whether the loader configuration file exists */ 196 #if 0 197 Status = OpenAndMapFile(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile, 198 &FileHandle, &FileSize, &SectionHandle, &ViewBase, FALSE); 199 if (!NT_SUCCESS(Status)) 200 #else 201 if (!DoesFileExist(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile)) 202 #endif 203 { 204 /* The loader does not exist, continue with another one */ 205 // FIXME: Consider it might be optional?? 206 DPRINT1("Loader configuration file '%S' does not exist\n", NtosBootLoaders[Type].LoaderConfigurationFile); 207 return STATUS_NOT_FOUND; 208 } 209 210 return STATUS_SUCCESS; 211 } 212 213 214 // 215 // TEMPORARY functions to migrate the DEPRECATED BootDrive and BootPartition 216 // values of BootSector boot entries in FREELDR.INI to the newer BootPath value. 217 // 218 // REMOVE THEM once they won't be necessary anymore, 219 // after the removal of their support in FreeLoader! 220 // 221 static VOID 222 FreeLdrMigrateBootDrivePartWorker( 223 _In_ PINI_SECTION OsIniSection) 224 { 225 PCWSTR KeyData; 226 PINI_KEYWORD OldKey; 227 228 /* 229 * Check whether we have a "BootPath" value (takes precedence 230 * over both "BootDrive" and "BootPartition"). 231 */ 232 if (IniGetKey(OsIniSection, L"BootPath", &KeyData) && KeyData && *KeyData) 233 { 234 /* We already have a BootPath value, do nothing more */ 235 return; 236 } 237 238 /* We don't have one: retrieve the BIOS drive and 239 * partition and convert them to a valid ARC path */ 240 241 /* Retrieve the boot drive */ 242 OldKey = IniGetKey(OsIniSection, L"BootDrive", &KeyData); 243 if (OldKey) 244 { 245 PCWSTR OldDrive = KeyData; 246 ULONG DriveNumber = 0; 247 ULONG PartitionNumber = 0; 248 UCHAR DriveType = 0; 249 WCHAR BufferBootPath[80]; // 80 chars is enough for "multi(0)disk(0)rdisk(x)partition(y)", with (x,y) == MAXULONG 250 251 /* If a number string is given, then just 252 * convert it to decimal (BIOS HW only) */ 253 PCWCH p = KeyData; 254 if (p[0] >= L'0' && p[0] <= L'9') 255 { 256 DriveNumber = wcstoul(p, (PWCHAR*)&p, 0); 257 if (DriveNumber >= 0x80) 258 { 259 /* It's quite probably a hard disk */ 260 DriveNumber -= 0x80; 261 DriveType = L'h'; 262 } 263 else 264 { 265 /* It's quite probably a floppy */ 266 DriveType = L'f'; 267 } 268 } 269 else if (p[0] && towlower(p[1]) == L'd') 270 { 271 /* Convert the drive number string into a number: 'hd1' = 1 */ 272 DriveType = tolower(p[0]); 273 DriveNumber = _wtoi(&p[2]); 274 } 275 276 /* Retrieve the boot partition (optional, fall back to zero otherwise) */ 277 if (IniGetKey(OsIniSection, L"BootPartition", &KeyData)) 278 PartitionNumber = _wtoi(KeyData); 279 280 if (DriveType == L'f') 281 { 282 /* Floppy disk path: multi(0)disk(0)fdisk(x) */ 283 RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath), 284 L"multi(0)disk(0)fdisk(%lu)", DriveNumber); 285 } 286 else if (DriveType == L'h') 287 { 288 /* Hard disk path: multi(0)disk(0)rdisk(x)partition(y) */ 289 RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath), 290 L"multi(0)disk(0)rdisk(%lu)partition(%lu)", 291 DriveNumber, PartitionNumber); 292 } 293 else if (DriveType == L'c') 294 { 295 /* CD-ROM disk path: multi(0)disk(0)cdrom(x) */ 296 RtlStringCchPrintfW(BufferBootPath, _countof(BufferBootPath), 297 L"multi(0)disk(0)cdrom(%lu)", DriveNumber); 298 } 299 else 300 { 301 /* This case should rarely happen, if ever */ 302 DPRINT1("Unrecognized BootDrive type '%C'\n", DriveType ? DriveType : L'?'); 303 304 /* Build the boot path in the form: hdX,Y */ 305 RtlStringCchCopyW(BufferBootPath, _countof(BufferBootPath), OldDrive); 306 if (KeyData && *KeyData) 307 { 308 RtlStringCchCatW(BufferBootPath, _countof(BufferBootPath), L","); 309 RtlStringCchCatW(BufferBootPath, _countof(BufferBootPath), KeyData); 310 } 311 } 312 313 /* Add the new BootPath value */ 314 IniInsertKey(OsIniSection, OldKey, INSERT_BEFORE, L"BootPath", BufferBootPath); 315 } 316 317 /* Delete the deprecated BootDrive and BootPartition values */ 318 IniRemoveKeyByName(OsIniSection, L"BootDrive"); 319 IniRemoveKeyByName(OsIniSection, L"BootPartition"); 320 } 321 322 static VOID 323 FreeLdrMigrateBootDrivePart( 324 _In_ PBOOT_STORE_INI_CONTEXT BootStore) 325 { 326 PINICACHEITERATOR Iterator; 327 PINI_SECTION OsIniSection; 328 PCWSTR SectionName, KeyData; 329 330 /* Enumerate all the valid entries in the "Operating Systems" section */ 331 Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData); 332 if (!Iterator) return; 333 do 334 { 335 /* Search for an existing boot entry section */ 336 OsIniSection = IniGetSection(BootStore->IniCache, SectionName); 337 if (!OsIniSection) 338 continue; 339 340 /* Check for boot type to migrate */ 341 if (!IniGetKey(OsIniSection, L"BootType", &KeyData) || !KeyData) 342 { 343 /* Certainly not a ReactOS installation */ 344 DPRINT1("No BootType value present\n"); 345 continue; 346 } 347 if ((_wcsicmp(KeyData, L"Drive") == 0) || 348 (_wcsicmp(KeyData, L"\"Drive\"") == 0) || 349 (_wcsicmp(KeyData, L"Partition") == 0) || 350 (_wcsicmp(KeyData, L"\"Partition\"") == 0)) 351 { 352 /* Modify the BootPath value */ 353 IniAddKey(OsIniSection, L"BootType", L"BootSector"); 354 goto migrate_drivepart; 355 } 356 if ((_wcsicmp(KeyData, L"BootSector") == 0) || 357 (_wcsicmp(KeyData, L"\"BootSector\"") == 0)) 358 { 359 migrate_drivepart: 360 DPRINT("This is a '%S' boot entry\n", KeyData); 361 FreeLdrMigrateBootDrivePartWorker(OsIniSection); 362 } 363 } 364 while (IniFindNextValue(Iterator, &SectionName, &KeyData)); 365 366 IniFindClose(Iterator); 367 } 368 ////////////// 369 370 371 static VOID 372 CreateCommonFreeLdrSections( 373 IN OUT PBOOT_STORE_INI_CONTEXT BootStore) 374 { 375 PINI_SECTION IniSection; 376 377 /* 378 * Cache the "FREELOADER" section for our future usage. 379 */ 380 381 /* Create the "FREELOADER" section */ 382 IniSection = IniAddSection(BootStore->IniCache, L"FREELOADER"); 383 if (!IniSection) 384 DPRINT1("CreateCommonFreeLdrSections: Failed to create 'FREELOADER' section!\n"); 385 386 BootStore->OptionsIniSection = IniSection; 387 388 /* TimeOut */ 389 IniAddKey(BootStore->OptionsIniSection, L"TimeOut", L"0"); 390 391 /* Create "Display" section */ 392 IniSection = IniAddSection(BootStore->IniCache, L"Display"); 393 394 /* TitleText and MinimalUI */ 395 IniAddKey(IniSection, L"TitleText", L"ReactOS Boot Manager"); 396 IniAddKey(IniSection, L"MinimalUI", L"Yes"); 397 398 /* 399 * Cache the "Operating Systems" section for our future usage. 400 */ 401 402 /* Create the "Operating Systems" section */ 403 IniSection = IniAddSection(BootStore->IniCache, L"Operating Systems"); 404 if (!IniSection) 405 DPRINT1("CreateCommonFreeLdrSections: Failed to create 'Operating Systems' section!\n"); 406 407 BootStore->OsIniSection = IniSection; 408 } 409 410 static NTSTATUS 411 OpenIniBootLoaderStore( 412 _Out_ PVOID* Handle, 413 _In_ HANDLE PartitionDirectoryHandle, // _In_opt_ 414 _In_ BOOT_STORE_TYPE Type, 415 _In_ BOOT_STORE_OPENMODE OpenMode, 416 _In_ BOOT_STORE_ACCESS Access) 417 { 418 NTSTATUS Status; 419 PBOOT_STORE_INI_CONTEXT BootStore; 420 UNICODE_STRING Name; 421 OBJECT_ATTRIBUTES ObjectAttributes; 422 IO_STATUS_BLOCK IoStatusBlock; 423 ACCESS_MASK DesiredAccess; 424 ULONG CreateDisposition; 425 426 // 427 // WARNING! We support the INI creation *ONLY* for FreeLdr, and not for NTLDR 428 // 429 if ((Type == NtLdr) && (OpenMode == BS_CreateNew || OpenMode == BS_CreateAlways || OpenMode == BS_RecreateExisting)) 430 { 431 DPRINT1("OpenIniBootLoaderStore() unsupported for NTLDR\n"); 432 return STATUS_NOT_SUPPORTED; 433 } 434 435 /* Create a boot store structure */ 436 BootStore = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*BootStore)); 437 if (!BootStore) 438 return STATUS_INSUFFICIENT_RESOURCES; 439 440 BootStore->Header.Type = Type; 441 442 /* 443 * So far, we only use the INI cache. The file itself is not created or 444 * opened yet, therefore FileHandle, SectionHandle, ViewBase and FileSize 445 * are all NULL. We will use this fact to know that the INI file was indeed 446 * created, and not just opened as an existing file. 447 */ 448 // BootStore->FileHandle = NULL; 449 BootStore->SectionHandle = NULL; 450 BootStore->ViewBase = NULL; 451 BootStore->FileSize = 0; 452 453 /* 454 * Create or open the loader configuration INI file as necessary. 455 */ 456 RtlInitUnicodeString(&Name, NtosBootLoaders[Type].LoaderConfigurationFile); 457 InitializeObjectAttributes(&ObjectAttributes, 458 &Name, 459 OBJ_CASE_INSENSITIVE, 460 PartitionDirectoryHandle, 461 NULL); 462 463 DesiredAccess = 464 ((Access & BS_ReadAccess ) ? FILE_GENERIC_READ : 0) | 465 ((Access & BS_WriteAccess) ? FILE_GENERIC_WRITE : 0); 466 467 CreateDisposition = FILE_OPEN; 468 switch (OpenMode) 469 { 470 case BS_CreateNew: 471 CreateDisposition = FILE_CREATE; 472 break; 473 case BS_CheckExisting: 474 case BS_OpenExisting: 475 CreateDisposition = FILE_OPEN; 476 break; 477 case BS_OpenAlways: 478 CreateDisposition = FILE_OPEN_IF; 479 break; 480 case BS_RecreateExisting: 481 CreateDisposition = FILE_OVERWRITE; 482 break; 483 case BS_CreateAlways: 484 CreateDisposition = FILE_OVERWRITE_IF; 485 break; 486 default: 487 ASSERT(FALSE); 488 } 489 490 IoStatusBlock.Information = 0; 491 Status = NtCreateFile(&BootStore->FileHandle, 492 DesiredAccess | SYNCHRONIZE, 493 &ObjectAttributes, 494 &IoStatusBlock, 495 NULL, 496 FILE_ATTRIBUTE_NORMAL, 497 FILE_SHARE_READ, 498 CreateDisposition, 499 FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE, 500 NULL, 501 0); 502 503 if (OpenMode == BS_CheckExisting) 504 { 505 /* We just want to check for file existence. If we either succeeded 506 * opening the file, or we failed because it exists but we do not 507 * currently have access to it, return success in either case. */ 508 BOOLEAN Success = (NT_SUCCESS(Status) || (Status == STATUS_ACCESS_DENIED)); 509 if (!Success) 510 { 511 DPRINT1("Couldn't find Loader configuration file '%S'\n", 512 NtosBootLoaders[Type].LoaderConfigurationFile); 513 } 514 if (BootStore->FileHandle) 515 NtClose(BootStore->FileHandle); 516 RtlFreeHeap(ProcessHeap, 0, BootStore); 517 return (Success ? STATUS_SUCCESS : Status); 518 } 519 520 /* 521 * If create/open failed because the file is in read-only mode, 522 * change its attributes and re-attempt opening it. 523 */ 524 if (Status == STATUS_ACCESS_DENIED) do 525 { 526 FILE_BASIC_INFORMATION FileInfo = {0}; 527 528 /* Reattempt to open it with limited access */ 529 Status = NtCreateFile(&BootStore->FileHandle, 530 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, 531 &ObjectAttributes, 532 &IoStatusBlock, 533 NULL, 534 FILE_ATTRIBUTE_NORMAL, 535 FILE_SHARE_READ, 536 FILE_OPEN, 537 FILE_NO_INTERMEDIATE_BUFFERING | 538 FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE, 539 NULL, 540 0); 541 /* Fail for real if we cannot open it that way */ 542 if (!NT_SUCCESS(Status)) 543 break; 544 545 /* Reset attributes to normal, no read-only */ 546 FileInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; 547 /* 548 * We basically don't care about whether it succeeds: 549 * if it didn't, later open will fail. 550 */ 551 NtSetInformationFile(BootStore->FileHandle, &IoStatusBlock, 552 &FileInfo, sizeof(FileInfo), 553 FileBasicInformation); 554 555 /* Close file */ 556 NtClose(BootStore->FileHandle); 557 558 /* And re-attempt create/open */ 559 Status = NtCreateFile(&BootStore->FileHandle, 560 DesiredAccess | SYNCHRONIZE, 561 &ObjectAttributes, 562 &IoStatusBlock, 563 NULL, 564 FILE_ATTRIBUTE_NORMAL, 565 FILE_SHARE_READ, 566 CreateDisposition, 567 FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE, 568 NULL, 569 0); 570 } while (0); 571 if (!NT_SUCCESS(Status)) 572 { 573 DPRINT1("Couldn't open Loader configuration file '%S' (Status 0x%08lx)\n", 574 NtosBootLoaders[Type].LoaderConfigurationFile, Status); 575 RtlFreeHeap(ProcessHeap, 0, BootStore); 576 return Status; 577 } 578 579 BootStore->Header.ReadOnly = !(Access & BS_WriteAccess); 580 581 if (IoStatusBlock.Information == FILE_CREATED || // with: FILE_CREATE, FILE_OVERWRITE_IF, FILE_OPEN_IF, FILE_SUPERSEDE 582 IoStatusBlock.Information == FILE_OVERWRITTEN || // with: FILE_OVERWRITE, FILE_OVERWRITE_IF 583 IoStatusBlock.Information == FILE_SUPERSEDED) // with: FILE_SUPERSEDE 584 { 585 /* 586 * The loader configuration INI file is (re)created 587 * fresh new, initialize its cache and its contents. 588 */ 589 BootStore->IniCache = IniCacheCreate(); 590 if (!BootStore->IniCache) 591 { 592 DPRINT1("IniCacheCreate() failed\n"); 593 NtClose(BootStore->FileHandle); 594 RtlFreeHeap(ProcessHeap, 0, BootStore); 595 return STATUS_INSUFFICIENT_RESOURCES; 596 } 597 598 if (Type == FreeLdr) 599 CreateCommonFreeLdrSections(BootStore); 600 } 601 else // if (IoStatusBlock.Information == FILE_OPENED) // with: FILE_OPEN, FILE_OPEN_IF 602 { 603 PINI_SECTION IniSection; 604 605 /* 606 * The loader configuration INI file exists and is opened, 607 * map its file contents into memory. 608 */ 609 #if 0 610 // FIXME: &BootStore->FileSize 611 Status = MapFile(BootStore->FileHandle, 612 &BootStore->SectionHandle, 613 &BootStore->ViewBase, 614 (Access & BS_WriteAccess)); 615 if (!NT_SUCCESS(Status)) 616 { 617 DPRINT1("Failed to map Loader configuration file '%S' (Status 0x%08lx)\n", 618 NtosBootLoaders[Type].LoaderConfigurationFile, Status); 619 NtClose(BootStore->FileHandle); 620 RtlFreeHeap(ProcessHeap, 0, BootStore); 621 return Status; 622 } 623 #else 624 BootStore->SectionHandle = UlongToPtr(1); // Workaround for CloseIniBootLoaderStore 625 #endif 626 627 /* Open an *existing* INI configuration file */ 628 #if 0 629 Status = IniCacheLoadFromMemory(&BootStore->IniCache, 630 BootStore->ViewBase, 631 BootStore->FileSize, 632 FALSE); 633 #else 634 Status = IniCacheLoadByHandle(&BootStore->IniCache, BootStore->FileHandle, FALSE); 635 #endif 636 if (!NT_SUCCESS(Status)) 637 { 638 DPRINT1("IniCacheLoadFromMemory() failed (Status 0x%08lx)\n", Status); 639 #if 0 640 /* Finally, unmap and close the file */ 641 UnMapAndCloseFile(BootStore->FileHandle, 642 BootStore->SectionHandle, 643 BootStore->ViewBase); 644 #else 645 NtClose(BootStore->FileHandle); 646 #endif 647 RtlFreeHeap(ProcessHeap, 0, BootStore); 648 return Status; 649 } 650 651 if (Type == FreeLdr) 652 { 653 /* 654 * Cache the "FREELOADER" section for our future usage. 655 */ 656 657 /* Get or create the "FREELOADER" section */ 658 IniSection = IniAddSection(BootStore->IniCache, L"FREELOADER"); 659 if (!IniSection) 660 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'FREELOADER' section!\n"); 661 662 BootStore->OptionsIniSection = IniSection; 663 664 /* 665 * Cache the "Operating Systems" section for our future usage. 666 */ 667 668 /* Get or create the "Operating Systems" section */ 669 IniSection = IniAddSection(BootStore->IniCache, L"Operating Systems"); 670 if (!IniSection) 671 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'Operating Systems' section!\n"); 672 673 BootStore->OsIniSection = IniSection; 674 675 // 676 // TEMPORARY: Migrate the DEPRECATED BootDrive and BootPartition 677 // values of BootSector boot entries to the newer BootPath value. 678 // 679 FreeLdrMigrateBootDrivePart(BootStore); 680 } 681 else 682 if (Type == NtLdr) 683 { 684 /* 685 * Cache the "boot loader" section for our future usage. 686 */ 687 /* 688 * HISTORICAL NOTE: 689 * 690 * While the "operating systems" section acquired its definitive 691 * name already when Windows NT was at its very early beta stage 692 * (NT 3.1 October 1991 Beta, 10-16-1991), this was not the case 693 * for its general settings section "boot loader". 694 * 695 * The following section names were successively introduced: 696 * 697 * - In NT 3.1 October 1991 Beta, 10-16-1991, using OS Loader V1.5, 698 * the section was named "multiboot". 699 * 700 * - In the next public beta version NT 3.10.340 Beta, 10-12-1992, 701 * using OS Loader V2.10, a new name was introduced: "flexboot". 702 * This is around this time that the NT OS Loader was also 703 * introduced as the "Windows NT FlexBoot" loader, as shown by 704 * the Windows NT FAQs that circulated around this time: 705 * http://cd.textfiles.com/cica9308/CIS_LIBS/WINNT/1/NTFAQ.TXT 706 * http://cd.textfiles.com/cica/cica9308/UNZIPPED/NT/NTFAQ/FTP/NEWS/NTFAQ1.TXT 707 * I can only hypothesize that the "FlexBoot" name was chosen 708 * as a marketing coup, possibly to emphasise its "flexibility" 709 * as a simple multiboot-aware boot manager. 710 * 711 * - A bit later, with NT 3.10.404 Beta, 3-7-1993, using an updated 712 * version of OS Loader V2.10, the final section name "boot loader" 713 * was introduced, and was kept since then. 714 * 715 * Due to the necessity to be able to boot and / or upgrade any 716 * Windows NT version at any time, including its NT Loader and the 717 * associated boot.ini file, all versions of NTLDR and the NT installer 718 * understand and parse these three section names, the default one 719 * being "boot loader", and if not present, they successively fall 720 * back to "flexboot" and then to "multiboot". 721 */ 722 723 /* Get the "boot loader" section */ 724 IniSection = IniGetSection(BootStore->IniCache, L"boot loader"); 725 if (!IniSection) 726 { 727 /* Fall back to "flexboot" */ 728 IniSection = IniGetSection(BootStore->IniCache, L"flexboot"); 729 if (!IniSection) 730 { 731 /* Fall back to "multiboot" */ 732 IniSection = IniGetSection(BootStore->IniCache, L"multiboot"); 733 } 734 } 735 #if 0 736 if (!IniSection) 737 { 738 /* It does not exist yet, so create it */ 739 IniSection = IniAddSection(BootStore->IniCache, L"boot loader"); 740 } 741 #endif 742 if (!IniSection) 743 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'boot loader' section!\n"); 744 745 BootStore->OptionsIniSection = IniSection; 746 747 /* 748 * Cache the "Operating Systems" section for our future usage. 749 */ 750 751 /* Get or create the "Operating Systems" section */ 752 IniSection = IniAddSection(BootStore->IniCache, L"operating systems"); 753 if (!IniSection) 754 DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'operating systems' section!\n"); 755 756 BootStore->OsIniSection = IniSection; 757 } 758 } 759 760 *Handle = BootStore; 761 return STATUS_SUCCESS; 762 } 763 764 /** 765 * @brief 766 * Selectively changes the attributes of a file. 767 * 768 * @param[in] FileHandle 769 * Handle to an opened file for which to change its attributes. 770 * 771 * @param[in] MaskAttributes 772 * A mask specifying which attributes to change; any other attributes 773 * will be maintained as they are. If this parameter is zero, all of 774 * the attributes in *Attributes will be changed. 775 * 776 * @param[in,out] Attributes 777 * In input, specifies the new attributes to set. Attributes that 778 * are not set, but are specified in MaskAttributes, are removed. 779 * In output, receives the original attributes of the file. 780 * 781 * @return 782 * STATUS_SUCCESS if the attributes were successfully changed, 783 * or a failure code if an error happened. 784 **/ 785 static NTSTATUS 786 ProtectFile( 787 _In_ HANDLE FileHandle, 788 _In_ ULONG MaskAttributes, 789 _Inout_ PULONG Attributes) 790 { 791 NTSTATUS Status; 792 IO_STATUS_BLOCK IoStatusBlock; 793 FILE_BASIC_INFORMATION FileInfo; 794 ULONG OldAttributes; 795 796 /* Retrieve the original file attributes */ 797 Status = NtQueryInformationFile(FileHandle, 798 &IoStatusBlock, 799 &FileInfo, 800 sizeof(FileInfo), 801 FileBasicInformation); 802 if (!NT_SUCCESS(Status)) 803 { 804 DPRINT1("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status); 805 return Status; 806 } 807 OldAttributes = FileInfo.FileAttributes; 808 809 /* Modify the attributes and return the old ones */ 810 if (MaskAttributes) 811 FileInfo.FileAttributes = (OldAttributes & ~MaskAttributes) | (*Attributes & MaskAttributes); 812 else 813 FileInfo.FileAttributes = *Attributes; 814 815 *Attributes = OldAttributes; 816 817 /* Set the new file attributes */ 818 Status = NtSetInformationFile(FileHandle, 819 &IoStatusBlock, 820 &FileInfo, 821 sizeof(FileInfo), 822 FileBasicInformation); 823 if (!NT_SUCCESS(Status)) 824 DPRINT1("NtSetInformationFile() failed (Status 0x%08lx)\n", Status); 825 826 return Status; 827 } 828 829 static NTSTATUS 830 CloseIniBootLoaderStore( 831 _In_ PVOID Handle) 832 { 833 /* Set or remove SYSTEM, HIDDEN and READONLY attributes */ 834 static const ULONG ProtectAttribs = 835 (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); 836 837 PBOOT_STORE_INI_CONTEXT BootStore = (PBOOT_STORE_INI_CONTEXT)Handle; 838 NTSTATUS Status = STATUS_SUCCESS; 839 ULONG FileAttribs; 840 841 ASSERT(BootStore); 842 843 /* If the INI file was opened in read-only mode, skip saving */ 844 if (BootStore->Header.ReadOnly) 845 goto Quit; 846 847 /* If the INI file was already opened because it already existed, unprotect it */ 848 if (BootStore->SectionHandle) 849 { 850 FileAttribs = 0; 851 Status = ProtectFile(BootStore->FileHandle, ProtectAttribs, &FileAttribs); 852 if (!NT_SUCCESS(Status)) 853 { 854 DPRINT1("Could not unprotect INI boot store (Status 0x%08lx)\n", Status); 855 goto Quit; 856 } 857 } 858 859 IniCacheSaveByHandle(BootStore->IniCache, BootStore->FileHandle); 860 861 /* Re-protect the INI file */ 862 FileAttribs = ProtectAttribs; 863 if (BootStore->Header.Type == FreeLdr) 864 { 865 // NOTE: CORE-19575: For the time being, don't add READONLY for ease 866 // of testing and modifying files, but it won't always stay this way. 867 FileAttribs &= ~FILE_ATTRIBUTE_READONLY; 868 } 869 /*Status =*/ ProtectFile(BootStore->FileHandle, FileAttribs, &FileAttribs); 870 Status = STATUS_SUCCESS; // Ignore the status and just succeed. 871 872 Quit: 873 IniCacheDestroy(BootStore->IniCache); 874 875 #if 0 876 if (BootStore->SectionHandle) 877 { 878 /* Finally, unmap and close the file */ 879 UnMapAndCloseFile(BootStore->FileHandle, 880 BootStore->SectionHandle, 881 BootStore->ViewBase); 882 } 883 else // if (BootStore->FileHandle) 884 #endif 885 { 886 /* Just close the file we have opened for creation */ 887 NtClose(BootStore->FileHandle); 888 } 889 890 /* Finally, free the boot store structure */ 891 RtlFreeHeap(ProcessHeap, 0, BootStore); 892 return Status; 893 } 894 895 896 NTSTATUS 897 OpenBootStoreByHandle( 898 _Out_ PVOID* Handle, 899 _In_ HANDLE PartitionDirectoryHandle, // _In_opt_ 900 _In_ BOOT_STORE_TYPE Type, 901 _In_ BOOT_STORE_OPENMODE OpenMode, 902 _In_ BOOT_STORE_ACCESS Access) 903 { 904 /* 905 * NOTE: Currently we open & map the loader configuration file without 906 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 907 * and NTLDR's boot.ini files. But as soon as we'll implement support for 908 * BOOTMGR detection, the "configuration file" will be the BCD registry 909 * hive and then, we'll have instead to mount the hive & open it. 910 */ 911 912 if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax) 913 { 914 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type); 915 return STATUS_NOT_SUPPORTED; 916 } 917 918 /* 919 * Verify the access modes to perform the open actions. 920 * The operating system may allow e.g. file creation even with 921 * read-only access, but we do not allow this because we want 922 * to protect any existing boot store file in case the caller 923 * specified such an open mode. 924 */ 925 // if ((OpenMode == BS_CheckExisting) && !(Access & BS_ReadAccess)) 926 // return STATUS_ACCESS_DENIED; 927 if ((OpenMode == BS_CreateNew || OpenMode == BS_CreateAlways || OpenMode == BS_RecreateExisting) && !(Access & BS_WriteAccess)) 928 return STATUS_ACCESS_DENIED; 929 if ((OpenMode == BS_OpenExisting || OpenMode == BS_OpenAlways) && !(Access & BS_ReadWriteAccess)) 930 return STATUS_ACCESS_DENIED; 931 932 return NtosBootLoaders[Type].OpenBootStore(Handle, 933 PartitionDirectoryHandle, 934 Type, 935 OpenMode, 936 Access); 937 } 938 939 NTSTATUS 940 OpenBootStore_UStr( 941 _Out_ PVOID* Handle, 942 _In_ PUNICODE_STRING SystemPartitionPath, 943 _In_ BOOT_STORE_TYPE Type, 944 _In_ BOOT_STORE_OPENMODE OpenMode, 945 _In_ BOOT_STORE_ACCESS Access) 946 { 947 NTSTATUS Status; 948 OBJECT_ATTRIBUTES ObjectAttributes; 949 IO_STATUS_BLOCK IoStatusBlock; 950 HANDLE PartitionDirectoryHandle; 951 952 /* 953 * NOTE: Currently we open & map the loader configuration file without 954 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 955 * and NTLDR's boot.ini files. But as soon as we'll implement support for 956 * BOOTMGR detection, the "configuration file" will be the BCD registry 957 * hive and then, we'll have instead to mount the hive & open it. 958 */ 959 960 if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax) 961 { 962 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type); 963 return STATUS_NOT_SUPPORTED; 964 } 965 966 /* Open SystemPartition */ 967 InitializeObjectAttributes(&ObjectAttributes, 968 SystemPartitionPath, 969 OBJ_CASE_INSENSITIVE, 970 NULL, 971 NULL); 972 Status = NtOpenFile(&PartitionDirectoryHandle, 973 FILE_LIST_DIRECTORY | FILE_ADD_FILE /* | FILE_ADD_SUBDIRECTORY | FILE_TRAVERSE*/ | SYNCHRONIZE, 974 &ObjectAttributes, 975 &IoStatusBlock, 976 FILE_SHARE_READ | FILE_SHARE_WRITE, 977 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE /* | FILE_OPEN_FOR_BACKUP_INTENT */); 978 if (!NT_SUCCESS(Status)) 979 { 980 DPRINT1("Failed to open SystemPartition '%wZ' (Status 0x%08lx)\n", 981 SystemPartitionPath, Status); 982 return Status; 983 } 984 985 Status = OpenBootStoreByHandle(Handle, 986 PartitionDirectoryHandle, 987 Type, 988 OpenMode, 989 Access); 990 991 /* Done! */ 992 NtClose(PartitionDirectoryHandle); 993 return Status; 994 } 995 996 NTSTATUS 997 OpenBootStore( 998 _Out_ PVOID* Handle, 999 _In_ PCWSTR SystemPartition, 1000 _In_ BOOT_STORE_TYPE Type, 1001 _In_ BOOT_STORE_OPENMODE OpenMode, 1002 _In_ BOOT_STORE_ACCESS Access) 1003 { 1004 UNICODE_STRING SystemPartitionPath; 1005 RtlInitUnicodeString(&SystemPartitionPath, SystemPartition); 1006 return OpenBootStore_UStr(Handle, 1007 &SystemPartitionPath, 1008 Type, 1009 OpenMode, 1010 Access); 1011 } 1012 1013 NTSTATUS 1014 CloseBootStore( 1015 _In_ PVOID Handle) 1016 { 1017 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1018 1019 if (!BootStore) 1020 return STATUS_INVALID_PARAMETER; 1021 1022 /* 1023 * NOTE: Currently we open & map the loader configuration file without 1024 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1025 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1026 * BOOTMGR detection, the "configuration file" will be the BCD registry 1027 * hive and then, we'll have instead to mount the hive & open it. 1028 */ 1029 1030 if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1031 { 1032 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1033 return STATUS_NOT_SUPPORTED; 1034 } 1035 1036 return NtosBootLoaders[BootStore->Type].CloseBootStore(Handle /* BootStore */); 1037 } 1038 1039 1040 static 1041 NTSTATUS 1042 CreateNTOSEntry( 1043 IN PBOOT_STORE_INI_CONTEXT BootStore, 1044 IN ULONG_PTR BootEntryKey, 1045 IN PBOOT_STORE_ENTRY BootEntry) 1046 { 1047 PINI_SECTION IniSection; 1048 PCWSTR Section = (PCWSTR)BootEntryKey; 1049 1050 /* Insert the entry into the "Operating Systems" section */ 1051 IniAddKey(BootStore->OsIniSection, Section, BootEntry->FriendlyName); 1052 1053 /* Create a new section */ 1054 IniSection = IniAddSection(BootStore->IniCache, Section); 1055 1056 if (BootEntry->OsOptionsLength >= sizeof(NTOS_OPTIONS) && 1057 RtlCompareMemory(&BootEntry->OsOptions /* Signature */, 1058 NTOS_OPTIONS_SIGNATURE, 1059 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) == 1060 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) 1061 { 1062 PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; 1063 1064 /* BootType, SystemPath and Options */ 1065 IniAddKey(IniSection, L"BootType", L"Windows2003"); 1066 IniAddKey(IniSection, L"SystemPath", Options->OsLoadPath); 1067 IniAddKey(IniSection, L"Options", Options->OsLoadOptions); 1068 } 1069 else 1070 if (BootEntry->OsOptionsLength >= sizeof(BOOTSECTOR_OPTIONS) && 1071 RtlCompareMemory(&BootEntry->OsOptions /* Signature */, 1072 BOOTSECTOR_OPTIONS_SIGNATURE, 1073 RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature)) == 1074 RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature)) 1075 { 1076 PBOOTSECTOR_OPTIONS Options = (PBOOTSECTOR_OPTIONS)&BootEntry->OsOptions; 1077 1078 /* BootType, BootPath and BootSector */ 1079 IniAddKey(IniSection, L"BootType", L"BootSector"); 1080 IniAddKey(IniSection, L"BootPath", Options->BootPath); 1081 IniAddKey(IniSection, L"BootSectorFile", Options->FileName); 1082 } 1083 else 1084 { 1085 // DPRINT1("Unsupported BootType %lu/'%*.s'\n", 1086 // BootEntry->OsOptionsLength, 8, &BootEntry->OsOptions); 1087 DPRINT1("Unsupported BootType %lu\n", BootEntry->OsOptionsLength); 1088 } 1089 1090 return STATUS_SUCCESS; 1091 } 1092 1093 NTSTATUS 1094 AddBootStoreEntry( 1095 IN PVOID Handle, 1096 IN PBOOT_STORE_ENTRY BootEntry, 1097 IN ULONG_PTR BootEntryKey) 1098 { 1099 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1100 1101 if (!BootStore || !BootEntry) 1102 return STATUS_INVALID_PARAMETER; 1103 1104 /* 1105 * NOTE: Currently we open & map the loader configuration file without 1106 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1107 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1108 * BOOTMGR detection, the "configuration file" will be the BCD registry 1109 * hive and then, we'll have instead to mount the hive & open it. 1110 */ 1111 1112 // 1113 // FIXME!! 1114 // 1115 1116 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1117 1118 if (BootStore->Type == FreeLdr) 1119 { 1120 if (BootEntry->Version != FreeLdr) 1121 return STATUS_INVALID_PARAMETER; 1122 1123 return CreateNTOSEntry((PBOOT_STORE_INI_CONTEXT)BootStore, 1124 BootEntryKey, BootEntry); 1125 } 1126 else 1127 if (BootStore->Type == NtLdr) 1128 { 1129 PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; 1130 PWCHAR Buffer; 1131 ULONG BufferLength; 1132 PCWSTR InstallName, OsOptions; 1133 // ULONG InstallNameLength, OsOptionsLength; 1134 BOOLEAN IsNameNotQuoted; 1135 1136 if (BootEntry->Version != NtLdr) 1137 return STATUS_INVALID_PARAMETER; 1138 1139 if (BootEntry->OsOptionsLength < sizeof(NTOS_OPTIONS) || 1140 RtlCompareMemory(&BootEntry->OsOptions /* Signature */, 1141 NTOS_OPTIONS_SIGNATURE, 1142 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) != 1143 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) 1144 { 1145 // DPRINT1("Unsupported BootType '%S'\n", BootEntry->Version); 1146 DPRINT1("Unsupported BootType %lu\n", BootEntry->OsOptionsLength); 1147 return STATUS_SUCCESS; // STATUS_NOT_SUPPORTED; 1148 } 1149 1150 InstallName = BootEntry->FriendlyName; 1151 OsOptions = Options->OsLoadOptions; 1152 1153 // if (InstallNameLength == 0) InstallName = NULL; 1154 // if (OsOptionsLength == 0) OsOptions = NULL; 1155 1156 IsNameNotQuoted = (InstallName[0] != L'\"' || InstallName[wcslen(InstallName)-1] != L'\"'); 1157 1158 BufferLength = (IsNameNotQuoted ? 2 /* Quotes for FriendlyName*/ : 0) + wcslen(InstallName); 1159 if (OsOptions) 1160 BufferLength += 1 /* Space between FriendlyName and options */ + wcslen(OsOptions); 1161 BufferLength++; /* NULL-termination */ 1162 1163 Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength * sizeof(WCHAR)); 1164 if (!Buffer) 1165 return STATUS_INSUFFICIENT_RESOURCES; 1166 1167 *Buffer = UNICODE_NULL; 1168 if (IsNameNotQuoted) RtlStringCchCatW(Buffer, BufferLength, L"\""); 1169 RtlStringCchCatW(Buffer, BufferLength, InstallName); 1170 if (IsNameNotQuoted) RtlStringCchCatW(Buffer, BufferLength, L"\""); 1171 if (OsOptions) 1172 { 1173 RtlStringCchCatW(Buffer, BufferLength, L" "); 1174 RtlStringCchCatW(Buffer, BufferLength, OsOptions); 1175 } 1176 1177 /* Insert the entry into the "Operating Systems" section */ 1178 IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OsIniSection, 1179 Options->OsLoadPath, Buffer); 1180 1181 RtlFreeHeap(ProcessHeap, 0, Buffer); 1182 return STATUS_SUCCESS; 1183 } 1184 else 1185 { 1186 DPRINT1("Loader type %d is currently unsupported!\n", BootStore->Type); 1187 return STATUS_NOT_SUPPORTED; 1188 } 1189 } 1190 1191 NTSTATUS 1192 DeleteBootStoreEntry( 1193 IN PVOID Handle, 1194 IN ULONG_PTR BootEntryKey) 1195 { 1196 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1197 1198 if (!BootStore) 1199 return STATUS_INVALID_PARAMETER; 1200 1201 /* 1202 * NOTE: Currently we open & map the loader configuration file without 1203 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1204 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1205 * BOOTMGR detection, the "configuration file" will be the BCD registry 1206 * hive and then, we'll have instead to mount the hive & open it. 1207 */ 1208 1209 // 1210 // FIXME!! 1211 // 1212 1213 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1214 if (BootStore->Type != FreeLdr) 1215 { 1216 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1217 return STATUS_NOT_SUPPORTED; 1218 } 1219 1220 // FIXME! This function needs my INI library rewrite to be implemented!! 1221 UNIMPLEMENTED; 1222 return STATUS_NOT_IMPLEMENTED; 1223 } 1224 1225 NTSTATUS 1226 ModifyBootStoreEntry( 1227 IN PVOID Handle, 1228 IN PBOOT_STORE_ENTRY BootEntry) 1229 { 1230 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1231 1232 if (!BootStore || !BootEntry) 1233 return STATUS_INVALID_PARAMETER; 1234 1235 /* 1236 * NOTE: Currently we open & map the loader configuration file without 1237 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1238 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1239 * BOOTMGR detection, the "configuration file" will be the BCD registry 1240 * hive and then, we'll have instead to mount the hive & open it. 1241 */ 1242 1243 // 1244 // FIXME!! 1245 // 1246 1247 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1248 if (BootStore->Type != FreeLdr) 1249 { 1250 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1251 return STATUS_NOT_SUPPORTED; 1252 } 1253 1254 // FIXME! This function needs my INI library rewrite to operate properly!! 1255 UNIMPLEMENTED; 1256 return STATUS_NOT_IMPLEMENTED; 1257 } 1258 1259 NTSTATUS 1260 QueryBootStoreEntry( 1261 IN PVOID Handle, 1262 IN ULONG_PTR BootEntryKey, 1263 OUT PBOOT_STORE_ENTRY BootEntry) // Technically this should be PBOOT_STORE_ENTRY* 1264 { 1265 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1266 1267 if (!BootStore) 1268 return STATUS_INVALID_PARAMETER; 1269 1270 /* 1271 * NOTE: Currently we open & map the loader configuration file without 1272 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1273 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1274 * BOOTMGR detection, the "configuration file" will be the BCD registry 1275 * hive and then, we'll have instead to mount the hive & open it. 1276 */ 1277 1278 // 1279 // FIXME!! 1280 // 1281 1282 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1283 if (BootStore->Type != FreeLdr) 1284 { 1285 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1286 return STATUS_NOT_SUPPORTED; 1287 } 1288 1289 // FIXME! This function needs my INI library rewrite to be implemented!! 1290 UNIMPLEMENTED; 1291 return STATUS_NOT_IMPLEMENTED; 1292 } 1293 1294 NTSTATUS 1295 QueryBootStoreOptions( 1296 IN PVOID Handle, 1297 IN OUT PBOOT_STORE_OPTIONS BootOptions 1298 /* , IN PULONG BootOptionsLength */ ) 1299 { 1300 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1301 PCWSTR TimeoutStr; 1302 1303 if (!BootStore || !BootOptions) 1304 return STATUS_INVALID_PARAMETER; 1305 1306 /* 1307 * NOTE: Currently we open & map the loader configuration file without 1308 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1309 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1310 * BOOTMGR detection, the "configuration file" will be the BCD registry 1311 * hive and then, we'll have instead to mount the hive & open it. 1312 */ 1313 1314 // 1315 // FIXME!! 1316 // 1317 1318 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1319 if (BootStore->Type != FreeLdr && BootStore->Type != NtLdr) 1320 { 1321 DPRINT1("Loader type %d is currently unsupported!\n", BootStore->Type); 1322 return STATUS_NOT_SUPPORTED; 1323 } 1324 1325 BootOptions->Timeout = 0; 1326 BootOptions->CurrentBootEntryKey = 0; 1327 BootOptions->NextBootEntryKey = 0; 1328 1329 if (IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, 1330 BootOptionNames[BootStore->Type][BO_TimeOut], 1331 &TimeoutStr) && TimeoutStr) 1332 { 1333 BootOptions->Timeout = _wtoi(TimeoutStr); 1334 } 1335 1336 IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, 1337 BootOptionNames[BootStore->Type][BO_DefaultOS], 1338 (PCWSTR*)&BootOptions->NextBootEntryKey); 1339 1340 /* 1341 * NOTE: BootOptions->CurrentBootEntryKey is an informative field only. 1342 * It indicates which boot entry has been selected for starting the 1343 * current OS instance. Such information is NOT stored in the INI file, 1344 * but has to be determined via other means. On UEFI the 'BootCurrent' 1345 * environment variable does that. Otherwise, one could heuristically 1346 * determine it by comparing the boot path and options of each entry 1347 * with those used by the current OS instance. 1348 * Since we currently do not need this information (and it can be costly 1349 * to determine), BootOptions->CurrentBootEntryKey is not evaluated. 1350 */ 1351 1352 return STATUS_SUCCESS; 1353 } 1354 1355 NTSTATUS 1356 SetBootStoreOptions( 1357 IN PVOID Handle, 1358 IN PBOOT_STORE_OPTIONS BootOptions, 1359 IN ULONG FieldsToChange) 1360 { 1361 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1362 1363 if (!BootStore || !BootOptions) 1364 return STATUS_INVALID_PARAMETER; 1365 1366 /* 1367 * NOTE: Currently we open & map the loader configuration file without 1368 * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini 1369 * and NTLDR's boot.ini files. But as soon as we'll implement support for 1370 * BOOTMGR detection, the "configuration file" will be the BCD registry 1371 * hive and then, we'll have instead to mount the hive & open it. 1372 */ 1373 1374 // 1375 // FIXME!! 1376 // 1377 1378 // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1379 if (BootStore->Type != FreeLdr && BootStore->Type != NtLdr) 1380 { 1381 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1382 return STATUS_NOT_SUPPORTED; 1383 } 1384 1385 // if (BootOptions->Length < sizeof(*BootOptions)) 1386 // return STATUS_INVALID_PARAMETER; 1387 1388 if (FieldsToChange & BOOT_OPTIONS_TIMEOUT) 1389 { 1390 WCHAR TimeoutStr[15]; 1391 RtlStringCchPrintfW(TimeoutStr, ARRAYSIZE(TimeoutStr), L"%d", BootOptions->Timeout); 1392 IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, 1393 BootOptionNames[BootStore->Type][BO_TimeOut], 1394 TimeoutStr); 1395 } 1396 if (FieldsToChange & BOOT_OPTIONS_NEXT_BOOTENTRY_KEY) 1397 { 1398 IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, 1399 BootOptionNames[BootStore->Type][BO_DefaultOS], 1400 (PCWSTR)BootOptions->NextBootEntryKey); 1401 } 1402 1403 return STATUS_SUCCESS; 1404 } 1405 1406 1407 static NTSTATUS 1408 FreeLdrEnumerateBootEntries( 1409 IN PBOOT_STORE_INI_CONTEXT BootStore, 1410 // IN ULONG Flags, // Determine which data to retrieve 1411 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 1412 IN PVOID Parameter OPTIONAL) 1413 { 1414 NTSTATUS Status = STATUS_SUCCESS; 1415 PINICACHEITERATOR Iterator; 1416 PINI_SECTION OsIniSection; 1417 PCWSTR SectionName, KeyData; 1418 UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + 1419 max(sizeof(NTOS_OPTIONS), sizeof(BOOTSECTOR_OPTIONS))]; 1420 PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry; 1421 PWCHAR Buffer; 1422 1423 /* Enumerate all the valid installations listed in the "Operating Systems" section */ 1424 Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData); 1425 if (!Iterator) return STATUS_SUCCESS; 1426 do 1427 { 1428 PCWSTR InstallName; 1429 ULONG InstallNameLength; 1430 1431 /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */ 1432 if (*KeyData == L'"') 1433 { 1434 /* Quoted name, copy up to the closing quote */ 1435 PWCHAR End = wcschr(KeyData + 1, L'"'); 1436 1437 if (End) 1438 { 1439 /* Skip the first quote */ 1440 InstallName = KeyData + 1; 1441 InstallNameLength = End - InstallName; 1442 } 1443 else // if (!End) 1444 { 1445 /* No corresponding closing quote, so we include the first one in the InstallName */ 1446 InstallName = KeyData; 1447 InstallNameLength = wcslen(InstallName); 1448 } 1449 if (InstallNameLength == 0) InstallName = NULL; 1450 } 1451 else 1452 { 1453 /* Non-quoted name, copy everything */ 1454 InstallName = KeyData; 1455 InstallNameLength = wcslen(InstallName); 1456 if (InstallNameLength == 0) InstallName = NULL; 1457 } 1458 1459 /* Allocate the temporary buffer */ 1460 Buffer = NULL; 1461 if (InstallNameLength) 1462 Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, (InstallNameLength + 1) * sizeof(WCHAR)); 1463 if (Buffer) 1464 { 1465 RtlCopyMemory(Buffer, InstallName, InstallNameLength * sizeof(WCHAR)); 1466 Buffer[InstallNameLength] = UNICODE_NULL; 1467 InstallName = Buffer; 1468 } 1469 1470 DPRINT("Boot entry '%S' in OS section '%S'\n", InstallName, SectionName); 1471 1472 BootEntry->Version = FreeLdr; 1473 BootEntry->BootEntryKey = MAKESTRKEY(SectionName); 1474 BootEntry->FriendlyName = InstallName; 1475 BootEntry->BootFilePath = NULL; 1476 BootEntry->OsOptionsLength = 0; 1477 1478 /* Search for an existing boot entry section */ 1479 OsIniSection = IniGetSection(BootStore->IniCache, SectionName); 1480 if (!OsIniSection) 1481 goto DoEnum; 1482 1483 /* Check for supported boot type */ 1484 if (!IniGetKey(OsIniSection, L"BootType", &KeyData) || !KeyData) 1485 { 1486 /* Certainly not a ReactOS installation */ 1487 DPRINT1("No BootType value present\n"); 1488 goto DoEnum; 1489 } 1490 1491 // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? 1492 if ((_wcsicmp(KeyData, L"Windows2003") == 0) || 1493 (_wcsicmp(KeyData, L"\"Windows2003\"") == 0)) 1494 { 1495 /* BootType is Windows2003 */ 1496 PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; 1497 1498 DPRINT("This is a '%S' boot entry\n", KeyData); 1499 1500 BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS); 1501 RtlCopyMemory(Options->Signature, 1502 NTOS_OPTIONS_SIGNATURE, 1503 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)); 1504 1505 // BootEntry->BootFilePath = NULL; 1506 1507 /* Check its SystemPath */ 1508 Options->OsLoadPath = NULL; 1509 if (IniGetKey(OsIniSection, L"SystemPath", &KeyData)) 1510 Options->OsLoadPath = KeyData; 1511 // KeyData == SystemRoot; 1512 1513 /* Check the optional Options */ 1514 Options->OsLoadOptions = NULL; 1515 if (IniGetKey(OsIniSection, L"Options", &KeyData)) 1516 Options->OsLoadOptions = KeyData; 1517 } 1518 else 1519 if ((_wcsicmp(KeyData, L"BootSector") == 0) || 1520 (_wcsicmp(KeyData, L"\"BootSector\"") == 0)) 1521 { 1522 /* BootType is BootSector */ 1523 PBOOTSECTOR_OPTIONS Options = (PBOOTSECTOR_OPTIONS)&BootEntry->OsOptions; 1524 1525 DPRINT("This is a '%S' boot entry\n", KeyData); 1526 1527 BootEntry->OsOptionsLength = sizeof(BOOTSECTOR_OPTIONS); 1528 RtlCopyMemory(Options->Signature, 1529 BOOTSECTOR_OPTIONS_SIGNATURE, 1530 RTL_FIELD_SIZE(BOOTSECTOR_OPTIONS, Signature)); 1531 1532 // BootEntry->BootFilePath = NULL; 1533 1534 /* Check its BootPath */ 1535 Options->BootPath = NULL; 1536 if (IniGetKey(OsIniSection, L"BootPath", &KeyData)) 1537 Options->BootPath = KeyData; 1538 1539 /* Check its BootSector */ 1540 Options->FileName = NULL; 1541 if (IniGetKey(OsIniSection, L"BootSectorFile", &KeyData)) 1542 Options->FileName = KeyData; 1543 } 1544 else 1545 { 1546 DPRINT1("Unrecognized BootType value '%S'\n", KeyData); 1547 // goto DoEnum; 1548 } 1549 1550 DoEnum: 1551 /* Call the user enumeration routine callback */ 1552 Status = EnumBootEntriesRoutine(FreeLdr, BootEntry, Parameter); 1553 1554 /* Free temporary buffers */ 1555 if (Buffer) 1556 RtlFreeHeap(ProcessHeap, 0, Buffer); 1557 1558 /* Stop the enumeration if needed */ 1559 if (!NT_SUCCESS(Status)) 1560 break; 1561 } 1562 while (IniFindNextValue(Iterator, &SectionName, &KeyData)); 1563 1564 IniFindClose(Iterator); 1565 return Status; 1566 } 1567 1568 static NTSTATUS 1569 NtLdrEnumerateBootEntries( 1570 IN PBOOT_STORE_INI_CONTEXT BootStore, 1571 // IN ULONG Flags, // Determine which data to retrieve 1572 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 1573 IN PVOID Parameter OPTIONAL) 1574 { 1575 NTSTATUS Status = STATUS_SUCCESS; 1576 PINICACHEITERATOR Iterator; 1577 PCWSTR SectionName, KeyData; 1578 UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)]; 1579 PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry; 1580 PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; 1581 PWCHAR Buffer; 1582 ULONG BufferLength; 1583 1584 /* Enumerate all the valid installations */ 1585 Iterator = IniFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData); 1586 if (!Iterator) return STATUS_SUCCESS; 1587 do 1588 { 1589 PCWSTR InstallName, OsOptions; 1590 ULONG InstallNameLength, OsOptionsLength; 1591 1592 /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */ 1593 if (*KeyData == L'"') 1594 { 1595 /* Quoted name, copy up to the closing quote */ 1596 OsOptions = wcschr(KeyData + 1, L'"'); 1597 1598 /* Retrieve the starting point of the installation name and the OS options */ 1599 if (OsOptions) 1600 { 1601 /* Skip the first quote */ 1602 InstallName = KeyData + 1; 1603 InstallNameLength = OsOptions - InstallName; 1604 if (InstallNameLength == 0) InstallName = NULL; 1605 1606 /* Skip the ending quote (if found) */ 1607 ++OsOptions; 1608 1609 /* Skip any whitespace */ 1610 while (iswspace(*OsOptions)) ++OsOptions; 1611 /* Get its final length */ 1612 OsOptionsLength = wcslen(OsOptions); 1613 if (OsOptionsLength == 0) OsOptions = NULL; 1614 } 1615 else 1616 { 1617 /* No corresponding closing quote, so we include the first one in the InstallName */ 1618 InstallName = KeyData; 1619 InstallNameLength = wcslen(InstallName); 1620 if (InstallNameLength == 0) InstallName = NULL; 1621 1622 /* There are no OS options */ 1623 // OsOptions = NULL; 1624 OsOptionsLength = 0; 1625 } 1626 } 1627 else 1628 { 1629 /* Non-quoted name, copy everything */ 1630 1631 /* Retrieve the starting point of the installation name */ 1632 InstallName = KeyData; 1633 InstallNameLength = wcslen(InstallName); 1634 if (InstallNameLength == 0) InstallName = NULL; 1635 1636 /* There are no OS options */ 1637 OsOptions = NULL; 1638 OsOptionsLength = 0; 1639 } 1640 1641 /* Allocate the temporary buffer */ 1642 Buffer = NULL; 1643 BufferLength = (InstallNameLength + OsOptionsLength) * sizeof(WCHAR); 1644 if (BufferLength) 1645 Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength + 2*sizeof(UNICODE_NULL)); 1646 if (Buffer) 1647 { 1648 PWCHAR ptr; 1649 1650 /* Copy the installation name, and make InstallName point into the buffer */ 1651 if (InstallName && InstallNameLength) 1652 { 1653 ptr = Buffer; 1654 RtlCopyMemory(ptr, InstallName, InstallNameLength * sizeof(WCHAR)); 1655 ptr[InstallNameLength] = UNICODE_NULL; 1656 InstallName = ptr; 1657 } 1658 1659 /* Copy the OS options, and make OsOptions point into the buffer */ 1660 if (OsOptions && OsOptionsLength) 1661 { 1662 ptr = Buffer + InstallNameLength + 1; 1663 RtlCopyMemory(ptr, OsOptions, OsOptionsLength * sizeof(WCHAR)); 1664 ptr[OsOptionsLength] = UNICODE_NULL; 1665 OsOptions = ptr; 1666 } 1667 } 1668 1669 DPRINT1("Boot entry '%S' in OS section (path) '%S'\n", InstallName, SectionName); 1670 // SectionName == SystemRoot; 1671 1672 BootEntry->Version = NtLdr; 1673 BootEntry->BootEntryKey = 0; // FIXME?? 1674 BootEntry->FriendlyName = InstallName; 1675 BootEntry->BootFilePath = NULL; 1676 1677 BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS); 1678 RtlCopyMemory(Options->Signature, 1679 NTOS_OPTIONS_SIGNATURE, 1680 RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)); 1681 1682 Options->OsLoadPath = SectionName; 1683 Options->OsLoadOptions = OsOptions; 1684 1685 /* Call the user enumeration routine callback */ 1686 Status = EnumBootEntriesRoutine(NtLdr, BootEntry, Parameter); 1687 1688 /* Free temporary buffers */ 1689 if (Buffer) 1690 RtlFreeHeap(ProcessHeap, 0, Buffer); 1691 1692 /* Stop the enumeration if needed */ 1693 if (!NT_SUCCESS(Status)) 1694 break; 1695 } 1696 while (IniFindNextValue(Iterator, &SectionName, &KeyData)); 1697 1698 IniFindClose(Iterator); 1699 return Status; 1700 } 1701 1702 NTSTATUS 1703 EnumerateBootStoreEntries( 1704 IN PVOID Handle, 1705 // IN ULONG Flags, // Determine which data to retrieve 1706 IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, 1707 IN PVOID Parameter OPTIONAL) 1708 { 1709 PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; 1710 1711 if (!BootStore) 1712 return STATUS_INVALID_PARAMETER; 1713 1714 if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) 1715 { 1716 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); 1717 /**/return STATUS_SUCCESS;/**/ 1718 // return STATUS_INVALID_PARAMETER; 1719 } 1720 1721 return NtosBootLoaders[BootStore->Type].EnumBootStoreEntries( 1722 (PBOOT_STORE_INI_CONTEXT)BootStore, // Flags, 1723 EnumBootEntriesRoutine, Parameter); 1724 } 1725 1726 /* EOF */ 1727