Skip to content

Commit 3ddb05d

Browse files
committed
[SETUPLIB] GetSourcePaths(): Fix determination of the installation source path.
Refine the algorithm introduced in commit c560342 (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 6f389a3 "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.
1 parent 2d655a4 commit 3ddb05d

File tree

2 files changed

+124
-76
lines changed

2 files changed

+124
-76
lines changed

base/setup/lib/setuplib.c

Lines changed: 121 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -378,29 +378,48 @@ InstallSetupInfFile(
378378
#endif
379379
}
380380

381+
/**
382+
* @brief
383+
* Determine the installation source path and isolate its useful
384+
* path components (root path and source sub-directory).
385+
*
386+
* The installation source path is based either on the installer's
387+
* image file path, or on the \SystemRoot full path.
388+
*
389+
* In case the \SystemRoot full path prefixes the image file path,
390+
* use the resolved \SystemRoot as the installation source path.
391+
* Otherwise, use the image file path.
392+
*
393+
* The returned strings are allocated with RtlCreateUnicodeString(),
394+
* and need to be freed with RtlFreeUnicodeString() after being used.
395+
*
396+
* Example of output:
397+
* SourcePath: '\Device\CdRom0\I386'
398+
* SourceRootPath: '\Device\CdRom0'
399+
* SourceRootDir: '\I386'
400+
**/
381401
NTSTATUS
382402
GetSourcePaths(
383-
OUT PUNICODE_STRING SourcePath,
384-
OUT PUNICODE_STRING SourceRootPath,
385-
OUT PUNICODE_STRING SourceRootDir)
403+
_Out_ PUNICODE_STRING SourcePath,
404+
_Out_ PUNICODE_STRING SourceRootPath,
405+
_Out_ PUNICODE_STRING SourceRootDir)
386406
{
387407
NTSTATUS Status;
388-
HANDLE LinkHandle;
389-
OBJECT_ATTRIBUTES ObjectAttributes;
390-
UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
391-
PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
392-
WCHAR SystemRootBuffer[MAX_PATH] = L"";
393-
UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
394408
ULONG BufferSize;
395409
PWCHAR Ptr;
410+
HANDLE LinkHandle;
411+
OBJECT_ATTRIBUTES ObjectAttributes;
412+
IO_STATUS_BLOCK IoStatusBlock;
413+
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } ImageFileBuffer;
414+
PUNICODE_STRING InstallSourcePath = &ImageFileBuffer.Name;
415+
struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } SystemRootBuffer;
416+
PUNICODE_STRING SystemRootPath = &SystemRootBuffer.Name;
417+
const UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
396418

397-
// FIXME: commented out to allow installation from USB
398-
#if 0
399-
/* Determine the installation source path via the full path of the installer */
419+
/* Retrieve the installer's full image file path */
400420
RtlInitEmptyUnicodeString(InstallSourcePath,
401-
(PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)),
402-
sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
403-
/* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
421+
ImageFileBuffer.Buffer,
422+
sizeof(ImageFileBuffer.Buffer));
404423
BufferSize = sizeof(ImageFileBuffer);
405424
Status = NtQueryInformationProcess(NtCurrentProcess(),
406425
ProcessImageFileName,
@@ -410,75 +429,114 @@ GetSourcePaths(
410429
// STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
411430
if (!NT_SUCCESS(Status))
412431
return Status;
413-
414-
/* Manually NULL-terminate */
432+
ASSERT(InstallSourcePath->Length < InstallSourcePath->MaximumLength);
433+
434+
/* Go to the beginning of the path component, stop at the separator */
435+
Ptr = ImageFileBuffer.Buffer + (InstallSourcePath->Length / sizeof(WCHAR));
436+
while ((Ptr > ImageFileBuffer.Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR))
437+
--Ptr;
438+
/* Strip the trailing file name (at the separator or beginning of buffer)
439+
* and manually NULL-terminate */
440+
InstallSourcePath->Length = (ULONG_PTR)Ptr - (ULONG_PTR)ImageFileBuffer.Buffer;
415441
InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
416442

