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