From 6681fb8af5eec6ac2897a3e4bde973992fa9456c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Thu, 25 May 2017 23:52:50 +0000 Subject: [PATCH] [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 --- base/setup/lib/CMakeLists.txt | 1 + base/setup/lib/bldrsup.c | 352 +++++++++++++++++++++++ base/setup/lib/bldrsup.h | 59 ++++ base/setup/lib/osdetect.c | 523 ++++++++++------------------------ 4 files changed, 556 insertions(+), 379 deletions(-) create mode 100644 base/setup/lib/bldrsup.c create mode 100644 base/setup/lib/bldrsup.h diff --git a/base/setup/lib/CMakeLists.txt b/base/setup/lib/CMakeLists.txt index 6d687deb1af..819ecf34bd8 100644 --- a/base/setup/lib/CMakeLists.txt +++ b/base/setup/lib/CMakeLists.txt @@ -1,6 +1,7 @@ list(APPEND SOURCE arcname.c + bldrsup.c filesup.c fsutil.c genlist.c diff --git a/base/setup/lib/bldrsup.c b/base/setup/lib/bldrsup.c new file mode 100644 index 00000000000..493c52237e7 --- /dev/null +++ b/base/setup/lib/bldrsup.c @@ -0,0 +1,352 @@ +/* + * 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 + + +/* 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 */ diff --git a/base/setup/lib/bldrsup.h b/base/setup/lib/bldrsup.h new file mode 100644 index 00000000000..a0965e88e97 --- /dev/null +++ b/base/setup/lib/bldrsup.h @@ -0,0 +1,59 @@ +/* + * 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). + +#pragma once + +typedef enum _NTOS_BOOT_LOADER_TYPE +{ + FreeLdr, // ReactOS' FreeLoader + NtLdr, // Windows <= 2k3 NT "FlexBoot" OS Loader NTLDR +// BootMgr, // Vista+ BCD-oriented BOOTMGR + BldrTypeMax +} NTOS_BOOT_LOADER_TYPE; + +/* + * This structure is inspired from the EFI boot entry structures + * BOOT_ENTRY, BOOT_OPTIONS and FILE_PATH that are defined in ndk/iotypes.h . + */ +typedef struct _NTOS_BOOT_ENTRY +{ + // ULONG Version; // We might use the ntldr version here?? Or the "BootType" as in freeldr? + // 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; +} NTOS_BOOT_ENTRY, *PNTOS_BOOT_ENTRY; + + +typedef NTSTATUS +(NTAPI *PENUM_BOOT_ENTRIES_ROUTINE)( + IN NTOS_BOOT_LOADER_TYPE Type, + IN PNTOS_BOOT_ENTRY BootEntry, + IN PVOID Parameter OPTIONAL); + + +NTSTATUS +FindNTOSBootLoader( // By handle + IN HANDLE PartitionHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + OUT PULONG Version); + +NTSTATUS +EnumerateNTOSBootEntries( + IN HANDLE PartitionHandle, // OPTIONAL + IN NTOS_BOOT_LOADER_TYPE Type, + IN PENUM_BOOT_ENTRIES_ROUTINE EnumBootEntriesRoutine, + IN PVOID Parameter OPTIONAL); + +/* EOF */ diff --git a/base/setup/lib/osdetect.c b/base/setup/lib/osdetect.c index a803910fc1a..20611bdc02e 100644 --- a/base/setup/lib/osdetect.c +++ b/base/setup/lib/osdetect.c @@ -12,9 +12,9 @@ #include "ntverrsrc.h" // #include "arcname.h" +#include "bldrsup.h" #include "filesup.h" #include "genlist.h" -#include "inicache.h" #include "partlist.h" #include "arcname.h" #include "osdetect.h" @@ -78,31 +78,6 @@ BOOL IsWindowsOS(VOID) #endif -typedef enum _NTOS_BOOT_LOADER_TYPE -{ - FreeLdr, // ReactOS' FreeLDR - NtLdr, // Windows <= 2k3 NTLDR -// BootMgr, // Vista+ BCD-oriented BOOTMGR -} NTOS_BOOT_LOADER_TYPE; - -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"}, - {NtLdr , L"setupldr" , L"txtsetup.sif"}, -// {BootMgr, L"bootmgr" , ???} -}; - static BOOLEAN IsValidNTOSInstallation_UStr( @@ -130,339 +105,146 @@ AddNTOSInstallation( IN PPARTENTRY PartEntry OPTIONAL, IN PCWSTR InstallationName); +typedef struct _ENUM_INSTALLS_DATA +{ + IN OUT PGENERIC_LIST List; + IN PPARTLIST PartList; + // IN PPARTENTRY PartEntry; +} ENUM_INSTALLS_DATA, *PENUM_INSTALLS_DATA; + +// PENUM_BOOT_ENTRIES_ROUTINE static NTSTATUS -FreeLdrEnumerateInstallations( - IN OUT PGENERIC_LIST List, - IN PPARTLIST PartList, - // IN PPARTENTRY PartEntry, - IN PCHAR FileBuffer, - IN ULONG FileLength) +NTAPI +EnumerateInstallations( + IN NTOS_BOOT_LOADER_TYPE Type, + IN PNTOS_BOOT_ENTRY BootEntry, + IN PVOID Parameter OPTIONAL) { - NTSTATUS Status; - PINICACHE IniCache; - PINICACHEITERATOR Iterator; - PINICACHESECTION IniSection, OsIniSection; - PWCHAR SectionName, KeyData; - UNICODE_STRING InstallName; + PENUM_INSTALLS_DATA Data = (PENUM_INSTALLS_DATA)Parameter; PNTOS_INSTALLATION NtOsInstall; UNICODE_STRING SystemRootPath; WCHAR SystemRoot[MAX_PATH]; WCHAR InstallNameW[MAX_PATH]; - /* Open an *existing* FreeLdr.ini configuration file */ - Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); - if (!NT_SUCCESS(Status)) - return Status; + ULONG DiskNumber = 0, PartitionNumber = 0; + PCWSTR PathComponent = NULL; + PDISKENTRY DiskEntry = NULL; + PPARTENTRY PartEntry = NULL; - /* Get the "Operating Systems" section */ - IniSection = IniCacheGetSection(IniCache, L"Operating Systems"); - if (IniSection == NULL) - { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; - } + /* We have a boot entry */ - /* 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("Possible installation '%wZ' in OS section '%S'\n", &InstallName, SectionName); - - /* Search for an existing ReactOS entry */ - OsIniSection = IniCacheGetSection(IniCache, SectionName); - if (!OsIniSection) - continue; + UNICODE_STRING InstallName; + // /**/RtlInitUnicodeString(&InstallName, BootEntry->FriendlyName);/**/ + InstallName = *BootEntry->FriendlyName; +#if 0 + if (Type == FreeLdr) + { /* 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 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); - - // TODO: Normalize the ARC path. - - /* - * Check whether we already have an installation with this ARC path. - * If this is the case, stop there. - */ - NtOsInstall = FindExistingNTOSInstall(List, KeyData, NULL); - if (NtOsInstall) - { - DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", - NtOsInstall->InstallationName, &NtOsInstall->SystemArcPath); - continue; - } - - /* - * Convert the ARC path into an NT path, from which we will deduce - * the real disk drive & partition on which the candidate installation - * resides, as well verifying whether it is indeed an NTOS installation. - */ - RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); - if (!ArcPathToNtPath(&SystemRootPath, KeyData, PartList)) + // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? + if ((BootType == NULL) || + ( (_wcsicmp(BootType, L"Windows2003") != 0) && + (_wcsicmp(BootType, L"\"Windows2003\"") != 0) )) { - DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", KeyData); - continue; - } - - DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", KeyData, &SystemRootPath); - - /* - * Check whether we already have an installation with this NT path. - * If this is the case, stop there. - */ - NtOsInstall = FindExistingNTOSInstall(List, NULL /*KeyData*/, &SystemRootPath); - if (NtOsInstall) - { - DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", - NtOsInstall->InstallationName, &NtOsInstall->SystemNtPath); - continue; - } - - /* Set SystemRootPath */ - DPRINT1("FreeLdrEnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath); - - if (IsValidNTOSInstallation_UStr(&SystemRootPath)) - { - ULONG DiskNumber = 0, PartitionNumber = 0; - PCWSTR PathComponent = NULL; - PDISKENTRY DiskEntry = NULL; - PPARTENTRY PartEntry = NULL; - - DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", KeyData, &SystemRootPath); - - /* From the NT path, compute the disk, partition and path components */ - if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent)) - { - DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", - &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); - - /* Retrieve the corresponding disk and partition */ - if (!GetDiskOrPartition(PartList, DiskNumber, PartitionNumber, &DiskEntry, &PartEntry)) - DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber, PartitionNumber); - } - else - { - DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath); - } - - if (PartEntry && PartEntry->DriveLetter) - { - /* We have retrieved a partition that is mounted */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%wZ\"", - PartEntry->DriveLetter, PathComponent, &InstallName); - } - else - { - /* We failed somewhere, just show the NT path */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%wZ\"", - &SystemRootPath, &InstallName); - } - AddNTOSInstallation(List, KeyData, &SystemRootPath, PathComponent, - DiskNumber, PartitionNumber, PartEntry, - InstallNameW); + /* This is not a ReactOS entry */ + /* Certainly not a ReactOS installation */ + return STATUS_SUCCESS; } } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); - - IniCacheFindClose(Iterator); - -Quit: - IniCacheDestroy(IniCache); - return STATUS_SUCCESS; -} - -static NTSTATUS -NtLdrEnumerateInstallations( - IN OUT PGENERIC_LIST List, - IN PPARTLIST PartList, - // IN PPARTENTRY PartEntry, - IN PCHAR FileBuffer, - IN ULONG FileLength) -{ - NTSTATUS Status; - PINICACHE IniCache; - PINICACHEITERATOR Iterator; - PINICACHESECTION IniSection; - PWCHAR SectionName, KeyData; - UNICODE_STRING InstallName; +#endif - PNTOS_INSTALLATION NtOsInstall; - UNICODE_STRING SystemRootPath; - WCHAR SystemRoot[MAX_PATH]; - WCHAR InstallNameW[MAX_PATH]; + 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); - /* Open an *existing* FreeLdr.ini configuration file */ - Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); - if (!NT_SUCCESS(Status)) - return Status; + // TODO: Normalize the ARC path. - /* Get the "Operating Systems" section */ - IniSection = IniCacheGetSection(IniCache, L"operating systems"); - if (IniSection == NULL) + /* + * Check whether we already have an installation with this ARC path. + * If this is the case, stop there. + */ + NtOsInstall = FindExistingNTOSInstall(Data->List, BootEntry->OsLoadPath, NULL); + if (NtOsInstall) { - IniCacheDestroy(IniCache); - return STATUS_UNSUCCESSFUL; + DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", + NtOsInstall->InstallationName, &NtOsInstall->SystemArcPath); + return STATUS_SUCCESS; } - /* Enumerate all the valid installations */ - Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); - if (!Iterator) goto Quit; - do + /* + * Convert the ARC path into an NT path, from which we will deduce + * the real disk drive & partition on which the candidate installation + * resides, as well verifying whether it is indeed an NTOS installation. + */ + RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); + if (!ArcPathToNtPath(&SystemRootPath, BootEntry->OsLoadPath, Data->PartList)) { - // 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("Possible installation '%wZ' with ARC path '%S'\n", &InstallName, SectionName); - - DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName, SectionName); + DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", BootEntry->OsLoadPath); + return STATUS_SUCCESS; + } - // TODO: Normalize the ARC path. + DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", + BootEntry->OsLoadPath, &SystemRootPath); - /* - * Check whether we already have an installation with this ARC path. - * If this is the case, stop there. - */ - NtOsInstall = FindExistingNTOSInstall(List, SectionName, NULL); - if (NtOsInstall) - { - DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", - NtOsInstall->InstallationName, &NtOsInstall->SystemArcPath); - continue; - } + /* + * Check whether we already have an installation with this NT path. + * If this is the case, stop there. + */ + NtOsInstall = FindExistingNTOSInstall(Data->List, NULL /*BootEntry->OsLoadPath*/, &SystemRootPath); + if (NtOsInstall) + { + DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", + NtOsInstall->InstallationName, &NtOsInstall->SystemNtPath); + return STATUS_SUCCESS; + } - /* - * Convert the ARC path into an NT path, from which we will deduce - * the real disk drive & partition on which the candidate installation - * resides, as well verifying whether it is indeed an NTOS installation. - */ - RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); - if (!ArcPathToNtPath(&SystemRootPath, SectionName, PartList)) - { - DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", SectionName); - continue; - } + DPRINT1("EnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath); - DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", SectionName, &SystemRootPath); + /* Check if this is a valid NTOS installation; stop there if it isn't one */ + if (!IsValidNTOSInstallation_UStr(&SystemRootPath)) + return STATUS_SUCCESS; - /* - * Check whether we already have an installation with this NT path. - * If this is the case, stop there. - */ - NtOsInstall = FindExistingNTOSInstall(List, NULL /*SectionName*/, &SystemRootPath); - if (NtOsInstall) - { - DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n", - NtOsInstall->InstallationName, &NtOsInstall->SystemNtPath); - continue; - } + DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", + BootEntry->OsLoadPath, &SystemRootPath); - /* Set SystemRootPath */ - DPRINT1("NtLdrEnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath); + /* From the NT path, compute the disk, partition and path components */ + if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent)) + { + DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", + &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); - if (IsValidNTOSInstallation_UStr(&SystemRootPath)) + /* Retrieve the corresponding disk and partition */ + if (!GetDiskOrPartition(Data->PartList, DiskNumber, PartitionNumber, &DiskEntry, &PartEntry)) { - ULONG DiskNumber = 0, PartitionNumber = 0; - PCWSTR PathComponent = NULL; - PDISKENTRY DiskEntry = NULL; - PPARTENTRY PartEntry = NULL; - - DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", SectionName, &SystemRootPath); - - /* From the NT path, compute the disk, partition and path components */ - if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent)) - { - DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", - &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); - - /* Retrieve the corresponding disk and partition */ - if (!GetDiskOrPartition(PartList, DiskNumber, PartitionNumber, &DiskEntry, &PartEntry)) - DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber, PartitionNumber); - } - else - { - DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath); - } - - if (PartEntry && PartEntry->DriveLetter) - { - /* We have retrieved a partition that is mounted */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%wZ\"", - PartEntry->DriveLetter, PathComponent, &InstallName); - } - else - { - /* We failed somewhere, just show the NT path */ - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%wZ\"", - &SystemRootPath, &InstallName); - } - AddNTOSInstallation(List, SectionName, &SystemRootPath, PathComponent, - DiskNumber, PartitionNumber, PartEntry, - InstallNameW); + DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", + DiskNumber, PartitionNumber); } } - while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + else + { + DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath); + } - IniCacheFindClose(Iterator); + if (PartEntry && PartEntry->DriveLetter) + { + /* We have retrieved a partition that is mounted */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%wZ\"", + PartEntry->DriveLetter, PathComponent, &InstallName); + } + else + { + /* We failed somewhere, just show the NT path */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%wZ\"", + &SystemRootPath, &InstallName); + } + AddNTOSInstallation(Data->List, BootEntry->OsLoadPath, + &SystemRootPath, PathComponent, + DiskNumber, PartitionNumber, PartEntry, + InstallNameW); -Quit: - IniCacheDestroy(IniCache); return STATUS_SUCCESS; } @@ -599,28 +381,32 @@ IsValidNTOSInstallationByHandle( IN HANDLE SystemRootDirectory) { BOOLEAN Success = FALSE; + PCWSTR PathName; USHORT i; UNICODE_STRING VendorName; WCHAR VendorNameBuffer[MAX_PATH]; /* Check for the existence of \SystemRoot\System32 */ - if (!DoesPathExist(SystemRootDirectory, L"System32\\")) + PathName = L"System32\\"; + if (!DoesPathExist(SystemRootDirectory, PathName)) { - // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status); + // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status); return FALSE; } /* Check for the existence of \SystemRoot\System32\drivers */ - if (!DoesPathExist(SystemRootDirectory, L"System32\\drivers\\")) + PathName = L"System32\\drivers\\"; + if (!DoesPathExist(SystemRootDirectory, PathName)) { - // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status); + // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status); return FALSE; } /* Check for the existence of \SystemRoot\System32\config */ - if (!DoesPathExist(SystemRootDirectory, L"System32\\config\\")) + PathName = L"System32\\config\\"; + if (!DoesPathExist(SystemRootDirectory, PathName)) { - // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status); + // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status); return FALSE; } @@ -629,14 +415,16 @@ IsValidNTOSInstallationByHandle( * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config * (but we don't check here whether they are actually valid). */ - if (!DoesFileExist(SystemRootDirectory, L"System32\\config\\SYSTEM")) + PathName = L"System32\\config\\SYSTEM"; + if (!DoesFileExist(SystemRootDirectory, PathName)) { - // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status); + // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status); return FALSE; } - if (!DoesFileExist(SystemRootDirectory, L"System32\\config\\SOFTWARE")) + PathName = L"System32\\config\\SOFTWARE"; + if (!DoesFileExist(SystemRootDirectory, PathName)) { - // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status); + // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status); return FALSE; } #endif @@ -644,9 +432,10 @@ IsValidNTOSInstallationByHandle( RtlInitEmptyUnicodeString(&VendorName, VendorNameBuffer, sizeof(VendorNameBuffer)); /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */ - Success = CheckForValidPEAndVendor(SystemRootDirectory, L"System32\\ntoskrnl.exe", &VendorName); + PathName = L"System32\\ntoskrnl.exe"; + Success = CheckForValidPEAndVendor(SystemRootDirectory, PathName, &VendorName); if (!Success) - DPRINT1("Kernel file ntoskrnl.exe is either not a PE file, or does not have any vendor?\n"); + DPRINT1("Kernel executable '%S' is either not a PE file, or does not have any vendor?\n", PathName); /* The kernel gives the OS its flavour */ if (Success) @@ -666,9 +455,10 @@ IsValidNTOSInstallationByHandle( /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */ /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */ - Success = CheckForValidPEAndVendor(SystemRootDirectory, L"System32\\ntdll.dll", &VendorName); + PathName = L"System32\\ntdll.dll"; + Success = CheckForValidPEAndVendor(SystemRootDirectory, PathName, &VendorName); if (!Success) - DPRINT1("User-mode file ntdll.dll is either not a PE file, or does not have any vendor?\n"); + DPRINT1("User-mode DLL '%S' is either not a PE file, or does not have any vendor?\n", PathName); if (Success) { for (i = 0; i < ARRAYSIZE(KnownVendors); ++i) @@ -676,7 +466,7 @@ IsValidNTOSInstallationByHandle( if (!!FindSubStrI(VendorName.Buffer, KnownVendors[i])) { /* We have found a correct vendor combination */ - DPRINT1("IsValidNTOSInstallation: The user-mode file ntdll.dll is from %S\n", KnownVendors[i]); + DPRINT1("IsValidNTOSInstallation: The user-mode DLL '%S' is from %S\n", PathName, KnownVendors[i]); break; } } @@ -876,15 +666,13 @@ FindNTOSInstallations( NTSTATUS Status; ULONG DiskNumber = PartEntry->DiskEntry->DiskNumber; ULONG PartitionNumber = PartEntry->PartitionNumber; - HANDLE PartitionHandle, FileHandle; + HANDLE PartitionHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING PartitionRootPath; - UINT i; - HANDLE SectionHandle; - // SIZE_T ViewSize; - ULONG FileSize; - PVOID ViewBase; + NTOS_BOOT_LOADER_TYPE Type; + ENUM_INSTALLS_DATA Data; + ULONG Version; WCHAR PathBuffer[MAX_PATH]; /* Set PartitionRootPath */ @@ -912,49 +700,26 @@ FindNTOSInstallations( return; } + Data.List = List; + Data.PartList = PartList; + /* Try to see whether we recognize some NT boot loaders */ - for (i = 0; i < ARRAYSIZE(NtosBootLoaders); ++i) + for (Type = FreeLdr; Type < BldrTypeMax; ++Type) { - /* Check whether the loader executable exists */ - if (!DoesFileExist(PartitionHandle, NtosBootLoaders[i].LoaderExecutable)) - { - /* The loader does not exist, continue with another one */ - DPRINT1("Loader executable '%S' does not exist, continue with another one...\n", NtosBootLoaders[i].LoaderExecutable); - continue; - } - - /* Check whether the loader configuration file exists */ - Status = OpenAndMapFile(PartitionHandle, NtosBootLoaders[i].LoaderConfigurationFile, - &FileHandle, &SectionHandle, &ViewBase, &FileSize); + Status = FindNTOSBootLoader(PartitionHandle, Type, &Version); 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[i].LoaderConfigurationFile); + DPRINT1("Loader type '%d' does not exist, or an error happened (Status 0x%08lx), continue with another one...\n", + Type, Status); continue; } - /* The loader configuration file exists, interpret it to find valid installations */ - DPRINT1("Analyse the OS installations inside '%S' in disk #%d, partition #%d\n", - NtosBootLoaders[i].LoaderConfigurationFile, DiskNumber, PartitionNumber); - switch (NtosBootLoaders[i].Type) - { - case FreeLdr: - Status = FreeLdrEnumerateInstallations(List, PartList, ViewBase, FileSize); - break; - - case NtLdr: - Status = NtLdrEnumerateInstallations(List, PartList, ViewBase, FileSize); - break; - - default: - DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[i].Type); - Status = STATUS_SUCCESS; - } + /* The loader exists, try to enumerate its boot entries */ + DPRINT1("Analyse the OS installations for loader type '%d' in disk #%d, partition #%d\n", + Type, DiskNumber, PartitionNumber); - /* Finally, unmap and close the file */ - UnMapFile(SectionHandle, ViewBase); - NtClose(FileHandle); + EnumerateNTOSBootEntries(PartitionHandle, Type, EnumerateInstallations, &Data); } /* Close the partition */ -- 2.17.1