[RTL]
[reactos.git] / reactos / lib / rtl / path.c
index dd2b3a2..ef9c250 100644 (file)
@@ -6,6 +6,8 @@
  * PROGRAMMERS:     Wine team
  *                  Thomas Weidenmueller
  *                  Gunnar Dalsnes
+ *                  Alex Ionescu (alex.ionescu@reactos.org)
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 
 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
 
+#define RTL_CURDIR_IS_REMOVABLE 0x1
+#define RTL_CURDIR_DROP_OLD_HANDLE 0x2
+#define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
+C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
+
 
 /* GLOBALS ********************************************************************/
 
@@ -28,9 +35,9 @@ static const WCHAR DeviceRootW[] = L"\\\\.\\";
 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
 
 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
-const UNICODE_STRING RtlpWin32NtRootSlash =   RTL_CONSTANT_STRING(L"\\\\?\\");
-const UNICODE_STRING RtlpDosSlashCONDevice  RTL_CONSTANT_STRING(L"\\\\.\\CON");
-const UNICODE_STRING RtlpDosDevicesPrefix   RTL_CONSTANT_STRING(L"\\??\\");
+const UNICODE_STRING RtlpWin32NtRootSlash    = RTL_CONSTANT_STRING(L"\\\\?\\");
+const UNICODE_STRING RtlpDosSlashCONDevice   = RTL_CONSTANT_STRING(L"\\\\.\\CON");
+const UNICODE_STRING RtlpDosDevicesPrefix    = RTL_CONSTANT_STRING(L"\\??\\");
 
 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");
 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");
@@ -39,11 +46,46 @@ const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");
 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
 
+PRTLP_CURDIR_REF RtlpCurDirRef;
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
+RTL_PATH_TYPE
+NTAPI
+RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
+{
+    PWCHAR Path;
+    ULONG Chars;
+
+    Path = PathString->Buffer;
+    Chars = PathString->Length / sizeof(WCHAR);
+
+    /* Return if there are no characters */
+    if (!Chars) return RtlPathTypeRelative;
+
+    /*
+     * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
+     * actually check for the path length before touching the characters
+     */
+    if (IS_PATH_SEPARATOR(Path[0]))
+    {
+        if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted;                /* \x             */
+        if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x            */
+        if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice;           /* \\.\x or \\?\x */
+        if (Chars != 3) return RtlPathTypeUncAbsolute;                                             /* \\.x or \\?x   */
+        return RtlPathTypeRootLocalDevice;                                                         /* \\. or \\?     */
+    }
+    else
+    {
+        if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative;                          /* x              */
+        if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative;         /* x:             */
+        return RtlPathTypeDriveAbsolute;                                                           /* x:\            */
+    }
+}
+
 ULONG
 NTAPI
-RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
+RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
 {
     UNICODE_STRING PathCopy;
     PWCHAR Start, End;
@@ -99,7 +141,7 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
     {
         /* Stop if we hit something else than a space or period */
         c = PathCopy.Buffer[PathChars - 1];
-        if ((c != '.') && (c != ' ')) break;
+        if ((c != L'.') && (c != L' ')) break;
 
         /* Fixup the lengths */
         PathCopy.Length -= sizeof(WCHAR);
@@ -118,18 +160,18 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
         {
             /* Check if the character is a path or drive separator */
             c = *End;
-            if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1)))
+            if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1)))
             {
                 /* Get the next lower case character */
                 End++;
-                c = *End | ' '; // ' ' == ('z' - 'Z')
+                c = RtlDowncaseUnicodeChar(*End);
 
                 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
                 if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) &&
-                    ((c == 'l') || (c == 'c') || (c == 'p') || (c == 'a') || (c == 'n')))
+                    ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n')))
                 {
                     /* Calculate the offset */
-                    ReturnOffset = (PCHAR)End - (PCHAR)PathCopy.Buffer;
+                    ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
 
                     /* Build the final string */
                     PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
@@ -148,8 +190,8 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
         }
 
         /* Get the next lower case character and check if it's a DOS device */
