reactos/base/setup/lib/bldrsup.c
Hermès Bélusca-Maïto 6681fb8af5
[SETUPLIB] Add a new module "bldrsup.c" (WIP) where I place all the NT boot loaders (i.e. ntldr, freeldr, and possibly bootmgr in the future) management functions.
So far we only have:
- a function FindNTOSBootLoader() that detects the existence of a given boot loader;
- a function EnumerateNTOSBootEntries() (and corresponding helpers) that enumerate the different boot entries in the configuration file(s) for a given boot loader, and for each entry, calls a user-provided callback.
Only supported at the moment: ntldr and freeldr.
Doing that allows me to simplify large portions of the NT-OS detection code so that it becomes more bootloader-agnostic, and this will help me for simplifying some parts of usetup/bootsup.c too, later...

svn path=/branches/setup_improvements/; revision=74661
2018-06-03 22:12:43 +02:00

352 lines
12 KiB
C

/*
* PROJECT: ReactOS Setup Library
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: NT 5.x family (MS Windows <= 2003, and ReactOS)
* boot loaders management.
* COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
*/
// TODO: Add support for NT 6.x family! (detection + BCD manipulation).
/* INCLUDES *****************************************************************/
#include "precomp.h"
#include "bldrsup.h"
#include "filesup.h"
#include "inicache.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
typedef struct _NTOS_BOOT_LOADER_FILES
{
NTOS_BOOT_LOADER_TYPE Type;
PCWSTR LoaderExecutable;
PCWSTR LoaderConfigurationFile;
// EnumerateInstallations;
} NTOS_BOOT_LOADER_FILES, *PNTOS_BOOT_LOADER_FILES;
// 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"}
};
/* 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 NTOS_BOOT_LOADER_TYPE Type,
OUT PULONG Version OPTIONAL)
// OUT PHANDLE ConfigFileHande OPTIONAL ????
{
// 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
/* Check whether the loader executable exists */
if (!DoesFileExist(PartitionHandle, NtosBootLoaders[Type].LoaderExecutable))
{
/* 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);
return STATUS_NOT_FOUND;
}
/* Check whether the loader configuration file exists */
if (!DoesFileExist(PartitionHandle, 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,
&FileHandle, &SectionHandle, &ViewBase, &FileSize);
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);
return STATUS_NOT_FOUND;
}
#endif
return STATUS_SUCCESS;
}
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)
{
NTSTATUS Status;
PINICACHE IniCache;
PINICACHEITERATOR Iterator;
PINICACHESECTION IniSection, OsIniSection;
PWCHAR SectionName, KeyData;
/**/NTOS_BOOT_ENTRY xxBootEntry;/**/
PNTOS_BOOT_ENTRY BootEntry = &xxBootEntry;
UNICODE_STRING InstallName;
/* Open an *existing* FreeLdr.ini configuration file */
Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE);
if (!NT_SUCCESS(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
{
// 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);
/* 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))
{
// 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;
}
/* BootType is Windows2003. Now check its SystemPath. */
Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData);
if (!NT_SUCCESS(Status))
{
DPRINT1(" A Win2k3 install '%wZ' without an ARC path?!\n", &InstallName);
continue;
}
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);
}
while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData));
IniCacheFindClose(Iterator);
Quit:
IniCacheDestroy(IniCache);
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)
{
NTSTATUS Status;
PINICACHE IniCache;
PINICACHEITERATOR Iterator;
PINICACHESECTION IniSection;
PWCHAR SectionName, KeyData;
/**/NTOS_BOOT_ENTRY xxBootEntry;/**/
PNTOS_BOOT_ENTRY BootEntry = &xxBootEntry;
UNICODE_STRING InstallName;
/* Open an *existing* boot.ini configuration file */
Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE);
if (!NT_SUCCESS(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
{
// 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);
}
while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData));
IniCacheFindClose(Iterator);
Quit:
IniCacheDestroy(IniCache);
return STATUS_SUCCESS;
}
// This function may be viewed as being similar to ntos:NtEnumerateBootEntries().
NTSTATUS
EnumerateNTOSBootEntries(
IN HANDLE PartitionHandle, // 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)
{
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
* 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.
*/
/* Check whether the loader configuration file exists */
Status = OpenAndMapFile(PartitionHandle, NtosBootLoaders[Type].LoaderConfigurationFile,
&FileHandle, &SectionHandle, &ViewBase, &FileSize);
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)
{
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);
return Status;
}
/* EOF */