https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3ddb05d44326c189e67e1…
commit 3ddb05d44326c189e67e1d71298e236048b2146a
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Wed Oct 23 15:20:46 2024 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Wed Oct 23 23:19:58 2024 +0200
[SETUPLIB] GetSourcePaths(): Fix determination of the installation source path.
Refine the algorithm introduced in commit c560342f08 (r75667, r75676),
whereby the installation source path is based on the full image file path
of the installer program, and of the \SystemRoot symlink.
Also reverts commit 6f389a35db "Add a workaround for installing from USB
drives"
CORE-17818
+ SAL2-annotate and add Doxygen comments.
----
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 \SystemRoot symlink target resolution needs full path 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
instead of keeping the former version (using Harddisk1\Partition1).
We then verify whether it prefixes the image file path, which is
a fully reparsed path.
---
base/setup/lib/setuplib.c | 194 +++++++++++++++++++++++++++++-----------------
base/setup/lib/setuplib.h | 6 +-
2 files changed, 124 insertions(+), 76 deletions(-)
diff --git a/base/setup/lib/setuplib.c b/base/setup/lib/setuplib.c
index f8a961ab3c1..632db24ec29 100644
--- a/base/setup/lib/setuplib.c
+++ b/base/setup/lib/setuplib.c
@@ -378,29 +378,48 @@ Quit:
#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)
+ _Out_ PUNICODE_STRING SourcePath,
+ _Out_ PUNICODE_STRING SourceRootPath,
+ _Out_ PUNICODE_STRING SourceRootDir)
{
NTSTATUS Status;
- HANDLE LinkHandle;
- OBJECT_ATTRIBUTES ObjectAttributes;
- UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
- PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
- WCHAR SystemRootBuffer[MAX_PATH] = L"";
- UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
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");
- // FIXME: commented out to allow installation from USB
-#if 0
- /* Determine the installation source path via the full path of the installer */
+ /* Retrieve the installer's full image file path */
RtlInitEmptyUnicodeString(InstallSourcePath,
- (PWSTR)((ULONG_PTR)ImageFileBuffer +
sizeof(UNICODE_STRING)),
- sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
- /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
+ ImageFileBuffer.Buffer,
+ sizeof(ImageFileBuffer.Buffer));
BufferSize = sizeof(ImageFileBuffer);
Status = NtQueryInformationProcess(NtCurrentProcess(),
ProcessImageFileName,
@@ -410,75 +429,114 @@ GetSourcePaths(
// STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
if (!NT_SUCCESS(Status))
return Status;
-
- /* Manually NULL-terminate */
+ 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;
- /* Strip the trailing file name */
- Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
- if (Ptr)
- *Ptr = UNICODE_NULL;
- InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) *
sizeof(WCHAR);
-#endif
/*
- * Now resolve the full path to \SystemRoot. In case it prefixes
- * the installation source path determined from the full path of
- * the installer, we use instead the resolved \SystemRoot as the
- * installation source path.
- * Otherwise, we use instead the path from the full installer path.
+ * 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,
- &SystemRootPath,
+ (PUNICODE_STRING)&SystemRoot,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
- Status = NtOpenSymbolicLinkObject(&LinkHandle,
- SYMBOLIC_LINK_QUERY,
- &ObjectAttributes);
- if (!NT_SUCCESS(Status))
- {
- /*
- * We failed at opening the \SystemRoot link (usually due to wrong
- * access rights). Do not consider this as a fatal error, but use
- * instead the image file path as the installation source path.
- */
- DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
- &SystemRootPath, Status);
- goto InitPaths;
- }
-
- RtlInitEmptyUnicodeString(&SystemRootPath,
- SystemRootBuffer,
- sizeof(SystemRootBuffer));
-
- /* Resolve the link and close its handle */
- Status = NtQuerySymbolicLinkObject(LinkHandle,
- &SystemRootPath,
- &BufferSize);
- NtClose(LinkHandle);
+ 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))
- return Status; // Unexpected error
-
- /* Check whether the resolved \SystemRoot is a prefix of the image file path */
- // FIXME: commented out to allow installation from USB
- // if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE))
{
- /* Yes it is, so we use instead SystemRoot as the installation source path */
- InstallSourcePath = &SystemRootPath;
+ 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;
-InitPaths:
/*
- * Retrieve the different source path components
+ * Retrieve the different source path components.
*/
RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
- /* Strip trailing directory */
+ /* Isolate and strip the trailing (source root) directory */
Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
if (Ptr)
{
@@ -986,10 +1044,6 @@ InitializeSetup(
NTSTATUS Status;
/* Get the source path and source root path */
- //
- // NOTE: Sometimes the source path may not be in SystemRoot !!
- // (and this is the case when using the 1st-stage GUI setup!)
- //
Status = GetSourcePaths(&pSetupData->SourcePath,
&pSetupData->SourceRootPath,
&pSetupData->SourceRootDir);
@@ -998,12 +1052,6 @@ InitializeSetup(
DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
return ERROR_NO_SOURCE_DRIVE;
}
- /*
- * Example of output:
- * SourcePath: '\Device\CdRom0\I386'
- * SourceRootPath: '\Device\CdRom0'
- * SourceRootDir: '\I386'
- */
DPRINT1("SourcePath (1): '%wZ'\n",
&pSetupData->SourcePath);
DPRINT1("SourceRootPath (1): '%wZ'\n",
&pSetupData->SourceRootPath);
DPRINT1("SourceRootDir (1): '%wZ'\n",
&pSetupData->SourceRootDir);
diff --git a/base/setup/lib/setuplib.h b/base/setup/lib/setuplib.h
index 93edbebad14..a69f7bba877 100644
--- a/base/setup/lib/setuplib.h
+++ b/base/setup/lib/setuplib.h
@@ -171,9 +171,9 @@ InstallSetupInfFile(
NTSTATUS
GetSourcePaths(
- OUT PUNICODE_STRING SourcePath,
- OUT PUNICODE_STRING SourceRootPath,
- OUT PUNICODE_STRING SourceRootDir);
+ _Out_ PUNICODE_STRING SourcePath,
+ _Out_ PUNICODE_STRING SourceRootPath,
+ _Out_ PUNICODE_STRING SourceRootDir);
ERROR_NUMBER
LoadSetupInf(