-        c = *PathCopy.Buffer | ' '; // ' ' == ('z' - 'Z')
-        if ((c != 'l') && (c != 'c') && (c != 'p') && (c != 'a') && (c != 'n'))
+        c = RtlDowncaseUnicodeChar(*PathCopy.Buffer);
+        if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n'))
         {
             /* Not LPT, COM, PRN, AUX, or NUL */
             return 0;
@@ -162,20 +204,20 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
     while (Start < End)
     {
         c = *Start;
-        if ((c == '.') || (c == ':')) break;
+        if ((c == L'.') || (c == L':')) break;
         Start++;
     }
 
     /* And then go backwards to get rid of spaces */
-    while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start;
+    while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start;
 
     /* Finally see how many characters are left, and that's our size */
-    PathChars = Start - PathCopy.Buffer;
+    PathChars = (USHORT)(Start - PathCopy.Buffer);
     PathCopy.Length = PathChars * sizeof(WCHAR);
 
     /* Check if this is a COM or LPT port, which has a digit after it */
     if ((PathChars == 4) &&
-        (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != '0')))
+        (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0')))
     {
         /* Don't compare the number part, just check for LPT or COM */
         PathCopy.Length -= sizeof(WCHAR);
@@ -202,42 +244,6 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
     return 0;
 }
 
-RTL_PATH_TYPE
-NTAPI
-RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
-{
-    PWCHAR Path;
-    ULONG Chars;
-
-    /* Validate the input */
-    if (!PathString) return RtlPathTypeUnknown;
-
-    Path = PathString->Buffer;
-    Chars = PathString->Length / sizeof(WCHAR);
-
-    /* Return if there are no characters */
-    if (!Chars) return RtlPathTypeUnknown;
-
-    /*
-     * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
-     * actually check for the path length before touching the characters
-     */
-    if ((Chars < 1) || (IS_PATH_SEPARATOR(Path[0])))
-    {
-        if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted;                /* \x             */
-        if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x            */
-        if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice;           /* \\.\x or \\?\x */
-        if (Chars != 3) return RtlPathTypeUncAbsolute;                                             /* \\.x or \\?x   */
-        return RtlPathTypeRootLocalDevice;                                                         /* \\. or \\?     */
-    }
-    else
-    {
-        if ((Chars < 2) || (!(Path[0]) || (Path[1] != L':'))) return RtlPathTypeRelative;          /* x              */
-        if ((Chars < 3) || (IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveAbsolute;          /* x:\            */
-        return RtlPathTypeDriveRelative;                                                           /* x:             */
-    }
-}
-
 NTSTATUS
 NTAPI
 RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
@@ -278,61 +284,233 @@ RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
     return Status;
 }
 
+
+
+/******************************************************************
+ *    RtlpCollapsePath (from WINE)
+ *
+ * Helper for RtlGetFullPathName_U.
+ * 1) Convert slashes into backslashes
+ * 2) Get rid of duplicate backslashes
+ * 3) Get rid of . and .. components in the path.
+ */
+static VOID
+RtlpCollapsePath(PWSTR path, ULONG mark, BOOLEAN SkipTrailingPathSeparators)
+{
+    PWSTR p, next;
+
+    /* convert every / into a \ */
+    for (p = path; *p; p++)
+    {
+        if (*p == L'/') *p = L'\\';
+    }
+
+    /* collapse duplicate backslashes */
+    next = path + max( 1, mark );
+    for (p = next; *p; p++)
+    {
+        if (*p != L'\\' || next[-1] != L'\\') *next++ = *p;
+    }
+    *next = UNICODE_NULL;
+
+    p = path + mark;
+    while (*p)
+    {
+        if (*p == L'.')
+        {
+            switch(p[1])
+            {
+                case UNICODE_NULL:  /* final . */
+                    if (p > path + mark) p--;
+                    *p = UNICODE_NULL;
+                    continue;
+                case L'\\': /* .\ component */
+                    next = p + 2;
+                    RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+                    continue;
+                case L'.':
+                    if (p[2] == L'\\')  /* ..\ component */
+                    {
+                        next = p + 3;
+                        if (p > path + mark)
+                        {
+                            p--;
+                            while (p > path + mark && p[-1] != L'\\') p--;
+                        }
+                        RtlMoveMemory( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+                        continue;
+                    }
+                    else if (!p[2])  /* final .. */
+                    {
+                        if (p > path + mark)
+                        {
+                            p--;
+                            while (p > path + mark && p[-1] != L'\\') p--;
+                            if (p > path + mark) p--;
+                        }
+                        *p = UNICODE_NULL;
+                        continue;
+                    }
+                    break;
+            }
+        }
+        /* skip to the next component */
+        while (*p && *p != L'\\') p++;
+        if (*p == L'\\')
+        {
+            /* remove last dot in previous dir name */
+            if (p > path + mark && p[-1] == L'.')
+                RtlMoveMemory( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
+            else
+                p++;
+        }
+    }
+
+    /* Remove trailing backslashes if needed (after the UNC part if it exists) */
+    if (SkipTrailingPathSeparators)
+    {
+        while (p > path + mark && IS_PATH_SEPARATOR(p[-1])) *p-- = UNICODE_NULL;
+    }
+
+    /* Remove trailing spaces and dots (for all the path) */
+    while (p > path && (p[-1] == L' ' || p[-1] == L'.')) *p-- = UNICODE_NULL;
+
+    /* Null-terminate the string */
+    *p = UNICODE_NULL;
+}
+
+/******************************************************************
+ *    RtlpSkipUNCPrefix (from WINE)
+ *
+ * Helper for RtlGetFullPathName_U
+ * Skip the \\share\dir part of a file name and return the new position
+ * (which can point on the last backslash of "dir\")
+ */
+static SIZE_T
+RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
+{
+    PCWSTR UncPath = FileNameBuffer + 2;
+    DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
+
+    while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++;  /* share name */
+    while (IS_PATH_SEPARATOR(*UncPath)) UncPath++;
+    while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++;  /* dir name */
+    /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
+
+    return (UncPath - FileNameBuffer);
+}
+
+NTSTATUS
+NTAPI
+RtlpApplyLengthFunction(IN ULONG Flags,
+                        IN ULONG Type,
+                        IN PVOID UnicodeStringOrUnicodeStringBuffer,
+                        IN PVOID LengthFunction)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags,
+                                              IN PWCHAR Path,
+                                              OUT PULONG LengthOut)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName,
+                              IN PUNICODE_STRING a2,
+                              IN PUNICODE_STRING a3)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 ULONG
 NTAPI
-RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName,
-                        IN ULONG Size,
-                        IN PWSTR Buffer,
-                        OUT PCWSTR *ShortName,
-                        OUT PBOOLEAN InvalidName,
-                        OUT RTL_PATH_TYPE *PathType)
+RtlGetFullPathName_Ustr(
+    _In_ PUNICODE_STRING FileName,
+    _In_ ULONG Size,
+    _Out_z_bytecap_(Size) PWSTR Buffer,
+    _Out_opt_ PCWSTR *ShortName,
+    _Out_opt_ PBOOLEAN InvalidName,
+    _Out_ RTL_PATH_TYPE *PathType)
 {
+    NTSTATUS Status;
     PWCHAR FileNameBuffer;
     ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength;
+    BOOLEAN SkipTrailingPathSeparators;
     WCHAR c;
-    NTSTATUS Status;
+
+
+    ULONG               reqsize = 0;
+    PCWSTR              ptr;
+
+    PCUNICODE_STRING    CurDirName;
+    UNICODE_STRING      EnvVarName, EnvVarValue;
+    WCHAR EnvVarNameBuffer[4];
+
+    ULONG  PrefixCut    = 0;    // Where the path really starts (after the skipped prefix)
+    PWCHAR Prefix       = NULL; // pointer to the string to be inserted as the new path prefix
+    ULONG  PrefixLength = 0;
+    PWCHAR Source;
+    ULONG  SourceLength;
+
 
     /* For now, assume the name is valid */
     DPRINT("Filename: %wZ\n", FileName);
-    DPRINT("Size and buffer: %lx %S\n", Size, Buffer);
+    DPRINT("Size and buffer: %lx %p\n", Size, Buffer);
     if (InvalidName) *InvalidName = FALSE;
 
     /* Handle initial path type and failure case */
     *PathType = RtlPathTypeUnknown;
-    if (!(Size) || !(Buffer) || !(FileName) ||
-        !(FileName->Length) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
+    if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
 
     /* Break filename into component parts */
     FileNameBuffer = FileName->Buffer;
     FileNameLength = FileName->Length;
-    FileNameChars = FileNameLength / sizeof(WCHAR);
+    FileNameChars  = FileNameLength / sizeof(WCHAR);
 
     /* Kill trailing spaces */
     c = FileNameBuffer[FileNameChars - 1];
-    while ((FileNameLength) && (c == L' '))
+    while ((FileNameLength != 0) && (c == L' '))
     {
         /* Keep going, ignoring the spaces */
         FileNameLength -= sizeof(WCHAR);
-        if (FileNameLength) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
+        if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
     }
 
     /* Check if anything is left */
-    if (!FileNameLength) return 0;
+    if (FileNameLength == 0) return 0;
+
+    /*
+     * Check whether we'll need to skip trailing path separators in the
+     * computed full path name. If the original file name already contained
+     * trailing separators, then we keep them in the full path name. On the
+     * other hand, if the original name didn't contain any trailing separators
+     * then we'll skip it in the full path name.
+     */
+    SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]);
 
     /* Check if this is a DOS name */
     DosLength = RtlIsDosDeviceName_Ustr(FileName);
     DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
-    if (DosLength)
+    if (DosLength != 0)
     {
         /* Zero out the short name */
         if (ShortName) *ShortName = NULL;
 
         /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
-        DosLengthOffset = DosLength >> 16;
-        DosLength = DosLength & 0xFFFF;
+        DosLengthOffset = HIWORD(DosLength);
+        DosLength       = LOWORD(DosLength);
 
         /* Do we have a DOS length, and does the caller want validity? */
-        if ((InvalidName) && (DosLengthOffset))
+        if (InvalidName && (DosLengthOffset != 0))
         {
             /* Do the check */
             Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
@@ -364,13 +542,226 @@ RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName,
         return FullLength + sizeof(UNICODE_NULL);
     }
 
-    /* This should work well enough for our current needs */
+    /* Zero out the destination buffer. FileName must be different from Buffer */
+    RtlZeroMemory(Buffer, Size);
+
+    /* Get the path type */
     *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
-    DPRINT("Path type: %lx\n", *PathType);
 
-    /* This is disgusting... but avoids re-writing everything */
-    DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer, Size, Buffer);
-    return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName);
+
+
+    /**********************************************
+     **    CODE REWRITTEN IS HAPPENING THERE     **
+     **********************************************/
+    Source       = FileNameBuffer;
+    SourceLength = FileNameLength;
+    EnvVarValue.Buffer = NULL;
+
+    /* Lock the PEB to get the current directory */
+    RtlAcquirePebLock();
+    CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
+
+    switch (*PathType)
+    {
+        case RtlPathTypeUncAbsolute:        /* \\foo   */
+            PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer);
+            break;
+
+        case RtlPathTypeLocalDevice:        /* \\.\foo */
+            PrefixCut = 4;
+            break;
+
+        case RtlPathTypeDriveAbsolute:      /* c:\foo  */
+            ASSERT(FileNameBuffer[1] == L':');
+            ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
+
+            Prefix = FileNameBuffer;
+            PrefixLength = 3 * sizeof(WCHAR);
+            Source += 3;
+            SourceLength -= 3 * sizeof(WCHAR);
+
+            PrefixCut = 3;
+            break;
+
+        case RtlPathTypeDriveRelative:      /* c:foo   */
+            Source += 2;
+            SourceLength -= 2 * sizeof(WCHAR);
+            if (RtlUpcaseUnicodeChar(FileNameBuffer[0]) != RtlUpcaseUnicodeChar(CurDirName->Buffer[0]) ||
+                CurDirName->Buffer[1] != L':')
+            {
+                EnvVarNameBuffer[0] = L'=';
+                EnvVarNameBuffer[1] = FileNameBuffer[0];
+                EnvVarNameBuffer[2] = L':';
+                EnvVarNameBuffer[3] = UNICODE_NULL;
+    
+                EnvVarName.Length = 3 * sizeof(WCHAR);
+                EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR);
+                EnvVarName.Buffer = EnvVarNameBuffer;
+
+                // FIXME: Is it possible to use the user-given buffer ?
+                // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
+                EnvVarValue.Length = 0;
+                EnvVarValue.MaximumLength = (USHORT)Size;
+                EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
+                if (EnvVarValue.Buffer == NULL)
+                {
+                    Prefix       = NULL;
+                    PrefixLength = 0;
+                    goto Quit;
+                }
+
+                Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue);
+                switch (Status)
+                {
+                    case STATUS_SUCCESS:
+                        /*
+                         * (From Wine)
+                         * FIXME: Win2k seems to check that the environment
+                         * variable actually points to an existing directory.
+                         * If not, root of the drive is used (this seems also
+                         * to be the only spot in RtlGetFullPathName that the
+                         * existence of a part of a path is checked).
+                         */
+                        EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\';
+                        Prefix       = EnvVarValue.Buffer;
+                        PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */
+                        break;
+
+                    case STATUS_BUFFER_TOO_SMALL:
+                        reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL);
+                        goto Quit;
+
+                    default:
+                        DPRINT1("RtlQueryEnvironmentVariable_U returned 0x%08lx\n", Status);
+
+                        EnvVarNameBuffer[0] = FileNameBuffer[0];
+                        EnvVarNameBuffer[1] = L':';
+                        EnvVarNameBuffer[2] = L'\\';
+                        EnvVarNameBuffer[3] = UNICODE_NULL;
+                        Prefix       = EnvVarNameBuffer;
+                        PrefixLength = 3 * sizeof(WCHAR);
+
+                        RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
+                        EnvVarValue.Buffer = NULL;
+                        break;
+                }
+                PrefixCut = 3;
+                break;
+            }
+            /* Fall through */
+            DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
+
+        case RtlPathTypeRelative:           /* foo     */
+            Prefix       = CurDirName->Buffer;
+            PrefixLength = CurDirName->Length;
+            if (CurDirName->Buffer[1] != L':')
+            {
+                PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
+            }
+            else
+            {
+                PrefixCut = 3;
+            }
+            break;
+
+        case RtlPathTypeRooted:             /* \xxx    */
+            if (CurDirName->Buffer[1] == L':')
+            {
+                // The path starts with "C:\"
+                ASSERT(CurDirName->Buffer[1] == L':');
+                ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2]));
+
+                Prefix = CurDirName->Buffer;
+                PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\"
+
+                PrefixCut = 3;      // Source index location incremented of + 3
+            }
+            else
+            {
+                PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
+                PrefixLength = PrefixCut * sizeof(WCHAR);
+                Prefix = CurDirName->Buffer;
+            }
+            break;
+
+        case RtlPathTypeRootLocalDevice:    /* \\.     */
+            Prefix       = DeviceRootString.Buffer;
+            PrefixLength = DeviceRootString.Length;
+            Source += 3;
+            SourceLength -= 3 * sizeof(WCHAR);
+
+            PrefixCut = 4;
+            break;
+
+        case RtlPathTypeUnknown:
+            goto Quit;
+    }
+
+    /* Do we have enough space for storing the full path? */
+    reqsize = PrefixLength;
+    if (reqsize + SourceLength + sizeof(WCHAR) > Size)
+    {
+        /* Not enough space, return needed size (including terminating '\0') */
+        reqsize += SourceLength + sizeof(WCHAR);
+        goto Quit;
+    }
+
+    /*
+     * Build the full path
+     */
+    // if (ShortName) DPRINT1("buffer(1) = %S\n", Buffer);
+    /* Copy the path's prefix */
+    if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength);
+    // if (ShortName) DPRINT1("buffer(2) = %S\n", Buffer);
+    /* Copy the remaining part of the path */
+    RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
+    // if (ShortName) DPRINT1("buffer(3) = %S\n", Buffer);
+
+    /* Some cleanup */
+    Prefix = NULL;
+    if (EnvVarValue.Buffer)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
+        EnvVarValue.Buffer = NULL;
+    }
+
+    /*
+     * Finally, put the path in canonical form,
+     * i.e. simplify redundant . and .., etc...
+     */
+    // if (*PathType == RtlPathTypeUncAbsolute) DPRINT1("RtlpCollapsePath('%S', %lu)\n", Buffer, PrefixCut);
+    RtlpCollapsePath(Buffer, PrefixCut, SkipTrailingPathSeparators);
+    // if (ShortName) DPRINT1("buffer(4) = %S\n", Buffer);
+
+    /* Get the length of the full path name, without its terminating null character */
+    reqsize = wcslen(Buffer) * sizeof(WCHAR);
+
+    /* Find the file part, which is present after the last path separator */
+    if (ShortName)
+    {
+        ptr = wcsrchr(Buffer, L'\\');
+        if (ptr) ++ptr; // Skip it
+
+        /*
+         * For UNC paths, the file part is after the \\share\dir part of the path.
+         */
+        PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3);
+
+        if (ptr && *ptr && (ptr >= Buffer + PrefixCut))
+        {
+            *ShortName = ptr;
+        }
+        else
+        {
+            /* Zero-out the short name */
+            *ShortName = NULL;
+        }
+    }
+
+Quit:
+    /* Release PEB lock */
+    RtlReleasePebLock();
+    return (ULONG)reqsize;
 }
 
 NTSTATUS