417-
/* Strip the trailing file name */
418-
Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
419-
if (Ptr)
420-
*Ptr = UNICODE_NULL;
421-
InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR);
422-
#endif
423443

424444
/*
425-
* Now resolve the full path to \SystemRoot. In case it prefixes
426-
* the installation source path determined from the full path of
427-
* the installer, we use instead the resolved \SystemRoot as the
428-
* installation source path.
429-
* Otherwise, we use instead the path from the full installer path.
445+
* Now, resolve the \SystemRoot symlink target full path.
446+
*
447+
* The symlink target path resolution requires reparsing, because it
448+
* can reference other symlinks. This is what happens, for example when
449+
* booting the installation from a removable hard-disk. We can have:
450+
*
451+
* \SystemRoot ---> \Device\Harddisk1\Partition1\ReactOS
452+
* and: \Device\Harddisk1\Partition1 ---> \Device\HarddiskVolume2
453+
* etc.
454+
* and we wish to resolve \SystemRoot to: \Device\HarddiskVolume2\ReactOS
455+
*
456+
* We then verify whether it prefixes the image file path obtained
457+
* from the step above, which is a fully reparsed path.
458+
*
459+
* - Using NtOpenSymbolicLinkObject(SYMBOLIC_LINK_QUERY) followed by
460+
* NtQuerySymbolicLinkObject() would only resolve the first symlink
461+
* but not the others (\Device\Harddisk1\Partition1 left as is).
462+
*
463+
* - Since \SystemRoot has to point to a directory, we try opening
464+
* the directory itself: NtOpenFile(..., FILE_DIRECTORY_FILE).
465+
*
466+
* - A call to NtQueryInformationFile(FileNameInformation) alone on
467+
* the obtained handle would only retrieve the FS directory name,
468+
* i.e. \ReactOS , but not the whole NT path.
469+
*
470+
* - We therefore use NtQueryObject(), which allows retrieving the
471+
* full resolved NT path (device name + FS directory name).
430472
*/
431473

432474
InitializeObjectAttributes(&ObjectAttributes,
433-
&SystemRootPath,
475+
(PUNICODE_STRING)&SystemRoot,
434476
OBJ_CASE_INSENSITIVE,
435477
NULL,
436478
NULL);
437479

438-
Status = NtOpenSymbolicLinkObject(&LinkHandle,
439-
SYMBOLIC_LINK_QUERY,
440-
&ObjectAttributes);
441-
if (!NT_SUCCESS(Status))
442-
{
443-
/*
444-
* We failed at opening the \SystemRoot link (usually due to wrong
445-
* access rights). Do not consider this as a fatal error, but use
446-
* instead the image file path as the installation source path.
447-
*/
448-
DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
449-
&SystemRootPath, Status);
450-
goto InitPaths;
451-
}
452-
453-
RtlInitEmptyUnicodeString(&SystemRootPath,
454-
SystemRootBuffer,
455-
sizeof(SystemRootBuffer));
456-
457-
/* Resolve the link and close its handle */
458-
Status = NtQuerySymbolicLinkObject(LinkHandle,
459-
&SystemRootPath,
460-
&BufferSize);
461-
NtClose(LinkHandle);
480+
RtlInitEmptyUnicodeString(SystemRootPath,
481+
SystemRootBuffer.Buffer,
482+
sizeof(SystemRootBuffer.Buffer));
462483

