Precision: r58138 was part 2a/3 of my changes.
[reactos.git] / reactos / dll / win32 / kernel32 / client / path.c
index e8f2767..2c92cce 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
-UNICODE_STRING BaseDllDirectory;
 UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
 
+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 */
 DWORD IllegalMask[4] =
@@ -28,7 +33,516 @@ DWORD IllegalMask[4] =
     0x10000000  // 7C not allowed
 };
 
-/* FUNCTIONS ******************************************************************/
+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
@@ -57,7 +571,7 @@ IsShortName_U(IN PWCHAR Name,
     /* Sure, any emtpy name is a short name */
     if (!Length) return TRUE;
 
-    /* This could be . or .. or somethign else */
+    /* This could be . or .. or something else */
     if (*Name == L'.')
     {
         /* Which one is it */
@@ -101,13 +615,13 @@ IsShortName_U(IN PWCHAR Name,
         }
 
         /* Check for illegal characters */
-        if ((c > 0x7F) || (IllegalMask[c / 32] && (1 << (c % 32))))
+        if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
         {
             return FALSE;
         }
 
         /* 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;
@@ -140,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;
         }
 
@@ -171,14 +688,14 @@ FindLFNorSFN_U(IN PWCHAR Path,
     while (TRUE)
     {
         /* Loop within the path skipping slashes */
-        while ((*Path) && ((*Path == L'\\') || (*Path == L'/'))) Path++;
+        while ((*Path == L'\\') || (*Path == L'/')) Path++;
 
         /* Make sure there's something after the slashes too! */
         if (*Path == UNICODE_NULL) break;
 
-        /* Now do the same thing with the last marker */
+        /* Now skip past the file name until we get to the first slash */
         p = Path + 1;
-        while ((*p) && ((*p == L'\\') || (*p == L'/'))) p++;
+        while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
 
         /* Whatever is in between those two is now the file name length */
         Length = p - Path;
@@ -187,11 +704,10 @@ FindLFNorSFN_U(IN PWCHAR Path,
          * Check if it is valid
          * Note that !IsShortName != IsLongName, these two functions simply help
          * us determine if a conversion is necessary or not.
+         * "Found" really means: "Is a conversion necessary?", hence the "!"
          */
-        Found = UseShort ? IsShortName_U(Path, Length) : IsLongName_U(Path, Length);
-
-        /* "Found" really means: "Is a conversion necessary?", hence the ! */
-        if (!Found)
+        Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
+        if (Found)
         {
             /* It is! did the caller request to know the markers? */
             if ((First) && (Last))
@@ -211,12 +727,12 @@ FindLFNorSFN_U(IN PWCHAR Path,
     }
 
     /* Return if anything was found and valid */
-    return !Found;
+    return Found;
 }
 
 PWCHAR
 WINAPI
-SkipPathTypeIndicator_U(IN PWCHAR Path)
+SkipPathTypeIndicator_U(IN LPWSTR Path)
 {
     PWCHAR ReturnPath;
     ULONG i;
@@ -268,6 +784,8 @@ BasepIsCurDirAllowedForPlainExeNames(VOID)
     return !NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL;
 }
 
+/* PUBLIC FUNCTIONS ***********************************************************/
+
 /*
  * @implemented
  */
@@ -451,56 +969,129 @@ NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName)
 
 /*
  * @implemented
+ *
+ * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
+ * beyond what you would usually expect. There are two main reasons:
+ *
+ * First, these APIs are subject to the ANSI/OEM File API selection status that
+ * the caller has chosen, so we must use the "8BitString" internal Base APIs.
+ *
+ * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
+ * length of the paths in "ANSI" by dividing their internal Wide character count
+ * by two... this is usually correct when dealing with pure-ASCII codepages but
+ * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
+ * for CJK, for example.
  */
 DWORD
 WINAPI
-GetFullPathNameA (
-        LPCSTR  lpFileName,
-        DWORD   nBufferLength,
-        LPSTR   lpBuffer,
-        LPSTR   *lpFilePart
-        )
+GetFullPathNameA(IN LPCSTR lpFileName,
+                 IN DWORD nBufferLength,
+                 IN LPSTR lpBuffer,
+                 IN LPSTR *lpFilePart)
 {
-   WCHAR BufferW[MAX_PATH];
-   PWCHAR FileNameW;
-   DWORD ret;
-   LPWSTR FilePartW = NULL;
-
-   DPRINT("GetFullPathNameA(lpFileName %s, nBufferLength %d, lpBuffer %p, "
-        "lpFilePart %p)\n",lpFileName,nBufferLength,lpBuffer,lpFilePart);
-
-   if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
-      return 0;
+    NTSTATUS Status;
+    PWCHAR Buffer;
+    ULONG PathSize, FilePartSize;
+    ANSI_STRING AnsiString;
+    UNICODE_STRING FileNameString, UniString;
+    PWCHAR LocalFilePart;
+    PWCHAR* FilePart;
+
+    /* If the caller wants filepart, use a local wide buffer since this is A */
+    FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
+
+    /* Initialize for Quickie */
+    FilePartSize = PathSize = 0;
+    FileNameString.Buffer = NULL;
+
+    /* First get our string in Unicode */
+    Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName);
+    if (!NT_SUCCESS(Status)) goto Quickie;
 
-   ret = GetFullPathNameW(FileNameW, MAX_PATH, BufferW, &FilePartW);
+    /* Allocate a buffer to hold teh path name */
+    Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
+                             0,
+                             MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+    if (!Buffer)
+    {
+        BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES);
+        goto Quickie;
+    }
 
-   if (!ret)
-      return 0;
+    /* Call into RTL to get the full Unicode path name */
+    PathSize = RtlGetFullPathName_U(FileNameString.Buffer,
+                                    MAX_PATH * sizeof(WCHAR),
+                                    Buffer,
+                                    FilePart);
+    if (PathSize <= (MAX_PATH * sizeof(WCHAR)))
+    {
+        /* The buffer will fit, get the real ANSI string size now */
+        Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize);
+        if (NT_SUCCESS(Status))
+        {
+            /* Now check if the user wanted file part size as well */
+            if ((PathSize) && (lpFilePart) && (LocalFilePart))
+            {
+                /* Yep, so in this case get the length of the file part too */
+                Status = RtlUnicodeToMultiByteSize(&FilePartSize,
+                                                   Buffer,
+                                                   (LocalFilePart - Buffer) *
+                                                   sizeof(WCHAR));
+                if (!NT_SUCCESS(Status))
+                {
+                    /* We failed to do that, so fail the whole call */
+                    BaseSetLastNTError(Status);
+                    PathSize = 0;
+                }
+            }
+        }
+    }
+    else
+    {
+        /* Reset the path size since the buffer is not large enough */
+        PathSize = 0;
+    }
 
