[RTL/NDK]
[reactos.git] / reactos / lib / rtl / path.c
index d2ae4a9..ba53f96 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 ********************************************************************/
 
@@ -39,15 +46,55 @@ 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;
+
+    /* 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:             */
+    }
+}
+
 ULONG
 NTAPI
-RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
+RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
 {
-    UNICODE_STRING DeviceName;
-    PWCHAR Start, End, Ptr;
-    ULONG DeviceNameLength;
+    UNICODE_STRING PathCopy;
+    PWCHAR Start, End;
+    USHORT PathChars, ColonCount = 0;
+    USHORT ReturnOffset = 0, ReturnLength, OriginalLength;
+    WCHAR c;
 
     /* Validate the input */
     if (!PathString) return 0;
@@ -69,98 +116,135 @@ RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString)
             }
             return 0;
 
-        /* Skip the drive name for drive relative or absolute paths */
-        case RtlPathTypeDriveAbsolute:
-        case RtlPathTypeDriveRelative:
-            Start = PathString->Buffer + 2;
-            break;
-
         default:
-            Start = PathString->Buffer;
             break;
     }
 
-    /* Find start of file name */
-    for (Ptr = Start; *Ptr; Ptr++)
-        if (IS_PATH_SEPARATOR(*Ptr))
-            Start = Ptr + 1;
+    /* Make a copy of the string */
+    PathCopy = *PathString;
+    OriginalLength = PathString->Length;
 
-    /* Truncate at extension or stream */
-    for (End = Start; *End; End++)
-        if (*End == L'.' || *End == L':')
-            break;
-    End--;
+    /* Return if there's no characters */
+    PathChars = PathCopy.Length / sizeof(WCHAR);
+    if (!PathChars) return 0;
 
-    /* Remove trailing spaces */
-    while (End >= Start && *End == L' ')
-        End--;
+    /* Check for drive path and truncate */
+    if (PathCopy.Buffer[PathChars - 1] == L':')
+    {
+        /* Fixup the lengths */
+        PathCopy.Length -= sizeof(WCHAR);
+        if (!--PathChars) return 0;
 
-    /* Build the device name string */
-    DeviceNameLength = End - Start + 1;
-    DeviceName.Buffer = Start;
-    DeviceName.Length = (USHORT)DeviceNameLength * sizeof(WCHAR);
-    DeviceName.MaximumLength = DeviceName.Length;
+        /* Remember this for later */
+        ColonCount = 1;
+    }
 
-    /* Check the device name */
-    if (DeviceNameLength == 3)
+    /* Check for extension or space, and truncate */
+    do
     {
-        if (RtlPrefixUnicodeString(&RtlpDosAUXDevice, &DeviceName, TRUE) ||
-            RtlPrefixUnicodeString(&RtlpDosCONDevice, &DeviceName, TRUE) ||
-            RtlPrefixUnicodeString(&RtlpDosNULDevice, &DeviceName, TRUE) ||
-            RtlPrefixUnicodeString(&RtlpDosPRNDevice, &DeviceName, TRUE))
+        /* Stop if we hit something else than a space or period */
+        c = PathCopy.Buffer[PathChars - 1];
+        if ((c != '.') && (c != ' ')) break;
+
+        /* Fixup the lengths */
+        PathCopy.Length -= sizeof(WCHAR);
+
+        /* Remember this for later */
+        ColonCount++;
+    } while (--PathChars);
+
+    /* Anything still left? */
+    if (PathChars)
+    {
+        /* Loop from the end */
+        for (End = &PathCopy.Buffer[PathChars - 1];
+             End >= PathCopy.Buffer;
+             --End)
         {
-            return MAKELONG(DeviceNameLength * sizeof(WCHAR), (Start - PathString->Buffer) * sizeof(WCHAR));
+            /* Check if the character is a path or drive separator */
+            c = *End;
+            if ((c == '\\') || (c == '/') || ((c == ':') && (End == PathCopy.Buffer + 1)))
+            {
+                /* Get the next lower case character */
+                End++;
+                c = *End | ' '; // ' ' == ('z' - 'Z')
+
+                /* 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')))
+                {
+                    /* Calculate the offset */
+                    ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
+
+                    /* Build the final string */
+                    PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
+                    PathCopy.Buffer = End;
+
+                    /* Save new amount of chars in the path */
+                    PathChars = PathCopy.Length / sizeof(WCHAR);
+
+                    break;
+                }
+                else
+                {
+                    return 0;
+                }
+            }
         }
-    }
-    else if (DeviceNameLength == 4)
-    {
-        if ((RtlPrefixUnicodeString(&RtlpDosCOMDevice, &DeviceName, TRUE) ||
-             RtlPrefixUnicodeString(&RtlpDosLPTDevice, &DeviceName, TRUE)) &&
-            iswdigit(DeviceName.Buffer[3]) &&
-            DeviceName.Buffer[3] != L'0')
+
+        /* 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'))
         {
-            return MAKELONG(DeviceNameLength * sizeof(WCHAR), (Start - PathString->Buffer) * sizeof(WCHAR));
+            /* Not LPT, COM, PRN, AUX, or NUL */
+            return 0;
         }
     }
 
