[FREELDR:NTLDR] Move NtLdrUpdateLoadOptions() to ntldropts.c and rename it to NtLdrUpdateOptions()

Also:
- Update SAL annotations to SAL2;
- Double-relicense ntldropts.c/h to GPL-2.0-or-later or MIT.
This commit is contained in:
Hermès Bélusca-Maïto
2026-04-25 22:33:24 +02:00
parent b2e33f26eb
commit 925d227faa
7 changed files with 201 additions and 194 deletions

View File

@@ -94,7 +94,7 @@ endif()
list(APPEND FREELDR_BASE_SOURCE
freeldr.c
ntldr/ntldropts.c # Should be in rosload, but is currently needed by machpc.c, etc.
ntldr/ntldropts.c # Is in rosload, but is currently needed by machpc.c and ramdisk.c
lib/rtl/libsupp.c)
if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64")

View File

@@ -54,13 +54,6 @@
@ cdecl MmGetSystemMemoryMapTypeString()
@ cdecl MmGetTotalPagesInLookupTable()
# NtLdr options
@ cdecl NtLdrGetNextOption()
@ cdecl NtLdrGetOption()
@ cdecl NtLdrGetOptionEx()
@ cdecl NtLdrGetOptionExN()
@ cdecl NtLdrAddOptions()
# PeLdr
@ cdecl PeLdrAllocateDataTableEntry()
@ cdecl PeLdrCheckForLoadedDll()

View File