@@ -416,30 +807,33 @@ RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
     if (PartName)
     {
         /* Loop from the back until we find a path separator */
-        p = &NewBuffer[(DosLength - 1) / sizeof (WCHAR)];
-        while (p > NewBuffer) if (*p-- == '\\') break;
-
-        /* Was one found? */
-        if (p > NewBuffer)
+        p = &NewBuffer[DosLength / sizeof(WCHAR)];
+        while (--p > NewBuffer)
         {
-            /* Move past it -- anything left? */
-            p++;
-            if (!*p)
-            {
-                /* The path ends with a path separator, no part name */
-                *PartName = NULL;
-            }
-            else
+            /* We found a path separator, move past it */
+            if (*p == OBJ_NAME_PATH_SEPARATOR)
             {
-                /* What follows the path separator is the part name */
-                *PartName = p;
+                ++p;
+                break;
             }
         }
+
+        /* Check whether a separator was found and if something remains */
+        if ((p > NewBuffer) && *p)
+        {
+            /* What follows the path separator is the partial name */
+            *PartName = p;
+        }
+        else
+        {
+            /* The path ends with a path separator, no partial name */
+            *PartName = NULL;
+        }
     }
 
     /* Build the final NT path string */
-    NtPath->Length = (USHORT)DosLength;
     NtPath->Buffer = NewBuffer;
+    NtPath->Length = (USHORT)DosLength;
     NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
     return STATUS_SUCCESS;
 }
@@ -455,14 +849,15 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
     WCHAR BigBuffer[MAX_PATH + 1];
     PWCHAR PrefixBuffer, NewBuffer, Buffer;
     ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
-    UNICODE_STRING CapturedDosName, PartNameString;
+    UNICODE_STRING CapturedDosName, PartNameString, FullPath;
     BOOLEAN QuickPath;
     RTL_PATH_TYPE InputPathType, BufferPathType;
     NTSTATUS Status;
     BOOLEAN NameInvalid;
+    PCURDIR CurrentDirectory;
 
     /* Assume MAX_PATH for now */
-    DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
+    DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
             HaveRelative, DosName, NtName, PartName, RelativeName);
     MaxLength = sizeof(BigBuffer);
 
@@ -472,13 +867,16 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
     /* Capture input string */
     CapturedDosName = *DosName;
 
-    /* Check for \\?\\ form */
+    /* Check for the presence or absence of the NT prefix "\\?\" form */
+    // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
     if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) ||
         (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) ||
         (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) ||
         (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) ||
         (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3]))
     {
+        /* NT prefix not present */
+
         /* Quick path won't be used */
         QuickPath = FALSE;
 
@@ -488,11 +886,13 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
 
         /* Allocate a buffer to hold the path */
         NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
-        DPRINT("Length: %lx\n", MaxLength);
+        DPRINT("MaxLength: %lx\n", MaxLength);
         if (!NewBuffer) return STATUS_NO_MEMORY;
     }
     else
     {
+        /* NT prefix present */
+
         /* Use the optimized path after acquiring the lock */
         QuickPath = TRUE;
         NewBuffer = NULL;
@@ -562,17 +962,16 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
         /* Nothing else is expected */
         default:
             ASSERT(FALSE);
-
     }
 
     /* Now copy the prefix and the buffer */
     RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
     RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
-                  &Buffer[PrefixCut],
+                  Buffer + PrefixCut,
                   PathLength - (PrefixCut * sizeof(WCHAR)));
 
     /* Compute the length */
-    Length = PathLength - PrefixCut * sizeof(WCHAR) + PrefixLength;
+    Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR);
     LengthChars = Length / sizeof(WCHAR);
 
     /* Setup the actual NT path string and terminate it */
@@ -580,7 +979,7 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
     NtName->Length = (USHORT)Length;
     NtName->MaximumLength = (USHORT)MaxLength;
     NewBuffer[LengthChars] = UNICODE_NULL;
-    DPRINT("new buffer: %S\n", NewBuffer);
+    DPRINT("New buffer: %S\n", NewBuffer);
     DPRINT("NT Name: %wZ\n", NtName);
 
     /* Check if a partial name was requested */
@@ -608,26 +1007,59 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
         /* Setup the structure */
         RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
         RelativeName->ContainingDirectory = NULL;
-        RelativeName->CurDirRef = 0;
+        RelativeName->CurDirRef = NULL;
 
         /* Check if the input path itself was relative */
         if (InputPathType == RtlPathTypeRelative)
         {
-            /* FIXME: HACK: Old code */
-            PCURDIR cd;
-            UNICODE_STRING us;
-            cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath);
-            if (cd->Handle)
+            /* Get current directory */
+            CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
+            if (CurrentDirectory->Handle)
             {
-                RtlInitUnicodeString(&us, Buffer);
-                us.Length = (cd->DosPath.Length < us.Length) ? cd->DosPath.Length : us.Length;
-                if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
+                Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
+                if (!NT_SUCCESS(Status))
+                {
+                    RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
+                    RtlReleasePebLock();
+                    return Status;
+                }
+
+                /* If current directory is bigger than full path, there's no way */
+                if (CurrentDirectory->DosPath.Length > FullPath.Length)
+                {
+                    RtlReleasePebLock();
+                    return Status;
+                }
+
+                /* File is in current directory */
+                if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE))
                 {
-                    Length = ((cd->DosPath.Length / sizeof(WCHAR)) - PrefixCut) + ((InputPathType == 1) ? 8 : 4);
-                    RelativeName->RelativeName.Buffer = NewBuffer + Length;
-                    RelativeName->RelativeName.Length = NtName->Length - (USHORT)(Length * sizeof(WCHAR));
+                    /* Make relative name string */
+                    RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR));
+                    RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
+                    /* If relative name starts with \, skip it */
+                    if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
+                    {
+                        RelativeName->RelativeName.Buffer++;
+                        RelativeName->RelativeName.Length -= sizeof(WCHAR);
+                    }
                     RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
-                    RelativeName->ContainingDirectory = cd->Handle;
+                    DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName));
+
+                    if (!HaveRelative)
+                    {
+                        RelativeName->ContainingDirectory = CurrentDirectory->Handle;
+                        return Status;
+                    }
+
+                    /* Give back current directory data & reference counter */
+                    RelativeName->CurDirRef = RtlpCurDirRef;
+                    if (RelativeName->CurDirRef)
+                    {
+                        InterlockedIncrement(&RtlpCurDirRef->RefCount);
+                    }
+
+                    RelativeName->ContainingDirectory = CurrentDirectory->Handle;
                 }
             }
         }
