From 7c3570f9f5362309a668172c913f2ff339e4467e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Tue, 9 Jan 2018 03:21:38 +0100 Subject: [PATCH] [REACTOS] Add helpers and modify ConvertNtPathToWin32Path() to make it use a cache of NT/Win32 path mappings. This increases performance for each time the SETUPLIB calls (using NT paths) Win32 SetupAPI functions which of course only accept Win32 paths. - Handle also the fact that a NT path to convert may start with \Device\HarddiskX\PartitionY\..., which can be a symlink to \Device\HarddiskVolumeN\... on some systems. In that case, the Win32 path mapping should be done slightly differently. - Add support for network mapped drives. --- base/setup/reactos/reactos.c | 198 +++++++++++++++++++++---- base/setup/reactos/reactos.h | 34 +++++ base/setup/reactos/spapisup/fileqsup.c | 17 ++- base/setup/reactos/spapisup/infsupp.c | 3 +- 4 files changed, 220 insertions(+), 32 deletions(-) diff --git a/base/setup/reactos/reactos.c b/base/setup/reactos/reactos.c index 626b0ad9f8c..230eafa38c9 100644 --- a/base/setup/reactos/reactos.c +++ b/base/setup/reactos/reactos.c @@ -1192,37 +1192,99 @@ BOOL LoadSetupData( return ret; } +VOID +InitNtToWin32PathMappingList( + IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList) +{ + InitializeListHead(&MappingList->List); + MappingList->MappingsCount = 0; +} + +VOID +FreeNtToWin32PathMappingList( + IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList) +{ + PLIST_ENTRY ListEntry; + PVOID Entry; + + while (!IsListEmpty(&MappingList->List)) + { + ListEntry = RemoveHeadList(&MappingList->List); + Entry = (PVOID)CONTAINING_RECORD(ListEntry, NT_WIN32_PATH_MAPPING, ListEntry); + HeapFree(ProcessHeap, 0, Entry); + } + + MappingList->MappingsCount = 0; +} + /* * Attempts to convert a pure NT file path into a corresponding Win32 path. * Adapted from GetInstallSourceWin32() in dll/win32/syssetup/wizard.c */ BOOL ConvertNtPathToWin32Path( + IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList, OUT PWSTR pwszPath, IN DWORD cchPathMax, IN PCWSTR pwszNTPath) { BOOL FoundDrive = FALSE, RetryOnce = FALSE; + PLIST_ENTRY ListEntry; + PNT_WIN32_PATH_MAPPING Entry; + PCWSTR pwszNtPathToMap = pwszNTPath; + PCWSTR pwszRemaining = NULL; DWORD cchDrives; PWCHAR pwszDrive; - PCWSTR pwszRemaining = NULL; WCHAR wszDrives[512]; WCHAR wszNTPath[MAX_PATH]; - WCHAR TargetPath[MAX_PATH] = L""; + WCHAR TargetPath[MAX_PATH]; *pwszPath = UNICODE_NULL; + /* + * We find first a mapping inside the MappingList. If one is found, use it + * to build the Win32 path. If there is none, we need to create one by + * checking the Win32 drives (and possibly NT symlinks too). + * In case of success, add the newly found mapping to the list and use it + * to build the Win32 path. + */ + + for (ListEntry = MappingList->List.Flink; + ListEntry != &MappingList->List; + ListEntry = ListEntry->Flink) + { + Entry = CONTAINING_RECORD(ListEntry, NT_WIN32_PATH_MAPPING, ListEntry); + + DPRINT("Testing '%S' --> '%S'\n", Entry->Win32Path, Entry->NtPath); + + /* Check whether the queried NT path prefixes the user-provided NT path */ + FoundDrive = !_wcsnicmp(pwszNtPathToMap, Entry->NtPath, wcslen(Entry->NtPath)); + if (FoundDrive) + { + /* Found it! */ + + /* Set the pointers and go build the Win32 path */ + pwszDrive = Entry->Win32Path; + pwszRemaining = pwszNTPath + wcslen(Entry->NtPath); + goto Quit; + } + } + + /* + * No mapping exists for this path yet: try to find one now. + */ + /* Retrieve the mounted drives (available drive letters) */ cchDrives = GetLogicalDriveStringsW(_countof(wszDrives) - 1, wszDrives); if (cchDrives == 0 || cchDrives >= _countof(wszDrives)) { /* Buffer too small or failure */ - DPRINT1("GetLogicalDriveStringsW failed\n"); + DPRINT1("ConvertNtPathToWin32Path: GetLogicalDriveStringsW failed\n"); return FALSE; } - -Retry: // We go back there once if RetryOnce == TRUE +/* We go back there once if RetryOnce == TRUE */ +Retry: /* Enumerate the mounted drives */ for (pwszDrive = wszDrives; *pwszDrive; pwszDrive += wcslen(pwszDrive) + 1) @@ -1235,25 +1297,87 @@ Retry: // We go back there once if RetryOnce == TRUE DPRINT("Testing '%S' --> '%S'\n", pwszDrive, wszNTPath); /* Check whether the queried NT path prefixes the user-provided NT path */ - if (!_wcsnicmp(wszNTPath, pwszNTPath, wcslen(wszNTPath))) + FoundDrive = !_wcsnicmp(pwszNtPathToMap, wszNTPath, wcslen(wszNTPath)); + if (!FoundDrive) + { + PWCHAR ptr, ptr2; + + /* + * Check whether this was a network share that has a drive letter, + * but the user-provided NT path points to this share without + * mentioning the drive letter. + * + * The format is: \Device\\;X:\share\path + * The corresponding drive letter is 'X'. + * A system-provided network redirector (LanManRedirector or Mup) + * or a 3rd-party one may be used. + * + * We check whether the user-provided NT path has the form: + * \Device\\\share\path + * as it obviously did not have the full form (the previous check + * would have been OK otherwise). + */ + if (!_wcsnicmp(wszNTPath, L"\\Device\\", _countof(L"\\Device\\")-1) && + (ptr = wcschr(wszNTPath + _countof(L"\\Device\\")-1, L'\\')) && + wcslen(++ptr) >= 3 && ptr[0] == L';' && ptr[2] == L':') + { + /* + * Normally the specified drive letter should correspond + * to the one used for the mapping. But we will ignore + * if it happens not to be the case. + */ + if (pwszDrive[0] != ptr[1]) + { + DPRINT1("Peculiar: expected network share drive letter %C different from actual one %C\n", + pwszDrive[0], ptr[1]); + } + + /* Remove the drive letter from the NT network share path */ + ptr2 = ptr + 3; + /* Swallow as many possible consecutive backslashes as there could be */ + while (*ptr2 == L'\\') ++ptr2; + + memmove(ptr, ptr2, (wcslen(ptr2) + 1) * sizeof(WCHAR)); + + /* Now do the check again */ + FoundDrive = !_wcsnicmp(pwszNtPathToMap, wszNTPath, wcslen(wszNTPath)); + } + } + if (FoundDrive) { /* Found it! */ - FoundDrive = TRUE; - if (!RetryOnce && pwszNTPath != TargetPath) + + pwszDrive[2] = UNICODE_NULL; // Remove the backslash + + if (pwszNtPathToMap == pwszNTPath) + { + ASSERT(!RetryOnce && pwszNTPath != TargetPath); pwszRemaining = pwszNTPath + wcslen(wszNTPath); + } break; } } if (FoundDrive) { - pwszDrive[2] = UNICODE_NULL; // Remove the backslash - StringCchPrintfW(pwszPath, cchPathMax, - L"%s%s", - pwszDrive, - pwszRemaining); - DPRINT1("ConvertNtPathToWin32Path: %S\n", pwszPath); - return TRUE; + /* A mapping was found, add it to the cache */ + Entry = HeapAlloc(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*Entry)); + if (!Entry) + { + DPRINT1("ConvertNtPathToWin32Path: Cannot allocate memory\n"); + return FALSE; + } + StringCchCopyNW(Entry->NtPath, _countof(Entry->NtPath), + pwszNTPath, pwszRemaining - pwszNTPath); + StringCchCopyW(Entry->Win32Path, _countof(Entry->Win32Path), pwszDrive); + + /* Insert it as the most recent entry */ + InsertHeadList(&MappingList->List, &Entry->ListEntry); + MappingList->MappingsCount++; + + /* Set the pointers and go build the Win32 path */ + pwszDrive = Entry->Win32Path; + goto Quit; } /* @@ -1309,10 +1433,12 @@ Retry: // We go back there once if RetryOnce == TRUE if (!NT_SUCCESS(Status)) { /* Not a symlink, or something else happened: bail out */ - DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", &SymLink, Status); + DPRINT1("ConvertNtPathToWin32Path: NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", + &SymLink, Status); return FALSE; } + *TargetPath = UNICODE_NULL; RtlInitEmptyUnicodeString(&Target, TargetPath, sizeof(TargetPath)); /* Resolve the link and close its handle */ @@ -1323,25 +1449,41 @@ Retry: // We go back there once if RetryOnce == TRUE if (!NT_SUCCESS(Status)) { /* Not a symlink, or something else happened: bail out */ - DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", &SymLink, Status); + DPRINT1("ConvertNtPathToWin32Path: NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n", + &SymLink, Status); return FALSE; } - /* Set pointers */ + /* Set the pointers */ pwszRemaining = pwszNTPath + Length; - pwszNTPath = TargetPath; + pwszNtPathToMap = TargetPath; // Point to our local buffer /* Retry once */ RetryOnce = TRUE; goto Retry; } + ASSERT(!FoundDrive); + +Quit: + if (FoundDrive) + { + StringCchPrintfW(pwszPath, cchPathMax, + L"%s%s", + pwszDrive, + pwszRemaining); + DPRINT("ConvertNtPathToWin32Path: %S\n", pwszPath); + return TRUE; + } + return FALSE; } /* Used to enable and disable the shutdown privilege */ /* static */ BOOL -EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege) +EnablePrivilege( + IN LPCWSTR lpszPrivilegeName, + IN BOOL bEnablePrivilege) { BOOL Success; HANDLE hToken; @@ -1382,6 +1524,14 @@ _tWinMain(HINSTANCE hInst, ProcessHeap = GetProcessHeap(); + SetupData.hInstance = hInst; + SetupData.hInstallThread = NULL; + SetupData.hHaltInstallEvent = NULL; + SetupData.bStopInstall = FALSE; + + /* Initialize the NT to Win32 path prefix mapping list */ + InitNtToWin32PathMappingList(&SetupData.MappingList); + /* Initialize Setup, phase 0 */ InitializeSetup(&SetupData.USetupData, 0); @@ -1406,11 +1556,6 @@ _tWinMain(HINSTANCE hInst, if (!LoadSetupData(&SetupData)) goto Quit; - SetupData.hInstance = hInst; - SetupData.hInstallThread = NULL; - SetupData.hHaltInstallEvent = NULL; - SetupData.bStopInstall = FALSE; - CheckUnattendedSetup(&SetupData.USetupData); SetupData.bUnattend = IsUnattendedSetup; // FIXME :-) @@ -1544,6 +1689,9 @@ Quit: /* Setup has finished */ FinishSetup(&SetupData.USetupData); + /* Free the NT to Win32 path prefix mapping list */ + FreeNtToWin32PathMappingList(&SetupData.MappingList); + #if 0 // NOTE: Disabled for testing purposes only! EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); ExitWindowsEx(EWX_REBOOT, 0); diff --git a/base/setup/reactos/reactos.h b/base/setup/reactos/reactos.h index 0c4a76eb408..ded1d85f74b 100644 --- a/base/setup/reactos/reactos.h +++ b/base/setup/reactos/reactos.h @@ -66,6 +66,35 @@ typedef struct _KBLAYOUT #endif +/* + * A mapping entry that maps an NT path to a corresponding Win32 path. + * + * Example is: + * NT path: "\Device\Harddisk0\Partition1\some\path1" + * Win32 path: "C:\some\path1" + * + * Here, the NT path prefix to be cached is only + * "\Device\Harddisk0\Partition1\", to be mapped with "C:\". + * + * Then the same entry would be reused if one wants to convert + * the NT path "\Device\Harddisk0\Partition1\another\path2", + * which converts to the Win32 path "C:\another\path2" . + */ +typedef struct _NT_WIN32_PATH_MAPPING +{ + LIST_ENTRY ListEntry; + WCHAR NtPath[MAX_PATH]; // MAX_PATH for both entries should be more than enough. + WCHAR Win32Path[MAX_PATH]; +} NT_WIN32_PATH_MAPPING, *PNT_WIN32_PATH_MAPPING; + +/* The list of NT to Win32 path prefix mappings */ +typedef struct _NT_WIN32_PATH_MAPPING_LIST +{ + LIST_ENTRY List; + ULONG MappingsCount; +} NT_WIN32_PATH_MAPPING_LIST, *PNT_WIN32_PATH_MAPPING_LIST; + + typedef struct _SETUPDATA { /* General */ @@ -81,6 +110,8 @@ typedef struct _SETUPDATA TCHAR szAbortMessage[512]; TCHAR szAbortTitle[64]; + NT_WIN32_PATH_MAPPING_LIST MappingList; + USETUP_DATA USetupData; BOOLEAN RepairUpdateFlag; // flag for update/repair an installed reactos @@ -113,6 +144,8 @@ typedef struct _SETUPDATA extern HANDLE ProcessHeap; extern BOOLEAN IsUnattendedSetup; +extern SETUPDATA SetupData; + typedef struct _IMGINFO { @@ -128,6 +161,7 @@ typedef struct _IMGINFO */ BOOL ConvertNtPathToWin32Path( + IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList, OUT PWSTR pwszPath, IN DWORD cchPathMax, IN PCWSTR pwszNTPath); diff --git a/base/setup/reactos/spapisup/fileqsup.c b/base/setup/reactos/spapisup/fileqsup.c index 9c15f8cdaca..ea46c03f75f 100644 --- a/base/setup/reactos/spapisup/fileqsup.c +++ b/base/setup/reactos/spapisup/fileqsup.c @@ -38,7 +38,8 @@ SpFileQueueCopy_NtToWin32( * the Win32 SetupQueueCopyW API only takes Win32 paths. We therefore * map the NT path to Win32 path and then call the Win32 API. */ - if (!ConvertNtPathToWin32Path(Win32SourceRootPath, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32SourceRootPath, _countof(Win32SourceRootPath), SourceRootPath)) { @@ -46,7 +47,8 @@ SpFileQueueCopy_NtToWin32( } /* SourcePath, SourceFileName and SourceCabinet are appended to SourceRootPath by the SetupApi function */ - if (!ConvertNtPathToWin32Path(Win32TargetDirectory, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32TargetDirectory, _countof(Win32TargetDirectory), TargetDirectory)) { @@ -64,7 +66,7 @@ SpFileQueueCopy_NtToWin32( SourcePath, SourceFileName, // Undocumented on MSDN is the fact that this parameter is mandatory *IF* one wants to take the TagFile into account! - L"foobar", + L"ReactOS", // SourceTagFile -- Special behaviour: use cabinet file present in ArchiveDir path! The API does not check for a ".cab" extension. SourceCabinet, Win32TargetDirectory, @@ -88,7 +90,8 @@ SpFileQueueDelete_NtToWin32( * the Win32 SetupQueueDeleteW API only takes Win32 paths. We therefore * map the NT path to Win32 path and then call the Win32 API. */ - if (!ConvertNtPathToWin32Path(Win32PathPart1, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32PathPart1, _countof(Win32PathPart1), PathPart1)) { @@ -116,7 +119,8 @@ SpFileQueueRename_NtToWin32( * the Win32 SetupQueueRenameW API only takes Win32 paths. We therefore * map the NT path to Win32 path and then call the Win32 API. */ - if (!ConvertNtPathToWin32Path(Win32SourcePath, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32SourcePath, _countof(Win32SourcePath), SourcePath)) { @@ -126,7 +130,8 @@ SpFileQueueRename_NtToWin32( if (TargetPath) { - if (!ConvertNtPathToWin32Path(Win32TargetPath, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32TargetPath, _countof(Win32TargetPath), TargetPath)) { diff --git a/base/setup/reactos/spapisup/infsupp.c b/base/setup/reactos/spapisup/infsupp.c index 7c40e8421e3..98e48b07bb3 100644 --- a/base/setup/reactos/spapisup/infsupp.c +++ b/base/setup/reactos/spapisup/infsupp.c @@ -48,7 +48,8 @@ SetupOpenInfFileExW( * the Win32 SetupOpenInfFileW API only takes Win32 paths. We therefore * map the NT path to Win32 path and then call the Win32 API. */ - if (!ConvertNtPathToWin32Path(Win32FileName, + if (!ConvertNtPathToWin32Path(&SetupData.MappingList, + Win32FileName, _countof(Win32FileName), FileName)) {