@@ -1,8 +1,10 @@
/*
* PROJECT: FreeLoader
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* LICENSE: Dual-licensed:
* GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* MIT (https://spdx.org/licenses/MIT)
* PURPOSE: NT Kernel Load Options Support Functions
* COPYRIGHT: Copyright 2020 Hermes Belusca-Maito
* COPYRIGHT: Copyright 2020-2026 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
*/
/* INCLUDES ******************************************************************/
@@ -14,8 +16,8 @@
PCSTR
NtLdrGetNextOption(
IN OUT PCSTR* Options,
OUT PULONG OptionLength OPTIONAL)
_Inout_ PCSTR* Options,
_Out_opt_ PULONG OptionLength)
{
PCSTR NextOption;
PCSTR Option = NULL;
@@ -39,10 +41,8 @@ NtLdrGetNextOption(
if (Option)
break;
/*
* Check whether a new option starts. Options are delimited
* with an option separator '/' or with whitespace.
*/
/* Check whether a new option starts. Options are delimited
* with an option separator '/' or with whitespace. */
if (*NextOption == '/')
++NextOption;
@@ -71,10 +71,10 @@ NtLdrGetNextOption(
*/
PCSTR
NtLdrGetOptionExN(
IN PCSTR Options,
IN PCCH OptionName,
IN ULONG OptNameLength,
OUT PULONG OptionLength OPTIONAL)
_In_ PCSTR Options,
_In_reads_(OptNameLength) PCCH OptionName,
_In_ ULONG OptNameLength,
_Out_opt_ PULONG OptionLength)
{
PCSTR NextOptions;
PCSTR Option = NULL;
@@ -115,9 +115,9 @@ NtLdrGetOptionExN(
PCSTR
NtLdrGetOptionEx(
IN PCSTR Options,
IN PCSTR OptionName,
OUT PULONG OptionLength OPTIONAL)
_In_ PCSTR Options,
_In_ PCSTR OptionName,
_Out_opt_ PULONG OptionLength)
{
return NtLdrGetOptionExN(Options, OptionName,
(ULONG)strlen(OptionName),
@@ -126,52 +126,53 @@ NtLdrGetOptionEx(
PCSTR
NtLdrGetOption(
IN PCSTR Options,
IN PCSTR OptionName)
_In_ PCSTR Options,
_In_ PCSTR OptionName)
{
return NtLdrGetOptionEx(Options, OptionName, NULL);
}
/*
/**
* @brief
* Appends or prepends new options to the ones originally contained
* in the buffer pointed by LoadOptions, of maximum size BufferSize.
*/
* in the buffer pointed by Options, of maximum size BufferSize.
**/
VOID
NtLdrAddOptions(
IN OUT PSTR LoadOptions,
IN ULONG BufferSize,
IN BOOLEAN Append,
IN PCSTR NewOptions OPTIONAL)
_Inout_updates_z_(BufferSize) PSTR Options,
_In_ ULONG BufferSize,
_In_ BOOLEAN Append,
_In_opt_ PCSTR NewOptions)
{
ULONG OptionsLength;
ULONG NewOptsLength;
BOOLEAN AddSeparator;
if (!LoadOptions || (BufferSize == 0))
if (!Options || (BufferSize == 0))
return;
// ASSERT(strlen(LoadOptions) + 1 <= BufferSize);
// ASSERT(strlen(Options) + 1 <= BufferSize);
if (!NewOptions || !*NewOptions)
return;
if (Append)
{
OptionsLength = (ULONG)strlen(LoadOptions);
OptionsLength = (ULONG)strlen(Options);
OptionsLength = min(OptionsLength, BufferSize-1);
/* Add a whitespace separator if needed */
if (OptionsLength != 0 &&
(LoadOptions[OptionsLength-1] != ' ') &&
(LoadOptions[OptionsLength-1] != '\t') &&
(Options[OptionsLength-1] != ' ') &&
(Options[OptionsLength-1] != '\t') &&
(*NewOptions != '\0') &&
(*NewOptions != ' ') &&
(*NewOptions != '\t'))
{
RtlStringCbCatA(LoadOptions, BufferSize * sizeof(CHAR), " ");
RtlStringCbCatA(Options, BufferSize * sizeof(CHAR), " ");
}
/* Append the options */
RtlStringCbCatA(LoadOptions, BufferSize * sizeof(CHAR), NewOptions);
RtlStringCbCatA(Options, BufferSize * sizeof(CHAR), NewOptions);
}
else
{
@@ -183,9 +184,9 @@ NtLdrAddOptions(
if (NewOptsLength != 0 &&
(NewOptions[NewOptsLength-1] != ' ') &&
(NewOptions[NewOptsLength-1] != '\t') &&
(*LoadOptions != '\0') &&
(*LoadOptions != ' ') &&
(*LoadOptions != '\t'))
(*Options != '\0') &&
(*Options != ' ') &&
(*Options != '\t'))
{
AddSeparator = TRUE;
++NewOptsLength;
@@ -196,18 +197,131 @@ NtLdrAddOptions(
* at the end if the buffer is not large enough) to make place for
* the options to prepend.
*/
OptionsLength = (ULONG)strlen(LoadOptions) + 1;
OptionsLength = (ULONG)strlen(Options) + 1;
OptionsLength = min(OptionsLength, BufferSize - NewOptsLength);
RtlMoveMemory(LoadOptions + NewOptsLength,
LoadOptions,
RtlMoveMemory(Options + NewOptsLength,
Options,
OptionsLength * sizeof(CHAR));
/* NULL-terminate */
(LoadOptions + NewOptsLength)[OptionsLength-1] = '\0';
(Options + NewOptsLength)[OptionsLength-1] = '\0';
/* Restore the new options length back to its original value */
if (AddSeparator) --NewOptsLength;
if (AddSeparator)
--NewOptsLength;
/* Prepend the options and add the whitespace separator if needed */
strncpy(LoadOptions, NewOptions, NewOptsLength);
if (AddSeparator) LoadOptions[NewOptsLength] = ' ';
strncpy(Options, NewOptions, NewOptsLength);
if (AddSeparator)
Options[NewOptsLength] = ' ';
}
}
/**
* @brief
* Updates the options in the buffer pointed by LoadOptions, of maximum size
* BufferSize, by first removing any specified options, and then adding any
* other ones.
*
* OptionsToAdd is a NULL-terminated array of string buffer pointers that
* specify the options to be added into LoadOptions. Whether they are
* prepended or appended to LoadOptions is controlled via the Append
* parameter. The options are added in the order specified by the array.
*
* OptionsToRemove is a NULL-terminated array of string buffer pointers that
* specify the options to remove from LoadOptions. Specifying also there
* any options to add, has the effect of removing from LoadOptions any
* duplicates of the options to be added, before adding them later into
* LoadOptions. The options are removed in the order specified by the array.
*
* The options string buffers in the OptionsToRemove array have the format:
* "/option1 /option2[=] ..."
*
* An option in the OptionsToRemove list with a trailing '=' or ':' designates
* an option in LoadOptions with user-specific data appended after the sign.
* When such an option is being removed from LoadOptions, all the appended
* data is also removed until the next option.
**/
VOID
NtLdrUpdateOptions(
_Inout_updates_z_(BufferSize) PSTR LoadOptions,
_In_ ULONG BufferSize,
_In_ BOOLEAN Append,
_In_opt_ PCSTR OptionsToAdd[],
_In_opt_ PCSTR OptionsToRemove[])
{
PCSTR NextOptions, NextOpt;
PSTR Options, Option;
ULONG NextOptLength;
ULONG OptionLength;
if (!LoadOptions || (BufferSize == 0))
return;
// ASSERT(strlen(LoadOptions) + 1 <= BufferSize);
/* Loop over the options to remove */
for (; OptionsToRemove && *OptionsToRemove; ++OptionsToRemove)
{
NextOptions = *OptionsToRemove;
while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength)))
{
/* Scan the load options */
Options = LoadOptions;
while ((Option = (PSTR)NtLdrGetNextOption((PCSTR*)&Options, &OptionLength)))
{
/*
* Check whether the option to find exactly matches the current
* load option, or is a prefix thereof if this is an option with
* appended data.
*/
if ((OptionLength >= NextOptLength) &&
(_strnicmp(Option, NextOpt, NextOptLength) == 0))
{
if ((OptionLength == NextOptLength) ||
(NextOpt[NextOptLength-1] == '=') ||
(NextOpt[NextOptLength-1] == ':'))
{
/* Eat any skipped option or whitespace separators */
while ((Option > LoadOptions) &&
(Option[-1] == '/' ||
Option[-1] == ' ' ||
Option[-1] == '\t'))
{
--Option;
}
/* If the option was not preceded by a whitespace
* separator, insert one and advance the pointer. */
if ((Option > LoadOptions) &&
(Option[-1] != ' ') &&
(Option[-1] != '\t') &&
(*Options != '\0') /* &&
** Not necessary since NtLdrGetNextOption() **
** stripped any leading separators. **
(*Options != ' ') &&
(*Options != '\t') */)
{
*Option++ = ' ';
}
/* Move the remaining options back, erasing the current one */
ASSERT(Option <= Options);
RtlMoveMemory(Option,
Options,
(strlen(Options) + 1) * sizeof(CHAR));
/* Reset the iterator */
Options = Option;
}
}
}
}
}
/* Now loop over the options to add */
for (; OptionsToAdd && *OptionsToAdd; ++OptionsToAdd)
{
NtLdrAddOptions(LoadOptions,
BufferSize,
Append,
*OptionsToAdd);
}
}

View File

@@ -1,38 +1,48 @@
/*
* PROJECT: FreeLoader
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* LICENSE: Dual-licensed:
* GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* MIT (https://spdx.org/licenses/MIT)
* PURPOSE: NT Kernel Load Options Support Functions
* COPYRIGHT: Copyright 2020 Hermes Belusca-Maito
* COPYRIGHT: Copyright 2020-2026 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
*/
#pragma once
PCSTR
NtLdrGetNextOption(
IN OUT PCSTR* Options,
OUT PULONG OptionLength OPTIONAL);
_Inout_ PCSTR* Options,
_Out_opt_ PULONG OptionLength);
PCSTR
NtLdrGetOptionExN(
IN PCSTR Options,
IN PCCH OptionName,
IN ULONG OptNameLength,
OUT PULONG OptionLength OPTIONAL);
_In_ PCSTR Options,
_In_reads_(OptNameLength) PCCH OptionName,
_In_ ULONG OptNameLength,
_Out_opt_ PULONG OptionLength);
PCSTR
NtLdrGetOptionEx(
IN PCSTR Options,
IN PCSTR OptionName,
OUT PULONG OptionLength OPTIONAL);
_In_ PCSTR Options,
_In_ PCSTR OptionName,
_Out_opt_ PULONG OptionLength);
PCSTR
NtLdrGetOption(
IN PCSTR Options,
IN PCSTR OptionName);
_In_ PCSTR Options,
_In_ PCSTR OptionName);
VOID
NtLdrAddOptions(
IN OUT PSTR LoadOptions,
IN ULONG BufferSize,
IN BOOLEAN Append,
IN PCSTR NewOptions OPTIONAL);
_Inout_updates_z_(BufferSize) PSTR Options,
_In_ ULONG BufferSize,
_In_ BOOLEAN Append,
_In_opt_ PCSTR NewOptions);
VOID
NtLdrUpdateOptions(
_Inout_updates_z_(BufferSize) PSTR LoadOptions,
_In_ ULONG BufferSize,
_In_ BOOLEAN Append,
_In_opt_ PCSTR OptionsToAdd[],
_In_opt_ PCSTR OptionsToRemove[]);