@@ -694,9 +1126,6 @@ RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
     NTSTATUS Status;
     FILE_BASIC_INFORMATION BasicInformation;
 
-    /* Validate the input */
-    if (!FileName) return FALSE;
-
     /* Get the NT Path */
     Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
                                                      &NtPathName,
@@ -795,8 +1224,13 @@ RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
     /* Check if a directory reference was grabbed */
     if (RelativeName->CurDirRef)
     {
-        /* FIXME: Not yet supported */
-        UNIMPLEMENTED;
+        /* Decrease reference count */
+        if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount))
+        {
+            /* If no one uses it any longer, close handle & free */
+            NtClose(RelativeName->CurDirRef->Handle);
+            RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef);
+        }
         RelativeName->CurDirRef = NULL;
     }
 }
@@ -814,21 +1248,60 @@ RtlGetLongestNtPathLength(VOID)
      * This is, and has always been equal to, 269 characters, except in Wine
      * which claims this is 277. Go figure.
      */
-    return (MAX_PATH + RtlpDosDevicesUncPrefix.Length + sizeof(ANSI_NULL));
+    return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL);
 }
 
 /*
  * @implemented
+ * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
+ *        (with a 'e' instead of a 'a' in "Seperators").
  */
-ULONG
+NTSTATUS
+NTAPI
+RtlGetLengthWithoutTrailingPathSeparators(IN  ULONG Flags,
+                                          IN  PCUNICODE_STRING PathString,
+                                          OUT PULONG Length)
+{
+    ULONG NumChars;
+
+    /* Parameters validation */
+    if (Length == NULL) return STATUS_INVALID_PARAMETER;
+
+    *Length = 0;
+
+    if (PathString == NULL) return STATUS_INVALID_PARAMETER;
+
+    /* No flags are supported yet */
+    if (Flags != 0) return STATUS_INVALID_PARAMETER;
+
+    NumChars = PathString->Length / sizeof(WCHAR);
+
+    /*
+     * Notice that we skip the last character, therefore:
+     * - if we have: "some/path/f" we test for: "some/path/"
+     * - if we have: "some/path/"  we test for: "some/path"
+     * - if we have: "s" we test for: ""
+     * - if we have: "" then NumChars was already zero and we aren't there
+     */
+
+    while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1]))
+    {
+        --NumChars;
+    }
+
+    *Length = NumChars;
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+RTL_PATH_TYPE
 NTAPI
 RtlDetermineDosPathNameType_U(IN PCWSTR Path)
 {
     DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
 
-    /* Validate the input */
-    if (!Path) return RtlPathTypeUnknown;
-
     /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
     if (IS_PATH_SEPARATOR(Path[0]))
     {
@@ -851,7 +1324,7 @@ RtlDetermineDosPathNameType_U(IN PCWSTR Path)
  */
 ULONG
 NTAPI
-RtlIsDosDeviceName_U(IN PWSTR Path)
+RtlIsDosDeviceName_U(IN PCWSTR Path)
 {
     UNICODE_STRING PathString;
     NTSTATUS Status;
@@ -873,8 +1346,9 @@ RtlIsDosDeviceName_U(IN PWSTR Path)
  */
 ULONG
 NTAPI
-RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
-                         IN PWSTR Buffer)
+RtlGetCurrentDirectory_U(
+    _In_ ULONG MaximumLength,
+    _Out_bytecap_(MaximumLength) PWSTR Buffer)
 {
     ULONG Length, Bytes;
     PCURDIR CurDir;
@@ -890,7 +1364,14 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
     Length = CurDir->DosPath.Length / sizeof(WCHAR);
     ASSERT((CurDirName != NULL) && (Length > 0));
 
-    /* Check for x:\ vs x:\path\foo (note the trailing slash) */
+    /*
+     * DosPath.Buffer should always have a trailing slash. There is an assert
+     * below which checks for this.
+     *
+     * This function either returns x:\ for a root (keeping the original buffer)
+     * or it returns x:\path\foo for a directory (replacing the trailing slash
+     * with a NULL.
+     */
     Bytes = Length * sizeof(WCHAR);
     if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
     {
@@ -899,13 +1380,13 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
         {
             /* Call has no space for it, fail, add the trailing slash */
             RtlReleasePebLock();
-            return Bytes + sizeof(L'\\');
+            return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR);
         }
     }
     else
     {
         /* Check if caller does not have enough space */
-        if (MaximumLength <= Bytes)
+        if (MaximumLength < Bytes)
         {
             /* Call has no space for it, fail */
             RtlReleasePebLock();
@@ -917,7 +1398,7 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
     RtlCopyMemory(Buffer, CurDirName, Bytes);
 
     /* The buffer should end with a path separator */
-    ASSERT(Buffer[Length - 1] == L'\\');
+    ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR);
 
     /* Again check for our two cases (drive root vs path) */
     if ((Length <= 1) || (Buffer[Length - 2] != L':'))
@@ -941,367 +1422,204 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
 /*
  * @implemented
  */
-NTSTATUS NTAPI
-RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
-{
-   UNICODE_STRING full;
-   FILE_FS_DEVICE_INFORMATION device_info;
-   OBJECT_ATTRIBUTES Attr;
-   IO_STATUS_BLOCK iosb;
-   PCURDIR cd;
-   NTSTATUS Status;
-   USHORT size;
-   HANDLE handle = NULL;
-   PWSTR ptr;
-
-   DPRINT("RtlSetCurrentDirectory %wZ\n", dir);
-
-   full.Buffer = NULL;
-
-   RtlAcquirePebLock ();
-
-   cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectory.DosPath;
-
-   if (!RtlDosPathNameToNtPathName_U (dir->Buffer, &full, 0, 0))
-   {
-      RtlReleasePebLock ();
-      return STATUS_OBJECT_NAME_INVALID;
-   }
-
-   DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full);
-
-   InitializeObjectAttributes (&Attr,
-                              &full,
-                              OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
-                              NULL,
-                              NULL);
-
-   Status = ZwOpenFile (&handle,
-                       SYNCHRONIZE | FILE_TRAVERSE,
-                       &Attr,
-                       &iosb,
-                       FILE_SHARE_READ | FILE_SHARE_WRITE,
-                       FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
-
-   if (!NT_SUCCESS(Status))
-   {
-      RtlFreeUnicodeString( &full);
-      RtlReleasePebLock ();
-      return Status;
-   }
-
-   /* don't keep the directory handle open on removable media */
-   if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle, &iosb, &device_info,
-                                                sizeof(device_info), FileFsDeviceInformation )) &&
-     (device_info.Characteristics & FILE_REMOVABLE_MEDIA))
-   {
-      DPRINT1("don't keep the directory handle open on removable media\n");
-      ZwClose( handle );
-      handle = 0;
-   }
-
-   if (cd->Handle)
-      ZwClose(cd->Handle);
-   cd->Handle = handle;
-
-   /* append trailing \ if missing */
-   size = full.Length / sizeof(WCHAR);
-   ptr = full.Buffer;
-   ptr += 4;  /* skip \??\ prefix */
-   size -= 4;
-
-   /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
-    * So the nullterm is replaced with \
-    * -Gunnar
-    */
-   if (size && ptr[size - 1] != '\\') ptr[size++] = '\\';
-
-   memcpy( cd->DosPath.Buffer, ptr, size * sizeof(WCHAR));
-   cd->DosPath.Buffer[size] = 0;
-   cd->DosPath.Length = size * sizeof(WCHAR);
-
-   RtlFreeUnicodeString( &full);
-   RtlReleasePebLock();
-
-   return STATUS_SUCCESS;
-}
-
-
-/******************************************************************
- *             collapse_path
- *
- * Helper for RtlGetFullPathName_U.
- * Get rid of . and .. components in the path.
- */
-void FORCEINLINE collapse_path( WCHAR *path, UINT mark )
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
 {
-    WCHAR *p, *next;
+    PCURDIR CurDir;
+    NTSTATUS Status;
+    RTL_PATH_TYPE PathType;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING FullPath, NtName;
+    PRTLP_CURDIR_REF OldCurDir = NULL;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo;
+    ULONG SavedLength, CharLength, FullPathLength;
+    HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL;
 
-    /* convert every / into a \ */
-    for (p = path; *p; p++) if (*p == '/') *p = '\\';
+    DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
 
-    /* collapse duplicate backslashes */
-    next = path + max( 1, mark );
-    for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
-    *next = 0;
+    /* Initialize for failure case */
+    RtlInitEmptyUnicodeString(&NtName, NULL, 0);
 
-    p = path + mark;
-    while (*p)
+    /* Can't set current directory on DOS device */
+    if (RtlIsDosDeviceName_Ustr(Path))
     {
-        if (*p == '.')
-        {
-            switch(p[1])
-            {
-            case '\\': /* .\ component */
-                next = p + 2;
-                memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
-                continue;
-            case 0:  /* final . */
-                if (p > path + mark) p--;
-                *p = 0;
-                continue;
-            case '.':
-                if (p[2] == '\\')  /* ..\ component */
-                {
-                    next = p + 3;
-                    if (p > path + mark)
-                    {
-                        p--;
-                        while (p > path + mark && p[-1] != '\\') p--;
-                    }
-                    memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
-                    continue;
-                }
-                else if (!p[2])  /* final .. */
-                {
-                    if (p > path + mark)
-                    {
-                        p--;
-                        while (p > path + mark && p[-1] != '\\') p--;
-                        if (p > path + mark) p--;
-                    }
-                    *p = 0;
-                    continue;
-                }
-                break;
-            }
-        }
-        /* skip to the next component */
-        while (*p && *p != '\\') p++;
-        if (*p == '\\')
-        {
-            /* remove last dot in previous dir name */
-            if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) );
-            else p++;
-        }
+        return STATUS_NOT_A_DIRECTORY;
     }
 