-    /* Otherwise, this is not a valid DOS device */
-    return 0;
-}
-
-RTL_PATH_TYPE
-NTAPI
-RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
-{
-    PWCHAR Path;
-    ULONG Chars;
-
-    /* Validate the input */
-    if (!PathString) return RtlPathTypeUnknown;
+    /* Now skip past any extra extension or drive letter characters */
+    Start = PathCopy.Buffer;
+    End = &Start[PathChars];
+    while (Start < End)
+    {
+        c = *Start;
+        if ((c == '.') || (c == ':')) break;
+        Start++;
+    }
 
-    Path = PathString->Buffer;
-    Chars = PathString->Length / sizeof(WCHAR);
+    /* And then go backwards to get rid of spaces */
+    while ((Start > PathCopy.Buffer) && (Start[-1] == ' ')) --Start;
 
-    /* Return if there are no characters */
-    if (!Chars) return RtlPathTypeUnknown;
+    /* Finally see how many characters are left, and that's our size */
+    PathChars = (USHORT)(Start - PathCopy.Buffer);
+    PathCopy.Length = PathChars * sizeof(WCHAR);
 
-    /*
-     * 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])))
+    /* 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')))
     {
-        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 \\?     */
+        /* Don't compare the number part, just check for LPT or COM */
+        PathCopy.Length -= sizeof(WCHAR);
+        if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) ||
+            (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
+        {
+            /* Found it */
+            ReturnLength = sizeof(L"COM1") - sizeof(WCHAR);
+            return MAKELONG(ReturnLength, ReturnOffset);
+        }
     }
-    else
+    else if ((PathChars == 3) &&
+             ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) ||
+              (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) ||
+              (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) ||
+              (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
     {
-        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:             */
+        /* Otherwise this was something like AUX, NUL, PRN, or CON */
+        ReturnLength = sizeof(L"AUX") - sizeof(WCHAR);
+        return MAKELONG(ReturnLength, ReturnOffset);
     }
+
+    /* Otherwise, this is not a valid DOS device */
+    return 0;
 }
 
 NTSTATUS
@@ -363,9 +447,9 @@ RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
     }
 
     /* Build the final NT path string */
-    NtPath->Length = DosLength;
+    NtPath->Length = (USHORT)DosLength;
     NtPath->Buffer = NewBuffer;
-    NtPath->MaximumLength = DosLength + sizeof(UNICODE_NULL);
+    NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
     return STATUS_SUCCESS;
 }
 
@@ -380,11 +464,12 @@ 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",
@@ -502,8 +587,8 @@ RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
 
     /* Setup the actual NT path string and terminate it */
     NtName->Buffer = NewBuffer;
-    NtName->Length = Length;
-    NtName->MaximumLength = MaxLength;
+    NtName->Length = (USHORT)Length;
+    NtName->MaximumLength = (USHORT)MaxLength;
     NewBuffer[LengthChars] = UNICODE_NULL;
     DPRINT("new buffer: %S\n", NewBuffer);
     DPRINT("NT Name: %wZ\n", NtName);
@@ -533,26 +618,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))
                 {
-                    Length = ((cd->DosPath.Length / sizeof(WCHAR)) - PrefixCut) + ((InputPathType == 1) ? 8 : 4);
-                    RelativeName->RelativeName.Buffer = NewBuffer + Length;
-                    RelativeName->RelativeName.Length = NtName->Length - (Length * sizeof(WCHAR));
+                    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))
+                {
+                    /* Make relative name string */
+                    RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + FullPath.Length - PrefixCut);
+                    RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
+                    /* If relative name starts with \, skip it */
+                    if (RelativeName->RelativeName.Buffer[0] == L'\\')
+                    {
+                        RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)RelativeName->RelativeName.Buffer + sizeof(WCHAR));
+                        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;
                 }
             }
         }
@@ -720,8 +838,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;
     }
 }
@@ -776,7 +899,7 @@ RtlDetermineDosPathNameType_U(IN PCWSTR Path)
  */
 ULONG
 NTAPI