-   if (ret > MAX_PATH)
-   {
-      SetLastError(ERROR_FILENAME_EXCED_RANGE);
-      return 0;
-   }
+    /* Either no path, or local buffer was too small, enter failure code */
+    if (!PathSize) goto Quickie;
 
-   ret = FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
+    /* If the *caller's* buffer was too small, fail, but add in space for NULL */
+    if (PathSize >= nBufferLength)
+    {
+        PathSize++;
+        goto Quickie;
+    }
 
-   if (ret < nBufferLength && lpFilePart)
-   {
-      /* if the path closed with '\', FilePart is NULL */
-      if (!FilePartW)
-         *lpFilePart=NULL;
-      else
-         *lpFilePart = (FilePartW - BufferW) + lpBuffer;
-   }
+    /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
+    RtlInitUnicodeString(&UniString, Buffer);
+    Status = BasepUnicodeStringTo8BitString(&AnsiString, &UniString, TRUE);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Final conversion failed, fail the call */
+        BaseSetLastNTError(Status);
+        PathSize = 0;
+    }
+    else
+    {
+        /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
+        RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1);
+        RtlFreeAnsiString(&AnsiString);
 
-   DPRINT("GetFullPathNameA ret: lpBuffer %s lpFilePart %s\n",
-        lpBuffer, (lpFilePart == NULL) ? "NULL" : *lpFilePart);
+        /* And finally, did the caller request file part information? */
+        if (lpFilePart)
+        {
+            /* Use the size we computed earlier and add it to the buffer */
+            *lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0;
+        }
+    }
 