-    /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
-    while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
-    *p = 0;
-}
+    /* Get current directory */
+    RtlAcquirePebLock();
+    CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
 
+    /* Check if we have to drop current handle */
+    if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE)
+    {
+        OldHandle = CurDir->Handle;
+        CurDir->Handle = NULL;
+    }
 
+    /* Allocate a buffer for full path (using max possible length */
+    FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength);
+    if (!FullPath.Buffer)
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Leave;
+    }
 
-/******************************************************************
- *    skip_unc_prefix
- *
- * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
- */
-static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
-{
-    ptr += 2;
-    while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++;  /* share name */
-    while (IS_PATH_SEPARATOR(*ptr)) ptr++;
-    while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++;  /* dir name */
-    while (IS_PATH_SEPARATOR(*ptr)) ptr++;
-    return ptr;
-}
+    /* Init string */
+    FullPath.Length = 0;
+    FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
 
+    /* Get new directory full path */
+    FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType);
+    if (!FullPathLength)
+    {
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Leave;
+    }
 
-/******************************************************************
- *    get_full_path_helper
- *
- * Helper for RtlGetFullPathName_U
- * Note: name and buffer are allowed to point to the same memory spot
- */
-static ULONG get_full_path_helper(
-   LPCWSTR name,
-   LPWSTR buffer,
-   ULONG size)
-{
-    SIZE_T                      reqsize = 0, mark = 0, dep = 0, deplen;
-    LPWSTR                      ins_str = NULL;
-    LPCWSTR                     ptr;
-    const UNICODE_STRING*       cd;
-    WCHAR                       tmp[4];
+    SavedLength = FullPath.MaximumLength;
+    CharLength = FullPathLength / sizeof(WCHAR);
 
-    /* return error if name only consists of spaces */
-    for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
-    if (!*ptr) return 0;
+    if (FullPathLength > FullPath.MaximumLength)
+    {
+        Status = STATUS_NAME_TOO_LONG;
+        goto Leave;
+    }
 
-    RtlAcquirePebLock();
+    /* Translate it to NT name */
+    if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
+    {
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Leave;
+    }
 
-    //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
-    cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath;
+   InitializeObjectAttributes(&ObjectAttributes, &NtName,
+                              OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+                              NULL, NULL);
 
-    switch (RtlDetermineDosPathNameType_U(name))
+    /* If previous current directory was removable, then check it for dropping */
+    if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
+    {
+        /* Get back normal handle */
+        CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
+        CurDir->Handle = NULL;
+
+        /* Get device information */
+        Status = NtQueryVolumeInformationFile(CurDirHandle,
+                                              &IoStatusBlock,
+                                              &FileFsDeviceInfo,
+                                              sizeof(FileFsDeviceInfo),
+                                              FileFsDeviceInformation);
+        /* Retry without taking care of removable device */
+        if (!NT_SUCCESS(Status))
+        {
+            Status = RtlSetCurrentDirectory_U(Path);
+            goto Leave;
+        }
+    }
+    else
     {
-    case RtlPathTypeUncAbsolute:              /* \\foo   */
-        ptr = skip_unc_prefix( name );
-        mark = (ptr - name);
-        break;
+        /* Open directory */
+        Status = NtOpenFile(&CurDirHandle,
+                            SYNCHRONIZE | FILE_TRAVERSE,
+                            &ObjectAttributes,
+                            &IoStatusBlock,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE,
+                            FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
+        if (!NT_SUCCESS(Status)) goto Leave;
+
+        /* Get device information */
+        Status = NtQueryVolumeInformationFile(CurDirHandle,
+                                              &IoStatusBlock,
+                                              &FileFsDeviceInfo,
+                                              sizeof(FileFsDeviceInfo),
+                                              FileFsDeviceInformation);
+        if (!NT_SUCCESS(Status)) goto Leave;
+    }
 
-    case RtlPathTypeLocalDevice:           /* \\.\foo */
-        mark = 4;
-        break;
+    /* If device is removable, mark handle */
+    if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
+    {
+        CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
+    }
 
-    case RtlPathTypeDriveAbsolute:   /* c:\foo  */
-        reqsize = sizeof(WCHAR);
-        tmp[0] = towupper(name[0]);
-        ins_str = tmp;
-        dep = 1;
-        mark = 3;
-        break;
+    FullPath.Length = (USHORT)FullPathLength;
 
-    case RtlPathTypeDriveRelative:   /* c:foo   */
-        dep = 2;
-        if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
+    /* If full path isn't \ terminated, do it */
+    if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR)
+    {
+        if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
         {
-            UNICODE_STRING      var, val;
-
-            tmp[0] = '=';
-            tmp[1] = name[0];
-            tmp[2] = ':';
-            tmp[3] = '\0';
-            var.Length = 3 * sizeof(WCHAR);
-            var.MaximumLength = 4 * sizeof(WCHAR);
-            var.Buffer = tmp;
-            val.Length = 0;
-            val.MaximumLength = (USHORT)size;
-            val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
-            if (val.Buffer == NULL)
-            {
-                reqsize = 0;
-                goto done;
-            }
-
-            switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
-            {
-            case STATUS_SUCCESS:
-                /* FIXME: Win2k seems to check that the environment variable actually points
-                 * to an existing directory. If not, root of the drive is used
-                 * (this seems also to be the only spot in RtlGetFullPathName that the
-                 * existence of a part of a path is checked)
-                 */
-                /* fall thru */
-            case STATUS_BUFFER_TOO_SMALL:
-                reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
-                val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
-                ins_str = val.Buffer;
-                break;
-            case STATUS_VARIABLE_NOT_FOUND:
-                reqsize = 3 * sizeof(WCHAR);
-                tmp[0] = name[0];
-                tmp[1] = ':';
-                tmp[2] = '\\';
-                ins_str = tmp;
-                RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
-                break;
-            default:
-                DPRINT1("Unsupported status code\n");
-                RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer);
-                break;
-            }
-            mark = 3;
-            break;
+            Status = STATUS_NAME_TOO_LONG;
+            goto Leave;
         }
-        /* fall through */
 
-    case RtlPathTypeRelative:         /* foo     */
-        reqsize = cd->Length;
-        ins_str = cd->Buffer;
-        if (cd->Buffer[1] != ':')
-        {
-            ptr = skip_unc_prefix( cd->Buffer );
-            mark = ptr - cd->Buffer;
-        }
-        else mark = 3;
-        break;
+        FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR;
+        FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
+        FullPath.Length += sizeof(WCHAR);
+    }
 
-    case RtlPathTypeRooted:         /* \xxx    */
-#ifdef __WINE__
-        if (name[0] == '/')  /* may be a Unix path */
-        {
-            const WCHAR *ptr = name;
-            int drive = find_drive_root( &ptr );
-            if (drive != -1)
-            {
-                reqsize = 3 * sizeof(WCHAR);
-                tmp[0] = 'A' + drive;
-                tmp[1] = ':';
-                tmp[2] = '\\';
-                ins_str = tmp;
-                mark = 3;
-                dep = ptr - name;
-                break;
-            }
-        }
-#endif
-        if (cd->Buffer[1] == ':')
-        {
-            reqsize = 2 * sizeof(WCHAR);
-            tmp[0] = cd->Buffer[0];
-            tmp[1] = ':';
-            ins_str = tmp;
-            mark = 3;
-        }
-        else
+    /* If we have previous current directory with only us as reference, save it */
+    if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1)
+    {
+        OldCurDirHandle = RtlpCurDirRef->Handle;
+    }
+    else
+    {
+        /* Allocate new current directory struct saving previous one */
+        OldCurDir = RtlpCurDirRef;
+        RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF));
+        if (!RtlpCurDirRef)
         {
-            ptr = skip_unc_prefix( cd->Buffer );
-            reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
-            mark = reqsize / sizeof(WCHAR);
-            ins_str = cd->Buffer;
+            RtlpCurDirRef = OldCurDir;
+            OldCurDir = NULL;
+            Status = STATUS_NO_MEMORY;
+            goto Leave;
         }
-        break;
 
-    case RtlPathTypeRootLocalDevice:         /* \\.     */
-        reqsize = 4 * sizeof(WCHAR);
-        dep = 3;
-        tmp[0] = '\\';
-        tmp[1] = '\\';
-        tmp[2] = '.';
-        tmp[3] = '\\';
-        ins_str = tmp;
-        mark = 4;
-        break;
+        /* Set reference to 1 (us) */
+        RtlpCurDirRef->RefCount = 1;
+    }
+
+    /* Save new data */
+    CurDir->Handle = CurDirHandle;
+    RtlpCurDirRef->Handle = CurDirHandle;
+    CurDirHandle = NULL;
+
+    /* Copy full path */
+    RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR));
+    CurDir->DosPath.Length = FullPath.Length;
+
+    Status = STATUS_SUCCESS;
 
