diff --git a/base/setup/lib/bldrsup.c b/base/setup/lib/bldrsup.c index 13a1ed34e87..80a951a6e18 100644 --- a/base/setup/lib/bldrsup.c +++ b/base/setup/lib/bldrsup.c @@ -22,79 +22,180 @@ /* GLOBALS ******************************************************************/ +typedef NTSTATUS +(*POPEN_BOOT_STORE)( + OUT PVOID* Handle, + IN HANDLE PartitionDirectoryHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew); + +typedef NTSTATUS +(*PCLOSE_BOOT_STORE)( + IN PVOID Handle); + +typedef NTSTATUS +(*PENUM_BOOT_STORE_ENTRIES)( + IN PVOID Handle, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL); + typedef struct _NTOS_BOOT_LOADER_FILES { NTOS_BOOT_LOADER_TYPE Type; - PCWSTR LoaderExecutable; + PCZZWSTR LoaderExecutables; PCWSTR LoaderConfigurationFile; - // EnumerateInstallations; + POPEN_BOOT_STORE OpenBootStore; + PCLOSE_BOOT_STORE CloseBootStore; + PENUM_BOOT_STORE_ENTRIES EnumBootStoreEntries; } NTOS_BOOT_LOADER_FILES, *PNTOS_BOOT_LOADER_FILES; + +/* + * Header for particular store contexts + */ +typedef struct _BOOT_STORE_CONTEXT +{ + NTOS_BOOT_LOADER_TYPE Type; +// PNTOS_BOOT_LOADER_FILES ?? +/* + PVOID PrivateData; +*/ +} BOOT_STORE_CONTEXT, *PBOOT_STORE_CONTEXT; + +typedef struct _BOOT_STORE_INI_CONTEXT +{ + BOOT_STORE_CONTEXT Header; + + /* + * If all these members are NULL, we know that the store is freshly created + * and is cached in memory only. At file closure we will therefore need to + * create the file proper and save its contents. + */ + HANDLE FileHandle; + HANDLE SectionHandle; + // SIZE_T ViewSize; + ULONG FileSize; + PVOID ViewBase; + + PINICACHE IniCache; + PINICACHESECTION OptionsIniSection; + PINICACHESECTION OsIniSection; +} BOOT_STORE_INI_CONTEXT, *PBOOT_STORE_INI_CONTEXT; + +// TODO! +typedef struct _BOOT_STORE_BCDREG_CONTEXT +{ + BOOT_STORE_CONTEXT Header; + ULONG PlaceHolder; +} BOOT_STORE_BCDREG_CONTEXT, *PBOOT_STORE_BCDREG_CONTEXT; + + +static NTSTATUS +OpenIniBootLoaderStore( + OUT PVOID* Handle, + IN HANDLE PartitionDirectoryHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew); + +static NTSTATUS +CloseIniBootLoaderStore( + IN PVOID Handle); + +static NTSTATUS +FreeLdrEnumerateBootEntries( + IN PBOOT_STORE_INI_CONTEXT BootStore, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL); + +static NTSTATUS +NtLdrEnumerateBootEntries( + IN PBOOT_STORE_INI_CONTEXT BootStore, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL); + + // Question 1: What if config file is optional? // Question 2: What if many config files are possible? NTOS_BOOT_LOADER_FILES NtosBootLoaders[] = { - {FreeLdr, L"freeldr.sys", L"freeldr.ini"}, - {NtLdr , L"ntldr" , L"boot.ini"}, // FIXME: What about osloader.exe, etc...? -// {NtLdr , L"setupldr" , L"txtsetup.sif"}, // FIXME -// {BootMgr, L"bootmgr" , L"BCD"} + {FreeLdr, L"freeldr.sys\0", L"freeldr.ini", + OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)FreeLdrEnumerateBootEntries}, + {NtLdr , L"ntldr\0" L"osloader.exe\0", L"boot.ini", + OpenIniBootLoaderStore, CloseIniBootLoaderStore, (PENUM_BOOT_STORE_ENTRIES)NtLdrEnumerateBootEntries }, +// {SetupLdr, L"setupldr\0" L"setupldr.bin\0" L"setupldr.exe\0", L"txtsetup.sif", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED} +// {BootMgr , L"bootmgr", L"BCD", UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED} }; +C_ASSERT(_countof(NtosBootLoaders) == BldrTypeMax); /* FUNCTIONS ****************************************************************/ -// -// We need, for each type of bootloader (FreeLdr, NtLdr, Bootmgr): -// 1. A function that detects its presence and its version; -// 2. A function that opens/closes its corresponding configuration file; -// 3. A function that adds a new boot entry. Note that for the first two BLDRs -// this is a .INI file, while in the latter case this is a registry hive... -// - NTSTATUS FindNTOSBootLoader( // By handle - IN HANDLE PartitionHandle, // OPTIONAL + IN HANDLE PartitionDirectoryHandle, // OPTIONAL IN NTOS_BOOT_LOADER_TYPE Type, OUT PULONG Version OPTIONAL) // OUT PHANDLE ConfigFileHande OPTIONAL ???? { + PCWSTR LoaderExecutable; // UINT i; if (Type >= BldrTypeMax) return STATUS_INVALID_PARAMETER; - // FIXME: Unused for now, but should be used later!! - *Version = 0; - // TODO: Check for BLDR version ONLY if Version != NULL + if (Version) + *Version = 0; - /* Check whether the loader executable exists */ - if (!DoesFileExist(PartitionHandle, NtosBootLoaders[Type].LoaderExecutable)) + /* Check whether any of the loader executables exist */ + LoaderExecutable = NtosBootLoaders[Type].LoaderExecutables; + while (*LoaderExecutable) { + if (DoesFileExist(PartitionDirectoryHandle, LoaderExecutable)) + { + /* A loader was found, stop there */ + DPRINT1("Found loader executable '%S'\n", LoaderExecutable); + break; + } + /* The loader does not exist, continue with another one */ - // DPRINT1("Loader executable '%S' does not exist, continue with another one...\n", NtosBootLoaders[Type].LoaderExecutable); - DPRINT1("Loader executable '%S' does not exist\n", NtosBootLoaders[Type].LoaderExecutable); + DPRINT1("Loader executable '%S' does not exist, continue with another one...\n", LoaderExecutable); + LoaderExecutable += wcslen(LoaderExecutable) + 1; + } + if (!*LoaderExecutable) + { + /* No loader was found */ + DPRINT1("No loader executable was found\n"); return STATUS_NOT_FOUND; } + /* Check for loader version if needed */ + if (Version) + { + *Version = 0; + // TODO: Check for BLDR version ONLY if Version != NULL + } + /* Check whether the loader configuration file exists */ - if (!DoesFileExist(PartitionHandle, NtosBootLoaders[Type].LoaderConfigurationFile)) + if (!DoesFileExist(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile)) { /* The loader does not exist, continue with another one */ // FIXME: Consider it might be optional?? - // DPRINT1("Loader configuration file '%S' does not exist, continue with another one...\n", NtosBootLoaders[Type].LoaderConfigurationFile); DPRINT1("Loader configuration file '%S' does not exist\n", NtosBootLoaders[Type].LoaderConfigurationFile); return STATUS_NOT_FOUND; } #if 0 /* Check whether the loader configuration file exists */ - Status = OpenAndMapFile(PartitionHandle, NtosBootLoaders[Type].LoaderConfigurationFile, + Status = OpenAndMapFile(PartitionDirectoryHandle, NtosBootLoaders[Type].LoaderConfigurationFile, &FileHandle, &SectionHandle, &ViewBase, &FileSize, FALSE); if (!NT_SUCCESS(Status)) { /* The loader does not exist, continue with another one */ // FIXME: Consider it might be optional?? - DPRINT1("Loader configuration file '%S' does not exist, continue with another one...\n", NtosBootLoaders[Type].LoaderConfigurationFile); + DPRINT1("Loader configuration file '%S' does not exist\n", NtosBootLoaders[Type].LoaderConfigurationFile); return STATUS_NOT_FOUND; } #endif @@ -103,206 +204,572 @@ FindNTOSBootLoader( // By handle } -static NTSTATUS -FreeLdrEnumerateBootEntries( - IN PCHAR FileBuffer, - IN ULONG FileLength, -// IN ULONG Flags, // Determine which data to retrieve - IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, - IN PVOID Parameter OPTIONAL) +static VOID +CreateCommonFreeLdrSections( + IN OUT PBOOT_STORE_INI_CONTEXT BootStore) { - NTSTATUS Status; - PINICACHE IniCache; - PINICACHEITERATOR Iterator; - PINICACHESECTION IniSection, OsIniSection; - PWCHAR SectionName, KeyData; -/**/NTOS_BOOT_ENTRY xxBootEntry;/**/ - PNTOS_BOOT_ENTRY BootEntry = &xxBootEntry; - UNICODE_STRING InstallName; + PINICACHESECTION IniSection; - /* Open an *existing* FreeLdr.ini configuration file */ - Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); - if (!NT_SUCCESS(Status)) - return Status; + /* + * Cache the "FREELOADER" section for our future usage. + */ - /* Get the "Operating Systems" section */ - IniSection = IniCacheGetSection(IniCache, L"Operating Systems"); - if (IniSection == NULL) + /* Get the "FREELOADER" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"FREELOADER"); + if (!IniSection) { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"FREELOADER"); + if (!IniSection) + { + DPRINT1("CreateCommonFreeLdrSections: Failed to create 'FREELOADER' section!\n"); + } } - /* Enumerate all the valid installations */ - Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); - if (!Iterator) goto Quit; - do + BootStore->OptionsIniSection = IniSection; + + /* Timeout=0 */ + IniCacheInsertKey(BootStore->OptionsIniSection, NULL, INSERT_LAST, + // L"TimeOut", L"0"); // FIXME!! There is a bug in the INI parser where a given key can be inserted twice in the same section... + L"TimeOut", L"10"); + + /* Create "Display" section */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"Display"); + + /* TitleText=ReactOS Boot Manager */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"TitleText", L"ReactOS Boot Manager"); + + /* StatusBarColor=Cyan */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"StatusBarColor", L"Cyan"); + + /* StatusBarTextColor=Black */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"StatusBarTextColor", L"Black"); + + /* BackdropTextColor=White */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BackdropTextColor", L"White"); + + /* BackdropColor=Blue */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BackdropColor", L"Blue"); + + /* BackdropFillStyle=Medium */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BackdropFillStyle", L"Medium"); + + /* TitleBoxTextColor=White */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"TitleBoxTextColor", L"White"); + + /* TitleBoxColor=Red */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"TitleBoxColor", L"Red"); + + /* MessageBoxTextColor=White */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MessageBoxTextColor", L"White"); + + /* MessageBoxColor=Blue */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MessageBoxColor", L"Blue"); + + /* MenuTextColor=White */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MenuTextColor", L"Gray"); + + /* MenuColor=Blue */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MenuColor", L"Black"); + + /* TextColor=Yellow */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"TextColor", L"Gray"); + + /* SelectedTextColor=Black */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"SelectedTextColor", L"Black"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"SelectedColor", L"Gray"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"ShowTime", L"No"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MenuBox", L"No"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"CenterMenu", L"No"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"MinimalUI", L"Yes"); + + /* SelectedColor=Gray */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"TimeText", + L"Seconds until highlighted choice will be started automatically: "); + + /* + * Cache the "Operating Systems" section for our future usage. + */ + + /* Get the "Operating Systems" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) { - // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni). - if (KeyData[0] == L'"') + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) { - /* Quoted name, copy up to the closing quote */ - PWCHAR Begin = &KeyData[1]; - PWCHAR End = wcschr(Begin, L'"'); - if (!End) - End = Begin + wcslen(Begin); - RtlInitEmptyUnicodeString(&InstallName, Begin, (ULONG_PTR)End - (ULONG_PTR)Begin); - InstallName.Length = InstallName.MaximumLength; + DPRINT1("CreateCommonFreeLdrSections: Failed to create 'Operating Systems' section!\n"); } - else + } + + BootStore->OsIniSection = IniSection; +} + +static NTSTATUS +OpenIniBootLoaderStore( + OUT PVOID* Handle, + IN HANDLE PartitionDirectoryHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew) +{ + NTSTATUS Status; + PBOOT_STORE_INI_CONTEXT BootStore; + + /* Create a boot store structure */ + BootStore = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*BootStore)); + if (!BootStore) + return STATUS_INSUFFICIENT_RESOURCES; + + BootStore->Header.Type = Type; + + if (CreateNew) + { + UNICODE_STRING Name; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + + // + // WARNING! We "support" the INI creation *ONLY* for FreeLdr, and not for NTLDR!! + // + if (Type == NtLdr) { - /* Non-quoted name, copy everything */ - RtlInitUnicodeString(&InstallName, KeyData); + DPRINT1("OpenIniBootLoaderStore() unsupported for NTLDR!\n"); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return STATUS_NOT_SUPPORTED; } - DPRINT1("Boot entry '%wZ' in OS section '%S'\n", &InstallName, SectionName); - - /* Search for an existing ReactOS entry */ - OsIniSection = IniCacheGetSection(IniCache, SectionName); - if (!OsIniSection) - continue; - - /* Check for supported boot type "Windows2003" */ - Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData); - if (NT_SUCCESS(Status)) + /* Initialize the INI file */ + BootStore->IniCache = IniCacheCreate(); + if (!BootStore->IniCache) { - // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? - if ((KeyData == NULL) || - ( (_wcsicmp(KeyData, L"Windows2003") != 0) && - (_wcsicmp(KeyData, L"\"Windows2003\"") != 0) )) - { - /* This is not a ReactOS entry */ - continue; - } - } - else - { - /* Certainly not a ReactOS installation */ - continue; + DPRINT1("IniCacheCreate() failed.\n"); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return STATUS_INSUFFICIENT_RESOURCES; } - /* BootType is Windows2003. Now check its SystemPath. */ - Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData); + /* + * So far, we only use the INI cache. The file itself is not created + * yet, therefore FileHandle, SectionHandle, ViewBase and FileSize + * are all NULL. We will use this fact to know that the INI file was + * indeed created, and not just opened as an existing file. + */ + // BootStore->FileHandle = NULL; + BootStore->SectionHandle = NULL; + BootStore->ViewBase = NULL; + BootStore->FileSize = 0; + + /* + * The INI file is fresh new, we need to create it now. + */ + + RtlInitUnicodeString(&Name, NtosBootLoaders[Type].LoaderConfigurationFile); + + InitializeObjectAttributes(&ObjectAttributes, + &Name, + OBJ_CASE_INSENSITIVE, + PartitionDirectoryHandle, + NULL); + + Status = NtCreateFile(&BootStore->FileHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, // Contains SYNCHRONIZE + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_SUPERSEDE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE, + NULL, + 0); if (!NT_SUCCESS(Status)) { - DPRINT1(" A Win2k3 install '%wZ' without an ARC path?!\n", &InstallName); - continue; + DPRINT1("NtCreateFile() failed (Status 0x%08lx)\n", Status); + IniCacheDestroy(BootStore->IniCache); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return Status; } - DPRINT1(" Found a candidate Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName, KeyData); - // KeyData == SystemRoot; - - BootEntry->FriendlyName = &InstallName; - BootEntry->OsLoadPath = KeyData; - /* Unused stuff (for now...) */ - BootEntry->BootFilePath = NULL; - BootEntry->OsOptions = NULL; - BootEntry->OsLoadOptions = NULL; - - Status = EnumBootEntriesRoutine(FreeLdr, BootEntry, Parameter); - // TODO: Stop enumeration if !NT_SUCCESS(Status); + /* Initialize the INI file contents */ + if (Type == FreeLdr) + CreateCommonFreeLdrSections(BootStore); } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + else + { + PINICACHESECTION IniSection; - IniCacheFindClose(Iterator); + /* + * Check whether the loader configuration INI file exists, + * and open it if so. + * TODO: FIXME: What if it doesn't exist yet??? + */ + Status = OpenAndMapFile(PartitionDirectoryHandle, + NtosBootLoaders[Type].LoaderConfigurationFile, + &BootStore->FileHandle, + &BootStore->SectionHandle, + &BootStore->ViewBase, + &BootStore->FileSize, + TRUE); + if (!NT_SUCCESS(Status)) + { + /* The loader configuration file does not exist */ + // FIXME: Consider it might be optional?? + DPRINT1("Loader configuration file '%S' does not exist (Status 0x%08lx)\n", + NtosBootLoaders[Type].LoaderConfigurationFile, Status); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return Status; + } -Quit: - IniCacheDestroy(IniCache); + /* Open an *existing* INI configuration file */ + // Status = IniCacheLoad(&BootStore->IniCache, NtosBootLoaders[Type].LoaderConfigurationFile, FALSE); + Status = IniCacheLoadFromMemory(&BootStore->IniCache, + BootStore->ViewBase, + BootStore->FileSize, + FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("IniCacheLoadFromMemory() failed (Status 0x%08lx)\n", Status); + + /* Finally, unmap and close the file */ + UnMapFile(BootStore->SectionHandle, BootStore->ViewBase); + NtClose(BootStore->FileHandle); + + RtlFreeHeap(ProcessHeap, 0, BootStore); + return Status; + } + + if (Type == FreeLdr) + { + /* + * Cache the "FREELOADER" section for our future usage. + */ + + /* Get the "FREELOADER" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"FREELOADER"); + if (!IniSection) + { + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"FREELOADER"); + if (!IniSection) + { + DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'FREELOADER' section!\n"); + } + } + + BootStore->OptionsIniSection = IniSection; + + /* + * Cache the "Operating Systems" section for our future usage. + */ + + /* Get the "Operating Systems" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) + { + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) + { + DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'Operating Systems' section!\n"); + } + } + + BootStore->OsIniSection = IniSection; + } + else + if (Type == NtLdr) + { + /* + * Cache the "boot loader" section for our future usage. + */ + /* + * HISTORICAL NOTE: + * + * While the "operating systems" section acquired its definitive + * name already when Windows NT was at its very early beta stage + * (NT 3.1 October 1991 Beta, 10-16-1991), this was not the case + * for its general settings section "boot loader". + * + * The following section names were successively introduced: + * + * - In NT 3.1 October 1991 Beta, 10-16-1991, using OS Loader V1.5, + * the section was named "multiboot". + * + * - In the next public beta version NT 3.10.340 Beta, 10-12-1992, + * using OS Loader V2.10, a new name was introduced: "flexboot". + * This is around this time that the NT OS Loader was also + * introduced as the "Windows NT FlexBoot" loader, as shown by + * the Windows NT FAQs that circulated around this time: + * http://cd.textfiles.com/cica9308/CIS_LIBS/WINNT/1/NTFAQ.TXT + * http://cd.textfiles.com/cica/cica9308/UNZIPPED/NT/NTFAQ/FTP/NEWS/NTFAQ1.TXT + * I can only hypothesize that the "FlexBoot" name was chosen + * as a marketing coup, possibly to emphasise its "flexibility" + * as a simple multiboot-aware boot manager. + * + * - A bit later, with NT 3.10.404 Beta, 3-7-1993, using an updated + * version of OS Loader V2.10, the final section name "boot loader" + * was introduced, and was kept since then. + * + * Due to the necessity to be able to boot and / or upgrade any + * Windows NT version at any time, including its NT Loader and the + * associated boot.ini file, all versions of NTLDR and the NT installer + * understand and parse these three section names, the default one + * being "boot loader", and if not present, they successively fall + * back to "flexboot" and then to "multiboot". + */ + + /* Get the "boot loader" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"boot loader"); + if (!IniSection) + { + /* Fall back to "flexboot" */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"flexboot"); + if (!IniSection) + { + /* Fall back to "multiboot" */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"multiboot"); + } + } +#if 0 + if (!IniSection) + { + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"boot loader"); + if (!IniSection) + { + DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'boot loader' section!\n"); + } + } +#endif + + BootStore->OptionsIniSection = IniSection; + + /* + * Cache the "Operating Systems" section for our future usage. + */ + + /* Get the "Operating Systems" section */ + IniSection = IniCacheGetSection(BootStore->IniCache, L"operating systems"); + if (!IniSection) + { +#if 0 + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"operating systems"); + if (!IniSection) + { + DPRINT1("OpenIniBootLoaderStore: Failed to retrieve 'operating systems' section!\n"); + } +#endif + } + + BootStore->OsIniSection = IniSection; + } + } + + *Handle = BootStore; return STATUS_SUCCESS; } static NTSTATUS -NtLdrEnumerateBootEntries( - IN PCHAR FileBuffer, - IN ULONG FileLength, -// IN ULONG Flags, // Determine which data to retrieve - IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, - IN PVOID Parameter OPTIONAL) +UnprotectBootIni( + IN HANDLE FileHandle, + OUT PULONG Attributes) { NTSTATUS Status; - PINICACHE IniCache; - PINICACHEITERATOR Iterator; - PINICACHESECTION IniSection; - PWCHAR SectionName, KeyData; -/**/NTOS_BOOT_ENTRY xxBootEntry;/**/ - PNTOS_BOOT_ENTRY BootEntry = &xxBootEntry; - UNICODE_STRING InstallName; + IO_STATUS_BLOCK IoStatusBlock; + FILE_BASIC_INFORMATION FileInfo; - /* Open an *existing* boot.ini configuration file */ - Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); + Status = NtQueryInformationFile(FileHandle, + &IoStatusBlock, + &FileInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); if (!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status); return Status; - - /* Get the "Operating Systems" section */ - IniSection = IniCacheGetSection(IniCache, L"operating systems"); - if (IniSection == NULL) - { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; } - /* Enumerate all the valid installations */ - Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); - if (!Iterator) goto Quit; - do + *Attributes = FileInfo.FileAttributes; + + /* Delete attributes SYSTEM, HIDDEN and READONLY */ + FileInfo.FileAttributes = FileInfo.FileAttributes & + ~(FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_READONLY); + + Status = NtSetInformationFile(FileHandle, + &IoStatusBlock, + &FileInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + DPRINT1("NtSetInformationFile() failed (Status 0x%08lx)\n", Status); + + return Status; +} + +static NTSTATUS +ProtectBootIni( + IN HANDLE FileHandle, + IN ULONG Attributes) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + FILE_BASIC_INFORMATION FileInfo; + + Status = NtQueryInformationFile(FileHandle, + &IoStatusBlock, + &FileInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) { - // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni). - if (KeyData[0] == L'"') - { - /* Quoted name, copy up to the closing quote */ - PWCHAR Begin = &KeyData[1]; - PWCHAR End = wcschr(Begin, L'"'); - if (!End) - End = Begin + wcslen(Begin); - RtlInitEmptyUnicodeString(&InstallName, Begin, (ULONG_PTR)End - (ULONG_PTR)Begin); - InstallName.Length = InstallName.MaximumLength; - } - else - { - /* Non-quoted name, copy everything */ - RtlInitUnicodeString(&InstallName, KeyData); - } - - DPRINT1("Boot entry '%wZ' in OS section '%S'\n", &InstallName, SectionName); - - DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName, SectionName); - // SectionName == SystemRoot; - - BootEntry->FriendlyName = &InstallName; - BootEntry->OsLoadPath = SectionName; - /* Unused stuff (for now...) */ - BootEntry->BootFilePath = NULL; - BootEntry->OsOptions = NULL; - BootEntry->OsLoadOptions = NULL; - - Status = EnumBootEntriesRoutine(NtLdr, BootEntry, Parameter); - // TODO: Stop enumeration if !NT_SUCCESS(Status); + DPRINT1("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status); + return Status; } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); - IniCacheFindClose(Iterator); + FileInfo.FileAttributes = FileInfo.FileAttributes | Attributes; + + Status = NtSetInformationFile(FileHandle, + &IoStatusBlock, + &FileInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + DPRINT1("NtSetInformationFile() failed (Status 0x%08lx)\n", Status); + + return Status; +} + +static NTSTATUS +CloseIniBootLoaderStore( + IN PVOID Handle) +{ + NTSTATUS Status; + PBOOT_STORE_INI_CONTEXT BootStore = (PBOOT_STORE_INI_CONTEXT)Handle; + ULONG FileAttribute = 0; + + // if (!BootStore) + // return STATUS_INVALID_PARAMETER; + + if (BootStore->SectionHandle) + { + /* + * The INI file was already opened because it already existed, + * thus (in the case of NTLDR's boot.ini), unprotect it. + */ + if (BootStore->Header.Type == NtLdr) + { + Status = UnprotectBootIni(BootStore->FileHandle, &FileAttribute); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Could not unprotect BOOT.INI ! (Status 0x%08lx)\n", Status); + goto Quit; + } + } + } + + IniCacheSaveByHandle(BootStore->IniCache, BootStore->FileHandle); + + /* In the case of NTLDR's boot.ini, re-protect the INI file */ + if (BootStore->Header.Type == NtLdr) + { + FileAttribute |= (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); + Status = ProtectBootIni(BootStore->FileHandle, FileAttribute); + } Quit: - IniCacheDestroy(IniCache); + IniCacheDestroy(BootStore->IniCache); + + if (BootStore->SectionHandle) + { + /* Finally, unmap and close the file */ + UnMapFile(BootStore->SectionHandle, BootStore->ViewBase); + NtClose(BootStore->FileHandle); + } + else // if (BootStore->FileHandle) + { + /* Just close the file we have opened for creation */ + NtClose(BootStore->FileHandle); + } + + /* Finally, free the boot store structure */ + RtlFreeHeap(ProcessHeap, 0, BootStore); + + // TODO: Use a correct Status based on the return values of the previous functions... return STATUS_SUCCESS; } -// This function may be viewed as being similar to ntos:NtEnumerateBootEntries(). NTSTATUS -EnumerateNTOSBootEntries( - IN HANDLE PartitionHandle, // OPTIONAL +OpenNTOSBootLoaderStoreByHandle( + OUT PVOID* Handle, + IN HANDLE PartitionDirectoryHandle, // OPTIONAL IN NTOS_BOOT_LOADER_TYPE Type, -// IN ULONG Flags, // Determine which data to retrieve - IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, - IN PVOID Parameter OPTIONAL) + IN BOOLEAN CreateNew) +{ + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type); + return STATUS_NOT_SUPPORTED; + } + + return NtosBootLoaders[Type].OpenBootStore(Handle, + PartitionDirectoryHandle, + Type, + CreateNew); +} + +NTSTATUS +OpenNTOSBootLoaderStore_UStr( + OUT PVOID* Handle, + IN PUNICODE_STRING SystemPartitionPath, + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew) { NTSTATUS Status; - HANDLE FileHandle; - HANDLE SectionHandle; - // SIZE_T ViewSize; - ULONG FileSize; - PVOID ViewBase; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE PartitionDirectoryHandle; /* * NOTE: Currently we open & map the loader configuration file without @@ -312,41 +779,789 @@ EnumerateNTOSBootEntries( * hive and then, we'll have instead to mount the hive & open it. */ - /* Check whether the loader configuration file exists */ - Status = OpenAndMapFile(PartitionHandle, NtosBootLoaders[Type].LoaderConfigurationFile, - &FileHandle, &SectionHandle, &ViewBase, &FileSize, FALSE); + if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type); + return STATUS_NOT_SUPPORTED; + } + + /* Open SystemPartition */ + InitializeObjectAttributes(&ObjectAttributes, + SystemPartitionPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenFile(&PartitionDirectoryHandle, + FILE_LIST_DIRECTORY | FILE_ADD_FILE /* | FILE_ADD_SUBDIRECTORY | FILE_TRAVERSE*/ | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE /* | FILE_OPEN_FOR_BACKUP_INTENT */); if (!NT_SUCCESS(Status)) { - /* The loader does not exist, continue with another one */ - // FIXME: Consider it might be optional?? - // DPRINT1("Loader configuration file '%S' does not exist, continue with another one...\n", NtosBootLoaders[Type].LoaderConfigurationFile); - DPRINT1("Loader configuration file '%S' does not exist\n", NtosBootLoaders[Type].LoaderConfigurationFile); + DPRINT1("Failed to open SystemPartition '%wZ', Status 0x%08lx\n", SystemPartitionPath, Status); return Status; } - /* The loader configuration file exists, interpret it to find valid installations */ - switch (NtosBootLoaders[Type].Type) - { - case FreeLdr: - Status = FreeLdrEnumerateBootEntries(ViewBase, FileSize, /* Flags, */ - EnumBootEntriesRoutine, Parameter); - break; - - case NtLdr: - Status = NtLdrEnumerateBootEntries(ViewBase, FileSize, /* Flags, */ - EnumBootEntriesRoutine, Parameter); - break; - - default: - DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[Type].Type); - Status = STATUS_SUCCESS; - } - - /* Finally, unmap and close the file */ - UnMapFile(SectionHandle, ViewBase); - NtClose(FileHandle); + Status = OpenNTOSBootLoaderStoreByHandle(Handle, PartitionDirectoryHandle, Type, CreateNew); + /* Done! */ + NtClose(PartitionDirectoryHandle); return Status; } +NTSTATUS +OpenNTOSBootLoaderStore( + OUT PVOID* Handle, + IN PCWSTR SystemPartition, + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew) +{ + UNICODE_STRING SystemPartitionPath; + RtlInitUnicodeString(&SystemPartitionPath, SystemPartition); + return OpenNTOSBootLoaderStore_UStr(Handle, &SystemPartitionPath, Type, CreateNew); +} + +NTSTATUS +CloseNTOSBootLoaderStore( + IN PVOID Handle) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + return NtosBootLoaders[BootStore->Type].CloseBootStore(Handle /* BootStore */); +} + + + +static +NTSTATUS +CreateNTOSEntry( + IN PBOOT_STORE_INI_CONTEXT BootStore, + IN ULONG_PTR BootEntryKey, + IN PNTOS_BOOT_ENTRY BootEntry) +{ + PINICACHESECTION IniSection; + PWCHAR Section = (PWCHAR)BootEntryKey; + + /* Insert the entry into the "Operating Systems" section */ + IniCacheInsertKey(BootStore->OsIniSection, NULL, INSERT_LAST, + Section, (PWSTR)BootEntry->FriendlyName); + + /* Create a new section */ + IniSection = IniCacheAppendSection(BootStore->IniCache, Section); + + // if (_wcsicmp(BootEntry->Version, L"Windows2003") == 0) + if (BootEntry->OsOptionsLength >= sizeof(NTOS_OPTIONS) && + RtlCompareMemory(&BootEntry->OsOptions /* Signature */, + NTOS_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) == + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) + { + PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; + + /* BootType= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BootType", L"Windows2003"); + + /* SystemPath= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"SystemPath", (PWSTR)Options->OsLoadPath); + + /* Options= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"Options", (PWSTR)Options->OsLoadOptions); + } + else + // if (_wcsicmp(BootEntry->Version, L"BootSector") == 0) + if (BootEntry->OsOptionsLength >= sizeof(BOOT_SECTOR_OPTIONS) && + RtlCompareMemory(&BootEntry->OsOptions /* Signature */, + BOOT_SECTOR_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(BOOT_SECTOR_OPTIONS, Signature)) == + RTL_FIELD_SIZE(BOOT_SECTOR_OPTIONS, Signature)) + { + PBOOT_SECTOR_OPTIONS Options = (PBOOT_SECTOR_OPTIONS)&BootEntry->OsOptions; + + /* BootType= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BootType", L"BootSector"); + + /* BootDrive= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BootDrive", (PWSTR)Options->Drive); + + /* BootPartition= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BootPartition", (PWSTR)Options->Partition); + + /* BootSector= */ + IniCacheInsertKey(IniSection, NULL, INSERT_LAST, + L"BootSectorFile", (PWSTR)Options->BootSectorFileName); + } + else + { + DPRINT1("Unsupported BootType '%S'\n", BootEntry->Version); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AddNTOSBootEntry( + IN PVOID Handle, + IN PNTOS_BOOT_ENTRY BootEntry, + IN ULONG_PTR BootEntryKey) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore || !BootEntry) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + + if (BootStore->Type == FreeLdr) + { + return CreateNTOSEntry((PBOOT_STORE_INI_CONTEXT)BootStore, + BootEntryKey, BootEntry); + } + else + if (BootStore->Type == NtLdr) + { + PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; + PWCHAR Buffer; + ULONG BufferLength; + PCWSTR InstallName, OsOptions; + // ULONG InstallNameLength, OsOptionsLength; + + // if (_wcsicmp(BootEntry->Version, L"Windows2003") != 0) + if (BootEntry->OsOptionsLength < sizeof(NTOS_OPTIONS) || + RtlCompareMemory(&BootEntry->OsOptions /* Signature */, + NTOS_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) != + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)) + { + DPRINT1("Unsupported BootType '%S'\n", BootEntry->Version); + return STATUS_SUCCESS; // STATUS_NOT_SUPPORTED; + } + + InstallName = BootEntry->FriendlyName; + OsOptions = Options->OsLoadOptions; + + // if (InstallNameLength == 0) InstallName = NULL; + // if (OsOptionsLength == 0) OsOptions = NULL; + + BufferLength = 2 /* Quotes for FriendlyName*/ + wcslen(InstallName); + if (OsOptions) + BufferLength += 1 /* Space between FriendlyName and options */ + wcslen(OsOptions); + BufferLength++; /* NULL-termination */ + + Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength * sizeof(WCHAR)); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + wcscpy(Buffer, L"\""); + wcscat(Buffer, InstallName); + wcscat(Buffer, L"\""); + if (OsOptions) + { + wcscat(Buffer, L" "); + wcscat(Buffer, OsOptions); + } + + /* Insert the entry into the "Operating Systems" section */ + IniCacheInsertKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OsIniSection, NULL, INSERT_LAST, + (PWSTR)Options->OsLoadPath, Buffer); + + RtlFreeHeap(ProcessHeap, 0, Buffer); + return STATUS_SUCCESS; + } + else + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS +DeleteNTOSBootEntry( + IN PVOID Handle, + IN ULONG_PTR BootEntryKey) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + if (BootStore->Type != FreeLdr) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + // FIXME! This function needs my INI library rewrite to be implemented!! + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +ModifyNTOSBootEntry( + IN PVOID Handle, + IN PNTOS_BOOT_ENTRY BootEntry) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore || !BootEntry) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + if (BootStore->Type != FreeLdr) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + // FIXME! This function needs my INI library rewrite to operate properly!! + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +QueryNTOSBootEntry( + IN PVOID Handle, + IN ULONG_PTR BootEntryKey, + OUT PNTOS_BOOT_ENTRY BootEntry) // Technically this should be PNTOS_BOOT_ENTRY* +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + if (BootStore->Type != FreeLdr) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + // FIXME! This function needs my INI library rewrite to be implemented!! + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +QueryNTOSBootOptions( + IN PVOID Handle, + IN OUT PNTOS_BOOT_OPTIONS BootOptions +/* , IN PULONG BootOptionsLength */ ) +{ + NTSTATUS Status = STATUS_SUCCESS; + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + PWCHAR TimeoutStr; + + if (!BootStore || !BootOptions) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + if (BootStore->Type != FreeLdr || BootStore->Type != NtLdr) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + if (BootStore->Type == FreeLdr) + { + Status = IniCacheGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + L"DefaultOS", (PWCHAR*)&BootOptions->CurrentBootEntryKey); + if (!NT_SUCCESS(Status)) + BootOptions->CurrentBootEntryKey = 0; + + Status = IniCacheGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + L"TimeOut", &TimeoutStr); + if (NT_SUCCESS(Status) && TimeoutStr) + BootOptions->Timeout = _wtoi(TimeoutStr); + else + BootOptions->Timeout = 0; + } + else if (BootStore->Type == NtLdr) + { + Status = IniCacheGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + L"default", (PWCHAR*)&BootOptions->CurrentBootEntryKey); + if (!NT_SUCCESS(Status)) + BootOptions->CurrentBootEntryKey = 0; + + Status = IniCacheGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + L"timeout", &TimeoutStr); + if (NT_SUCCESS(Status) && TimeoutStr) + BootOptions->Timeout = _wtoi(TimeoutStr); + else + BootOptions->Timeout = 0; + } + + return STATUS_SUCCESS; // FIXME: use Status; instead? +} + +NTSTATUS +SetNTOSBootOptions( + IN PVOID Handle, + IN PNTOS_BOOT_OPTIONS BootOptions, + IN ULONG FieldsToChange) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + WCHAR TimeoutStr[15]; + + if (!BootStore || !BootOptions) + return STATUS_INVALID_PARAMETER; + + /* + * NOTE: Currently we open & map the loader configuration file without + * further tests. It's OK as long as we only deal with FreeLdr's freeldr.ini + * and NTLDR's boot.ini files. But as soon as we'll implement support for + * BOOTMGR detection, the "configuration file" will be the BCD registry + * hive and then, we'll have instead to mount the hive & open it. + */ + + // + // FIXME!! + // + + // if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + if (BootStore->Type != FreeLdr) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + return STATUS_NOT_SUPPORTED; + } + + // + // TODO: Depending on the flags set in 'FieldsToChange', + // change either one or both these bootloader options. + // + IniCacheInsertKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + NULL, INSERT_LAST, + L"DefaultOS", (PWCHAR)BootOptions->CurrentBootEntryKey); + + StringCchPrintfW(TimeoutStr, ARRAYSIZE(TimeoutStr), L"%d", BootOptions->Timeout); + IniCacheInsertKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection, + NULL, INSERT_LAST, + L"TimeOut", TimeoutStr); + + return STATUS_SUCCESS; +} + + + +static NTSTATUS +FreeLdrEnumerateBootEntries( + IN PBOOT_STORE_INI_CONTEXT BootStore, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL) +{ + NTSTATUS Status = STATUS_SUCCESS; + PINICACHEITERATOR Iterator; + PINICACHESECTION OsIniSection; + PWCHAR SectionName, KeyData; + UCHAR xxBootEntry[FIELD_OFFSET(NTOS_BOOT_ENTRY, OsOptions) + + max(sizeof(NTOS_OPTIONS), sizeof(BOOT_SECTOR_OPTIONS))]; + PNTOS_BOOT_ENTRY BootEntry = (PNTOS_BOOT_ENTRY)&xxBootEntry; + PWCHAR Buffer; + + /* Enumerate all the valid installations listed in the "Operating Systems" section */ + Iterator = IniCacheFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData); + if (!Iterator) return STATUS_SUCCESS; + do + { + PWCHAR InstallName; + ULONG InstallNameLength; + + /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */ + if (*KeyData == L'"') + { + /* Quoted name, copy up to the closing quote */ + PWCHAR End = wcschr(KeyData + 1, L'"'); + + if (End) + { + /* Skip the first quote */ + InstallName = KeyData + 1; + InstallNameLength = End - InstallName; + } + else // if (!End) + { + /* No corresponding closing quote, so we include the first one in the InstallName */ + InstallName = KeyData; + InstallNameLength = wcslen(InstallName); + } + if (InstallNameLength == 0) InstallName = NULL; + } + else + { + /* Non-quoted name, copy everything */ + InstallName = KeyData; + InstallNameLength = wcslen(InstallName); + if (InstallNameLength == 0) InstallName = NULL; + } + + /* Allocate the temporary buffer */ + Buffer = NULL; + if (InstallNameLength) + Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, (InstallNameLength + 1) * sizeof(WCHAR)); + if (Buffer) + { + RtlCopyMemory(Buffer, InstallName, InstallNameLength * sizeof(WCHAR)); + Buffer[InstallNameLength] = UNICODE_NULL; + InstallName = Buffer; + } + + DPRINT1("Boot entry '%S' in OS section '%S'\n", InstallName, SectionName); + + BootEntry->Version = NULL; + BootEntry->BootEntryKey = MAKESTRKEY(SectionName); + BootEntry->FriendlyName = InstallName; + BootEntry->BootFilePath = NULL; + BootEntry->OsOptionsLength = 0; + + /* Search for an existing boot entry section */ + OsIniSection = IniCacheGetSection(BootStore->IniCache, SectionName); + if (!OsIniSection) + goto DoEnum; + + /* Check for supported boot type "Windows2003" */ + Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData); + if (!NT_SUCCESS(Status) || (KeyData == NULL)) + { + /* Certainly not a ReactOS installation */ + DPRINT1("No BootType value present!\n"); + goto DoEnum; + } + + // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? + if ((_wcsicmp(KeyData, L"Windows2003") == 0) || + (_wcsicmp(KeyData, L"\"Windows2003\"") == 0)) + { + /* BootType is Windows2003 */ + PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; + + BootEntry->Version = L"Windows2003"; + DPRINT1("This is a '%S' boot entry\n", BootEntry->Version); + + BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS); + RtlCopyMemory(Options->Signature, + NTOS_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)); + + // BootEntry->BootFilePath = NULL; + + /* Check its SystemPath */ + Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData); + if (!NT_SUCCESS(Status)) + Options->OsLoadPath = NULL; + else + Options->OsLoadPath = KeyData; + // KeyData == SystemRoot; + + /* Check the optional Options */ + Status = IniCacheGetKey(OsIniSection, L"Options", &KeyData); + if (!NT_SUCCESS(Status)) + Options->OsLoadOptions = NULL; + else + Options->OsLoadOptions = KeyData; + } + else + if ((_wcsicmp(KeyData, L"BootSector") == 0) || + (_wcsicmp(KeyData, L"\"BootSector\"") == 0)) + { + /* BootType is BootSector */ + PBOOT_SECTOR_OPTIONS Options = (PBOOT_SECTOR_OPTIONS)&BootEntry->OsOptions; + + BootEntry->Version = L"BootSector"; + DPRINT1("This is a '%S' boot entry\n", BootEntry->Version); + + BootEntry->OsOptionsLength = sizeof(BOOT_SECTOR_OPTIONS); + RtlCopyMemory(Options->Signature, + BOOT_SECTOR_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(BOOT_SECTOR_OPTIONS, Signature)); + + // BootEntry->BootFilePath = NULL; + + /* Check its BootDrive */ + Status = IniCacheGetKey(OsIniSection, L"BootDrive", &KeyData); + if (!NT_SUCCESS(Status)) + Options->Drive = NULL; + else + Options->Drive = KeyData; + + /* Check its BootPartition */ + Status = IniCacheGetKey(OsIniSection, L"BootPartition", &KeyData); + if (!NT_SUCCESS(Status)) + Options->Partition = NULL; + else + Options->Partition = KeyData; + + /* Check its BootSector */ + Status = IniCacheGetKey(OsIniSection, L"BootSectorFile", &KeyData); + if (!NT_SUCCESS(Status)) + Options->BootSectorFileName = NULL; + else + Options->BootSectorFileName = KeyData; + } + else + { + DPRINT1("Unrecognized BootType value '%S'\n", KeyData); + // BootEntry->Version = KeyData; + // goto DoEnum; + } + +DoEnum: + /* Call the user enumeration routine callback */ + Status = EnumBootEntriesRoutine(FreeLdr, BootEntry, Parameter); + + /* Free temporary buffers */ + if (Buffer) + RtlFreeHeap(ProcessHeap, 0, Buffer); + + /* Stop the enumeration if needed */ + if (!NT_SUCCESS(Status)) + break; + } + while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + + IniCacheFindClose(Iterator); + return Status; +} + +static NTSTATUS +NtLdrEnumerateBootEntries( + IN PBOOT_STORE_INI_CONTEXT BootStore, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL) +{ + NTSTATUS Status = STATUS_SUCCESS; + PINICACHEITERATOR Iterator; + PWCHAR SectionName, KeyData; + UCHAR xxBootEntry[FIELD_OFFSET(NTOS_BOOT_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)]; + PNTOS_BOOT_ENTRY BootEntry = (PNTOS_BOOT_ENTRY)&xxBootEntry; + PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions; + PWCHAR Buffer; + ULONG BufferLength; + + /* Enumerate all the valid installations */ + Iterator = IniCacheFindFirstValue(BootStore->OsIniSection, &SectionName, &KeyData); + if (!Iterator) return STATUS_SUCCESS; + do + { + PWCHAR InstallName, OsOptions; + ULONG InstallNameLength, OsOptionsLength; + + /* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */ + if (*KeyData == L'"') + { + /* Quoted name, copy up to the closing quote */ + OsOptions = wcschr(KeyData + 1, L'"'); + + /* Retrieve the starting point of the installation name and the OS options */ + if (OsOptions) + { + /* Skip the first quote */ + InstallName = KeyData + 1; + InstallNameLength = OsOptions - InstallName; + if (InstallNameLength == 0) InstallName = NULL; + + /* Skip the ending quote (if found) */ + ++OsOptions; + + /* Skip any whitespace */ + while (iswspace(*OsOptions)) ++OsOptions; + /* Get its final length */ + OsOptionsLength = wcslen(OsOptions); + if (OsOptionsLength == 0) OsOptions = NULL; + } + else + { + /* No corresponding closing quote, so we include the first one in the InstallName */ + InstallName = KeyData; + InstallNameLength = wcslen(InstallName); + if (InstallNameLength == 0) InstallName = NULL; + + /* There are no OS options */ + // OsOptions = NULL; + OsOptionsLength = 0; + } + } + else + { + /* Non-quoted name, copy everything */ + + /* Retrieve the starting point of the installation name */ + InstallName = KeyData; + InstallNameLength = wcslen(InstallName); + if (InstallNameLength == 0) InstallName = NULL; + + /* There are no OS options */ + OsOptions = NULL; + OsOptionsLength = 0; + } + + /* Allocate the temporary buffer */ + Buffer = NULL; + BufferLength = (InstallNameLength + OsOptionsLength) * sizeof(WCHAR); + if (BufferLength) + Buffer = RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, BufferLength + 2*sizeof(UNICODE_NULL)); + if (Buffer) + { + PWCHAR ptr; + + /* Copy the installation name, and make InstallName point into the buffer */ + if (InstallName && InstallNameLength) + { + ptr = Buffer; + RtlCopyMemory(ptr, InstallName, InstallNameLength * sizeof(WCHAR)); + ptr[InstallNameLength] = UNICODE_NULL; + InstallName = ptr; + } + + /* Copy the OS options, and make OsOptions point into the buffer */ + if (OsOptions && OsOptionsLength) + { + ptr = Buffer + InstallNameLength + 1; + RtlCopyMemory(ptr, OsOptions, OsOptionsLength * sizeof(WCHAR)); + ptr[OsOptionsLength] = UNICODE_NULL; + OsOptions = ptr; + } + } + + DPRINT1("Boot entry '%S' in OS section (path) '%S'\n", InstallName, SectionName); + // SectionName == SystemRoot; + + BootEntry->Version = L"Windows2003"; + BootEntry->BootEntryKey = 0; // FIXME?? + BootEntry->FriendlyName = InstallName; + BootEntry->BootFilePath = NULL; + + BootEntry->OsOptionsLength = sizeof(NTOS_OPTIONS); + RtlCopyMemory(Options->Signature, + NTOS_OPTIONS_SIGNATURE, + RTL_FIELD_SIZE(NTOS_OPTIONS, Signature)); + + Options->OsLoadPath = SectionName; + Options->OsLoadOptions = OsOptions; + + /* Call the user enumeration routine callback */ + Status = EnumBootEntriesRoutine(NtLdr, BootEntry, Parameter); + + /* Free temporary buffers */ + if (Buffer) + RtlFreeHeap(ProcessHeap, 0, Buffer); + + /* Stop the enumeration if needed */ + if (!NT_SUCCESS(Status)) + break; + } + while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + + IniCacheFindClose(Iterator); + return Status; +} + +NTSTATUS +EnumerateNTOSBootEntries( + IN PVOID Handle, +// IN ULONG Flags, // Determine which data to retrieve + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL) +{ + PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle; + + if (!BootStore) + return STATUS_INVALID_PARAMETER; + + if (BootStore->Type >= BldrTypeMax || NtosBootLoaders[BootStore->Type].Type >= BldrTypeMax) + { + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[BootStore->Type].Type); + /**/return STATUS_SUCCESS;/**/ + // return STATUS_INVALID_PARAMETER; + } + + return NtosBootLoaders[BootStore->Type].EnumBootStoreEntries( + (PBOOT_STORE_INI_CONTEXT)BootStore, // Flags, + EnumBootEntriesRoutine, Parameter); +} + /* EOF */ diff --git a/base/setup/lib/bldrsup.h b/base/setup/lib/bldrsup.h index a0965e88e97..1c34be7e62f 100644 --- a/base/setup/lib/bldrsup.h +++ b/base/setup/lib/bldrsup.h @@ -10,7 +10,7 @@ #pragma once -typedef enum _NTOS_BOOT_LOADER_TYPE +typedef enum _NTOS_BOOT_LOADER_TYPE // _BOOT_STORE_TYPE { FreeLdr, // ReactOS' FreeLoader NtLdr, // Windows <= 2k3 NT "FlexBoot" OS Loader NTLDR @@ -19,22 +19,98 @@ typedef enum _NTOS_BOOT_LOADER_TYPE } NTOS_BOOT_LOADER_TYPE; /* - * This structure is inspired from the EFI boot entry structures - * BOOT_ENTRY, BOOT_OPTIONS and FILE_PATH that are defined in ndk/iotypes.h . + * Some references about EFI boot entries: + * https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/overview-of-boot-options-in-efi + * https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/identifying-backup-files-for-existing-boot-entries */ -typedef struct _NTOS_BOOT_ENTRY + +/* + * This structure is inspired from the EFI boot entry structure + * BOOT_OPTIONS that is defined in ndk/iotypes.h . + */ +typedef struct _NTOS_BOOT_OPTIONS // _BOOT_STORE_OPTIONS { - // ULONG Version; // We might use the ntldr version here?? Or the "BootType" as in freeldr? + // ULONG Version; // ULONG Length; - // ULONG Id; // Boot entry number (position) in the list -/** PCWSTR FriendlyName; // Human-readable boot entry description **/ - PUNICODE_STRING FriendlyName; - PCWSTR BootFilePath; // Path to e.g. osloader.exe, or winload.efi - PCWSTR OsLoadPath; // The OS SystemRoot path - PCWSTR OsOptions; - PCWSTR OsLoadOptions; + ULONG Timeout; + ULONG_PTR CurrentBootEntryKey; + // ULONG_PTR NextBootEntryKey; + // WCHAR HeadlessRedirection[1]; +} NTOS_BOOT_OPTIONS, *PNTOS_BOOT_OPTIONS; + +/* + * These macros are used to set a value for the BootEntryKey member of a + * NTOS_BOOT_ENTRY structure, much in the same idea as MAKEINTRESOURCE and + * IS_INTRESOURCE macros for Win32 resources. + * + * A key consists of either a boot ID number, + * comprised between 0 and MAX_USHORT == 0xFFFF == 65535, or can be a pointer + * to a human-readable string (section name), as in the case of FreeLDR, or + * to a GUID, as in the case of BOOTMGR. + * + * If IS_INTKEY(BootEntryKey) == TRUE, i.e. the key is <= 65535, this means + * the key is a boot ID number, otherwise it is typically a pointer to a string. + */ +#define MAKESTRKEY(i) ((ULONG_PTR)(i)) +#define MAKEINTKEY(i) ((ULONG_PTR)((USHORT)(i))) +#define IS_INTKEY(i) (((ULONG_PTR)(i) >> 16) == 0) + +/* + * This structure is inspired from the EFI boot entry structures + * BOOT_ENTRY and FILE_PATH that are defined in ndk/iotypes.h . + */ +typedef struct _NTOS_BOOT_ENTRY // _BOOT_STORE_ENTRY +{ + // ULONG Version; // Equivalent of the "BootType" in FreeLdr + PWCHAR Version; // HACK!!! + // ULONG Length; + ULONG_PTR BootEntryKey; // Boot entry "key" + PCWSTR FriendlyName; // Human-readable boot entry description // LoadIdentifier + PCWSTR BootFilePath; // Path to e.g. osloader.exe, or winload.efi // EfiOsLoaderFilePath + ULONG OsOptionsLength; // Loader-specific options blob (can be a string, or a binary structure...) + UCHAR OsOptions[ANYSIZE_ARRAY]; +/* + * In packed form, this structure would contain offsets to 'FriendlyName' + * and 'BootFilePath' strings and, after the OsOptions blob, there would + * be the following data: + * + * WCHAR FriendlyName[ANYSIZE_ARRAY]; + * FILE_PATH BootFilePath; + */ } NTOS_BOOT_ENTRY, *PNTOS_BOOT_ENTRY; +/* "NTOS" (aka. ReactOS or MS Windows NT) <= 5.x options */ +typedef struct _NTOS_OPTIONS +{ + UCHAR Signature[8]; // "NTOS_5\0\0" + // ULONG Version; + // ULONG Length; + PCWSTR OsLoadPath; // The OS SystemRoot path // OsLoaderFilePath // OsFilePath + PCWSTR OsLoadOptions; // OsLoadOptions +/* + * In packed form, this structure would contain an offset to the 'OsLoadPath' + * string, and the 'OsLoadOptions' member would be: + * WCHAR OsLoadOptions[ANYSIZE_ARRAY]; + * followed by: + * FILE_PATH OsLoadPath; + */ +} NTOS_OPTIONS, *PNTOS_OPTIONS; + +#define NTOS_OPTIONS_SIGNATURE "NTOS_5\0\0" + +/* Options for boot-sector boot entries */ +typedef struct _BOOT_SECTOR_OPTIONS +{ + UCHAR Signature[8]; // "BootSect" + // ULONG Version; + // ULONG Length; + PCWSTR Drive; + PCWSTR Partition; + PCWSTR BootSectorFileName; +} BOOT_SECTOR_OPTIONS, *PBOOT_SECTOR_OPTIONS; + +#define BOOT_SECTOR_OPTIONS_SIGNATURE "BootSect" + typedef NTSTATUS (NTAPI *PENUM_BOOT_ENTRIES_ROUTINE)( @@ -45,14 +121,74 @@ typedef NTSTATUS NTSTATUS FindNTOSBootLoader( // By handle - IN HANDLE PartitionHandle, // OPTIONAL + IN HANDLE PartitionDirectoryHandle, // OPTIONAL IN NTOS_BOOT_LOADER_TYPE Type, OUT PULONG Version); + +NTSTATUS +OpenNTOSBootLoaderStoreByHandle( + OUT PVOID* Handle, + IN HANDLE PartitionDirectoryHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew); + +NTSTATUS +OpenNTOSBootLoaderStore_UStr( + OUT PVOID* Handle, + IN PUNICODE_STRING SystemPartitionPath, + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew); + +NTSTATUS +OpenNTOSBootLoaderStore( + OUT PVOID* Handle, + IN PCWSTR SystemPartition, + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew); + +NTSTATUS +CloseNTOSBootLoaderStore( + IN PVOID Handle); + +NTSTATUS +AddNTOSBootEntry( + IN PVOID Handle, + IN PNTOS_BOOT_ENTRY BootEntry, + IN ULONG_PTR BootEntryKey); + +NTSTATUS +DeleteNTOSBootEntry( + IN PVOID Handle, + IN ULONG_PTR BootEntryKey); + +NTSTATUS +ModifyNTOSBootEntry( + IN PVOID Handle, + IN PNTOS_BOOT_ENTRY BootEntry); + +NTSTATUS +QueryNTOSBootEntry( + IN PVOID Handle, + IN ULONG_PTR BootEntryKey, + OUT PNTOS_BOOT_ENTRY BootEntry); // Technically this should be PNTOS_BOOT_ENTRY* + +NTSTATUS +QueryNTOSBootOptions( + IN PVOID Handle, + IN OUT PNTOS_BOOT_OPTIONS BootOptions +/* , IN PULONG BootOptionsLength */ ); + +NTSTATUS +SetNTOSBootOptions( + IN PVOID Handle, + IN PNTOS_BOOT_OPTIONS BootOptions, + IN ULONG FieldsToChange); + NTSTATUS EnumerateNTOSBootEntries( - IN HANDLE PartitionHandle, // OPTIONAL - IN NTOS_BOOT_LOADER_TYPE Type, + IN PVOID Handle, +// IN ULONG Flags, // Determine which data to retrieve IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, IN PVOID Parameter OPTIONAL); diff --git a/base/setup/lib/osdetect.c b/base/setup/lib/osdetect.c index 6b16e5624d3..6bdd5ea4166 100644 --- a/base/setup/lib/osdetect.c +++ b/base/setup/lib/osdetect.c @@ -19,9 +19,6 @@ #include "arcname.h" #include "osdetect.h" -// HACK! -#include - #define NDEBUG #include @@ -34,51 +31,6 @@ static const PCWSTR KnownVendors[] = { L"ReactOS", L"Microsoft" }; /* FUNCTIONS ****************************************************************/ -#if 0 - -BOOL IsWindowsOS(VOID) -{ - // TODO? : - // Load the "SystemRoot\System32\Config\SOFTWARE" hive and mount it, - // then go to (SOFTWARE\\)Microsoft\\Windows NT\\CurrentVersion, - // check the REG_SZ value "ProductName" and see whether it's "Windows" - // or "ReactOS". One may also check the REG_SZ "CurrentVersion" value, - // the REG_SZ "SystemRoot" and "PathName" values (what are the differences??). - // - // Optionally, looking at the SYSTEM hive, CurrentControlSet\\Control, - // REG_SZ values "SystemBootDevice" (and "FirmwareBootDevice" ??)... - // - - /* ReactOS reports as Windows NT 5.2 */ - HKEY hKey = NULL; - - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", - 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) - { - LONG ret; - DWORD dwType = 0, dwBufSize = 0; - - ret = RegQueryValueExW(hKey, L"ProductName", NULL, &dwType, NULL, &dwBufSize); - if (ret == ERROR_SUCCESS && dwType == REG_SZ) - { - LPTSTR lpszProductName = (LPTSTR)MemAlloc(0, dwBufSize); - RegQueryValueExW(hKey, L"ProductName", NULL, &dwType, (LPBYTE)lpszProductName, &dwBufSize); - - bIsWindowsOS = (FindSubStrI(lpszProductName, _T("Windows")) != NULL); - - MemFree(lpszProductName); - } - - RegCloseKey(hKey); - } - - return bIsWindowsOS; -} - -#endif - - static BOOLEAN IsValidNTOSInstallation_UStr( IN PUNICODE_STRING SystemRootPath); @@ -134,33 +86,31 @@ EnumerateInstallations( /* We have a boot entry */ - UNICODE_STRING InstallName; - // /**/RtlInitUnicodeString(&InstallName, BootEntry->FriendlyName);/**/ - InstallName = *BootEntry->FriendlyName; - -#if 0 - if (Type == FreeLdr) + /* Check for supported boot type "Windows2003" */ + // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? + if ((BootEntry->Version == NULL) || + ( (_wcsicmp(BootEntry->Version, L"Windows2003") != 0) && + (_wcsicmp(BootEntry->Version, L"\"Windows2003\"") != 0) )) { - /* Check for supported boot type "Windows2003" */ - - // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? - if ((BootType == NULL) || - ( (_wcsicmp(BootType, L"Windows2003") != 0) && - (_wcsicmp(BootType, L"\"Windows2003\"") != 0) )) - { - /* This is not a ReactOS entry */ - /* Certainly not a ReactOS installation */ - DPRINT1(" A Win2k3 install '%wZ' without an ARC path?!\n", &InstallName); - /* Continue the enumeration */ - return STATUS_SUCCESS; - } + /* This is not a ReactOS entry */ + DPRINT1(" An installation '%S' of unsupported type '%S'\n", + BootEntry->FriendlyName, BootEntry->Version ? BootEntry->Version : L"n/a"); + /* Continue the enumeration */ + return STATUS_SUCCESS; } -#endif - DPRINT1(" Found a candidate Win2k3 install '%wZ' with ARC path '%S'\n", - &InstallName, BootEntry->OsLoadPath); - // DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", - // &InstallName, BootEntry->OsLoadPath); + if (!BootEntry->OsLoadPath || !*BootEntry->OsLoadPath) + { + /* Certainly not a ReactOS installation */ + DPRINT1(" A Win2k3 install '%S' without an ARC path?!\n", BootEntry->FriendlyName); + /* Continue the enumeration */ + return STATUS_SUCCESS; + } + + DPRINT1(" Found a candidate Win2k3 install '%S' with ARC path '%S'\n", + BootEntry->FriendlyName, BootEntry->OsLoadPath); + // DPRINT1(" Found a Win2k3 install '%S' with ARC path '%S'\n", + // BootEntry->FriendlyName, BootEntry->OsLoadPath); // TODO: Normalize the ARC path. @@ -240,14 +190,19 @@ EnumerateInstallations( if (PartEntry && PartEntry->DriveLetter) { /* We have retrieved a partition that is mounted */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%wZ\"", - PartEntry->DriveLetter, PathComponent, &InstallName); + RtlStringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), + L"%C:%s \"%s\"", + PartEntry->DriveLetter, + PathComponent, + BootEntry->FriendlyName); } else { /* We failed somewhere, just show the NT path */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%wZ\"", - &SystemRootPath, &InstallName); + RtlStringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), + L"%wZ \"%s\"", + &SystemRootPath, + BootEntry->FriendlyName); } AddNTOSInstallation(Data->List, BootEntry->OsLoadPath, &SystemRootPath, PathComponent, @@ -347,9 +302,9 @@ CheckForValidPEAndVendor( wCodePage = LOWORD(*(ULONG*)pvData); wLangID = HIWORD(*(ULONG*)pvData); - StringCchPrintfW(FileInfo, ARRAYSIZE(FileInfo), - L"StringFileInfo\\%04X%04X\\CompanyName", - wCodePage, wLangID); + RtlStringCchPrintfW(FileInfo, ARRAYSIZE(FileInfo), + L"StringFileInfo\\%04X%04X\\CompanyName", + wCodePage, wLangID); Status = NtVerQueryValue(VersionBuffer, FileInfo, &pvData, &BufLen); @@ -362,8 +317,8 @@ CheckForValidPEAndVendor( /* BufLen includes the NULL terminator count */ DPRINT1("Found version vendor: \"%S\" for file '%S'\n", pvData, PathNameToFile); - StringCbCopyNW(VendorName->Buffer, VendorName->MaximumLength, - pvData, BufLen * sizeof(WCHAR)); + RtlStringCbCopyNW(VendorName->Buffer, VendorName->MaximumLength, + pvData, BufLen * sizeof(WCHAR)); VendorName->Length = wcslen(VendorName->Buffer) * sizeof(WCHAR); Success = TRUE; @@ -503,7 +458,7 @@ IsValidNTOSInstallation_UStr( NULL, NULL); Status = NtOpenFile(&SystemRootDirectory, - FILE_LIST_DIRECTORY | SYNCHRONIZE, + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -659,10 +614,12 @@ AddNTOSInstallation( NtOsInstall->PathComponent = NtOsInstall->SystemNtPath.Buffer + (PathComponent - SystemRootNtPath->Buffer); - StringCchCopyW(NtOsInstall->InstallationName, ARRAYSIZE(NtOsInstall->InstallationName), InstallationName); + RtlStringCchCopyW(NtOsInstall->InstallationName, + ARRAYSIZE(NtOsInstall->InstallationName), + InstallationName); // Having the GENERIC_LIST storing the display item string plainly sucks... - StringCchPrintfA(InstallNameA, ARRAYSIZE(InstallNameA), "%S", InstallationName); + RtlStringCchPrintfA(InstallNameA, ARRAYSIZE(InstallNameA), "%S", InstallationName); AppendGenericListEntry(List, InstallNameA, NtOsInstall, FALSE); return NtOsInstall; @@ -677,19 +634,20 @@ FindNTOSInstallations( NTSTATUS Status; ULONG DiskNumber = PartEntry->DiskEntry->DiskNumber; ULONG PartitionNumber = PartEntry->PartitionNumber; - HANDLE PartitionHandle; + HANDLE PartitionDirectoryHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING PartitionRootPath; NTOS_BOOT_LOADER_TYPE Type; + PVOID BootStoreHandle; ENUM_INSTALLS_DATA Data; ULONG Version; WCHAR PathBuffer[MAX_PATH]; /* Set PartitionRootPath */ - StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), - L"\\Device\\Harddisk%lu\\Partition%lu\\", - DiskNumber, PartitionNumber); + RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), + L"\\Device\\Harddisk%lu\\Partition%lu\\", + DiskNumber, PartitionNumber); RtlInitUnicodeString(&PartitionRootPath, PathBuffer); DPRINT1("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath); @@ -699,8 +657,8 @@ FindNTOSInstallations( OBJ_CASE_INSENSITIVE, NULL, NULL); - Status = NtOpenFile(&PartitionHandle, - FILE_LIST_DIRECTORY | SYNCHRONIZE, + Status = NtOpenFile(&PartitionDirectoryHandle, + FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -717,7 +675,7 @@ FindNTOSInstallations( /* Try to see whether we recognize some NT boot loaders */ for (Type = FreeLdr; Type < BldrTypeMax; ++Type) { - Status = FindNTOSBootLoader(PartitionHandle, Type, &Version); + Status = FindNTOSBootLoader(PartitionDirectoryHandle, Type, &Version); if (!NT_SUCCESS(Status)) { /* The loader does not exist, continue with another one */ @@ -730,11 +688,19 @@ FindNTOSInstallations( DPRINT1("Analyse the OS installations for loader type '%d' in disk #%d, partition #%d\n", Type, DiskNumber, PartitionNumber); - EnumerateNTOSBootEntries(PartitionHandle, Type, EnumerateInstallations, &Data); + Status = OpenNTOSBootLoaderStoreByHandle(&BootStoreHandle, PartitionDirectoryHandle, Type, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Could not open the NTOS boot store of type '%d' (Status 0x%08lx), continue with another one...\n", + Type, Status); + continue; + } + EnumerateNTOSBootEntries(BootStoreHandle, EnumerateInstallations, &Data); + CloseNTOSBootLoaderStore(BootStoreHandle); } /* Close the partition */ - NtClose(PartitionHandle); + NtClose(PartitionDirectoryHandle); } // static