@@ -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+ **/
381401NTSTATUS
382402GetSourcePaths (
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 );
0 commit comments