reactos/base/setup/lib/setuplib.c
Hermès Bélusca-Maïto d7c1d220b5
[SETUPLIB][REACTOS][USETUP] Turn setuplib into a DLL shared between TUI and GUI 1st-stage setups (#7523)
CORE-13525

Notes:
- Most of the exported functions have been turned from default cdecl to explicit stdcall / "NTAPI".
- The two InitializeSetup() phases have been collapsed to make the initialization simpler.

Average reductions (percentages; see PR #7523 for actual numbers):

x86 Debug builds:
reactos.exe: 35.1%
smss.exe   : 39.8%
Total (including setuplib.dll): 17.9%

x86 Release builds:
reactos.exe: 22.3%
smss.exe   : 25.0%
Total (including setuplib.dll): 10.6%

x64 Debug builds:
reactos.exe: 40.6%
smss.exe   : 41.6%
Total (including setuplib.dll): 20.0%

x64 Release builds:
reactos.exe: 22.8%
smss.exe   : 22.3%
Total (including setuplib.dll): 10.1%
2024-12-02 23:05:38 +01:00

1434 lines
47 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Setup Library
* FILE: base/setup/lib/setuplib.c
* PURPOSE: Setup Library - Main initialization helpers
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *****************************************************************/
#include "precomp.h"
#include "filesup.h"
#include "infsupp.h"
#include "inicache.h"
#include "setuplib.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
HANDLE ProcessHeap;
BOOLEAN IsUnattendedSetup = FALSE;
/* FUNCTIONS ****************************************************************/
VOID
NTAPI
CheckUnattendedSetup(
IN OUT PUSETUP_DATA pSetupData)
{
INFCONTEXT Context;
HINF UnattendInf;
UINT ErrorLine;
INT IntValue;
PCWSTR Value;
WCHAR UnattendInfPath[MAX_PATH];
CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
pSetupData->SourcePath.Buffer, L"unattend.inf");
DPRINT("UnattendInf path: '%S'\n", UnattendInfPath);
if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
{
DPRINT("Does not exist: %S\n", UnattendInfPath);
return;
}
/* Load 'unattend.inf' from installation media */
UnattendInf = SpInfOpenInfFile(UnattendInfPath,
NULL,
INF_STYLE_OLDNT,
pSetupData->LanguageId,
&ErrorLine);
if (UnattendInf == INVALID_HANDLE_VALUE)
{
DPRINT("SpInfOpenInfFile() failed\n");
return;
}
/* Open 'Unattend' section */
if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"Signature", &Context))
{
DPRINT("SpInfFindFirstLine() failed for section 'Unattend'\n");
goto Quit;
}
/* Get pointer 'Signature' key */
if (!INF_GetData(&Context, NULL, &Value))
{
DPRINT("INF_GetData() failed for key 'Signature'\n");
goto Quit;
}
/* Check 'Signature' string */
if (_wcsicmp(Value, L"$ReactOS$") != 0)
{
DPRINT("Signature not $ReactOS$\n");
INF_FreeData(Value);
goto Quit;
}
INF_FreeData(Value);
/* Check if Unattend setup is enabled */
if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"UnattendSetupEnabled", &Context))
{
DPRINT("Can't find key 'UnattendSetupEnabled'\n");
goto Quit;
}
if (!INF_GetData(&Context, NULL, &Value))
{
DPRINT("Can't read key 'UnattendSetupEnabled'\n");
goto Quit;
}
if (_wcsicmp(Value, L"yes") != 0)
{
DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n");
INF_FreeData(Value);
goto Quit;
}
INF_FreeData(Value);
/* Search for 'DestinationDiskNumber' */
if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"DestinationDiskNumber", &Context))
{
DPRINT("SpInfFindFirstLine() failed for key 'DestinationDiskNumber'\n");
goto Quit;
}
if (!SpInfGetIntField(&Context, 1, &IntValue))
{
DPRINT("SpInfGetIntField() failed for key 'DestinationDiskNumber'\n");
goto Quit;
}
pSetupData->DestinationDiskNumber = (LONG)IntValue;
/* Search for 'DestinationPartitionNumber' */
if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"DestinationPartitionNumber", &Context))
{
DPRINT("SpInfFindFirstLine() failed for key 'DestinationPartitionNumber'\n");
goto Quit;
}
if (!SpInfGetIntField(&Context, 1, &IntValue))
{
DPRINT("SpInfGetIntField() failed for key 'DestinationPartitionNumber'\n");
goto Quit;
}
pSetupData->DestinationPartitionNumber = (LONG)IntValue;
/* Search for 'InstallationDirectory' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"InstallationDirectory", &Context))
{
if (INF_GetData(&Context, NULL, &Value))
{
RtlStringCchCopyW(pSetupData->InstallationDirectory,
ARRAYSIZE(pSetupData->InstallationDirectory),
Value);
INF_FreeData(Value);
}
else
{
DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
}
}
IsUnattendedSetup = TRUE;
DPRINT("Running unattended setup\n");
/* Search for 'BootLoaderLocation' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"BootLoaderLocation", &Context))
{
if (SpInfGetIntField(&Context, 1, &IntValue))
pSetupData->BootLoaderLocation = IntValue;
}
/* Search for 'FormatPartition' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FormatPartition", &Context))
{
if (SpInfGetIntField(&Context, 1, &IntValue))
pSetupData->FormatPartition = IntValue;
}
/* Search for 'AutoPartition' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"AutoPartition", &Context))
{
if (SpInfGetIntField(&Context, 1, &IntValue))
pSetupData->AutoPartition = IntValue;
}
/* Search for 'LocaleID' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"LocaleID", &Context))
{
if (INF_GetData(&Context, NULL, &Value))
{
LONG Id = wcstol(Value, NULL, 16);
RtlStringCchPrintfW(pSetupData->LocaleID,
ARRAYSIZE(pSetupData->LocaleID),
L"%08lx", Id);
INF_FreeData(Value);
}
}
/* Search for 'FsType' (optional) */
if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FsType", &Context))
{
if (SpInfGetIntField(&Context, 1, &IntValue))
pSetupData->FsType = IntValue;
}
Quit:
SpInfCloseInfFile(UnattendInf);
}
VOID
NTAPI
InstallSetupInfFile(
IN OUT PUSETUP_DATA pSetupData)
{
NTSTATUS Status;
PINICACHE IniCache;
#if 0 // HACK FIXME!
PINICACHE UnattendCache;
PINICACHEITERATOR Iterator;
#else
// WCHAR CrLf[] = {L'\r', L'\n'};
CHAR CrLf[] = {'\r', '\n'};
HANDLE FileHandle, UnattendFileHandle, SectionHandle;
FILE_STANDARD_INFORMATION FileInfo;
ULONG FileSize;
PVOID ViewBase;
UNICODE_STRING FileName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
#endif
PINI_SECTION IniSection;
WCHAR PathBuffer[MAX_PATH];
WCHAR UnattendInfPath[MAX_PATH];
/* Create a $winnt$.inf file with default entries */
IniCache = IniCacheCreate();
if (!IniCache)
return;
IniSection = IniAddSection(IniCache, L"SetupParams");
if (IniSection)
{
/* Key "skipmissingfiles" */
// RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
// L"\"%s\"", L"WinNt5.2");
// IniAddKey(IniSection, L"Version", PathBuffer);
}
IniSection = IniAddSection(IniCache, L"Data");
if (IniSection)
{
RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
IniAddKey(IniSection, L"UnattendedInstall", PathBuffer);
// "floppylessbootpath" (yes/no)
RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\"%s\"", L"winnt");
IniAddKey(IniSection, L"ProductType", PathBuffer);
RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
IniAddKey(IniSection, L"SourcePath", PathBuffer);
// "floppyless" ("0")
}
#if 0
/* TODO: Append the standard unattend.inf file */
CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
pSetupData->SourcePath.Buffer, L"unattend.inf");
if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
{
DPRINT("Does not exist: %S\n", UnattendInfPath);
goto Quit;
}
Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
goto Quit;
}
IniCacheDestroy(UnattendCache);
Quit:
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
IniCacheSave(IniCache, PathBuffer);
IniCacheDestroy(IniCache);
#else
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
IniCacheSave(IniCache, PathBuffer);
IniCacheDestroy(IniCache);
/* TODO: Append the standard unattend.inf file */
CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
pSetupData->SourcePath.Buffer, L"unattend.inf");
if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
{
DPRINT("Does not exist: %S\n", UnattendInfPath);
return;
}
RtlInitUnicodeString(&FileName, PathBuffer);
InitializeObjectAttributes(&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
NULL,
NULL);
Status = NtOpenFile(&FileHandle,
FILE_APPEND_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
return;
}
/* Query the file size */
Status = NtQueryInformationFile(FileHandle,
&IoStatusBlock,
&FileInfo,
sizeof(FileInfo),
FileStandardInformation);
if (!NT_SUCCESS(Status))
{
DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
FileInfo.EndOfFile.QuadPart = 0ULL;
}
Status = OpenAndMapFile(NULL,
UnattendInfPath,
&UnattendFileHandle,
&FileSize,
&SectionHandle,
&ViewBase,
FALSE);
if (!NT_SUCCESS(Status))
{
DPRINT1("Cannot load %S !\n", UnattendInfPath);
NtClose(FileHandle);
return;
}
/* Write to the INI file */
/* "\r\n" */
Status = NtWriteFile(FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
(PVOID)CrLf,
sizeof(CrLf),
&FileInfo.EndOfFile,
NULL);
Status = NtWriteFile(FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
ViewBase,
FileSize,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
}
/* Finally, unmap and close the file */
UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
NtClose(FileHandle);
#endif
}
/**
* @brief
* Determine the installation source path and isolate its useful
* path components (root path and source sub-directory).
*
* The installation source path is based either on the installer's
* image file path, or on the \SystemRoot full path.
*
* In case the \SystemRoot full path prefixes the image file path,
* use the resolved \SystemRoot as the installation source path.
* Otherwise, use the image file path.
*
* The returned strings are allocated with RtlCreateUnicodeString(),
* and need to be freed with RtlFreeUnicodeString() after being used.
*
* Example of output:
* SourcePath: '\Device\CdRom0\I386'
* SourceRootPath: '\Device\CdRom0'
* SourceRootDir: '\I386'
**/
NTSTATUS
GetSourcePaths(
_Out_ PUNICODE_STRING SourcePath,
_Out_ PUNICODE_STRING SourceRootPath,
_Out_ PUNICODE_STRING SourceRootDir)
{
NTSTATUS Status;
ULONG BufferSize;
PWCHAR Ptr;
HANDLE LinkHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } ImageFileBuffer;
PUNICODE_STRING InstallSourcePath = &ImageFileBuffer.Name;
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } SystemRootBuffer;
PUNICODE_STRING SystemRootPath = &SystemRootBuffer.Name;
const UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
/* Retrieve the installer's full image file path */
RtlInitEmptyUnicodeString(InstallSourcePath,
ImageFileBuffer.Buffer,
sizeof(ImageFileBuffer.Buffer));
BufferSize = sizeof(ImageFileBuffer);
Status = NtQueryInformationProcess(NtCurrentProcess(),
ProcessImageFileName,
InstallSourcePath,
BufferSize,
NULL);
// STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
if (!NT_SUCCESS(Status))
return Status;
ASSERT(InstallSourcePath->Length < InstallSourcePath->MaximumLength);
/* Go to the beginning of the path component, stop at the separator */
Ptr = ImageFileBuffer.Buffer + (InstallSourcePath->Length / sizeof(WCHAR));
while ((Ptr > ImageFileBuffer.Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR))
--Ptr;
/* Strip the trailing file name (at the separator or beginning of buffer)
* and manually NULL-terminate */
InstallSourcePath->Length = (ULONG_PTR)Ptr - (ULONG_PTR)ImageFileBuffer.Buffer;
InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
/*
* Now, resolve the \SystemRoot symlink target full path.
*
* The symlink target path resolution requires reparsing, because it
* can reference other symlinks. This is what happens, for example when
* booting the installation from a removable hard-disk. We can have:
*
* \SystemRoot ---> \Device\Harddisk1\Partition1\ReactOS
* and: \Device\Harddisk1\Partition1 ---> \Device\HarddiskVolume2
* etc.
* and we wish to resolve \SystemRoot to: \Device\HarddiskVolume2\ReactOS
*
* We then verify whether it prefixes the image file path obtained
* from the step above, which is a fully reparsed path.
*
* - Using NtOpenSymbolicLinkObject(SYMBOLIC_LINK_QUERY) followed by
* NtQuerySymbolicLinkObject() would only resolve the first symlink
* but not the others (\Device\Harddisk1\Partition1 left as is).
*
* - Since \SystemRoot has to point to a directory, we try opening
* the directory itself: NtOpenFile(..., FILE_DIRECTORY_FILE).
*
* - A call to NtQueryInformationFile(FileNameInformation) alone on
* the obtained handle would only retrieve the FS directory name,
* i.e. \ReactOS , but not the whole NT path.
*
* - We therefore use NtQueryObject(), which allows retrieving the
* full resolved NT path (device name + FS directory name).
*/
InitializeObjectAttributes(&ObjectAttributes,
(PUNICODE_STRING)&SystemRoot,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
RtlInitEmptyUnicodeString(SystemRootPath,
SystemRootBuffer.Buffer,
sizeof(SystemRootBuffer.Buffer));
Status = NtOpenFile(&LinkHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
/*| FILE_OPEN_FOR_BACKUP_INTENT*/);
if (NT_SUCCESS(Status))
{
/* Resolve the path and close its handle */
Status = NtQueryObject(LinkHandle,
ObjectNameInformation,
&SystemRootBuffer,
sizeof(SystemRootBuffer),
&BufferSize);
NtClose(LinkHandle);
}
/* If any of the calls above failed, try to naively resolve the symlink */
if (!NT_SUCCESS(Status))
{
RtlInitEmptyUnicodeString(SystemRootPath,
SystemRootBuffer.Buffer,
sizeof(SystemRootBuffer.Buffer));
Status = NtOpenSymbolicLinkObject(&LinkHandle,
SYMBOLIC_LINK_QUERY,
&ObjectAttributes);
if (NT_SUCCESS(Status))
{
/* Resolve the link and close its handle */
Status = NtQuerySymbolicLinkObject(LinkHandle,
SystemRootPath,
&BufferSize);
NtClose(LinkHandle);
}
}
ASSERT(SystemRootPath->Length < SystemRootPath->MaximumLength);
/*
* If the resolved \SystemRoot is a prefix of the image file path,
* use \SystemRoot instead as the installation source path.
*
* If opening the \SystemRoot link failed (usually due to wrong
* access rights), do not consider this as a fatal error, and
* use the image file path as the installation source path.
*/
if (NT_SUCCESS(Status) && RtlPrefixUnicodeString(SystemRootPath, InstallSourcePath, TRUE))
InstallSourcePath = SystemRootPath;
/*
* Retrieve the different source path components.
*/
RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
/* Isolate and strip the trailing (source root) directory */
Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
if (Ptr)
{
RtlCreateUnicodeString(SourceRootDir, Ptr);
*Ptr = UNICODE_NULL;
}
else
{
RtlCreateUnicodeString(SourceRootDir, L"");
}
RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer);
return STATUS_SUCCESS;
}
ERROR_NUMBER
LoadSetupInf(
IN OUT PUSETUP_DATA pSetupData)
{
INFCONTEXT Context;
UINT ErrorLine;
INT IntValue;
PCWSTR Value;
WCHAR FileNameBuffer[MAX_PATH];
CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
pSetupData->SourcePath.Buffer, L"txtsetup.sif");
DPRINT("SetupInf path: '%S'\n", FileNameBuffer);
pSetupData->SetupInf =
SpInfOpenInfFile(FileNameBuffer,
NULL,
INF_STYLE_WIN4,
pSetupData->LanguageId,
&ErrorLine);
if (pSetupData->SetupInf == INVALID_HANDLE_VALUE)
return ERROR_LOAD_TXTSETUPSIF;
/* Open 'Version' section */
if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Version", L"Signature", &Context))
return ERROR_CORRUPT_TXTSETUPSIF;
/* Get pointer 'Signature' key */
if (!INF_GetData(&Context, NULL, &Value))
return ERROR_CORRUPT_TXTSETUPSIF;
/* Check 'Signature' string */
if (_wcsicmp(Value, L"$ReactOS$") != 0 &&
_wcsicmp(Value, L"$Windows NT$") != 0)
{
INF_FreeData(Value);
return ERROR_SIGNATURE_TXTSETUPSIF;
}
INF_FreeData(Value);
/* Open 'DiskSpaceRequirements' section */
if (!SpInfFindFirstLine(pSetupData->SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
return ERROR_CORRUPT_TXTSETUPSIF;
pSetupData->RequiredPartitionDiskSpace = ~0;
/* Get the 'FreeSysPartDiskSpace' value */
if (!SpInfGetIntField(&Context, 1, &IntValue))
return ERROR_CORRUPT_TXTSETUPSIF;
pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
//
// Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
// See CORE-9023
// Support for that should also be added in setupldr.
//
/* Update the Setup Source paths */
if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourceDevice", &Context))
{
/*
* Get optional pointer 'SetupSourceDevice' key, its presence
* will dictate whether we also need 'SetupSourcePath'.
*/
if (INF_GetData(&Context, NULL, &Value))
{
/* Free the old source root path string and create the new one */
RtlFreeUnicodeString(&pSetupData->SourceRootPath);
RtlCreateUnicodeString(&pSetupData->SourceRootPath, Value);
INF_FreeData(Value);
if (!SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourcePath", &Context))
{
/* The 'SetupSourcePath' value is mandatory! */
return ERROR_CORRUPT_TXTSETUPSIF;
}
/* Get pointer 'SetupSourcePath' key */
if (!INF_GetData(&Context, NULL, &Value))
{
/* The 'SetupSourcePath' value is mandatory! */
return ERROR_CORRUPT_TXTSETUPSIF;
}
/* Free the old source path string and create the new one */
RtlFreeUnicodeString(&pSetupData->SourceRootDir);
RtlCreateUnicodeString(&pSetupData->SourceRootDir, Value);
INF_FreeData(Value);
}
}
/* Search for 'DefaultPath' in the 'SetupData' section */
pSetupData->InstallationDirectory[0] = 0;
if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"DefaultPath", &Context))
{
/* Get pointer 'DefaultPath' key */
if (!INF_GetData(&Context, NULL, &Value))
return ERROR_CORRUPT_TXTSETUPSIF;
RtlStringCchCopyW(pSetupData->InstallationDirectory,
ARRAYSIZE(pSetupData->InstallationDirectory),
Value);
INF_FreeData(Value);
}
return ERROR_SUCCESS;
}
/**
* @brief Find or set the active system partition.
**/
BOOLEAN
NTAPI
InitSystemPartition(
/**/_In_ PPARTLIST PartitionList, /* HACK HACK! */
/**/_In_ PPARTENTRY InstallPartition, /* HACK HACK! */
/**/_Out_ PPARTENTRY* pSystemPartition, /* HACK HACK! */
_In_opt_ PFSVOL_CALLBACK FsVolCallback,
_In_opt_ PVOID Context)
{
FSVOL_OP Result;
PPARTENTRY SystemPartition;
PPARTENTRY OldActivePart;
/*
* If we install on a fixed disk, try to find a supported system
* partition on the system. Otherwise if we install on a removable disk
* use the install partition as the system partition.
*/
if (InstallPartition->DiskEntry->MediaType == FixedMedia)
{
SystemPartition = FindSupportedSystemPartition(PartitionList,
FALSE,
InstallPartition->DiskEntry,
InstallPartition);
/* Use the original system partition as the old active partition hint */
OldActivePart = PartitionList->SystemPartition;
if ( SystemPartition && PartitionList->SystemPartition &&
(SystemPartition != PartitionList->SystemPartition) )
{
DPRINT1("We are using a different system partition!!\n");
Result = FsVolCallback(Context,
ChangeSystemPartition,
(ULONG_PTR)SystemPartition,
0);
if (Result != FSVOL_DOIT)
return FALSE;
}
}
else // if (InstallPartition->DiskEntry->MediaType == RemovableMedia)
{
SystemPartition = InstallPartition;
/* Don't specify any old active partition hint */
OldActivePart = NULL;
}
if (!SystemPartition)
{
FsVolCallback(Context,
FSVOLNOTIFY_PARTITIONERROR,
ERROR_SYSTEM_PARTITION_NOT_FOUND,
0);
return FALSE;
}
*pSystemPartition = SystemPartition;
/*
* If the system partition can be created in some
* non-partitioned space, create it now.
*/
if (!SystemPartition->IsPartitioned)
{
/* Automatically create the partition; it will be
* formatted later with default parameters */
// FIXME: Don't use the whole empty space, but a minimal size
// specified from the TXTSETUP.SIF or unattended setup.
CreatePartition(PartitionList,
SystemPartition,
0ULL,
0);
ASSERT(SystemPartition->IsPartitioned);
}
/* Set it as such */
if (!SetActivePartition(PartitionList, SystemPartition, OldActivePart))
{
DPRINT1("SetActivePartition(0x%p) failed?!\n", SystemPartition);
ASSERT(FALSE);
}
/*
* In all cases, whether or not we are going to perform a formatting,
* we must perform a filesystem check of the system partition.
*/
if (SystemPartition->Volume)
SystemPartition->Volume->NeedsCheck = TRUE;
return TRUE;
}
#define IS_PATH_SEPARATOR(c) ((c) == L'\\' || (c) == L'/')
/**
* @brief
* Verify whether the given directory is suitable for ReactOS installation.
* Each path component must be a valid 8.3 name.
**/
BOOLEAN
NTAPI
IsValidInstallDirectory(
_In_ PCWSTR InstallDir)
{
PCWCH p;
/* As with the NT installer, fail if the path is empty or "\\" */
p = InstallDir;
if (!*p || (IS_PATH_SEPARATOR(*p) && !*(p + 1)))
return FALSE;
/* The path must contain only valid characters */
for (p = InstallDir; *p; ++p)
{
if (!IS_VALID_INSTALL_PATH_CHAR(*p))
return FALSE;
}
/*
* Loop over each path component and verify that each is a valid 8.3 name.
*/
for (p = InstallDir; *p;)
{
PCWSTR Path;
SIZE_T Length;
UNICODE_STRING Name;
BOOLEAN IsNameLegal, SpacesInName;
/* Skip any first separator */
if (IS_PATH_SEPARATOR(*p))
++p;
/* Now skip past the path component until we reach the next separator */
Path = p;
while (*p && !IS_PATH_SEPARATOR(*p))
++p;
if (p == Path)
{
/* Succeed if nothing else follows this separator; otherwise
* it's a separator and consecutive ones are not supported */
return (!*p);
}
/* Calculate the path component length */
Length = p - Path;
/* As with the NT installer, fail for '.' and '..';
* RtlIsNameLegalDOS8Dot3() would succeed otherwise */
if ((Length == 1 && *Path == '.') || (Length == 2 && *Path == '.' && *(Path + 1) == '.'))
return FALSE;
/* As with the NT installer, allow _only ONE trailing_ dot in
* the path component (but not 2 or more), by reducing Length
* in that case; RtlIsNameLegalDOS8Dot3() would fail otherwise */
if (Length > 1 && *(p - 2) != L'.' && *(p - 1) == L'.')
--Length;
if (Length == 0)
return FALSE;
/* Verify that the path component is a valid 8.3 name */
// if (Length > 8+1+3)
// return FALSE;
Name.Length = Name.MaximumLength = (USHORT)(Length * sizeof(WCHAR));
Name.Buffer = (PWCHAR)Path;
SpacesInName = FALSE;
IsNameLegal = RtlIsNameLegalDOS8Dot3(&Name, NULL, &SpacesInName);
/* If it isn't legal or contain spaces, fail */
if (!IsNameLegal || SpacesInName)
{
DPRINT("'%wZ' is %s 8.3 filename %s spaces\n",
&Name,
(IsNameLegal ? "a valid" : "an invalid"),
(SpacesInName ? "with" : "without"));
return FALSE;
}
/* Go to the next path component */
}
return TRUE;
}
NTSTATUS
NTAPI
InitDestinationPaths(
_Inout_ PUSETUP_DATA pSetupData,
_In_ PCWSTR InstallationDir,
_In_ PVOLENTRY Volume)
{
NTSTATUS Status;
PPARTENTRY PartEntry = Volume->PartEntry;
PDISKENTRY DiskEntry = PartEntry->DiskEntry;
WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(VOLINFO, DeviceName) + 1];
ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
/* Create 'pSetupData->DestinationRootPath' string */
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
Status = RtlStringCchPrintfW(PathBuffer, _countof(PathBuffer),
L"%s\\", Volume->Info.DeviceName);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
return Status;
}
Status = RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
return Status;
}
DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath);
// FIXME! Which variable to choose?
if (!InstallationDir)
InstallationDir = pSetupData->InstallationDirectory;
/** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
/* Create 'pSetupData->DestinationArcPath' */
RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
if (DiskEntry->MediaType == FixedMedia)
{
if (DiskEntry->BiosFound)
{
#if 1
Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
DiskEntry->HwFixedDiskNumber,
PartEntry->OnDiskPartitionNumber);
#else
Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"multi(%lu)disk(%lu)rdisk(%lu)partition(%lu)\\",
DiskEntry->HwAdapterNumber,
DiskEntry->HwControllerNumber,
DiskEntry->HwFixedDiskNumber,
PartEntry->OnDiskPartitionNumber);
#endif
DPRINT1("Fixed disk found by BIOS, using MULTI ARC path '%S'\n", PathBuffer);
}
else
{
Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"scsi(%u)disk(%u)rdisk(%u)partition(%lu)\\",
DiskEntry->Port,
DiskEntry->Bus,
DiskEntry->Id,
PartEntry->OnDiskPartitionNumber);
DPRINT1("Fixed disk not found by BIOS, using SCSI ARC path '%S'\n", PathBuffer);
}
}
else // if (DiskEntry->MediaType == RemovableMedia)
{
#if 1
Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
0, 1);
DPRINT1("Removable disk, using MULTI ARC path '%S'\n", PathBuffer);
#else
Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"signature(%08x)disk(%u)rdisk(%u)partition(%lu)\\",
DiskEntry->LayoutBuffer->Signature,
DiskEntry->Bus,
DiskEntry->Id,
PartEntry->OnDiskPartitionNumber);
DPRINT1("Removable disk, using SIGNATURE ARC path '%S'\n", PathBuffer);
#endif
}
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
Status = ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
if (!NT_SUCCESS(Status))
{
DPRINT1("ConcatPaths() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
Status = RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
/** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
/* Create 'pSetupData->DestinationPath' string */
RtlFreeUnicodeString(&pSetupData->DestinationPath);
Status = CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
pSetupData->DestinationRootPath.Buffer, InstallationDir);
if (!NT_SUCCESS(Status))
{
DPRINT1("CombinePaths() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
Status = RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
/** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
// FIXME: This is only temporary!! Must be removed later!
Status = RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
RtlFreeUnicodeString(&pSetupData->DestinationPath);
RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
return Status;
}
return STATUS_SUCCESS;
}
// NTSTATUS
ERROR_NUMBER
NTAPI
InitializeSetup(
_Inout_ PUSETUP_DATA pSetupData,
_In_opt_ PSETUP_ERROR_ROUTINE ErrorRoutine,
_In_ PSPFILE_EXPORTS pSpFileExports,
_In_ PSPINF_EXPORTS pSpInfExports)
{
ERROR_NUMBER Error;
NTSTATUS Status;
IsUnattendedSetup = FALSE;
RtlZeroMemory(pSetupData, sizeof(*pSetupData));
/* Initialize error handling */
pSetupData->LastErrorNumber = ERROR_SUCCESS;
pSetupData->ErrorRoutine = ErrorRoutine;
/* Initialize global unicode strings */
RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
// FIXME: This is only temporary!! Must be removed later!
/***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
/* Initialize SpFile and SpInf support */
RtlCopyMemory(&SpFileExports, pSpFileExports, sizeof(SpFileExports));
RtlCopyMemory(&SpInfExports, pSpInfExports, sizeof(SpInfExports));
//
// TODO: Load and start SetupDD, and ask it for the information
//
/* Get the source path and source root path */
Status = GetSourcePaths(&pSetupData->SourcePath,
&pSetupData->SourceRootPath,
&pSetupData->SourceRootDir);
if (!NT_SUCCESS(Status))
{
DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
return ERROR_NO_SOURCE_DRIVE;
}
DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
/* Set up default values */
pSetupData->DestinationDiskNumber = 0;
pSetupData->DestinationPartitionNumber = 1;
pSetupData->BootLoaderLocation = 2; // Default to "System partition"
pSetupData->FormatPartition = 0;
pSetupData->AutoPartition = 0;
pSetupData->FsType = 0;
/* Load 'txtsetup.sif' from the installation media */
Error = LoadSetupInf(pSetupData);
if (Error != ERROR_SUCCESS)
{
DPRINT1("LoadSetupInf() failed (Error 0x%lx)\n", Error);
return Error;
}
DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
/* Retrieve the target machine architecture type */
// FIXME: This should be determined at runtime!!
// FIXME: Allow for (pre-)installing on an architecture
// different from the current one?
#if defined(SARCH_XBOX)
pSetupData->ArchType = ARCH_Xbox;
// #elif defined(SARCH_PC98)
#else // TODO: Arc, UEFI
pSetupData->ArchType = (IsNEC_98 ? ARCH_NEC98x86 : ARCH_PcAT);
#endif
return ERROR_SUCCESS;
}
VOID
NTAPI
FinishSetup(
IN OUT PUSETUP_DATA pSetupData)
{
/* Destroy the computer settings list */
if (pSetupData->ComputerList != NULL)
{
DestroyGenericList(pSetupData->ComputerList, TRUE);
pSetupData->ComputerList = NULL;
}
/* Destroy the display settings list */
if (pSetupData->DisplayList != NULL)
{
DestroyGenericList(pSetupData->DisplayList, TRUE);
pSetupData->DisplayList = NULL;
}
/* Destroy the keyboard settings list */
if (pSetupData->KeyboardList != NULL)
{
DestroyGenericList(pSetupData->KeyboardList, TRUE);
pSetupData->KeyboardList = NULL;
}
/* Destroy the keyboard layout list */
if (pSetupData->LayoutList != NULL)
{
DestroyGenericList(pSetupData->LayoutList, TRUE);
pSetupData->LayoutList = NULL;
}
/* Destroy the languages list */
if (pSetupData->LanguageList != NULL)
{
DestroyGenericList(pSetupData->LanguageList, FALSE);
pSetupData->LanguageList = NULL;
}
/* Close the Setup INF */
SpInfCloseInfFile(pSetupData->SetupInf);
}
/*
* SIDEEFFECTS
* Calls RegInitializeRegistry
* Calls ImportRegistryFile
* Calls SetDefaultPagefile
* Calls SetMountedDeviceValues
*/
ERROR_NUMBER
NTAPI
UpdateRegistry(
IN OUT PUSETUP_DATA pSetupData,
/**/IN BOOLEAN RepairUpdateFlag, /* HACK HACK! */
/**/IN PPARTLIST PartitionList, /* HACK HACK! */
/**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
/**/IN PCWSTR SelectedLanguageId, /* HACK HACK! */
IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL,
IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL)
{
ERROR_NUMBER ErrorNumber;
NTSTATUS Status;
INFCONTEXT InfContext;
PCWSTR Action;
PCWSTR File;
PCWSTR Section;
BOOLEAN Success;
BOOLEAN ShouldRepairRegistry = FALSE;
BOOLEAN Delete;
if (RepairUpdateFlag)
{
DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
/* Verify the registry hives and check whether we need to update or repair any of them */
Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
if (!NT_SUCCESS(Status))
{
DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
ShouldRepairRegistry = FALSE;
}
if (!ShouldRepairRegistry)
DPRINT1("No need to repair the registry\n");
}
DoUpdate:
ErrorNumber = ERROR_SUCCESS;
/* Update the registry */
if (StatusRoutine) StatusRoutine(RegHiveUpdate);
/* Initialize the registry and setup the registry hives */
Status = RegInitializeRegistry(&pSetupData->DestinationPath);
if (!NT_SUCCESS(Status))
{
DPRINT1("RegInitializeRegistry() failed\n");
/********** HACK!!!!!!!!!!! **********/
if (Status == STATUS_NOT_IMPLEMENTED)
{
/* The hack was called, return its corresponding error */
return ERROR_INITIALIZE_REGISTRY;
}
else
/*************************************/
{
/* Something else failed */
return ERROR_CREATE_HIVE;
}
}
if (!RepairUpdateFlag || ShouldRepairRegistry)
{
/*
* We fully setup the hives, in case we are doing a fresh installation
* (RepairUpdateFlag == FALSE), or in case we are doing an update
* (RepairUpdateFlag == TRUE) BUT we have some registry hives to
* "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
*/
Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext); // Windows-compatible
if (!Success)
Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
if (!Success)
{
DPRINT1("SpInfFindFirstLine() failed\n");
ErrorNumber = ERROR_FIND_REGISTRY;
goto Cleanup;
}
}
else // if (RepairUpdateFlag && !ShouldRepairRegistry)
{
/*
* In case we are doing an update (RepairUpdateFlag == TRUE) and
* NO registry hives need a repair (ShouldRepairRegistry == FALSE),
* we only update the hives.
*/
Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
if (!Success)
{
/* Nothing to do for update! */
DPRINT1("No update needed for the registry!\n");
goto Cleanup;
}
}
do
{
INF_GetDataField(&InfContext, 0, &Action);
INF_GetDataField(&InfContext, 1, &File);
INF_GetDataField(&InfContext, 2, &Section);
DPRINT("Action: %S File: %S Section %S\n", Action, File, Section);
if (Action == NULL)
{
INF_FreeData(Action);
INF_FreeData(File);
INF_FreeData(Section);
break; // Hackfix
}
if (!_wcsicmp(Action, L"AddReg"))
Delete = FALSE;
else if (!_wcsicmp(Action, L"DelReg"))
Delete = TRUE;
else
{
DPRINT1("Unrecognized registry INF action '%S'\n", Action);
INF_FreeData(Action);
INF_FreeData(File);
INF_FreeData(Section);
continue;
}
INF_FreeData(Action);
if (StatusRoutine) StatusRoutine(ImportRegHive, File);
if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
File, Section,
pSetupData->LanguageId, Delete))
{
DPRINT1("Importing %S failed\n", File);
INF_FreeData(File);
INF_FreeData(Section);
ErrorNumber = ERROR_IMPORT_HIVE;
goto Cleanup;
}
} while (SpInfFindNextLine(&InfContext, &InfContext));
if (!RepairUpdateFlag || ShouldRepairRegistry)
{
/* See the explanation for this test above */
PGENERIC_LIST_ENTRY Entry;
PCWSTR LanguageId; // LocaleID;
Entry = GetCurrentListEntry(pSetupData->DisplayList);
ASSERT(Entry);
pSetupData->DisplayType = ((PGENENTRY)GetListEntryData(Entry))->Id;
ASSERT(pSetupData->DisplayType);
/* Update display registry settings */
if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayType))
{
ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
goto Cleanup;
}
Entry = GetCurrentListEntry(pSetupData->LanguageList);
ASSERT(Entry);
LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id;
ASSERT(LanguageId);
/* Set the locale */
if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
if (!ProcessLocaleRegistry(/*pSetupData->*/LanguageId))
{
ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
goto Cleanup;
}
/* Add the keyboard layouts for the given language (without user override) */
if (StatusRoutine) StatusRoutine(KeybLayouts);
if (!AddKeyboardLayouts(SelectedLanguageId))
{
ErrorNumber = ERROR_ADDING_KBLAYOUTS;
goto Cleanup;
}
if (!IsUnattendedSetup)
{
Entry = GetCurrentListEntry(pSetupData->LayoutList);
ASSERT(Entry);
pSetupData->LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id;
ASSERT(pSetupData->LayoutId);
/* Update keyboard layout settings with user-overridden values */
// FIXME: Wouldn't it be better to do it all at once
// with the AddKeyboardLayouts() step?
if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutId, SelectedLanguageId))
{
ErrorNumber = ERROR_UPDATE_KBSETTINGS;
goto Cleanup;
}
}
/* Set GeoID */
if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
{
ErrorNumber = ERROR_UPDATE_GEOID;
goto Cleanup;
}
/* Add codepage information to registry */
if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
if (!AddCodePage(SelectedLanguageId))
{
ErrorNumber = ERROR_ADDING_CODEPAGE;
goto Cleanup;
}
/* Set the default pagefile entry */
SetDefaultPagefile(DestinationDriveLetter);
/* Update the mounted devices list */
// FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
SetMountedDeviceValues(PartitionList);
}
#ifdef __REACTOS__
if (SubstSettings)
{
/* HACK */
DoRegistryFontFixup(SubstSettings, wcstoul(SelectedLanguageId, NULL, 16));
}
#endif
Cleanup:
//
// TODO: Unload all the registry stuff, perform cleanup,
// and copy the created hive files into .sav files.
//
RegCleanupRegistry(&pSetupData->DestinationPath);
/*
* Check whether we were in update/repair mode but we were actually
* repairing the registry hives. If so, we have finished repairing them,
* and we now reset the flag and run the proper registry update.
* Otherwise we have finished the registry update!
*/
if (RepairUpdateFlag && ShouldRepairRegistry)
{
ShouldRepairRegistry = FALSE;
goto DoUpdate;
}
return ErrorNumber;
}
/* ENTRY-POINT ***************************************************************/
/* Declared in ndk/umfuncs.h */
NTSTATUS
NTAPI
LdrDisableThreadCalloutsForDll(
_In_ PVOID BaseAddress);
BOOL
NTAPI
DllMain(
_In_ HINSTANCE hDll,
_In_ ULONG dwReason,
_In_opt_ PVOID pReserved)
{
UNREFERENCED_PARAMETER(pReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
LdrDisableThreadCalloutsForDll(hDll);
ProcessHeap = RtlGetProcessHeap();
}
return TRUE;
}
/* EOF */