Precision: r58138 was part 2a/3 of my changes.
[reactos.git] / reactos / dll / win32 / kernel32 / client / path.c
index bfa5872..2c92cce 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
-UNICODE_STRING BaseDllDirectory;
 UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
-UNICODE_STRING SystemDirectory;
-UNICODE_STRING WindowsDirectory;
-UNICODE_STRING BaseDefaultPathAppend;
-UNICODE_STRING BaseDefaultPath;
+
+UNICODE_STRING BaseWindowsSystemDirectory, BaseWindowsDirectory;
+UNICODE_STRING BaseDefaultPathAppend, BaseDefaultPath, BaseDllDirectory;
+
+PVOID gpTermsrvGetWindowsDirectoryA;
+PVOID gpTermsrvGetWindowsDirectoryW;
 
 /* This is bitmask for each illegal filename character */
 /* If someone has time, please feel free to use 0b notation */
@@ -32,8 +33,517 @@ DWORD IllegalMask[4] =
     0x10000000  // 7C not allowed
 };
 
+BASE_SEARCH_PATH_TYPE BaseDllOrderCurrent[BaseCurrentDirPlacementMax][BaseSearchPathMax] =
+{
+    {
+        BaseSearchPathApp,
+        BaseSearchPathCurrent,
+        BaseSearchPathDefault,
+        BaseSearchPathEnv,
+        BaseSearchPathInvalid
+    },
+    {
+        BaseSearchPathApp,
+        BaseSearchPathDefault,
+        BaseSearchPathCurrent,
+        BaseSearchPathEnv,
+        BaseSearchPathInvalid
+    }
+};
+
+BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent[BaseSearchPathMax] =
+{
+    BaseSearchPathApp,
+    BaseSearchPathDefault,
+    BaseSearchPathEnv,
+    BaseSearchPathInvalid,
+    BaseSearchPathInvalid
+};
+
+BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent[BaseSearchPathMax] =
+{
+    BaseSearchPathApp,
+    BaseSearchPathDll,
+    BaseSearchPathDefault,
+    BaseSearchPathEnv,
+    BaseSearchPathInvalid
+};
+
+BASE_SEARCH_PATH_TYPE BaseProcessOrder[BaseSearchPathMax] =
+{
+    BaseSearchPathApp,
+    BaseSearchPathCurrent,
+    BaseSearchPathDefault,
+    BaseSearchPathEnv,
+    BaseSearchPathInvalid
+};
+
+BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement = BaseCurrentDirPlacementInvalid;
+
+extern UNICODE_STRING BasePathVariableName;
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
+PWCHAR
+WINAPI
+BasepEndOfDirName(IN PWCHAR FileName)
+{
+    PWCHAR FileNameEnd, FileNameSeparator;
+
+    /* Find the first slash */
+    FileNameSeparator = wcschr(FileName, OBJ_NAME_PATH_SEPARATOR);
+    if (FileNameSeparator)
+    {
+        /* Find the last one */
+        FileNameEnd = wcsrchr(FileNameSeparator, OBJ_NAME_PATH_SEPARATOR);
+        ASSERT(FileNameEnd);
+
+        /* Handle the case where they are one and the same */
+        if (FileNameEnd == FileNameSeparator) FileNameEnd++;
+    }
+    else
+    {
+        /* No directory was specified */
+        FileNameEnd = NULL;
+    }
+
+    /* Return where the directory ends and the filename starts */
+    return FileNameEnd;
+}
+
+LPWSTR
+WINAPI
+BasepComputeProcessPath(IN PBASE_SEARCH_PATH_TYPE PathOrder,
+                        IN LPWSTR AppName,
+                        IN LPVOID Environment)
+{
+    PWCHAR PathBuffer, Buffer, AppNameEnd, PathCurrent;
+    ULONG PathLengthInBytes;
+    NTSTATUS Status;
+    UNICODE_STRING EnvPath;
+    PBASE_SEARCH_PATH_TYPE Order;
+
+    /* Initialize state */
+    AppNameEnd = Buffer = PathBuffer = NULL;
+    Status = STATUS_SUCCESS;
+    PathLengthInBytes = 0;
+
+    /* Loop the ordering array */
+    for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
+    switch (*Order)
+    {
+        /* Compute the size of the DLL path */
+        case BaseSearchPathDll:
+
+            /* This path only gets called if SetDllDirectory was called */
+            ASSERT(BaseDllDirectory.Buffer != NULL);
+
+            /* Make sure there's a DLL directory size */
+            if (BaseDllDirectory.Length)
+            {
+                /* Add it, plus the separator */
+                PathLengthInBytes += BaseDllDirectory.Length + sizeof(L';');
+            }
+            break;
+
+        /* Compute the size of the current path */
+        case BaseSearchPathCurrent:
+
+            /* Add ".;" */
+            PathLengthInBytes += (2 * sizeof(WCHAR));
+            break;
+
+        /* Compute the size of the "PATH" environment variable */
+        case BaseSearchPathEnv:
+
+            /* Grab PEB lock if one wasn't passed in */
+            if (!Environment) RtlAcquirePebLock();
+
+            /* Query the size first */
+            EnvPath.MaximumLength = 0;
+            Status = RtlQueryEnvironmentVariable_U(Environment,
+                                                   &BasePathVariableName,
+                                                   &EnvPath);
+            if (Status == STATUS_BUFFER_TOO_SMALL)
+            {
+                /* Compute the size we'll need for the environment */
+                EnvPath.MaximumLength = EnvPath.Length + sizeof(WCHAR);
+                if ((EnvPath.Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
+                {
+                    /* Don't let it overflow */
+                    EnvPath.MaximumLength = EnvPath.Length;
+                }
+
+                /* Allocate the environment buffer */
+                Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
+                                         0,
+                                         EnvPath.MaximumLength);
+                if (Buffer)
+                {
+                    /* Now query the PATH environment variable */
+                    EnvPath.Buffer = Buffer;
+                    Status = RtlQueryEnvironmentVariable_U(Environment,
+                                                           &BasePathVariableName,
+                                                           &EnvPath);
+                }
+                else
+                {
+                    /* Failure case */
+                    Status = STATUS_NO_MEMORY;
+                }
+            }
+
+            /* Release the PEB lock from above */
+            if (!Environment) RtlReleasePebLock();
+
+            /* There might not be a PATH */
+            if (Status == STATUS_VARIABLE_NOT_FOUND)
+            {
+                /* In this case, skip this PathOrder */
+                EnvPath.Length = EnvPath.MaximumLength = 0;
+                Status = STATUS_SUCCESS;
+            }
+            else if (!NT_SUCCESS(Status))
+            {
+                /* An early failure, go to exit code */
+                goto Quickie;
+            }
+            else
+            {
+                /* Add the length of the PATH variable */
+                ASSERT(!(EnvPath.Length & 1));
+                PathLengthInBytes += (EnvPath.Length + sizeof(L';'));
+            }
+            break;
+
+        /* Compute the size of the default search path */
+        case BaseSearchPathDefault:
+
+            /* Just add it... it already has a ';' at the end */
+            ASSERT(!(BaseDefaultPath.Length & 1));
+            PathLengthInBytes += BaseDefaultPath.Length;
+            break;
+
+        /* Compute the size of the current app directory */
+        case BaseSearchPathApp:
+            /* Find out where the app name ends, to get only the directory */
+            if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
+
+            /* Check if there was no application name passed in */
+            if (!(AppName) || !(AppNameEnd))
+            {
+                /* Do we have a per-thread CURDIR to use? */
+                if (NtCurrentTeb()->NtTib.SubSystemTib)
+                {
+                    /* This means someone added RTL_PERTHREAD_CURDIR */
+                    UNIMPLEMENTED_DBGBREAK();
+                }
+
+                /* We do not. Do we have the LDR_ENTRY for the executable? */
+                if (!BasepExeLdrEntry)
+                {
+                    /* We do not. Grab it */
+                    LdrEnumerateLoadedModules(0,
+                                              BasepLocateExeLdrEntry,
+                                              NtCurrentPeb()->ImageBaseAddress);
+                }
+
+                /* Now do we have it? */
+                if (BasepExeLdrEntry)
+                {
+                    /* Yes, so read the name out of it */
+                    AppName = BasepExeLdrEntry->FullDllName.Buffer;
+                }
+
+                /* Find out where the app name ends, to get only the directory */
+                if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
+            }
+
+            /* So, do we have an application name and its directory? */
+            if ((AppName) && (AppNameEnd))
+            {
+                /* Add the size of the app's directory, plus the separator */
+                PathLengthInBytes += ((AppNameEnd - AppName) * sizeof(WCHAR)) + sizeof(L';');
+            }
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    /* Bam, all done, we now have the final path size */
+    ASSERT(PathLengthInBytes > 0);
+    ASSERT(!(PathLengthInBytes & 1));
+
+    /* Allocate the buffer to hold it */
+    PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes);
+    if (!PathBuffer)
+    {
+        /* Failure path */
+        Status = STATUS_NO_MEMORY;
+        goto Quickie;
+    }
+
+    /* Now we loop again, this time to copy the data */
+    PathCurrent = PathBuffer;
+    for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
+    switch (*Order)
+    {
+        /* Add the DLL path */
+        case BaseSearchPathDll:
+            if (BaseDllDirectory.Length)
+            {
+                /* Copy it in the buffer, ASSERT there's enough space */
+                ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + BaseDllDirectory.Length) <= PathLengthInBytes);
+                RtlCopyMemory(PathCurrent,
+                              BaseDllDirectory.Buffer,
+                              BaseDllDirectory.Length);
+
+                /* Update the current pointer, add a separator */
+                PathCurrent += (BaseDllDirectory.Length / sizeof(WCHAR));
+                *PathCurrent++ = ';';
+            }
+            break;
+
+        /* Add the current application path */
+        case BaseSearchPathApp:
+            if ((AppName) && (AppNameEnd))
+            {
+                /* Copy it in the buffer, ASSERT there's enough space */
+                ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(WCHAR)) <= PathLengthInBytes);
+                RtlCopyMemory(PathCurrent,
+                              AppName,
+                              (AppNameEnd - AppName) * sizeof(WCHAR));
+
+                /* Update the current pointer, add a separator */
+                PathCurrent += AppNameEnd - AppName;
+                *PathCurrent++ = ';';
+            }
+            break;
+
+        /* Add the default search path */
+        case BaseSearchPathDefault:
+            /* Copy it in the buffer, ASSERT there's enough space */
+            ASSERT((((PathCurrent - PathBuffer) * sizeof(WCHAR)) + BaseDefaultPath.Length) <= PathLengthInBytes);
+            RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length);
+
+            /* Update the current pointer. The default path already has a ";" */
+            PathCurrent += (BaseDefaultPath.Length / sizeof(WCHAR));
+            break;
+
+        /* Add the path in the PATH environment variable */
+        case BaseSearchPathEnv:
+            if (EnvPath.Length)
+            {
+                /* Copy it in the buffer, ASSERT there's enough space */
+                ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + EnvPath.Length) <= PathLengthInBytes);
+                RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length);
+
+                /* Update the current pointer, add a separator */
+                PathCurrent += (EnvPath.Length / sizeof(WCHAR));
+                *PathCurrent++ = ';';
+            }
+            break;
+
+        /* Add the current dierctory */
+        case BaseSearchPathCurrent:
+
+            /* Copy it in the buffer, ASSERT there's enough space */
+            ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(WCHAR)) <= PathLengthInBytes);
+            *PathCurrent++ = '.';
+
+            /* Add the path separator */
+            *PathCurrent++ = ';';
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    /* Everything should've perfectly fit in there */
+    ASSERT((PathCurrent - PathBuffer) * sizeof(WCHAR) == PathLengthInBytes);
+    ASSERT(PathCurrent > PathBuffer);
+
+    /* Terminate the whole thing */
+    PathCurrent[-1] = UNICODE_NULL;
+
+Quickie:
+    /* Exit path: free our buffers */
+    if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+    if (PathBuffer)
+    {
+        /* This only gets freed in the failure path, since caller wants it */
+        if (!NT_SUCCESS(Status))
+        {
+            RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
+            PathBuffer = NULL;
+        }
+    }
+
+    /* Return the path! */
+    return PathBuffer;
+}
+
+LPWSTR
+WINAPI
+BaseComputeProcessSearchPath(VOID)
+{
+    DPRINT("Computing Process Search path\n");
+
+    /* Compute the path using default process order */
+    return BasepComputeProcessPath(BaseProcessOrder, NULL, NULL);
+}
+
+LPWSTR
+WINAPI
+BaseComputeProcessExePath(IN LPWSTR FullPath)
+{
+    PBASE_SEARCH_PATH_TYPE PathOrder;
+    DPRINT1("Computing EXE path: %wZ\n", FullPath);
+
+    /* Check if we should use the current directory */
+    PathOrder = NeedCurrentDirectoryForExePathW(FullPath) ?
+                BaseProcessOrder : BaseProcessOrderNoCurrent;
+
+    /* And now compute the path */
+    return BasepComputeProcessPath(PathOrder, NULL, NULL);
+}
+
+LPWSTR
+WINAPI
+BaseComputeProcessDllPath(IN LPWSTR FullPath,
+                          IN PVOID Environment)
+{
+    LPWSTR DllPath = NULL;
+    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
+    UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode");
+    OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
+    KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
+    HANDLE KeyHandle;
+    NTSTATUS Status;
+    ULONG ResultLength;
+    BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement, OldCurrentDirPlacement;
+
+    /* Acquire DLL directory lock */
+    RtlEnterCriticalSection(&BaseDllDirectoryLock);
+
+    /* Check if we have a base dll directory */
+    if (BaseDllDirectory.Buffer)
+    {
+        /* Then compute the process path using DLL order (without curdir) */
+        DllPath = BasepComputeProcessPath(BaseDllOrderNoCurrent, FullPath, Environment);
+
+        /* Release DLL directory lock */
+        RtlLeaveCriticalSection(&BaseDllDirectoryLock);
+
+        /* Return dll path */
+        return DllPath;
+    }
+
+    /* Release DLL directory lock */
+    RtlLeaveCriticalSection(&BaseDllDirectoryLock);
+
+    /* Read the current placement */
+    CurrentDirPlacement = BasepDllCurrentDirPlacement;
+    if (CurrentDirPlacement == BaseCurrentDirPlacementInvalid)
+    {
+        /* Open the configuration key */
+        Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
+        if (NT_SUCCESS(Status))
+        {
+            /* Query if safe search is enabled */
+            Status = NtQueryValueKey(KeyHandle,
+                                     &ValueName,
+                                     KeyValuePartialInformation,
+                                     &PartialInfo,
+                                     sizeof(PartialInfo),
+                                     &ResultLength);
+            if (NT_SUCCESS(Status))
+            {
+                /* Read the value if the size is OK */
+                if (ResultLength == sizeof(PartialInfo))
+                {
+                    CurrentDirPlacement = *(PULONG)PartialInfo.Data;
+                }
+            }
+
+            /* Close the handle */
+            NtClose(KeyHandle);
+
+            /* Validate the registry value */
+            if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
+                (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
+            {
+                /* Default to safe search */
+                CurrentDirPlacement = BaseCurrentDirPlacementSafe;
+            }
+        }
+
+        /* Update the placement and read the old one */
+        OldCurrentDirPlacement = InterlockedCompareExchange((PLONG)&BasepDllCurrentDirPlacement,
+                                                            CurrentDirPlacement,
+                                                            BaseCurrentDirPlacementInvalid);
+        if (OldCurrentDirPlacement != BaseCurrentDirPlacementInvalid)
+        {
+            /* If there already was a placement, use it */
+            CurrentDirPlacement = OldCurrentDirPlacement;
+        }
+    }
+
+    /* Check if the placement is invalid or not set */
+    if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
+        (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
+    {
+        /* Default to safe search */
+        CurrentDirPlacement = BaseCurrentDirPlacementSafe;
+    }
+
+    /* Compute the process path using either normal or safe search */
+    DllPath = BasepComputeProcessPath(BaseDllOrderCurrent[CurrentDirPlacement],
+                                      FullPath,
+                                      Environment);
+
+    /* Return dll path */
+    return DllPath;
+}
+
+BOOLEAN
+WINAPI
+CheckForSameCurdir(IN PUNICODE_STRING DirName)
+{
+    PUNICODE_STRING CurDir;
+    USHORT CurLength;
+    BOOLEAN Result;
+    UNICODE_STRING CurDirCopy;
+
+    CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
+
+    CurLength = CurDir->Length;
+    if (CurDir->Length <= 6)
+    {
+        if (CurLength != DirName->Length) return FALSE;
+    }
+    else
+    {
+        if ((CurLength - 2) != DirName->Length) return FALSE;
+    }
+
+    RtlAcquirePebLock();
+
+    CurDirCopy = *CurDir;
+    if (CurDirCopy.Length > 6) CurDirCopy.Length -= 2;
+
+    Result = 0;
+
+    if (RtlEqualUnicodeString(&CurDirCopy, DirName, TRUE)) Result = TRUE;
+
+    RtlReleasePebLock();
+
+    return Result;
+}
+
 /*
  * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
  * identical (other than the Rtl can optionally check for spaces), however the
@@ -111,7 +621,7 @@ IsShortName_U(IN PWCHAR Name,
         }
 
         /* Check if this is perhaps an extension? */
-        if (c == L'.')
+        if (c == '.')
         {
             /* Unless the extension is too large or there's more than one */
             if ((HasExtension) || (Dots > 3)) return FALSE;
@@ -144,10 +654,13 @@ IsLongName_U(IN PWCHAR FileName,
     for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
     {
         /* Check if this could be an extension */
-        if (*FileName == '.')
+        if (FileName[i] == L'.')
         {
             /* Unlike the short case, we WANT more than one extension, or a long one */
-            if ((HasExtension) || (Dots > 3)) return TRUE;
+            if ((HasExtension) || (Dots > 3))
+            {
+                return TRUE;
+            }
             HasExtension = TRUE;
         }
 
@@ -219,7 +732,7 @@ FindLFNorSFN_U(IN PWCHAR Path,
 
 PWCHAR
 WINAPI
-SkipPathTypeIndicator_U(IN PWCHAR Path)
+SkipPathTypeIndicator_U(IN LPWSTR Path)
 {
     PWCHAR ReturnPath;
     ULONG i;
@@ -543,7 +1056,7 @@ GetFullPathNameA(IN LPCSTR lpFileName,
     if (!PathSize) goto Quickie;
 
     /* If the *caller's* buffer was too small, fail, but add in space for NULL */
-    if (PathSize < nBufferLength)
+    if (PathSize >= nBufferLength)
     {
         PathSize++;
         goto Quickie;
@@ -771,108 +1284,161 @@ Quickie:
     return PathSize;
 }
 
-/***********************************************************************
- *           ContainsPath (Wine name: contains_pathW)
- *
- * Check if the file name contains a path; helper for SearchPathW.
- * A relative path is not considered a path unless it starts with ./ or ../
- */
-static
-BOOL
-ContainsPath(LPCWSTR name)
-{
-    if (RtlDetermineDosPathNameType_U(name) != RtlPathTypeRelative) return TRUE;
-    if (name[0] != '.') return FALSE;
-    if (name[1] == '/' || name[1] == '\\' || name[1] == '\0') return TRUE;
-    return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
-}
-
-
 /*
  * @implemented
  */
 DWORD
 WINAPI
-SearchPathW(LPCWSTR lpPath,
-            LPCWSTR lpFileName,
-            LPCWSTR lpExtension,
-            DWORD nBufferLength,
-            LPWSTR lpBuffer,
-            LPWSTR *lpFilePart)
+SearchPathW(IN LPCWSTR lpPath,
+            IN LPCWSTR lpFileName,
+            IN LPCWSTR lpExtension,
+            IN DWORD nBufferLength,
+            IN LPWSTR lpBuffer,
+            OUT LPWSTR *lpFilePart)
 {
-    DWORD ret = 0;
+    UNICODE_STRING FileNameString, ExtensionString, PathString, CallerBuffer;
+    ULONG Flags, LengthNeeded, FilePartSize;
+    NTSTATUS Status;
+    DWORD Result = 0;
+
+    /* Default flags for RtlDosSearchPath_Ustr */
+    Flags = 6;
+
+    /* Clear file part in case we fail */
+    if (lpFilePart) *lpFilePart = NULL;
 
-    if (!lpFileName || !lpFileName[0])
+    /* Initialize path buffer for free later */
+    PathString.Buffer = NULL;
+
+    /* Convert filename to a unicode string and eliminate trailing spaces */
+    RtlInitUnicodeString(&FileNameString, lpFileName);
+    while ((FileNameString.Length >= sizeof(WCHAR)) &&
+           (FileNameString.Buffer[(FileNameString.Length / sizeof(WCHAR)) - 1] == L' '))
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
+        FileNameString.Length -= sizeof(WCHAR);
     }
 
-    /* If the name contains an explicit path, ignore the path */
-    if (ContainsPath(lpFileName))
+    /* Was it all just spaces? */
+    if (!FileNameString.Length)
     {
-        /* try first without extension */
-        if (RtlDoesFileExists_U(lpFileName))
-            return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
+        /* Fail out */
+        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
+        goto Quickie;
+    }
+
+    /* Convert extension to a unicode string */
+    RtlInitUnicodeString(&ExtensionString, lpExtension);
 
-        if (lpExtension)
+    /* Check if the user sent a path */
+    if (lpPath)
+    {
+        /* Convert it to a unicode string too */
+        Status = RtlInitUnicodeStringEx(&PathString, lpPath);
+        if (NT_ERROR(Status))
         {
-            LPCWSTR p = wcsrchr(lpFileName, '.');
-            if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
-                lpExtension = NULL;  /* Ignore the specified extension */
+            /* Fail if it was too long */
+            BaseSetLastNTError(Status);
+            goto Quickie;
         }
-
-        /* Allocate a buffer for the file name and extension */
-        if (lpExtension)
+    }
+    else
+    {
+        /* A path wasn't sent, so compute it ourselves */
+        PathString.Buffer = BaseComputeProcessSearchPath();
+        if (!PathString.Buffer)
         {
-            LPWSTR tmp;
-            DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
+            /* Fail if we couldn't compute it */
+            BaseSetLastNTError(STATUS_NO_MEMORY);
+            goto Quickie;
+        }
 
-            if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
-            {
-                SetLastError(ERROR_OUTOFMEMORY);
-                return 0;
-            }
-            wcscpy(tmp, lpFileName);
-            wcscat(tmp, lpExtension);
-            if (RtlDoesFileExists_U(tmp))
-                ret = GetFullPathNameW(tmp, nBufferLength, lpBuffer, lpFilePart);
-            RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
+        /* See how big the computed path is */
+        LengthNeeded = lstrlenW(PathString.Buffer);
+        if (LengthNeeded > UNICODE_STRING_MAX_CHARS)
+        {
+            /* Fail if it's too long */
+            BaseSetLastNTError(STATUS_NAME_TOO_LONG);
+            goto Quickie;
         }
+
+        /* Set the path size now that we have it */
+        PathString.MaximumLength = PathString.Length = LengthNeeded * sizeof(WCHAR);
+
+        /* Request SxS isolation from RtlDosSearchPath_Ustr */
+        Flags |= 1;
     }
-    else if (lpPath && lpPath[0])  /* search in the specified path */
+
+    /* Create the string that describes the output buffer from the caller */
+    CallerBuffer.Length = 0;
+    CallerBuffer.Buffer = lpBuffer;
+
+    /* How much space does the caller have? */
+    if (nBufferLength <= UNICODE_STRING_MAX_CHARS)
     {
-        ret = RtlDosSearchPath_U(lpPath,
-                                 lpFileName,
-                                 lpExtension,
-                                 nBufferLength * sizeof(WCHAR),
-                                 lpBuffer,
-                                 lpFilePart) / sizeof(WCHAR);
+        /* Add it into the string */
+        CallerBuffer.MaximumLength = nBufferLength * sizeof(WCHAR);
     }
-    else  /* search in the default path */
+    else
     {
-        WCHAR *DllPath = GetDllLoadPath(NULL);
+        /* Caller wants too much, limit it to the maximum length of a string */
+        CallerBuffer.MaximumLength = UNICODE_STRING_MAX_BYTES;
+    }
 
-        if (DllPath)
+    /* Call Rtl to do the work */
+    Status = RtlDosSearchPath_Ustr(Flags,
+                                   &PathString,
+                                   &FileNameString,
+                                   &ExtensionString,
+                                   &CallerBuffer,
+                                   NULL,
+                                   NULL,
+                                   &FilePartSize,
+                                   &LengthNeeded);
+    if (NT_ERROR(Status))
+    {
+        /* Check for unusual status codes */
+        if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
         {
-            ret = RtlDosSearchPath_U(DllPath,
-                                     lpFileName,
-                                     lpExtension,
-                                     nBufferLength * sizeof(WCHAR),
-                                     lpBuffer,
-                                     lpFilePart) / sizeof(WCHAR);
-            RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
+            /* Print them out since maybe an app needs fixing */
+            DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
+                     __FUNCTION__,
+                     &FileNameString,
+                     Status);
+            DbgPrint("    Path = %wZ\n", &PathString);
+        }
+
+        /* Check if the failure was due to a small buffer */
+        if (Status == STATUS_BUFFER_TOO_SMALL)
+        {
+            /* Check if the length was actually too big for Rtl to work with */
+            Result = LengthNeeded / sizeof(WCHAR);
+            if (Result > 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG);
         }
         else
         {
-            SetLastError(ERROR_OUTOFMEMORY);
-            return 0;
+            /* Some other error, set the error code */
+            BaseSetLastNTError(Status);
         }
     }
+    else
+    {
+        /* It worked! Write the file part now */
+        if (lpFilePart) *lpFilePart = &lpBuffer[FilePartSize];
+
+        /* Convert the final result length */
+        Result = CallerBuffer.Length / sizeof(WCHAR);
+    }
 
-    if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
+Quickie:
+    /* Check if there was a dynamic path stirng to free */
+    if ((PathString.Buffer != lpPath) && (PathString.Buffer))
+    {
+        /* And free it */
+        RtlFreeHeap(RtlGetProcessHeap(), 0, PathString.Buffer);
+    }
 
-    return ret;
+    /* Return the final result lenght */
+    return Result;
 }
 
 /*
@@ -890,7 +1456,7 @@ GetLongPathNameW(IN LPCWSTR lpszShortPath,
     HANDLE FindHandle;
     DWORD ReturnLength;
     ULONG ErrorMode;
-    BOOLEAN Found;
+    BOOLEAN Found = FALSE;
     WIN32_FIND_DATAW FindFileData;
 
     /* Initialize so Quickie knows there's nothing to do */
@@ -917,13 +1483,12 @@ GetLongPathNameW(IN LPCWSTR lpszShortPath,
     }
 
     /* Now get a pointer to the actual path, skipping indicators */
-    Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
-
-    /* Try to find a file name in there */
-    if (Path) Found = FindLFNorSFN_U(Path, &First, &Last, FALSE);
+    Path = SkipPathTypeIndicator_U((LPWSTR)lpszShortPath);
 
     /* Is there any path or filename in there? */
-    if (!(Path) || (*Path == UNICODE_NULL) || !(Found))
+    if (!(Path) ||
+        (*Path == UNICODE_NULL) ||
+        !(FindLFNorSFN_U(Path, &First, &Last, FALSE)))
     {
         /* There isn't, so the long path is simply the short path */
         ReturnLength = wcslen(lpszShortPath);
@@ -953,7 +1518,7 @@ GetLongPathNameW(IN LPCWSTR lpszShortPath,
     Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
     if (!Original) goto ErrorQuickie;
 
-    /* Make a copy oft */
+    /* Make a copy of it */
     RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR));
 
     /* Compute the new first and last markers */
@@ -1269,7 +1834,7 @@ GetShortPathNameW(IN LPCWSTR lpszLongPath,
     HANDLE FindHandle;
     DWORD ReturnLength;
     ULONG ErrorMode;
-    BOOLEAN Found;
+    BOOLEAN Found = FALSE;
     WIN32_FIND_DATAW FindFileData;
 
     /* Initialize so Quickie knows there's nothing to do */
@@ -1288,7 +1853,7 @@ GetShortPathNameW(IN LPCWSTR lpszLongPath,
     ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
 
     /* Do a simple check to see if the path exists */
-    if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
+    if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
     {
         /* Windows checks for an application compatibility flag to allow this */
         if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
@@ -1300,19 +1865,18 @@ GetShortPathNameW(IN LPCWSTR lpszLongPath,
     }
 
     /* Now get a pointer to the actual path, skipping indicators */
-    Path = SkipPathTypeIndicator_U((PWCHAR)lpszShortPath);
-
-    /* Try to find a file name in there */
-    if (Path) Found = FindLFNorSFN_U(Path, &First, &Last, TRUE);
+    Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath);
 
     /* Is there any path or filename in there? */
-    if (!(Path) || (*Path == UNICODE_NULL) || !(Found))
+    if (!(Path) ||
+        (*Path == UNICODE_NULL) ||
+        !(FindLFNorSFN_U(Path, &First, &Last, TRUE)))
     {
         /* There isn't, so the long path is simply the short path */
         ReturnLength = wcslen(lpszLongPath);
 
         /* Is there space for it? */
-        if ((cchBuffer > ReturnLength) && (lpszLongPath))
+        if ((cchBuffer > ReturnLength) && (lpszShortPath))
         {
             /* Make sure the pointers aren't already the same */
             if (lpszLongPath != lpszShortPath)
@@ -1475,81 +2039,6 @@ Quickie:
     return ReturnLength;
 }
 
-/*
- * @implemented
- */
-DWORD
-WINAPI
-GetCurrentDirectoryA(IN DWORD nBufferLength,
-                     IN LPSTR lpBuffer)
-{
-   WCHAR BufferW[MAX_PATH];
-   DWORD ret;
-
-   ret = GetCurrentDirectoryW(MAX_PATH, BufferW);
-
-   if (!ret) return 0;
-   if (ret > MAX_PATH)
-   {
-      SetLastError(ERROR_FILENAME_EXCED_RANGE);
-      return 0;
-   }
-
-   return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
-}
-
-/*
- * @implemented
- */
-DWORD
-WINAPI
-GetCurrentDirectoryW(IN DWORD nBufferLength,
-                     IN LPWSTR lpBuffer)
-{
-    ULONG Length;
-
-    Length = RtlGetCurrentDirectory_U (nBufferLength * sizeof(WCHAR), lpBuffer);
-    return (Length / sizeof (WCHAR));
-}
-
-/*
- * @implemented
- */
-BOOL
-WINAPI
-SetCurrentDirectoryA(IN LPCSTR lpPathName)
-{
-   PWCHAR PathNameW;
-
-   DPRINT("setcurrdir: %s\n",lpPathName);
-
-   if (!(PathNameW = FilenameA2W(lpPathName, FALSE))) return FALSE;
-
-   return SetCurrentDirectoryW(PathNameW);
-}
-
-/*
- * @implemented
- */
-BOOL
-WINAPI
-SetCurrentDirectoryW(IN LPCWSTR lpPathName)
-{
-    UNICODE_STRING UnicodeString;
-    NTSTATUS Status;
-
-    RtlInitUnicodeString(&UnicodeString, lpPathName);
-
-    Status = RtlSetCurrentDirectory_U(&UnicodeString);
-    if (!NT_SUCCESS(Status))
-    {
-        BaseSetLastNTError (Status);
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
 /*
  * @implemented
  *
@@ -1636,6 +2125,143 @@ GetTempPathW(IN DWORD count,
    return ret;
 }
 
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetCurrentDirectoryA(IN DWORD nBufferLength,
+                     IN LPSTR lpBuffer)
+{
+    ANSI_STRING AnsiString;
+    NTSTATUS Status;
+    PUNICODE_STRING StaticString;
+    ULONG MaxLength;
+
+    StaticString = &NtCurrentTeb()->StaticUnicodeString;
+
+    MaxLength = nBufferLength;
+    if (nBufferLength >= UNICODE_STRING_MAX_BYTES)
+    {
+        MaxLength = UNICODE_STRING_MAX_BYTES - 1;
+    }
+
+    StaticString->Length = RtlGetCurrentDirectory_U(StaticString->MaximumLength,
+                                                    StaticString->Buffer);
+    Status = RtlUnicodeToMultiByteSize(&nBufferLength,
+                                       StaticString->Buffer,
+                                       StaticString->Length);
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return 0;
+    }
+
+    if (MaxLength <= nBufferLength)
+    {
+        return nBufferLength + 1;
+    }
+
+    AnsiString.Buffer = lpBuffer;
+    AnsiString.MaximumLength = MaxLength;
+    Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return 0;
+    }
+
+    return AnsiString.Length;
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetCurrentDirectoryW(IN DWORD nBufferLength,
+                     IN LPWSTR lpBuffer)
+{
+    return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetCurrentDirectoryA(IN LPCSTR lpPathName)
+{
+    PUNICODE_STRING DirName;
+    NTSTATUS Status;
+
+    if (!lpPathName)
+    {
+        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    DirName = Basep8BitStringToStaticUnicodeString(lpPathName);
+    if (!DirName) return FALSE;
+
+    if (CheckForSameCurdir(DirName)) return TRUE;
+
+    Status = RtlSetCurrentDirectory_U(DirName);
+    if (NT_SUCCESS(Status)) return TRUE;
+
+    if ((*DirName->Buffer != L'"') || (DirName->Length <= 2))
+    {
+        BaseSetLastNTError(Status);
+        return 0;
+    }
+
+    DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1);
+    if (!DirName) return FALSE;
+
+    Status = RtlSetCurrentDirectory_U(DirName);
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetCurrentDirectoryW(IN LPCWSTR lpPathName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING UnicodeString;
+
+    if (!lpPathName)
+    {
+        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName);
+    if (NT_SUCCESS(Status))
+    {
+        if (!CheckForSameCurdir(&UnicodeString))
+        {
+            Status = RtlSetCurrentDirectory_U(&UnicodeString);
+        }
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        BaseSetLastNTError(Status);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 /*
  * @implemented
  */
@@ -1644,7 +2270,26 @@ WINAPI
 GetSystemDirectoryA(IN LPSTR lpBuffer,
                     IN UINT uSize)
 {
-   return FilenameU2A_FitOrFail(lpBuffer, uSize, &SystemDirectory);
+    ANSI_STRING AnsiString;
+    NTSTATUS Status;
+    ULONG AnsiLength;
+
+    /* Get the correct size of the Unicode Base directory */
+    Status = RtlUnicodeToMultiByteSize(&AnsiLength,
+                                       BaseWindowsSystemDirectory.Buffer,
+                                       BaseWindowsSystemDirectory.MaximumLength);
+    if (!NT_SUCCESS(Status)) return 0;
+
+    if (uSize < AnsiLength) return AnsiLength;
+
+    RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
+
+    Status = BasepUnicodeStringTo8BitString(&AnsiString,
+                                            &BaseWindowsSystemDirectory,
+                                            FALSE);
+    if (!NT_SUCCESS(Status)) return 0;
+
+    return AnsiString.Length;
 }
 
 /*
@@ -1655,21 +2300,20 @@ WINAPI
 GetSystemDirectoryW(IN LPWSTR lpBuffer,
                     IN UINT uSize)
 {
-    ULONG Length;
-
-    Length = SystemDirectory.Length / sizeof (WCHAR);
+    ULONG ReturnLength;
 
-    if (lpBuffer == NULL) return Length + 1;
-
-    if (uSize > Length)
+    ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
+    if ((uSize * sizeof(WCHAR)) >= ReturnLength)
     {
-        memmove(lpBuffer, SystemDirectory.Buffer, SystemDirectory.Length);
-        lpBuffer[Length] = 0;
+        RtlCopyMemory(lpBuffer,
+                      BaseWindowsSystemDirectory.Buffer,
+                      BaseWindowsSystemDirectory.Length);
+        lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
 
-        return Length;   //good: ret chars excl. nullchar
+        ReturnLength = BaseWindowsSystemDirectory.Length;
     }
 
-    return Length+1;    //bad: ret space needed incl. nullchar
+    return ReturnLength / sizeof(WCHAR);
 }
 
 /*
@@ -1680,7 +2324,11 @@ WINAPI
 GetWindowsDirectoryA(IN LPSTR lpBuffer,
                      IN UINT uSize)
 {
-   return FilenameU2A_FitOrFail(lpBuffer, uSize, &WindowsDirectory);
+    /* Is this a TS installation? */
+    if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
+
+    /* Otherwise, call the System API */
+    return GetSystemWindowsDirectoryA(lpBuffer, uSize);
 }
 
 /*
@@ -1691,21 +2339,11 @@ WINAPI
 GetWindowsDirectoryW(IN LPWSTR lpBuffer,
                      IN UINT uSize)
 {
-    ULONG Length;
-
-    Length = WindowsDirectory.Length / sizeof (WCHAR);
-
-    if (lpBuffer == NULL) return Length + 1;
-
-    if (uSize > Length)
-    {
-        memmove(lpBuffer, WindowsDirectory.Buffer, WindowsDirectory.Length);
-        lpBuffer[Length] = 0;
+    /* Is this a TS installation? */
+    if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
 
-        return Length;   //good: ret chars excl. nullchar
-    }
-
-    return Length+1;   //bad: ret space needed incl. nullchar
+    /* Otherwise, call the System API */
+    return GetSystemWindowsDirectoryW(lpBuffer, uSize);
 }
 
 /*
@@ -1716,7 +2354,26 @@ WINAPI
 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer,
                            IN UINT uSize)
 {
-    return GetWindowsDirectoryA(lpBuffer, uSize);
+    ANSI_STRING AnsiString;
+    NTSTATUS Status;
+    ULONG AnsiLength;
+
+    /* Get the correct size of the Unicode Base directory */
+    Status = RtlUnicodeToMultiByteSize(&AnsiLength,
+                                       BaseWindowsDirectory.Buffer,
+                                       BaseWindowsDirectory.MaximumLength);
+    if (!NT_SUCCESS(Status)) return 0;
+
+    if (uSize < AnsiLength) return AnsiLength;
+
+    RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
+
+    Status = BasepUnicodeStringTo8BitString(&AnsiString,
+                                            &BaseWindowsDirectory,
+                                            FALSE);
+    if (!NT_SUCCESS(Status)) return 0;
+
+    return AnsiString.Length;
 }
 
 /*
@@ -1727,7 +2384,20 @@ WINAPI
 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer,
                            IN UINT uSize)
 {
-    return GetWindowsDirectoryW(lpBuffer, uSize);
+    ULONG ReturnLength;
+
+    ReturnLength = BaseWindowsDirectory.MaximumLength;
+    if ((uSize * sizeof(WCHAR)) >= ReturnLength)
+    {
+        RtlCopyMemory(lpBuffer,
+                      BaseWindowsDirectory.Buffer,
+                      BaseWindowsDirectory.Length);
+        lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
+
+        ReturnLength = BaseWindowsDirectory.Length;
+    }
+
+    return ReturnLength / sizeof(WCHAR);
 }
 
 /*