mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 14:30:57 +00:00
6681fb8af5
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
352 lines
12 KiB
C
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 */
|