-   return ret;
+Quickie:
+    /* Cleanup and return the path size */
+    if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString);
+    if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+    return PathSize;
 }
 
-
 /*
  * @implemented
  */
@@ -511,6 +1102,7 @@ GetFullPathNameW(IN LPCWSTR lpFileName,
                  IN LPWSTR lpBuffer,
                  OUT LPWSTR *lpFilePart)
 {
+    /* Call Rtl to do the work */
     return RtlGetFullPathName_U((LPWSTR)lpFileName,
                                 nBufferLength * sizeof(WCHAR),
                                 lpBuffer,
@@ -522,252 +1114,331 @@ GetFullPathNameW(IN LPCWSTR lpFileName,
  */
 DWORD
 WINAPI
-SearchPathA (
-        LPCSTR  lpPath,
-        LPCSTR  lpFileName,
-        LPCSTR  lpExtension,
-        DWORD   nBufferLength,
-        LPSTR   lpBuffer,
-        LPSTR   *lpFilePart
-        )
+SearchPathA(IN LPCSTR lpPath,
+            IN LPCSTR lpFileName,
+            IN LPCSTR lpExtension,
+            IN DWORD nBufferLength,
+            IN LPSTR lpBuffer,
+            OUT LPSTR *lpFilePart)
 {
-        UNICODE_STRING PathU      = { 0, 0, NULL };
-        UNICODE_STRING FileNameU  = { 0, 0, NULL };
-        UNICODE_STRING ExtensionU = { 0, 0, NULL };
-        UNICODE_STRING BufferU    = { 0, 0, NULL };
-        ANSI_STRING Path;
-        ANSI_STRING FileName;
-        ANSI_STRING Extension;
-        ANSI_STRING Buffer;
-        PWCHAR FilePartW;
-        DWORD RetValue = 0;
-        NTSTATUS Status = STATUS_SUCCESS;
-
-        if (!lpFileName)
-        {
-            SetLastError(ERROR_INVALID_PARAMETER);
-            return 0;
-        }
+    PUNICODE_STRING FileNameString;
+    UNICODE_STRING PathString, ExtensionString;
+    NTSTATUS Status;
+    ULONG PathSize, FilePartSize, AnsiLength;
+    PWCHAR LocalFilePart, Buffer;
+    PWCHAR* FilePart;
 
-        RtlInitAnsiString (&Path,
-                           (LPSTR)lpPath);
-        RtlInitAnsiString (&FileName,
-                           (LPSTR)lpFileName);
-        RtlInitAnsiString (&Extension,
-                           (LPSTR)lpExtension);
+    /* If the caller wants filepart, use a local wide buffer since this is A */
+    FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
 
-        /* convert ansi (or oem) strings to unicode */
-        if (bIsFileApiAnsi)
-        {
-                Status = RtlAnsiStringToUnicodeString (&PathU,
-                                                       &Path,
-                                                       TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
+    /* Initialize stuff for Quickie */
+    PathSize = 0;
+    Buffer = NULL;
+    ExtensionString.Buffer = PathString.Buffer = NULL;
 
-                Status = RtlAnsiStringToUnicodeString (&FileNameU,
-                                                       &FileName,
-                                                       TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
+    /* Get the UNICODE_STRING file name */
+    FileNameString = Basep8BitStringToStaticUnicodeString(lpFileName);
+    if (!FileNameString) return 0;
 
-                Status = RtlAnsiStringToUnicodeString (&ExtensionU,
-                                                       &Extension,
-                                                       TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
-        }
-        else
-        {
-                Status = RtlOemStringToUnicodeString (&PathU,
-                                                      &Path,
-                                                      TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
-                Status = RtlOemStringToUnicodeString (&FileNameU,
-                                                      &FileName,
-                                                      TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
+    /* Did the caller specify an extension */
+    if (lpExtension)
+    {
+        /* Yup, convert it into UNICODE_STRING */
+        Status = Basep8BitStringToDynamicUnicodeString(&ExtensionString,
+                                                       lpExtension);
+        if (!NT_SUCCESS(Status)) goto Quickie;
+    }
 
-                Status = RtlOemStringToUnicodeString (&ExtensionU,
-                                                      &Extension,
-                                                      TRUE);
-                if (!NT_SUCCESS(Status))
-                    goto Cleanup;
+    /* Did the caller specify a path */
+    if (lpPath)
+    {
+        /* Yup, convert it into UNICODE_STRING */
+        Status = Basep8BitStringToDynamicUnicodeString(&PathString, lpPath);
+        if (!NT_SUCCESS(Status)) goto Quickie;
+    }
+
+    /* Allocate our output buffer */
+    Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength * sizeof(WCHAR));
+    if (!Buffer)
+    {
+        /* It failed, bail out */
+        BaseSetLastNTError(STATUS_NO_MEMORY);
+        goto Quickie;
+    }
+
+    /* Now run the Wide search with the input buffer lengths */
+    PathSize = SearchPathW(PathString.Buffer,
+                           FileNameString->Buffer,
+                           ExtensionString.Buffer,
+                           nBufferLength,
+                           Buffer,
+                           FilePart);
+    if (PathSize <= nBufferLength)
+    {
+        /* It fits, but is it empty? If so, bail out */
+        if (!PathSize) goto Quickie;
+
+        /* The length above is inexact, we need it in ANSI */
+        Status = RtlUnicodeToMultiByteSize(&AnsiLength, Buffer, PathSize * sizeof(WCHAR));
+        if (!NT_SUCCESS(Status))
+        {
+            /* Conversion failed, fail the call */
+            PathSize = 0;
+            BaseSetLastNTError(Status);
+            goto Quickie;
         }
 
-        BufferU.MaximumLength = min(nBufferLength * sizeof(WCHAR), USHRT_MAX);
-        BufferU.Buffer = RtlAllocateHeap (RtlGetProcessHeap (),
-                                          0,
-                                          BufferU.MaximumLength);
-        if (BufferU.Buffer == NULL)
+        /* If the correct ANSI size is too big, return requird length plus a NULL */
+        if (AnsiLength >= nBufferLength)
         {
-            Status = STATUS_NO_MEMORY;
-            goto Cleanup;
+            PathSize = AnsiLength + 1;
+            goto Quickie;
         }
 
-        Buffer.MaximumLength = min(nBufferLength, USHRT_MAX);
-        Buffer.Buffer = lpBuffer;
+        /* Now apply the final conversion to ANSI */
+        Status = RtlUnicodeToMultiByteN(lpBuffer,
+                                        nBufferLength - 1,
+                                        &AnsiLength,
+                                        Buffer,
+                                        PathSize * sizeof(WCHAR));
+        if (!NT_SUCCESS(Status))
+        {
+            /* Conversion failed, fail the whole call */
+            PathSize = 0;
+            BaseSetLastNTError(STATUS_NO_MEMORY);
+            goto Quickie;
+        }
 
-        RetValue = SearchPathW (NULL == lpPath ? NULL : PathU.Buffer,
-                                NULL == lpFileName ? NULL : FileNameU.Buffer,
-                                NULL == lpExtension ? NULL : ExtensionU.Buffer,
-                                nBufferLength,
-                                BufferU.Buffer,
-                                &FilePartW);
+        /* NULL-terminate and return the real ANSI length */
+        lpBuffer[AnsiLength] = ANSI_NULL;
+        PathSize = AnsiLength;
 
-        if (0 != RetValue)
+        /* Now check if the user wanted file part size as well */
+        if (lpFilePart)
         {
-                BufferU.Length = wcslen(BufferU.Buffer) * sizeof(WCHAR);
-                /* convert ansi (or oem) string to unicode */
-                if (bIsFileApiAnsi)
-                    Status = RtlUnicodeStringToAnsiString(&Buffer,
-                                                          &BufferU,
-                                                          FALSE);
-                else
-                    Status = RtlUnicodeStringToOemString(&Buffer,
-                                                         &BufferU,
-                                                         FALSE);
-
-                if (NT_SUCCESS(Status) && Buffer.Buffer)
+            /* If we didn't get a file part, clear the caller's */
+            if (!LocalFilePart)
+            {
+                *lpFilePart = NULL;
+            }
+            else
+            {
+                /* Yep, so in this case get the length of the file part too */
+                Status = RtlUnicodeToMultiByteSize(&FilePartSize,
+                                                   Buffer,
+                                                   (LocalFilePart - Buffer) *
+                                                   sizeof(WCHAR));
+                if (!NT_SUCCESS(Status))
                 {
-                    /* nul-terminate ascii string */
-                    Buffer.Buffer[BufferU.Length / sizeof(WCHAR)] = '\0';
-
-                    if (NULL != lpFilePart && BufferU.Length != 0)
-                    {
-                        *lpFilePart = strrchr (lpBuffer, '\\') + 1;
-                    }
+                    /* We failed to do that, so fail the whole call */
+                    BaseSetLastNTError(Status);
+                    PathSize = 0;
                 }
-        }
 
-Cleanup:
-        RtlFreeHeap (RtlGetProcessHeap (),
-                     0,
-                     PathU.Buffer);
-        RtlFreeHeap (RtlGetProcessHeap (),
-                     0,
-                     FileNameU.Buffer);
-        RtlFreeHeap (RtlGetProcessHeap (),
-                     0,
-                     ExtensionU.Buffer);
-        RtlFreeHeap (RtlGetProcessHeap (),
-                     0,
-                     BufferU.Buffer);
+                /* Return the file part buffer */
+                *lpFilePart = lpBuffer + FilePartSize;
+            }
+        }
+    }
+    else
+    {
+        /* Our initial buffer guess was too small, allocate a bigger one */
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+        Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize * sizeof(WCHAR));
+        if (!Buffer)
+        {
+            /* Out of memory, fail everything */
+            BaseSetLastNTError(STATUS_NO_MEMORY);
+            goto Quickie;
+        }
 
-        if (!NT_SUCCESS(Status))
+        /* Do the search again -- it will fail, we just want the path size */
+        PathSize = SearchPathW(PathString.Buffer,
+                               FileNameString->Buffer,
+                               ExtensionString.Buffer,
+                               PathSize,
+                               Buffer,
+                               FilePart);
+        if (!PathSize) goto Quickie;
+
+        /* Convert it to a correct size */
+        Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize * sizeof(WCHAR));
+        if (NT_SUCCESS(Status))
         {
+            /* Make space for the NULL-char */
+            PathSize++;
+        }
+        else
+        {
+            /* Conversion failed for some reason, fail the call */
             BaseSetLastNTError(Status);
-            return 0;
+            PathSize = 0;
         }
+    }
 
-        return RetValue;
-}
-
-
-/***********************************************************************
- *           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] == '\\'));
+Quickie:
+    /* Cleanup/complete path */
+    if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+    if (ExtensionString.Buffer) RtlFreeUnicodeString(&ExtensionString);
+    if (PathString.Buffer) RtlFreeUnicodeString(&PathString);
+    return PathSize;
 }
 
-
 /*
  * @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;
+
+    /* 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' '))
+    {
+        FileNameString.Length -= sizeof(WCHAR);
+    }
+
+    /* Was it all just spaces? */
+    if (!FileNameString.Length)
+    {
+        /* Fail out */
+        BaseSetLastNTError(STATUS_INVALID_PARAMETER);
+        goto Quickie;
+    }
+
+    /* Convert extension to a unicode string */
+    RtlInitUnicodeString(&ExtensionString, lpExtension);
 
-    if (!lpFileName || !lpFileName[0])
+    /* Check if the user sent a path */
+    if (lpPath)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
+        /* Convert it to a unicode string too */
+        Status = RtlInitUnicodeStringEx(&PathString, lpPath);
+        if (NT_ERROR(Status))
+        {
+            /* Fail if it was too long */
+            BaseSetLastNTError(Status);
+            goto Quickie;
+        }
     }
-
-    /* If the name contains an explicit path, ignore the path */
-    if (ContainsPath(lpFileName))
+    else
     {
-        /* try first without extension */
-        if (RtlDoesFileExists_U(lpFileName))
-            return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
-
-        if (lpExtension)
+        /* A path wasn't sent, so compute it ourselves */
+        PathString.Buffer = BaseComputeProcessSearchPath();
+        if (!PathString.Buffer)
         {
-            LPCWSTR p = wcsrchr(lpFileName, '.');
-            if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
-                lpExtension = NULL;  /* Ignore the specified extension */
+            /* Fail if we couldn't compute it */
+            BaseSetLastNTError(STATUS_NO_MEMORY);
+            goto Quickie;
         }
 
-        /* Allocate a buffer for the file name and extension */
-        if (lpExtension)
+        /* See how big the computed path is */
+        LengthNeeded = lstrlenW(PathString.Buffer);
+        if (LengthNeeded > UNICODE_STRING_MAX_CHARS)
         {
-            LPWSTR tmp;
-            DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
-
-            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);
+            /* 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)
+    {
+        /* Add it into the string */
+        CallerBuffer.MaximumLength = nBufferLength * sizeof(WCHAR);
+    }
+    else
     {
-        ret = RtlDosSearchPath_U(lpPath,
-                                 lpFileName,
-                                 lpExtension,
-                                 nBufferLength * sizeof(WCHAR),
-                                 lpBuffer,
-                                 lpFilePart) / sizeof(WCHAR);
+        /* Caller wants too much, limit it to the maximum length of a string */
+        CallerBuffer.MaximumLength = UNICODE_STRING_MAX_BYTES;
     }
-    else  /* search in the default path */
+
+    /* Call Rtl to do the work */
+    Status = RtlDosSearchPath_Ustr(Flags,
+                                   &PathString,
+                                   &FileNameString,
+                                   &ExtensionString,
+                                   &CallerBuffer,
+                                   NULL,
+                                   NULL,
+                                   &FilePartSize,
+                                   &LengthNeeded);
+    if (NT_ERROR(Status))
     {
-        WCHAR *DllPath = GetDllLoadPath(NULL);
+        /* Check for unusual status codes */
+        if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
+        {
+            /* 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);
+        }
 
-        if (DllPath)
+        /* Check if the failure was due to a small buffer */
+        if (Status == STATUS_BUFFER_TOO_SMALL)
         {
-            ret = RtlDosSearchPath_U(DllPath,
-                                     lpFileName,
-                                     lpExtension,
-                                     nBufferLength * sizeof(WCHAR),
-                                     lpBuffer,
-                                     lpFilePart) / sizeof(WCHAR);
-            RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
+            /* 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;
 }
 
 /*
@@ -785,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 */
@@ -804,7 +1475,7 @@ GetLongPathNameW(IN LPCWSTR lpszShortPath,
     ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
 
     /* Do a simple check to see if the path exists */
-    if (GetFileAttributesW(lpszShortPath) == 0xFFFFFFF)
+    if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
     {
         /* It doesn't, so fail */
         ReturnLength = 0;
@@ -812,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);
@@ -848,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 */
@@ -1111,7 +1781,7 @@ GetShortPathNameA(IN LPCSTR lpszLongPath,
         }
         else
         {
-            PathLength = GetLongPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
+            PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
         }
     }
 
@@ -1164,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 */
@@ -1183,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) == 0xFFFFFFF)
+    if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
     {
         /* Windows checks for an application compatibility flag to allow this */
         if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
@@ -1195,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)
@@ -1370,4 +2039,399 @@ Quickie:
     return ReturnLength;
 }
 
+/*
+ * @implemented
+ *
+ * NOTE: Windows returns a dos/short (8.3) path
+ */
+DWORD
+WINAPI
+GetTempPathA(IN DWORD nBufferLength,
+             IN LPSTR lpBuffer)
+{
+   WCHAR BufferW[MAX_PATH];
+   DWORD ret;
+
+   ret = GetTempPathW(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
+ *
+ * ripped from wine
+ */
+DWORD
+WINAPI
+GetTempPathW(IN DWORD count,
+             IN LPWSTR path)
+{
+    static const WCHAR tmp[]  = { 'T', 'M', 'P', 0 };
+    static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
+    static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
+    WCHAR tmp_path[MAX_PATH];
+    UINT ret;
+
+    DPRINT("%u,%p\n", count, path);
+
+    if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
+        !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
+        !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
+        !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
+        return 0;
+
+   if (ret > MAX_PATH)
+   {
+     SetLastError(ERROR_FILENAME_EXCED_RANGE);
+     return 0;
+   }
+
+   ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
+   if (!ret) return 0;
+
+   if (ret > MAX_PATH - 2)
+   {
+     SetLastError(ERROR_FILENAME_EXCED_RANGE);
+     return 0;
+   }
+
+   if (tmp_path[ret-1] != '\\')
+   {
+     tmp_path[ret++] = '\\';
+     tmp_path[ret]   = '\0';
+   }
+
+   ret++; /* add space for terminating 0 */
+
+   if (count)
+   {
+     lstrcpynW(path, tmp_path, count);
+     if (count >= ret)
+         ret--; /* return length without 0 */
+     else if (count < 4)
+         path[0] = 0; /* avoid returning ambiguous "X:" */
+   }
+
+   DPRINT("GetTempPathW returning %u, %S\n", ret, path);
+   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
+ */
+UINT
+WINAPI
+GetSystemDirectoryA(IN LPSTR lpBuffer,
+                    IN UINT uSize)
+{
+    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;
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetSystemDirectoryW(IN LPWSTR lpBuffer,
+                    IN UINT uSize)
+{
+    ULONG ReturnLength;
+
+    ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
+    if ((uSize * sizeof(WCHAR)) >= ReturnLength)
+    {
+        RtlCopyMemory(lpBuffer,
+                      BaseWindowsSystemDirectory.Buffer,
+                      BaseWindowsSystemDirectory.Length);
+        lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
+
+        ReturnLength = BaseWindowsSystemDirectory.Length;
+    }
+
+    return ReturnLength / sizeof(WCHAR);
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetWindowsDirectoryA(IN LPSTR lpBuffer,
+                     IN UINT uSize)
+{
+    /* Is this a TS installation? */
+    if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
+
+    /* Otherwise, call the System API */
+    return GetSystemWindowsDirectoryA(lpBuffer, uSize);
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetWindowsDirectoryW(IN LPWSTR lpBuffer,
+                     IN UINT uSize)
+{
+    /* Is this a TS installation? */
+    if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
+
+    /* Otherwise, call the System API */
+    return GetSystemWindowsDirectoryW(lpBuffer, uSize);
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetSystemWindowsDirectoryA(IN LPSTR lpBuffer,
+                           IN UINT 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;
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer,
+                           IN UINT 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);
+}
+
+/*
+ * @unimplemented
+ */
+UINT
+WINAPI
+GetSystemWow64DirectoryW(IN LPWSTR lpBuffer,
+                         IN UINT uSize)
+{
+#ifdef _WIN64
+    UNIMPLEMENTED;
+    return 0;
+#else
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return 0;
+#endif
+}
+
+/*
+ * @unimplemented
+ */
+UINT
+WINAPI
+GetSystemWow64DirectoryA(IN LPSTR lpBuffer,
+                         IN UINT uSize)
+{
+#ifdef _WIN64
+    UNIMPLEMENTED;
+    return 0;
+#else
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return 0;
+#endif
+}
+
 /* EOF */