[REACTOS] Add helpers and modify ConvertNtPathToWin32Path() to make it use a cache...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 9 Jan 2018 02:21:38 +0000 (03:21 +0100)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 5 Nov 2018 23:04:30 +0000 (00:04 +0100)
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
base/setup/reactos/reactos.h
base/setup/reactos/spapisup/fileqsup.c
base/setup/reactos/spapisup/infsupp.c

index 626b0ad..230eafa 100644 (file)
@@ -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\<network_redirector>\;X:<data>\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\<network_redirector>\<data>\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);
index 0c4a76e..ded1d85 100644 (file)
@@ -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);
index 9c15f8c..ea46c03 100644 (file)
@@ -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))
         {
index 7c40e84..98e48b0 100644 (file)
@@ -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))
     {