484+
Status = NtOpenFile(&LinkHandle,
485+
SYNCHRONIZE,
486+
&ObjectAttributes,
487+
&IoStatusBlock,
488+
FILE_SHARE_READ | FILE_SHARE_WRITE,
489+
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
490+
/*| FILE_OPEN_FOR_BACKUP_INTENT*/);
491+
if (NT_SUCCESS(Status))
492+
{
493+
/* Resolve the path and close its handle */
494+
Status = NtQueryObject(LinkHandle,
495+
ObjectNameInformation,
496+
&SystemRootBuffer,
497+
sizeof(SystemRootBuffer),
498+
&BufferSize);
499+
NtClose(LinkHandle);
500+
}
501+
/* If any of the calls above failed, try to naively resolve the symlink */
463502
if (!NT_SUCCESS(Status))
464-
return Status; // Unexpected error
465-
466-
/* Check whether the resolved \SystemRoot is a prefix of the image file path */
467-
// FIXME: commented out to allow installation from USB
468-
// if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE))
469503
{
470-
/* Yes it is, so we use instead SystemRoot as the installation source path */
471-
InstallSourcePath = &SystemRootPath;
504+
RtlInitEmptyUnicodeString(SystemRootPath,
505+
SystemRootBuffer.Buffer,
506+
sizeof(SystemRootBuffer.Buffer));
507+
508+
Status = NtOpenSymbolicLinkObject(&LinkHandle,
509+
SYMBOLIC_LINK_QUERY,
510+
&ObjectAttributes);
511+
if (NT_SUCCESS(Status))
512+
{
513+
/* Resolve the link and close its handle */
514+
Status = NtQuerySymbolicLinkObject(LinkHandle,
515+
SystemRootPath,
516+
&BufferSize);
517+
NtClose(LinkHandle);
518+
}
472519
}
520+
ASSERT(SystemRootPath->Length < SystemRootPath->MaximumLength);
521+
522+
/*
523+
* If the resolved \SystemRoot is a prefix of the image file path,
524+
* use \SystemRoot instead as the installation source path.
525+
*
526+
* If opening the \SystemRoot link failed (usually due to wrong
527+
* access rights), do not consider this as a fatal error, and
528+
* use the image file path as the installation source path.
529+
*/
530+
if (NT_SUCCESS(Status) && RtlPrefixUnicodeString(SystemRootPath, InstallSourcePath, TRUE))
531+
InstallSourcePath = SystemRootPath;
473532

474533

475-
InitPaths:
476534
/*
477-
* Retrieve the different source path components
535+
* Retrieve the different source path components.
478536
*/
479537
RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
480538

481-
/* Strip trailing directory */
539+
/* Isolate and strip the trailing (source root) directory */
482540
Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
483541
if (Ptr)
484542
{
@@ -986,10 +1044,6 @@ InitializeSetup(
9861044
NTSTATUS Status;
9871045

9881046
/* Get the source path and source root path */
989-
//
990-
// NOTE: Sometimes the source path may not be in SystemRoot !!
991-
// (and this is the case when using the 1st-stage GUI setup!)
992-
//
9931047
Status = GetSourcePaths(&pSetupData->SourcePath,
9941048
&pSetupData->SourceRootPath,
9951049
&pSetupData->SourceRootDir);
@@ -998,12 +1052,6 @@ InitializeSetup(
9981052
DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
9991053
return ERROR_NO_SOURCE_DRIVE;
10001054
}
1001-
/*
1002-
* Example of output:
1003-
* SourcePath: '\Device\CdRom0\I386'
1004-
* SourceRootPath: '\Device\CdRom0'
1005-
* SourceRootDir: '\I386'
1006-
*/
10071055
DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
10081056
DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
10091057
DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);

base/setup/lib/setuplib.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ InstallSetupInfFile(
171171

172172
NTSTATUS
173173
GetSourcePaths(
174-
OUT PUNICODE_STRING SourcePath,
175-
OUT PUNICODE_STRING SourceRootPath,
176-
OUT PUNICODE_STRING SourceRootDir);
174+
_Out_ PUNICODE_STRING SourcePath,
175+
_Out_ PUNICODE_STRING SourceRootPath,
176+
_Out_ PUNICODE_STRING SourceRootDir);
177177

178178
ERROR_NUMBER
179179
LoadSetupInf(

0 commit comments

Comments
 (0)