-    case RtlPathTypeUnknown:
-        goto done;
+Leave:
+    RtlReleasePebLock();
+
+    if (FullPath.Buffer)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
     }
 
-    /* enough space ? */
-    deplen = wcslen(name + dep) * sizeof(WCHAR);
-    if (reqsize + deplen + sizeof(WCHAR) > size)
+    if (NtName.Buffer)
     {
-        /* not enough space, return need size (including terminating '\0') */
-        reqsize += deplen + sizeof(WCHAR);
-        goto done;
+        RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
     }
 
-    memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
-    if (reqsize) memcpy(buffer, ins_str, reqsize);
-    reqsize += deplen;
+    if (CurDirHandle) NtClose(CurDirHandle);
 
-    if (ins_str != tmp && ins_str != cd->Buffer)
-        RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
+    if (OldHandle) NtClose(OldHandle);
 
-    collapse_path( buffer, (ULONG)mark );
-    reqsize = wcslen(buffer) * sizeof(WCHAR);
+    if (OldCurDirHandle) NtClose(OldCurDirHandle);
 
-done:
-    RtlReleasePebLock();
-    return (ULONG)reqsize;
+    if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
+    {
+        NtClose(OldCurDir->Handle);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
+    }
+
+    return Status;
 }
 
 
@@ -1317,57 +1635,33 @@ done:
  *
  * @implemented
  */
-ULONG NTAPI RtlGetFullPathName_U(
-   const WCHAR* name,
-   ULONG size,
-   WCHAR* buffer,
-   WCHAR** file_part)
-{
-    WCHAR*      ptr;
-    ULONG       dosdev;
-    ULONG       reqsize;
-
-    DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
 
-    if (!name || !*name) return 0;
-
-    if (file_part) *file_part = NULL;
-
-    /* check for DOS device name */
-    dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
-    if (dosdev)
-    {
-        DWORD   offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
-        DWORD   sz = LOWORD(dosdev); /* in bytes */
-
-        if (8 + sz + 2 > size) return sz + 10;
-        wcscpy(buffer, DeviceRootW);
-        memmove(buffer + 4, name + offset, sz);
-        buffer[4 + sz / sizeof(WCHAR)] = '\0';
-        /* file_part isn't set in this case */
-        return sz + 8;
-    }
+/*
+ * @implemented
+ */
+ULONG
+NTAPI
+RtlGetFullPathName_U(
+    _In_ PCWSTR FileName,
+    _In_ ULONG Size,
+    _Out_z_bytecap_(Size) PWSTR Buffer,
+    _Out_opt_ PWSTR *ShortName)
+{
+    NTSTATUS Status;
+    UNICODE_STRING FileNameString;
+    RTL_PATH_TYPE PathType;
 
-    reqsize = get_full_path_helper(name, buffer, size);
-    if (!reqsize) return 0;
-    if (reqsize > size)
-    {
-        LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
-        if (tmp == NULL) return 0;
-        reqsize = get_full_path_helper(name, tmp, reqsize);
-        if (reqsize + sizeof(WCHAR) > size)  /* it may have worked the second time */
-        {
-            RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
-            return reqsize + sizeof(WCHAR);
-        }
-        memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
-        RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
-    }
+    /* Build the string */
+    Status = RtlInitUnicodeStringEx(&FileNameString, FileName);
+    if (!NT_SUCCESS(Status)) return 0;
 
-    /* find file part */
-    if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
-        *file_part = ptr;
-    return reqsize;
+    /* Call the extended function */
+    return RtlGetFullPathName_Ustr(&FileNameString,
+                                   Size,
+                                   Buffer,
+                                   (PCWSTR*)ShortName,
+                                   NULL,
+                                   &PathType);
 }
 
 /*
@@ -1411,7 +1705,7 @@ RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,
  */
 BOOLEAN
 NTAPI
-RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName,
+RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,
                                      OUT PUNICODE_STRING NtName,
                                      OUT PCWSTR *PartName,
                                      OUT PRTL_RELATIVE_NAME_U RelativeName)
@@ -1430,7 +1724,7 @@ RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName,
  */
 NTSTATUS
 NTAPI
-RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PWSTR DosName,
+RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,
                                                 OUT PUNICODE_STRING NtName,
                                                 OUT PCWSTR *PartName,
                                                 OUT PRTL_RELATIVE_NAME_U RelativeName)