-RtlIsDosDeviceName_U(IN PWSTR Path)
+RtlIsDosDeviceName_U(IN PCWSTR Path)
 {
     UNICODE_STRING PathString;
     NTSTATUS Status;
@@ -815,7 +938,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':'))
     {
@@ -824,13 +954,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();
@@ -842,7 +972,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':'))
@@ -866,89 +996,204 @@ RtlGetCurrentDirectory_U(IN ULONG MaximumLength,
 /*
  * @implemented
  */
-NTSTATUS NTAPI
-RtlSetCurrentDirectory_U(PUNICODE_STRING dir)
+NTSTATUS
+NTAPI
+RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
 {
-   UNICODE_STRING full;
-   FILE_FS_DEVICE_INFORMATION device_info;
-   OBJECT_ATTRIBUTES Attr;
-   IO_STATUS_BLOCK iosb;
-   PCURDIR cd;
-   NTSTATUS Status;
-   ULONG 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;
+    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;
+
+    DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
+
+    /* Initialize for failure case */
+    RtlInitEmptyUnicodeString(&NtName, NULL, 0);
+
+    /* Can't set current directory on DOS device */
+    if (RtlIsDosDeviceName_Ustr(Path))
+    {
+        return STATUS_NOT_A_DIRECTORY;
+    }
+
+    /* 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;
+    }
+
+    /* 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;
+    }
+
+    SavedLength = FullPath.MaximumLength;
+    CharLength = FullPathLength / sizeof(WCHAR);
+
+    if (FullPathLength > FullPath.MaximumLength)
+    {
+        Status = STATUS_NAME_TOO_LONG;
+        goto Leave;
+    }
+
+    /* Translate it to NT name */
+    if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
+    {
+        Status = STATUS_OBJECT_NAME_INVALID;
+        goto Leave;
+    }
+
+   InitializeObjectAttributes(&ObjectAttributes, &NtName,
+                              OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+                              NULL, NULL);
+
+    /* 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
+    {
+        /* 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;
+    }
+
+    /* If device is removable, mark handle */
+    if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
+    {
+        CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
+    }
+
+    FullPath.Length = (USHORT)FullPathLength;
+
+    /* If full path isn't \ terminated, do it */
+    if (FullPath.Buffer[CharLength - 1] != L'\\')
+    {
+        if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
+        {
+            Status = STATUS_NAME_TOO_LONG;
+            goto Leave;
+        }
+
+        FullPath.Buffer[CharLength] = L'\\';
+        FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
+        FullPath.Length += sizeof(WCHAR);
+    }
+
+    /* 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)
+        {
+            RtlpCurDirRef = OldCurDir;
+            OldCurDir = NULL;
+            Status = STATUS_NO_MEMORY;
+            goto Leave;
+        }
+
+        /* 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;
+
+Leave:
+    RtlReleasePebLock();
+
+    if (FullPath.Buffer)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
+    }
+
+    if (NtName.Buffer)
+    {
+        RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
+    }
+
+    if (CurDirHandle) NtClose(CurDirHandle);
+
+    if (OldHandle) NtClose(OldHandle);
+
+    if (OldCurDirHandle) NtClose(OldCurDirHandle);
+
+    if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
+    {
+        NtClose(OldCurDir->Handle);
+        RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
+    }
+
+    return Status;
 }
 
 
@@ -1055,7 +1300,7 @@ static ULONG get_full_path_helper(
    LPWSTR buffer,
    ULONG size)
 {
-    ULONG                       reqsize = 0, mark = 0, dep = 0, deplen;
+    SIZE_T                      reqsize = 0, mark = 0, dep = 0, deplen;
     LPWSTR                      ins_str = NULL;
     LPCWSTR                     ptr;
     const UNICODE_STRING*       cd;
@@ -1103,7 +1348,7 @@ static ULONG get_full_path_helper(
             var.MaximumLength = 4 * sizeof(WCHAR);
             var.Buffer = tmp;
             val.Length = 0;
-            val.MaximumLength = size;
+            val.MaximumLength = (USHORT)size;
             val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
             if (val.Buffer == NULL)
             {
@@ -1221,12 +1466,12 @@ static ULONG get_full_path_helper(
     if (ins_str != tmp && ins_str != cd->Buffer)
         RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
 
-    collapse_path( buffer, mark );
+    collapse_path( buffer, (ULONG)mark );
     reqsize = wcslen(buffer) * sizeof(WCHAR);
 
 done:
     RtlReleasePebLock();
-    return reqsize;
+    return (ULONG)reqsize;
 }
 
 
@@ -1336,7 +1581,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)
@@ -1355,7 +1600,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)
@@ -1530,6 +1775,725 @@ 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: %d 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 ((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
+ */
+ULONG
+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 (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__,
+                             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 */
+            RtlCopyMemory(StaticCandidateString.Buffer,
+                          FileNameString->Buffer,
+                          FileNameString->Length);
+
+            /* Copy the extension */
+            RtlCopyMemory(&StaticCandidateString.Buffer[FileNameString->Length / sizeof(WCHAR)],
+                          ExtensionString->Buffer,
+                          ExtensionString->Length);
+
+            /* Now NULL-terminate */
+            StaticCandidateString.Buffer[StaticCandidateString.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+            /* Finalize the length of the string to make it valid */
+            StaticCandidateString.Length = FileNameString->Length + ExtensionString->Length;
+            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
  */