From 27603a101cf2a30d06d39b2744504f0c2885e486 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Wed, 7 Jun 2017 15:58:44 +0000 Subject: [PATCH] [SETUPLIB] Introduce a lot of (Work in progress) functions to manipulate boot entries from different boot "stores". This concerns so far, only freeldr.ini and to an extent, boot.ini, but planning in the future to add support for registry-oriented BCD as well as possibly direct (u)EFI entries, using the corresponding NT functions. This code is so far only used in osdetect.c, but will be soon used by usetup's bootsup.c (note that some helper functions: CreateCommonFreeLdrSections() and (Un)protectBootIni() are already taken from it and used in bldrsup.c). - In EnumerateNTOSBootEntries(), continue enumerating the boot entries until the user callback returns an status code that is not successful. - Remove some old code from osdetect.c; use directly BootEntry->FriendlyName when building the display names of the available installations, since now BootEntry->FriendlyName is a PCWSTR (and not a UNICODE_STRING anymore). svn path=/branches/setup_improvements/; revision=74943 [SETUPLIB] Make the NTOS_BOOT_ENTRY structure more generic, so that it can wrap around either actual NTOS boot entry options, or FreeLdr-like boot-sector options. In a sense, the NTOS_BOOT_ENTRY structure now looks much more like the NT structure "BOOT_ENTRY". - Adapt the code in bldrsup.c to these modifications, and re-enable FreeLdr-like boot-sector-file support code that was commented out. More code cleanup will follow later. svn path=/branches/setup_improvements/; revision=74952 --- base/setup/lib/bldrsup.c | 1613 ++++++++++++++++++++++++++++++++----- base/setup/lib/bldrsup.h | 164 +++- base/setup/lib/osdetect.c | 150 ++-- 3 files changed, 1622 insertions(+), 305 deletions(-) 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,207 +204,541 @@ 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 "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("CreateCommonFreeLdrSections: Failed to create 'FREELOADER' section!\n"); + } + } + + 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(IniCache, L"Operating Systems"); - if (IniSection == NULL) + IniSection = IniCacheGetSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; + /* It does not exist yet, so create it */ + IniSection = IniCacheAppendSection(BootStore->IniCache, L"Operating Systems"); + if (!IniSection) + { + DPRINT1("CreateCommonFreeLdrSections: Failed to create 'Operating Systems' section!\n"); + } } - /* Enumerate all the valid installations */ - Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); - if (!Iterator) goto Quit; - do + 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) { - // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni). - if (KeyData[0] == L'"') + 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) { - /* 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("OpenIniBootLoaderStore() unsupported for NTLDR!\n"); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return STATUS_NOT_SUPPORTED; } - else + + /* Initialize the INI file */ + BootStore->IniCache = IniCacheCreate(); + if (!BootStore->IniCache) { - /* Non-quoted name, copy everything */ - RtlInitUnicodeString(&InstallName, KeyData); + DPRINT1("IniCacheCreate() failed.\n"); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return STATUS_INSUFFICIENT_RESOURCES; } - DPRINT1("Boot entry '%wZ' in OS section '%S'\n", &InstallName, SectionName); + /* + * 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; - /* Search for an existing ReactOS entry */ - OsIniSection = IniCacheGetSection(IniCache, SectionName); - if (!OsIniSection) - continue; + /* + * The INI file is fresh new, we need to create it now. + */ - /* Check for supported boot type "Windows2003" */ - Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData); - if (NT_SUCCESS(Status)) + 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)) { - // 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; - } + DPRINT1("NtCreateFile() failed (Status 0x%08lx)\n", Status); + IniCacheDestroy(BootStore->IniCache); + RtlFreeHeap(ProcessHeap, 0, BootStore); + return Status; } - else + + /* Initialize the INI file contents */ + if (Type == FreeLdr) + CreateCommonFreeLdrSections(BootStore); + } + else + { + PINICACHESECTION IniSection; + + /* + * 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)) { - /* Certainly not a ReactOS installation */ - continue; + /* 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; } - /* BootType is Windows2003. Now check its SystemPath. */ - Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData); + /* 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(" A Win2k3 install '%wZ' without an ARC path?!\n", &InstallName); - continue; + 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; } - DPRINT1(" Found a candidate Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName, KeyData); - // KeyData == SystemRoot; + if (Type == FreeLdr) + { + /* + * Cache the "FREELOADER" section for our future usage. + */ - BootEntry->FriendlyName = &InstallName; - BootEntry->OsLoadPath = KeyData; - /* Unused stuff (for now...) */ - BootEntry->BootFilePath = NULL; - BootEntry->OsOptions = NULL; - BootEntry->OsLoadOptions = NULL; + /* 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"); + } + } - Status = EnumBootEntriesRoutine(FreeLdr, BootEntry, Parameter); - // TODO: Stop enumeration if !NT_SUCCESS(Status); - } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + BootStore->OptionsIniSection = IniSection; - IniCacheFindClose(Iterator); + /* + * Cache the "Operating Systems" section for our future usage. + */ -Quit: - IniCacheDestroy(IniCache); + /* 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) + *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)) { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; + DPRINT1("NtQueryInformationFile() failed (Status 0x%08lx)\n", Status); + return Status; } - /* Enumerate all the valid installations */ - Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); - if (!Iterator) goto Quit; - do + 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) { - // 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 + /* + * 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) { - /* Non-quoted name, copy everything */ - RtlInitUnicodeString(&InstallName, KeyData); + Status = UnprotectBootIni(BootStore->FileHandle, &FileAttribute); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Could not unprotect BOOT.INI ! (Status 0x%08lx)\n", Status); + goto Quit; + } } + } - DPRINT1("Boot entry '%wZ' in OS section '%S'\n", &InstallName, SectionName); + IniCacheSaveByHandle(BootStore->IniCache, BootStore->FileHandle); - DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName, SectionName); - // SectionName == SystemRoot; + /* 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); + } - BootEntry->FriendlyName = &InstallName; - BootEntry->OsLoadPath = SectionName; - /* Unused stuff (for now...) */ - BootEntry->BootFilePath = NULL; - BootEntry->OsOptions = NULL; - BootEntry->OsLoadOptions = NULL; +Quit: + IniCacheDestroy(BootStore->IniCache); - Status = EnumBootEntriesRoutine(NtLdr, BootEntry, Parameter); - // TODO: Stop enumeration if !NT_SUCCESS(Status); + 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); } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); - IniCacheFindClose(Iterator); + /* Finally, free the boot store structure */ + RtlFreeHeap(ProcessHeap, 0, BootStore); -Quit: - IniCacheDestroy(IniCache); + // 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) { - NTSTATUS Status; - HANDLE FileHandle; - HANDLE SectionHandle; - // SIZE_T ViewSize; - ULONG FileSize; - PVOID ViewBase; - /* * 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 @@ -312,41 +747,821 @@ 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 (!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; - } - - /* The loader configuration file exists, interpret it to find valid installations */ - switch (NtosBootLoaders[Type].Type) + if (Type >= BldrTypeMax || NtosBootLoaders[Type].Type >= BldrTypeMax) { - 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; + return STATUS_NOT_SUPPORTED; } - /* Finally, unmap and close the file */ - UnMapFile(SectionHandle, ViewBase); - NtClose(FileHandle); + return NtosBootLoaders[Type].OpenBootStore(Handle, + PartitionDirectoryHandle, + Type, + CreateNew); +} - return Status; +NTSTATUS +OpenNTOSBootLoaderStore_UStr( + OUT PVOID* Handle, + IN PUNICODE_STRING SystemPartitionPath, + IN NTOS_BOOT_LOADER_TYPE Type, + IN BOOLEAN CreateNew) +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE PartitionDirectoryHandle; + + /* + * 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; + } + + /* 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)) + { + DPRINT1("Failed to open SystemPartition '%wZ', Status 0x%08lx\n", SystemPartitionPath, Status); + return Status; + } + + 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 @@ -18,23 +18,99 @@ typedef enum _NTOS_BOOT_LOADER_TYPE BldrTypeMax } NTOS_BOOT_LOADER_TYPE; +/* + * 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 + */ + +/* + * 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; + // ULONG Length; + 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, BOOT_OPTIONS and FILE_PATH that are defined in ndk/iotypes.h . + * BOOT_ENTRY and FILE_PATH that are defined in ndk/iotypes.h . */ -typedef struct _NTOS_BOOT_ENTRY +typedef struct _NTOS_BOOT_ENTRY // _BOOT_STORE_ENTRY { - // ULONG Version; // We might use the ntldr version here?? Or the "BootType" as in freeldr? + // ULONG Version; // Equivalent of the "BootType" in FreeLdr + PWCHAR Version; // HACK!!! // 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_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 -EnumerateNTOSBootEntries( - IN HANDLE PartitionHandle, // OPTIONAL +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 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" */ + /* 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; + } - // 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; - } + 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; } -#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); + 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 -- 2.17.1