@@ -1472,9 +1766,6 @@ RtlDosSearchPath_U(IN PCWSTR Path,
     PWCHAR NewBuffer, BufferStart;
     PCWSTR p;
 
-    /* Validate the input */
-    if (!(Path) || !(FileName)) return 0;
-
     /* Check if this is an absolute path */
     if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
     {
@@ -1494,7 +1785,7 @@ RtlDosSearchPath_U(IN PCWSTR Path,
     while (*p)
     {
         /* Looking for an extension */
-        if (*p == '.')
+        if (*p == L'.')
         {
             /* No extension string needed -- it's part of the filename */
             Extension = NULL;
@@ -1552,7 +1843,7 @@ RtlDosSearchPath_U(IN PCWSTR Path,
         if (*Path)
         {
             /* Loop as long as there's no semicolon */
-            while (*Path != ';')
+            while (*Path != L';')
             {
                 /* Copy the next character */
                 *BufferStart++ = *Path++;
@@ -1560,13 +1851,13 @@ RtlDosSearchPath_U(IN PCWSTR Path,
             }
 
             /* We found a semi-colon, to stop path processing on this loop */
-            if (*Path == ';') ++Path;
+            if (*Path == L';') ++Path;
         }
 
         /* Add a terminating slash if needed */
-        if ((BufferStart != NewBuffer) && (BufferStart[-1] != '\\'))
+        if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR))
         {
-            *BufferStart++ = '\\';
+            *BufferStart++ = OBJ_NAME_PATH_SEPARATOR;
         }
 
         /* Bail out if we reached the end */
@@ -1605,6 +1896,719 @@ RtlDosSearchPath_U(IN PCWSTR Path,
     return Length;
 }
 
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,
+                          IN PUNICODE_STRING StaticString,
+                          IN PUNICODE_STRING DynamicString,
+                          IN PUNICODE_STRING *StringUsed,
+                          IN PSIZE_T FilePartSize,
+                          OUT PBOOLEAN NameInvalid,
+                          OUT RTL_PATH_TYPE* PathType,
+                          OUT PSIZE_T LengthNeeded)
+{
+    NTSTATUS Status;
+    PWCHAR StaticBuffer;
+    PCWCH ShortName;
+    ULONG Length;
+    USHORT StaticLength;
+    UNICODE_STRING TempDynamicString;
+
+    /* Initialize all our locals */
+    ShortName = NULL;
+    StaticBuffer = NULL;
+    TempDynamicString.Buffer = NULL;
+
+    /* Initialize the input parameters */
+    if (StringUsed) *StringUsed = NULL;
+    if (LengthNeeded) *LengthNeeded = 0;
+    if (FilePartSize) *FilePartSize = 0;
+
+    /* Check for invalid parameters */
+    if ((DynamicString) && !(StringUsed) && (StaticString))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Check if we did not get an input string */
+    if (!StaticString)
+    {
+        /* Allocate one */
+        StaticLength = MAX_PATH * sizeof(WCHAR);
+        StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR);
+        if (!StaticBuffer) return STATUS_NO_MEMORY;
+    }
+    else
+    {
+        /* Use the one we received */
+        StaticBuffer = StaticString->Buffer;
+        StaticLength = StaticString->MaximumLength;
+    }
+
+    /* Call the lower-level function */
+    Length = RtlGetFullPathName_Ustr(FileName,
+                                     StaticLength,
+                                     StaticBuffer,
+                                     &ShortName,
+                                     NameInvalid,
+                                     PathType);
+    DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer);
+    if (!Length)
+    {
+        /* Fail if it failed */
+        DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
+                 __FUNCTION__,
+                 __LINE__);
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Quickie;
+    }
+
+    /* Check if it fits inside our static string */
+    if ((StaticString) && (Length < StaticLength))
+    {
+        /* Set the final length */
+        StaticString->Length = (USHORT)Length;
+
+        /* Set the file part size */
+        if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
+
+        /* Return the static string if requested */
+        if (StringUsed) *StringUsed = StaticString;
+
+        /* We are done with success */
+        Status = STATUS_SUCCESS;
+        goto Quickie;
+    }
+
+    /* Did we not have an input dynamic string ?*/
+    if (!DynamicString)
+    {
+        /* Return the length we need */
+        if (LengthNeeded) *LengthNeeded = Length;
+
+        /* And fail such that the caller can try again */
+        Status = STATUS_BUFFER_TOO_SMALL;
+        goto Quickie;
+    }
+
+    /* Check if it fits in our static buffer */
+    if ((StaticBuffer) && (Length < StaticLength))
+    {
+        /* NULL-terminate it */
+        StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+        /* Set the settings for the dynamic string the caller sent */
+        DynamicString->MaximumLength = StaticLength;
+        DynamicString->Length = (USHORT)Length;
+        DynamicString->Buffer = StaticBuffer;
+
+        /* Set the part size */
+        if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0;
+
+        /* Return the dynamic string if requested */
+        if (StringUsed) *StringUsed = DynamicString;
+
+        /* Do not free the static buffer on exit, and return success */
+        StaticBuffer = NULL;
+        Status = STATUS_SUCCESS;
+        goto Quickie;
+    }
+
+    /* Now try again under the PEB lock */
+    RtlAcquirePebLock();
+    Length = RtlGetFullPathName_Ustr(FileName,
+                                     StaticLength,
+                                     StaticBuffer,
+                                     &ShortName,
+                                     NameInvalid,
+                                     PathType);
+    if (!Length)
+    {
+        /* It failed */
+        DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
+                 __FUNCTION__, __LINE__);
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Release;
+    }
+
+    /* Check if it fits inside our static string now */
+    if ((StaticString) && (Length < StaticLength))
+    {
+        /* Set the final length */
+        StaticString->Length = (USHORT)Length;
+
+        /* Set the file part size */
+        if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
+
+        /* Return the static string if requested */
+        if (StringUsed) *StringUsed = StaticString;
+
+        /* We are done with success */
+        Status = STATUS_SUCCESS;
+        goto Release;
+    }
+
+    /* Check if the path won't even fit in a real string */
+    if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
+    {
+        /* Name is way too long, fail */
+        Status = STATUS_NAME_TOO_LONG;
+        goto Release;
+    }
+
+    /* Allocate the string to hold the path name now */
+    TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR),
+                                                        TAG_USTR);
+    if (!TempDynamicString.Buffer)
+    {
+        /* Out of memory, fail */
+        Status = STATUS_NO_MEMORY;
+        goto Release;
+    }
+
+    /* Add space for a NULL terminator, and now check the full path */
+    TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
+    Length = RtlGetFullPathName_Ustr(FileName,
+                                     Length,
+                                     TempDynamicString.Buffer,
+                                     &ShortName,
+                                     NameInvalid,
+                                     PathType);
+    if (!Length)
+    {
+        /* Some path error, so fail out */
+        DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
+                 __FUNCTION__, __LINE__);
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Release;
+    }
+
+    /* It should fit in the string we just allocated */
+    ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR)));
+    if (Length > TempDynamicString.MaximumLength)
+    {
+        /* This is really weird and would mean some kind of race */
+        Status = STATUS_INTERNAL_ERROR;
+        goto Release;
+    }
+
+    /* Return the file part size */
+    if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0;
+
+    /* Terminate the whole string now */
+    TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Finalize the string and return it to the user */
+    DynamicString->Buffer = TempDynamicString.Buffer;
+    DynamicString->Length = (USHORT)Length;
+    DynamicString->MaximumLength = TempDynamicString.MaximumLength;
+    if (StringUsed) *StringUsed = DynamicString;
+
+    /* Return success and make sure we don't free the buffer on exit */
+    TempDynamicString.Buffer = NULL;
+    Status = STATUS_SUCCESS;
+
+Release:
+    /* Release the PEB lock */
+    RtlReleasePebLock();
+
+Quickie:
+    /* Free any buffers we should be freeing */
+    DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer);
+    if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer))
+    {
+        RtlpFreeMemory(StaticBuffer, TAG_USTR);
+    }
+    if (TempDynamicString.Buffer)
+    {
+        RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR);
+    }
+
+    /* Print out any unusual errors */
+    if ((NT_ERROR(Status)) &&
+        (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
+    {
+        DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
+                __FUNCTION__, FileName, Status);
+    }
+
+    /* Return, we're all done */
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RtlDosSearchPath_Ustr(IN ULONG Flags,
+                      IN PUNICODE_STRING PathString,
+                      IN PUNICODE_STRING FileNameString,
+                      IN PUNICODE_STRING ExtensionString,
+                      IN PUNICODE_STRING CallerBuffer,
+                      IN OUT PUNICODE_STRING DynamicString OPTIONAL,
+                      OUT PUNICODE_STRING* FullNameOut OPTIONAL,
+                      OUT PSIZE_T FilePartSize OPTIONAL,
+                      OUT PSIZE_T LengthNeeded OPTIONAL)
+{
+    WCHAR StaticCandidateBuffer[MAX_PATH];
+    UNICODE_STRING StaticCandidateString;
+    NTSTATUS Status;
+    RTL_PATH_TYPE PathType;
+    PWCHAR p, End, CandidateEnd, SegmentEnd;
+    SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0;
+    USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0;
+    PUNICODE_STRING FullIsolatedPath;
+    DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
+            Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString);
+
+    /* Initialize the input string */
+    RtlInitEmptyUnicodeString(&StaticCandidateString,
+                              StaticCandidateBuffer,
+                              sizeof(StaticCandidateBuffer));
+
+    /* Initialize optional arguments */
+    if (FullNameOut ) *FullNameOut  = NULL;
+    if (FilePartSize) *FilePartSize = 0;
+    if (LengthNeeded) *LengthNeeded = 0;
+    if (DynamicString)
+    {
+        DynamicString->Length = DynamicString->MaximumLength = 0;
+        DynamicString->Buffer = NULL;
+    }
+
+    /* Check for invalid parameters */
+    if ((Flags & ~7) ||
+        !(PathString) ||
+        !(FileNameString) ||
+        ((CallerBuffer) && (DynamicString) && !(FullNameOut)))
+    {
+        /* Fail */
+        DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__);
+        Status = STATUS_INVALID_PARAMETER;
+        goto Quickie;
+    }
+
+    /* First check what kind of path this is */
+    PathType = RtlDetermineDosPathNameType_Ustr(FileNameString);
+
+    /* Check if the caller wants to prevent relative .\ and ..\ paths */
+    if ((Flags & 2) &&
+         (PathType == RtlPathTypeRelative) &&
+         (FileNameString->Length >= (2 * sizeof(WCHAR))) &&
+         (FileNameString->Buffer[0] == L'.') &&
+         ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) ||
+          ((FileNameString->Buffer[1] == L'.') &&
+           ((FileNameString->Length >= (3 * sizeof(WCHAR))) &&
+           (IS_PATH_SEPARATOR(FileNameString->Buffer[2]))))))
+    {
+        /* Yes, and this path is like that, so make it seem unknown */
+        PathType = RtlPathTypeUnknown;
+    }
+
+    /* Now check relative vs non-relative paths */
+    if (PathType == RtlPathTypeRelative)
+    {
+        /* Does the caller want SxS? */
+        if (Flags & 1)
+        {
+            /* Apply the SxS magic */
+            FullIsolatedPath = NULL;
+            Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
+                                                              FileNameString,
+                                                              ExtensionString,
+                                                              CallerBuffer,
+                                                              DynamicString,
+                                                              &FullIsolatedPath,
+                                                              NULL,
+                                                              FilePartSize,
+                                                              LengthNeeded);
+            if (NT_SUCCESS(Status))
+            {
+                /* We found the SxS path, return it */
+                if (FullNameOut) *FullNameOut = FullIsolatedPath;
+                goto Quickie;
+            }
+            else if (Status != STATUS_SXS_KEY_NOT_FOUND)
+            {
+                /* Critical SxS error, fail */
+                DbgPrint("%s: Failing because call to "
+                         "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
+                          "status 0x%08lx\n",
+                         __FUNCTION__,
+                         FileNameString,
+                         Status);
+                goto Quickie;
+            }
+        }
+
+        /* No SxS key found, or not requested, check if there's an extension */
+        if (ExtensionString)
+        {
+            /* Save the extension length, and check if there's a file name */
+            ExtensionLength = ExtensionString->Length;
+            if (FileNameString->Length)
+            {
+                /* Start parsing the file name */
+                End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)];
+                while (End > FileNameString->Buffer)
+                {
+                    /* If we find a path separator, there's no extension */
+                    if (IS_PATH_SEPARATOR(*--End)) break;
+
+                    /* Otherwise, did we find an extension dot? */
+                    if (*End == L'.')
+                    {
+                        /* Ignore what the caller sent it, use the filename's */
+                        ExtensionString = NULL;
+                        ExtensionLength = 0;
+                        break;
+                    }
+                }
+            }
+        }
+
+        /* Check if we got a path */
+        if (PathString->Length)
+        {
+            /* Start parsing the path name, looking for path separators */
+            End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)];
+            p = End;
+            while ((p > PathString->Buffer) && (*--p == L';'))
+            {
+                /* This is the size of the path -- handle a trailing slash */
+                PathSize = End - p - 1;
+                if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
+
+                /* Check if we found a bigger path than before */
+                if (PathSize > MaxPathSize) MaxPathSize = PathSize;
+
+                /* Keep going with the path after this path separator */
+                End = p;
+            }
+
+            /* This is the trailing path, run the same code as above */
+            PathSize = End - p;
+            if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
+            if (PathSize > MaxPathSize) MaxPathSize = PathSize;
+
+            /* Finally, convert the largest path size into WCHAR */
+            MaxPathSize *= sizeof(WCHAR);
+        }
+
+        /* Use the extension, the file name, and the largest path as the size */
+        WorstCaseLength = ExtensionLength +
+                          FileNameString->Length +
+                          (USHORT)MaxPathSize +
+                          sizeof(UNICODE_NULL);
+        if (WorstCaseLength > UNICODE_STRING_MAX_BYTES)
+        {
+            /* It has to fit in a registry string, if not, fail here */
+            DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
+                     "worst case file name length is %Iu bytes\n",
+                     __FUNCTION__,
+                     WorstCaseLength);
+            Status = STATUS_NAME_TOO_LONG;
+            goto Quickie;
+        }
+
+        /* Scan the path now, to see if we can find the file */
+        p = PathString->Buffer;
+        End = &p[PathString->Length / sizeof(WCHAR)];
+        while (p < End)
+        {
+            /* Find out where this path ends */
+            for (SegmentEnd = p;
+                 ((SegmentEnd != End) && (*SegmentEnd != L';'));
+                 SegmentEnd++);
+
+            /* Compute the size of this path */
+            ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR);
+
+            /* Handle trailing slash if there isn't one */
+            if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1))))
+            {
+                /* Add space for one */
+                SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR);
+            }
+
+            /* Now check if our initial static buffer is too small */
+            if (StaticCandidateString.MaximumLength <
+                (SegmentSize + ExtensionLength + FileNameString->Length))
+            {
+                /* At this point we should've been using our static buffer */
+                ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer);
+                if (StaticCandidateString.Buffer != StaticCandidateBuffer)
+                {
+                    /* Something is really messed up if this was the dynamic string */
+                    DbgPrint("%s: internal error #1; "
+                             "CandidateString.Buffer = %p; "
+                             "StaticCandidateBuffer = %p\n",
+                            __FUNCTION__,
+                            StaticCandidateString.Buffer,
+                            StaticCandidateBuffer);
+                    Status = STATUS_INTERNAL_ERROR;
+                    goto Quickie;
+                }
+
+                /* We checked before that the maximum possible size shoudl fit! */
+                ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) <
+                        UNICODE_STRING_MAX_BYTES);
+                if ((SegmentSize + ExtensionLength + FileNameString->Length) >
+                    (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR)))
+                {
+                    /* For some reason it's not fitting anymore. Something messed up */
+                    DbgPrint("%s: internal error #2; SegmentSize = %u, "
+                             "FileName->Length = %u, DefaultExtensionLength = %u\n",
+                             __FUNCTION__,
+                             SegmentSize,
+                             FileNameString->Length,
+                             ExtensionLength);
+                    Status = STATUS_INTERNAL_ERROR;
+                    goto Quickie;
+                }
+
+                /* Now allocate the dynamic string */
+                StaticCandidateString.MaximumLength = FileNameString->Length +
+                                                      WorstCaseLength;
+                StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength,
+                                                                        TAG_USTR);
+                if (!StaticCandidateString.Buffer)
+                {
+                    /* Out of memory, fail */
+                    DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
+                             __FUNCTION__,
+                             StaticCandidateString.MaximumLength);
+                    Status = STATUS_NO_MEMORY;
+                    goto Quickie;
+                }
+            }
+
+            /* Copy the path in the string */
+            RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount);
+
+            /* Get to the end of the string, and add the trailing slash if missing */
+            CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)];
+            if ((SegmentSize) && (SegmentSize != ByteCount))
+            {
+                *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR;
+            }
+
+            /* Copy the filename now */
+            RtlCopyMemory(CandidateEnd,
+                          FileNameString->Buffer,
+                          FileNameString->Length);
+            CandidateEnd += (FileNameString->Length / sizeof(WCHAR));
+
+            /* Check if there was an extension */
+            if (ExtensionString)
+            {
+                /* Copy the extension too */
+                RtlCopyMemory(CandidateEnd,
+                              ExtensionString->Buffer,
+                              ExtensionString->Length);
+                          CandidateEnd += (ExtensionString->Length / sizeof(WCHAR));
+            }
+
+            /* We are done, terminate it */
+            *CandidateEnd = UNICODE_NULL;
+
+            /* Now set the final length of the string so it becomes valid */
+            StaticCandidateString.Length = (USHORT)(CandidateEnd -
+                                            StaticCandidateString.Buffer) *
+                                           sizeof(WCHAR);
+
+            /* Check if this file exists */
+            DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer);
+            if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE))
+            {
+                /* Awesome, it does, now get the full path */
+                Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
+                                                   CallerBuffer,
+                                                   DynamicString,
+                                                   (PUNICODE_STRING*)FullNameOut,
+                                                   FilePartSize,
+                                                   NULL,
+                                                   &PathType,
+                                                   LengthNeeded);
+                if (!(NT_SUCCESS(Status)) &&
+                    ((Status != STATUS_NO_SUCH_FILE) &&
+                     (Status != STATUS_BUFFER_TOO_SMALL)))
+                {
+                    DbgPrint("%s: Failing because we thought we found %wZ on "
+                             "the search path, but RtlGetfullPathNameUStrEx() "
+                             "returned %08lx\n",
+                             __FUNCTION__,
+                             &StaticCandidateString,
+                             Status);
+                }
+                DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+                goto Quickie;
+            }
+            else
+            {
+                /* Otherwise, move to the next path */
+                if (SegmentEnd != End)
+                {
+                    /* Handle the case of the path separator trailing */
+                    p = SegmentEnd + 1;
+                }
+                else
+                {
+                    p = SegmentEnd;
+                }
+            }
+        }
+
+        /* Loop finished and we didn't break out -- fail */
+        Status = STATUS_NO_SUCH_FILE;
+    }
+    else
+    {
+        /* We have a full path, so check if it does exist */
+        DPRINT("%wZ\n", FileNameString);
+        if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE))
+        {
+            /* It doesn't exist, did we have an extension? */
+            if (!(ExtensionString) || !(ExtensionString->Length))
+            {
+                /* No extension, so just fail */
+                Status = STATUS_NO_SUCH_FILE;
+                goto Quickie;
+            }
+
+            /* There was an extension, check if the filename already had one */
+            if (!(Flags & 4) && (FileNameString->Length))
+            {
+                /* Parse the filename */
+                p = FileNameString->Buffer;
+                End = &p[FileNameString->Length / sizeof(WCHAR)];
+                while (End > p)
+                {
+                    /* If there's a path separator, there's no extension */
+                    if (IS_PATH_SEPARATOR(*--End)) break;
+
+                    /* Othwerwise, did we find an extension dot? */
+                    if (*End == L'.')
+                    {
+                        /* File already had an extension, so fail */
+                        Status = STATUS_NO_SUCH_FILE;
+                        goto Quickie;
+                    }
+                }
+            }
+
+            /* So there is an extension, we'll try again by adding it */
+            NamePlusExtLength = FileNameString->Length +
+                                ExtensionString->Length +
+                                sizeof(UNICODE_NULL);
+            if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES)
+            {
+                /* It won't fit in any kind of valid string, so fail */
+                DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
+                         __FUNCTION__,
+                         NamePlusExtLength);
+                Status = STATUS_NAME_TOO_LONG;
+                goto Quickie;
+            }
+
+            /* Fill it fit in our temporary string? */
+            if (NamePlusExtLength > StaticCandidateString.MaximumLength)
+            {
+                /* It won't fit anymore, allocate a dynamic string for it */
+                StaticCandidateString.MaximumLength = NamePlusExtLength;
+                StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength,
+                                                                        TAG_USTR);
+                if (!StaticCandidateString.Buffer)
+                {
+                    /* Ran out of memory, so fail */
+                    DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
+                             __FUNCTION__);
+                    Status = STATUS_NO_MEMORY;
+                    goto Quickie;
+                }
+            }
+
+            /* Copy the filename */
+            RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
+
+            /* Copy the extension */
+            RtlAppendUnicodeStringToString(&StaticCandidateString,
+                                           ExtensionString);
+
+            DPRINT("SB: %wZ\n", &StaticCandidateString);
+
+            /* And check if this file now exists */
+            if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE))
+            {
+                /* Still no joy, fail out */
+                Status = STATUS_NO_SUCH_FILE;
+                goto Quickie;
+            }
+
+            /* File was found, get the final full path */
+            Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
+                                               CallerBuffer,
+                                               DynamicString,
+                                               (PUNICODE_STRING*)FullNameOut,
+                                               FilePartSize,
+                                               NULL,
+                                               &PathType,
+                                               LengthNeeded);
+            if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE))
+            {
+                DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
+                         "failed with status %08lx\n",
+                         __FUNCTION__,
+                         &StaticCandidateString,
+                         Status);
+            }
+            DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+        }
+        else
+        {
+            /* File was found on the first try, get the final full path */
+            Status = RtlGetFullPathName_UstrEx(FileNameString,
+                                               CallerBuffer,
+                                               DynamicString,
+                                               (PUNICODE_STRING*)FullNameOut,
+                                               FilePartSize,
+                                               NULL,
+                                               &PathType,
+                                               LengthNeeded);
+            if (!(NT_SUCCESS(Status)) &&
+                ((Status != STATUS_NO_SUCH_FILE) &&
+                (Status != STATUS_BUFFER_TOO_SMALL)))
+            {
+                DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
+                         "failed with status %08lx\n",
+                         __FUNCTION__,
+                         FileNameString,
+                         Status);
+            }
+            DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
+        }
+    }
+
+Quickie:
+    /* Anything that was not an error, turn into STATUS_SUCCESS */
+    if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS;
+
+    /* Check if we had a dynamic string */
+    if ((StaticCandidateString.Buffer) &&
+        (StaticCandidateString.Buffer != StaticCandidateBuffer))
+    {
+        /* Free it */
+        RtlFreeUnicodeString(&StaticCandidateString);
+    }
+
+    /* Return the status */
+    return Status;
+}
+
 /*
  * @implemented
  */