View File

@@ -230,117 +230,6 @@ SetupLdrScanBootDrivers(
/* SETUP STARTER **************************************************************/
/*
* Update the options in the buffer pointed by LoadOptions, of maximum size
* BufferSize, by first removing any specified options, and then adding any
* other ones.
*
* OptionsToAdd is a NULL-terminated array of string buffer pointers that
* specify the options to be added into LoadOptions. Whether they are
* prepended or appended to LoadOptions is controlled via the Append
* parameter. The options are added in the order specified by the array.
*
* OptionsToRemove is a NULL-terminated array of string buffer pointers that
* specify the options to remove from LoadOptions. Specifying also there
* any options to add, has the effect of removing from LoadOptions any
* duplicates of the options to be added, before adding them later into
* LoadOptions. The options are removed in the order specified by the array.
*
* The options string buffers in the OptionsToRemove array have the format:
* "/option1 /option2[=] ..."
*
* An option in the OptionsToRemove list with a trailing '=' or ':' designates
* an option in LoadOptions with user-specific data appended after the sign.
* When such an option is being removed from LoadOptions, all the appended
* data is also removed until the next option.
*/
VOID
NtLdrUpdateLoadOptions(
IN OUT PSTR LoadOptions,
IN ULONG BufferSize,
IN BOOLEAN Append,
IN PCSTR OptionsToAdd[] OPTIONAL,
IN PCSTR OptionsToRemove[] OPTIONAL)
{
PCSTR NextOptions, NextOpt;
PSTR Options, Option;
ULONG NextOptLength;
ULONG OptionLength;
if (!LoadOptions || (BufferSize == 0))
return;
// ASSERT(strlen(LoadOptions) + 1 <= BufferSize);
/* Loop over the options to remove */
for (; OptionsToRemove && *OptionsToRemove; ++OptionsToRemove)
{
NextOptions = *OptionsToRemove;
while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength)))
{
/* Scan the load options */
Options = LoadOptions;
while ((Option = (PSTR)NtLdrGetNextOption((PCSTR*)&Options, &OptionLength)))
{
/*
* Check whether the option to find exactly matches the current
* load option, or is a prefix thereof if this is an option with
* appended data.
*/
if ((OptionLength >= NextOptLength) &&
(_strnicmp(Option, NextOpt, NextOptLength) == 0))
{
if ((OptionLength == NextOptLength) ||
(NextOpt[NextOptLength-1] == '=') ||
(NextOpt[NextOptLength-1] == ':'))
{
/* Eat any skipped option or whitespace separators */
while ((Option > LoadOptions) &&
(Option[-1] == '/' ||
Option[-1] == ' ' ||
Option[-1] == '\t'))
{
--Option;
}
/* If the option was not preceded by a whitespace
* separator, insert one and advance the pointer. */
if ((Option > LoadOptions) &&
(Option[-1] != ' ') &&
(Option[-1] != '\t') &&
(*Options != '\0') /* &&
** Not necessary since NtLdrGetNextOption() **
** stripped any leading separators. **
(*Options != ' ') &&
(*Options != '\t') */)
{
*Option++ = ' ';
}
/* Move the remaining options back, erasing the current one */
ASSERT(Option <= Options);
RtlMoveMemory(Option,
Options,
(strlen(Options) + 1) * sizeof(CHAR));
/* Reset the iterator */
Options = Option;
}
}
}
}
}
/* Now loop over the options to add */
for (; OptionsToAdd && *OptionsToAdd; ++OptionsToAdd)
{
NtLdrAddOptions(LoadOptions,
BufferSize,
Append,
*OptionsToAdd);
}
}
/*
* List of options and their corresponding higher priority ones,
* that are either checked before any other ones, or whose name
@@ -675,11 +564,11 @@ LoadReactOSSetup(
RtlStringCbCopyA(UserBootOptions, sizeof(UserBootOptions), BootOptions);
/* Remove the private switch from the options */
NtLdrUpdateLoadOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
NULL,
OptionsToRemove);
NtLdrUpdateOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
NULL,
OptionsToRemove);
}
else // if (!*BootOptions || NtLdrGetOption(BootOptions, "SIFOPTIONSADD"))
{
@@ -751,11 +640,11 @@ LoadReactOSSetup(
*/
OptionsToAdd[0] = (PSTR)DbgLoadOptions;
OptionsToRemove[2] = (PSTR)DbgLoadOptions;
NtLdrUpdateLoadOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
(PCSTR*)OptionsToAdd,
(PCSTR*)OptionsToRemove);
NtLdrUpdateOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
(PCSTR*)OptionsToAdd,
(PCSTR*)OptionsToRemove);
if (ExtraOptions)
FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS);
@@ -781,11 +670,11 @@ LoadReactOSSetup(
* take precedence over those from TXTSETUP.SIF. */
OptionsToAdd[0] = (PSTR)BootOptions;
OptionsToRemove[1] = (PSTR)BootOptions;
NtLdrUpdateLoadOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
(PCSTR*)OptionsToAdd,
(PCSTR*)OptionsToRemove);
NtLdrUpdateOptions(UserBootOptions,
sizeof(UserBootOptions),
FALSE,
(PCSTR*)OptionsToAdd,
(PCSTR*)OptionsToRemove);
if (ExtraOptions)
FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS);

View File

@@ -22,12 +22,12 @@ list(APPEND ROSLOAD_SOURCE
ntldr/conversion.c
ntldr/headless.c
ntldr/inffile.c
ntldr/ntldropts.c
ntldr/registry.c
ntldr/setupldr.c
ntldr/winldr.c
ntldr/wlmemory.c
ntldr/wlregistry.c
)
ntldr/wlregistry.c)
if(ARCH STREQUAL "i386")

View File

@@ -60,6 +60,7 @@ list(APPEND FREELDR_NTLDR_SOURCE
ntldr/conversion.c
ntldr/headless.c
ntldr/inffile.c
ntldr/ntldropts.c
ntldr/registry.c
ntldr/setupldr.c
ntldr/winldr.c