xref: /reactos/sdk/lib/rtl/path.c (revision 50cf16b3)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/rtl/path.c
5  * PURPOSE:         Path and current directory functions
6  * PROGRAMMERS:     Wine team
7  *                  Thomas Weidenmueller
8  *                  Gunnar Dalsnes
9  *                  Alex Ionescu (alex.ionescu@reactos.org)
10  *                  Pierre Schweitzer (pierre@reactos.org)
11  */
12 
13 /* INCLUDES *******************************************************************/
14 
15 #include <rtl.h>
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 /* DEFINITONS and MACROS ******************************************************/
21 
22 #define MAX_PFX_SIZE       16
23 
24 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
25 
26 #define RTL_CURDIR_IS_REMOVABLE 0x1
27 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2
28 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
29 C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
30 
31 
32 /* GLOBALS ********************************************************************/
33 
34 const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
35 
36 const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");
37 const UNICODE_STRING RtlpWin32NtRootSlash    = RTL_CONSTANT_STRING(L"\\\\?\\");
38 const UNICODE_STRING RtlpDosSlashCONDevice   = RTL_CONSTANT_STRING(L"\\\\.\\CON");
39 const UNICODE_STRING RtlpDosDevicesPrefix    = RTL_CONSTANT_STRING(L"\\??\\");
40 
41 const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");
42 const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");
43 const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN");
44 const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");
45 const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");
46 const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
47 
48 const UNICODE_STRING RtlpDoubleSlashPrefix   = RTL_CONSTANT_STRING(L"\\\\");
49 
50 static const UNICODE_STRING RtlpDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
51 static const UNICODE_STRING RtlpDotLocal = RTL_CONSTANT_STRING(L".Local\\");
52 static const UNICODE_STRING RtlpPathDividers = RTL_CONSTANT_STRING(L"\\/");
53 
54 
55 PRTLP_CURDIR_REF RtlpCurDirRef;
56 
57 /* PRIVATE FUNCTIONS **********************************************************/
58 
59 RTL_PATH_TYPE
60 NTAPI
61 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString)
62 {
63     PWCHAR Path;
64     ULONG Chars;
65 
66     Path = PathString->Buffer;
67     Chars = PathString->Length / sizeof(WCHAR);
68 
69     /* Return if there are no characters */
70     if (!Chars) return RtlPathTypeRelative;
71 
72     /*
73      * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
74      * actually check for the path length before touching the characters
75      */
76     if (IS_PATH_SEPARATOR(Path[0]))
77     {
78         if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted;                /* \x             */
79         if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x            */
80         if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice;           /* \\.\x or \\?\x */
81         if (Chars != 3) return RtlPathTypeUncAbsolute;                                             /* \\.x or \\?x   */
82         return RtlPathTypeRootLocalDevice;                                                         /* \\. or \\?     */
83     }
84     else
85     {
86         if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative;                          /* x              */
87         if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative;         /* x:             */
88         return RtlPathTypeDriveAbsolute;                                                           /* x:\            */
89     }
90 }
91 
92 ULONG
93 NTAPI
94 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString)
95 {
96     UNICODE_STRING PathCopy;
97     PWCHAR Start, End;
98     USHORT PathChars, ColonCount = 0;
99     USHORT ReturnOffset = 0, ReturnLength, OriginalLength;
100     WCHAR c;
101 
102     /* Validate the input */
103     if (!PathString) return 0;
104 
105     /* Check what type of path this is */
106     switch (RtlDetermineDosPathNameType_Ustr(PathString))
107     {
108         /* Fail for UNC or unknown paths */
109         case RtlPathTypeUnknown:
110         case RtlPathTypeUncAbsolute:
111             return 0;
112 
113         /* Make special check for the CON device */
114         case RtlPathTypeLocalDevice:
115             if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE))
116             {
117                 /* This should return 0x80006 */
118                 return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length);
119             }
120             return 0;
121 
122         default:
123             break;
124     }
125 
126     /* Make a copy of the string */
127     PathCopy = *PathString;
128     OriginalLength = PathString->Length;
129 
130     /* Return if there's no characters */
131     PathChars = PathCopy.Length / sizeof(WCHAR);
132     if (!PathChars) return 0;
133 
134     /* Check for drive path and truncate */
135     if (PathCopy.Buffer[PathChars - 1] == L':')
136     {
137         /* Fixup the lengths */
138         PathCopy.Length -= sizeof(WCHAR);
139         if (!--PathChars) return 0;
140 
141         /* Remember this for later */
142         ColonCount = 1;
143     }
144 
145     /* Check for extension or space, and truncate */
146     do
147     {
148         /* Stop if we hit something else than a space or period */
149         c = PathCopy.Buffer[PathChars - 1];
150         if ((c != L'.') && (c != L' ')) break;
151 
152         /* Fixup the lengths */
153         PathCopy.Length -= sizeof(WCHAR);
154 
155         /* Remember this for later */
156         ColonCount++;
157     } while (--PathChars);
158 
159     /* Anything still left? */
160     if (PathChars)
161     {
162         /* Loop from the end */
163         for (End = &PathCopy.Buffer[PathChars - 1];
164              End >= PathCopy.Buffer;
165              --End)
166         {
167             /* Check if the character is a path or drive separator */
168             c = *End;
169             if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1)))
170             {
171                 /* Get the next lower case character */
172                 End++;
173                 c = RtlpDowncaseUnicodeChar(*End);
174 
175                 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
176                 if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) &&
177                     ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n')))
178                 {
179                     /* Calculate the offset */
180                     ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
181 
182                     /* Build the final string */
183                     PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR));
184                     PathCopy.Buffer = End;
185 
186                     /* Save new amount of chars in the path */
187                     PathChars = PathCopy.Length / sizeof(WCHAR);
188 
189                     break;
190                 }
191                 else
192                 {
193                     return 0;
194                 }
195             }
196         }
197 
198         /* Get the next lower case character and check if it's a DOS device */
199         c = RtlpDowncaseUnicodeChar(*PathCopy.Buffer);
200         if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n'))
201         {
202             /* Not LPT, COM, PRN, AUX, or NUL */
203             return 0;
204         }
205     }
206 
207     /* Now skip past any extra extension or drive letter characters */
208     Start = PathCopy.Buffer;
209     End = &Start[PathChars];
210     while (Start < End)
211     {
212         c = *Start;
213         if ((c == L'.') || (c == L':')) break;
214         Start++;
215     }
216 
217     /* And then go backwards to get rid of spaces */
218     while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start;
219 
220     /* Finally see how many characters are left, and that's our size */
221     PathChars = (USHORT)(Start - PathCopy.Buffer);
222     PathCopy.Length = PathChars * sizeof(WCHAR);
223 
224     /* Check if this is a COM or LPT port, which has a digit after it */
225     if ((PathChars == 4) &&
226         (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0')))
227     {
228         /* Don't compare the number part, just check for LPT or COM */
229         PathCopy.Length -= sizeof(WCHAR);
230         if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) ||
231             (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE)))
232         {
233             /* Found it */
234             ReturnLength = sizeof(L"COM1") - sizeof(WCHAR);
235             return MAKELONG(ReturnLength, ReturnOffset);
236         }
237     }
238     else if ((PathChars == 3) &&
239              ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) ||
240               (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) ||
241               (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) ||
242               (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE))))
243     {
244         /* Otherwise this was something like AUX, NUL, PRN, or CON */
245         ReturnLength = sizeof(L"AUX") - sizeof(WCHAR);
246         return MAKELONG(ReturnLength, ReturnOffset);
247     }
248 
249     /* Otherwise, this is not a valid DOS device */
250     return 0;
251 }
252 
253 NTSTATUS
254 NTAPI
255 RtlpCheckDeviceName(IN PUNICODE_STRING FileName,
256                     IN ULONG Length,
257                     OUT PBOOLEAN NameInvalid)
258 {
259     PWCHAR Buffer;
260     NTSTATUS Status;
261 
262     /* Allocate a large enough buffer */
263     Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length);
264     if (Buffer)
265     {
266         /* Assume failure */
267         *NameInvalid = TRUE;
268 
269         /* Copy the filename */
270         RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length);
271 
272         /* And add a dot at the end */
273         Buffer[Length / sizeof(WCHAR)] = L'.';
274         Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
275 
276         /* Check if the file exists or not */
277         *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE;
278 
279         /* Get rid of the buffer now */
280         Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
281     }
282     else
283     {
284         /* Assume the name is ok, but fail the call */
285         *NameInvalid = FALSE;
286         Status = STATUS_NO_MEMORY;
287     }
288 
289     /* Return the status */
290     return Status;
291 }
292 
293 
294 
295 /******************************************************************
296  *    RtlpCollapsePath (from WINE)
297  *
298  * Helper for RtlGetFullPathName_U
299  *
300  * 1) Converts slashes into backslashes and gets rid of duplicated ones;
301  * 2) Gets rid of . and .. components in the path.
302  *
303  * Returns the full path length without its terminating NULL character.
304  */
305 static ULONG
306 RtlpCollapsePath(PWSTR Path, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark, BOOLEAN SkipTrailingPathSeparators)
307 {
308     PWSTR p, next;
309 
310     // FIXME: Do not suppose NULL-terminated strings!!
311 
312     ULONG PathLength = wcslen(Path);
313     PWSTR EndBuffer = Path + PathLength; // Path + PathBufferSize / sizeof(WCHAR);
314     PWSTR EndPath;
315 
316     /* Convert slashes into backslashes */
317     for (p = Path; *p; p++)
318     {
319         if (*p == L'/') *p = L'\\';
320     }
321 
322     /* Collapse duplicate backslashes */
323     next = Path + max( 1, mark );
324     for (p = next; *p; p++)
325     {
326         if (*p != L'\\' || next[-1] != L'\\') *next++ = *p;
327     }
328     *next = UNICODE_NULL;
329     EndPath = next;
330 
331     p = Path + mark;
332     while (*p)
333     {
334         if (*p == L'.')
335         {
336             switch (p[1])
337             {
338                 case UNICODE_NULL:  /* final . */
339                     if (p > Path + mark) p--;
340                     *p = UNICODE_NULL;
341                     EndPath = p;
342                     continue;
343 
344                 case L'\\': /* .\ component */
345                     next = p + 2;
346                     // ASSERT(EndPath - next == wcslen(next));
347                     RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
348                     EndPath -= (next - p);
349                     continue;
350 
351                 case L'.':
352                     if (p[2] == L'\\')  /* ..\ component */
353                     {
354                         next = p + 3;
355                         if (p > Path + mark)
356                         {
357                             p--;
358                             while (p > Path + mark && p[-1] != L'\\') p--;
359                         }
360                         // ASSERT(EndPath - next == wcslen(next));
361                         RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR));
362                         EndPath -= (next - p);
363                         continue;
364                     }
365                     else if (p[2] == UNICODE_NULL)  /* final .. */
366                     {
367                         if (p > Path + mark)
368                         {
369                             p--;
370                             while (p > Path + mark && p[-1] != L'\\') p--;
371                             if (p > Path + mark) p--;
372                         }
373                         *p = UNICODE_NULL;
374                         EndPath = p;
375                         continue;
376                     }
377                     break;
378             }
379         }
380 
381         /* Skip to the next component */
382         while (*p && *p != L'\\') p++;
383         if (*p == L'\\')
384         {
385             /* Remove last dot in previous dir name */
386             if (p > Path + mark && p[-1] == L'.')
387             {
388                 // ASSERT(EndPath - p == wcslen(p));
389                 RtlMoveMemory(p - 1, p, (EndPath - p + 1) * sizeof(WCHAR));
390                 EndPath--;
391             }
392             else
393             {
394                 p++;
395             }
396         }
397     }
398 
399     /* Remove trailing backslashes if needed (after the UNC part if it exists) */
400     if (SkipTrailingPathSeparators)
401     {
402         while (p > Path + mark && IS_PATH_SEPARATOR(p[-1])) p--;
403     }
404 
405     /* Remove trailing spaces and dots (for all the path) */
406     while (p > Path && (p[-1] == L' ' || p[-1] == L'.')) p--;
407 
408     /*
409      * Zero-out the discarded buffer zone, starting just after
410      * the path string and going up to the end of the buffer.
411      * It also NULL-terminate the path string.
412      */
413     ASSERT(EndBuffer >= p);
414     RtlZeroMemory(p, (EndBuffer - p + 1) * sizeof(WCHAR));
415 
416     /* Return the real path length */
417     PathLength = (p - Path);
418     // ASSERT(PathLength == wcslen(Path));
419     return (PathLength * sizeof(WCHAR));
420 }
421 
422 /******************************************************************
423  *    RtlpSkipUNCPrefix (from WINE)
424  *
425  * Helper for RtlGetFullPathName_U
426  *
427  * Skips the \\share\dir part of a file name and returns the new position
428  * (which can point on the last backslash of "dir\").
429  */
430 static SIZE_T
431 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer)
432 {
433     PCWSTR UncPath = FileNameBuffer + 2;
434     DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
435 
436     while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++;  /* share name */
437     while (IS_PATH_SEPARATOR(*UncPath)) UncPath++;
438     while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++;  /* dir name */
439     /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
440 
441     return (UncPath - FileNameBuffer);
442 }
443 
444 NTSTATUS
445 NTAPI
446 RtlpApplyLengthFunction(IN ULONG Flags,
447                         IN ULONG Type,
448                         IN PVOID UnicodeStringOrUnicodeStringBuffer,
449                         IN PVOID LengthFunction)
450 {
451     UNIMPLEMENTED;
452     return STATUS_NOT_IMPLEMENTED;
453 }
454 
455 NTSTATUS
456 NTAPI
457 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags,
458                                               IN PWCHAR Path,
459                                               OUT PULONG LengthOut)
460 {
461     UNIMPLEMENTED;
462     return STATUS_NOT_IMPLEMENTED;
463 }
464 
465 NTSTATUS
466 NTAPI
467 RtlComputePrivatizedDllName_U(
468     _In_ PUNICODE_STRING DllName,
469     _Inout_ PUNICODE_STRING RealName,
470     _Inout_ PUNICODE_STRING LocalName)
471 {
472     static const UNICODE_STRING ExtensionChar = RTL_CONSTANT_STRING(L".");
473 
474     USHORT Position;
475     UNICODE_STRING ImagePathName, DllNameOnly, CopyRealName, CopyLocalName;
476     BOOLEAN HasExtension;
477     ULONG RequiredSize;
478     NTSTATUS Status;
479     C_ASSERT(sizeof(UNICODE_NULL) == sizeof(WCHAR));
480 
481     CopyRealName = *RealName;
482     CopyLocalName = *LocalName;
483 
484 
485     /* Get the image path */
486     ImagePathName = RtlGetCurrentPeb()->ProcessParameters->ImagePathName;
487 
488     /* Check if it's not normalized */
489     if (!(RtlGetCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_NORMALIZED))
490     {
491         /* Normalize it */
492         ImagePathName.Buffer = (PWSTR)((ULONG_PTR)ImagePathName.Buffer + (ULONG_PTR)RtlGetCurrentPeb()->ProcessParameters);
493     }
494 
495 
496     if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
497                                                DllName, &RtlpPathDividers, &Position)))
498     {
499         DllNameOnly = *DllName;
500     }
501     else
502     {
503         /* Just keep the dll name, ignore path components */
504         Position += sizeof(WCHAR);
505         DllNameOnly.Buffer = DllName->Buffer + Position / sizeof(WCHAR);
506         DllNameOnly.Length = DllName->Length - Position;
507         DllNameOnly.MaximumLength = DllName->MaximumLength - Position;
508     }
509 
510     if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
511                                                &DllNameOnly, &ExtensionChar, &Position)))
512     {
513         Position = 0;
514     }
515 
516     HasExtension = Position > 1;
517 
518     /* First we create the c:\path\processname.exe.Local\something.dll path */
519     RequiredSize = ImagePathName.Length + RtlpDotLocal.Length + DllNameOnly.Length +
520         (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
521 
522     /* This is not going to work out */
523     if (RequiredSize > UNICODE_STRING_MAX_BYTES)
524         return STATUS_NAME_TOO_LONG;
525 
526     /* We need something extra */
527     if (RequiredSize > CopyLocalName.MaximumLength)
528     {
529         CopyLocalName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR);
530         if (CopyLocalName.Buffer == NULL)
531             return STATUS_NO_MEMORY;
532         CopyLocalName.MaximumLength = RequiredSize;
533     }
534     /* Now build the entire path */
535     CopyLocalName.Length = 0;
536     Status = RtlAppendUnicodeStringToString(&CopyLocalName, &ImagePathName);
537     ASSERT(NT_SUCCESS(Status));
538     if (NT_SUCCESS(Status))
539     {
540         Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDotLocal);
541         ASSERT(NT_SUCCESS(Status));
542     }
543     if (NT_SUCCESS(Status))
544     {
545         Status = RtlAppendUnicodeStringToString(&CopyLocalName, &DllNameOnly);
546         ASSERT(NT_SUCCESS(Status));
547     }
548     /* Do we need to append an extension? */
549     if (NT_SUCCESS(Status) && !HasExtension)
550     {
551         Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDefaultExtension);
552         ASSERT(NT_SUCCESS(Status));
553     }
554 
555     if (NT_SUCCESS(Status))
556     {
557         /* then we create the c:\path\something.dll path */
558         if (NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
559                                                   &ImagePathName, &RtlpPathDividers, &Position)))
560         {
561             ImagePathName.Length = Position + sizeof(WCHAR);
562         }
563 
564         RequiredSize = ImagePathName.Length + DllNameOnly.Length +
565             (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
566 
567         if (RequiredSize >= UNICODE_STRING_MAX_BYTES)
568         {
569             Status = STATUS_NAME_TOO_LONG;
570         }
571         else
572         {
573             if (RequiredSize > CopyRealName.MaximumLength)
574             {
575                 CopyRealName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR);
576                 if (CopyRealName.Buffer == NULL)
577                     Status = STATUS_NO_MEMORY;
578                 CopyRealName.MaximumLength = RequiredSize;
579             }
580             CopyRealName.Length = 0;
581             if (NT_SUCCESS(Status))
582             {
583                 Status = RtlAppendUnicodeStringToString(&CopyRealName, &ImagePathName);
584                 ASSERT(NT_SUCCESS(Status));
585             }
586             if (NT_SUCCESS(Status))
587             {
588                 Status = RtlAppendUnicodeStringToString(&CopyRealName, &DllNameOnly);
589                 ASSERT(NT_SUCCESS(Status));
590             }
591             if (NT_SUCCESS(Status) && !HasExtension)
592             {
593                 Status = RtlAppendUnicodeStringToString(&CopyRealName, &RtlpDefaultExtension);
594                 ASSERT(NT_SUCCESS(Status));
595             }
596         }
597     }
598 
599     if (!NT_SUCCESS(Status))
600     {
601         if (CopyRealName.Buffer && CopyRealName.Buffer != RealName->Buffer)
602             RtlpFreeStringMemory(CopyRealName.Buffer, TAG_USTR);
603         if (CopyLocalName.Buffer && CopyLocalName.Buffer != LocalName->Buffer)
604             RtlpFreeStringMemory(CopyLocalName.Buffer, TAG_USTR);
605         return Status;
606     }
607 
608     *RealName = CopyRealName;
609     *LocalName = CopyLocalName;
610     return STATUS_SUCCESS;
611 }
612 
613 ULONG
614 NTAPI
615 RtlGetFullPathName_Ustr(
616     _In_ PUNICODE_STRING FileName,
617     _In_ ULONG Size,
618     _Out_z_bytecap_(Size) PWSTR Buffer,
619     _Out_opt_ PCWSTR *ShortName,
620     _Out_opt_ PBOOLEAN InvalidName,
621     _Out_ RTL_PATH_TYPE *PathType)
622 {
623     NTSTATUS Status;
624     PWCHAR FileNameBuffer;
625     ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength;
626     BOOLEAN SkipTrailingPathSeparators;
627     WCHAR c;
628 
629 
630     ULONG               reqsize = 0;
631     PCWSTR              ptr;
632 
633     PCUNICODE_STRING    CurDirName;
634     UNICODE_STRING      EnvVarName, EnvVarValue;
635     WCHAR EnvVarNameBuffer[4];
636 
637     ULONG  PrefixCut    = 0;    // Where the path really starts (after the skipped prefix)
638     PWCHAR Prefix       = NULL; // pointer to the string to be inserted as the new path prefix
639     ULONG  PrefixLength = 0;
640     PWCHAR Source;
641     ULONG  SourceLength;
642 
643 
644     /* For now, assume the name is valid */
645     DPRINT("Filename: %wZ\n", FileName);
646     DPRINT("Size and buffer: %lx %p\n", Size, Buffer);
647     if (InvalidName) *InvalidName = FALSE;
648 
649     /* Handle initial path type and failure case */
650     *PathType = RtlPathTypeUnknown;
651     if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
652 
653     /* Break filename into component parts */
654     FileNameBuffer = FileName->Buffer;
655     FileNameLength = FileName->Length;
656     FileNameChars  = FileNameLength / sizeof(WCHAR);
657 
658     /* Kill trailing spaces */
659     c = FileNameBuffer[FileNameChars - 1];
660     while ((FileNameLength != 0) && (c == L' '))
661     {
662         /* Keep going, ignoring the spaces */
663         FileNameLength -= sizeof(WCHAR);
664         if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1];
665     }
666 
667     /* Check if anything is left */
668     if (FileNameLength == 0) return 0;
669 
670     /*
671      * Check whether we'll need to skip trailing path separators in the
672      * computed full path name. If the original file name already contained
673      * trailing separators, then we keep them in the full path name. On the
674      * other hand, if the original name didn't contain any trailing separators
675      * then we'll skip it in the full path name.
676      */
677     SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]);
678 
679     /* Check if this is a DOS name */
680     DosLength = RtlIsDosDeviceName_Ustr(FileName);
681     DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName);
682     if (DosLength != 0)
683     {
684         /* Zero out the short name */
685         if (ShortName) *ShortName = NULL;
686 
687         /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
688         DosLengthOffset = HIWORD(DosLength);
689         DosLength       = LOWORD(DosLength);
690 
691         /* Do we have a DOS length, and does the caller want validity? */
692         if (InvalidName && (DosLengthOffset != 0))
693         {
694             /* Do the check */
695             Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
696 
697             /* If the check failed, or the name is invalid, fail here */
698             if (!NT_SUCCESS(Status)) return 0;
699             if (*InvalidName) return 0;
700         }
701 
702         /* Add the size of the device root and check if it fits in the size */
703         FullLength = DosLength + DeviceRootString.Length;
704         if (FullLength < Size)
705         {
706             /* Add the device string */
707             RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length);
708 
709             /* Now add the DOS device name */
710             RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length,
711                           (PCHAR)FileNameBuffer + DosLengthOffset,
712                           DosLength);
713 
714             /* Null terminate */
715             *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL;
716             return FullLength;
717         }
718 
719         /* Otherwise, there's no space, so return the buffer size needed */
720         if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0;
721         return FullLength + sizeof(UNICODE_NULL);
722     }
723 
724     /* Zero-out the destination buffer. FileName must be different from Buffer */
725     RtlZeroMemory(Buffer, Size);
726 
727     /* Get the path type */
728     *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
729 
730 
731 
732     /**********************************************
733      **    CODE REWRITING IS HAPPENING THERE     **
734      **********************************************/
735     Source       = FileNameBuffer;
736     SourceLength = FileNameLength;
737     EnvVarValue.Buffer = NULL;
738 
739     /* Lock the PEB to get the current directory */
740     RtlAcquirePebLock();
741     CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
742 
743     switch (*PathType)
744     {
745         case RtlPathTypeUncAbsolute:        /* \\foo   */
746         {
747             PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer);
748             break;
749         }
750 
751         case RtlPathTypeLocalDevice:        /* \\.\foo */
752         {
753             PrefixCut = 4;
754             break;
755         }
756 
757         case RtlPathTypeDriveAbsolute:      /* c:\foo  */
758         {
759             ASSERT(FileNameBuffer[1] == L':');
760             ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
761 
762             // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
763             Prefix = FileNameBuffer;
764             PrefixLength = 3 * sizeof(WCHAR);
765             Source += 3;
766             SourceLength -= 3 * sizeof(WCHAR);
767 
768             PrefixCut = 3;
769             break;
770         }
771 
772         case RtlPathTypeDriveRelative:      /* c:foo   */
773         {
774             WCHAR CurDrive, NewDrive;
775 
776             Source += 2;
777             SourceLength -= 2 * sizeof(WCHAR);
778 
779             CurDrive = RtlpUpcaseUnicodeChar(CurDirName->Buffer[0]);
780             NewDrive = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
781 
782             if ((NewDrive != CurDrive) || CurDirName->Buffer[1] != L':')
783             {
784                 EnvVarNameBuffer[0] = L'=';
785                 EnvVarNameBuffer[1] = NewDrive;
786                 EnvVarNameBuffer[2] = L':';
787                 EnvVarNameBuffer[3] = UNICODE_NULL;
788 
789                 EnvVarName.Length = 3 * sizeof(WCHAR);
790                 EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR);
791                 EnvVarName.Buffer = EnvVarNameBuffer;
792 
793                 // FIXME: Is it possible to use the user-given buffer ?
794                 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
795                 EnvVarValue.Length = 0;
796                 EnvVarValue.MaximumLength = (USHORT)Size;
797                 EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
798                 if (EnvVarValue.Buffer == NULL)
799                 {
800                     Prefix       = NULL;
801                     PrefixLength = 0;
802                     goto Quit;
803                 }
804 
805                 Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue);
806                 switch (Status)
807                 {
808                     case STATUS_SUCCESS:
809                         /*
810                          * (From Wine)
811                          * FIXME: Win2k seems to check that the environment
812                          * variable actually points to an existing directory.
813                          * If not, root of the drive is used (this seems also
814                          * to be the only place in RtlGetFullPathName that the
815                          * existence of a part of a path is checked).
816                          */
817                         EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\';
818                         Prefix       = EnvVarValue.Buffer;
819                         PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */
820                         break;
821 
822                     case STATUS_BUFFER_TOO_SMALL:
823                         reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL);
824                         goto Quit;
825 
826                     default:
827                         DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName, Status);
828 
829                         EnvVarNameBuffer[0] = NewDrive;
830                         EnvVarNameBuffer[1] = L':';
831                         EnvVarNameBuffer[2] = L'\\';
832                         EnvVarNameBuffer[3] = UNICODE_NULL;
833                         Prefix       = EnvVarNameBuffer;
834                         PrefixLength = 3 * sizeof(WCHAR);
835 
836                         RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
837                         EnvVarValue.Buffer = NULL;
838                         break;
839                 }
840                 PrefixCut = 3;
841                 break;
842             }
843             /* Fall through */
844             DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
845         }
846 
847         case RtlPathTypeRelative:           /* foo     */
848         {
849             Prefix       = CurDirName->Buffer;
850             PrefixLength = CurDirName->Length;
851             if (CurDirName->Buffer[1] != L':')
852             {
853                 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
854             }
855             else
856             {
857                 PrefixCut = 3;
858             }
859             break;
860         }
861 
862         case RtlPathTypeRooted:             /* \xxx    */
863         {
864             if (CurDirName->Buffer[1] == L':')
865             {
866                 // The path starts with "C:\"
867                 ASSERT(CurDirName->Buffer[1] == L':');
868                 ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2]));
869 
870                 Prefix = CurDirName->Buffer;
871                 PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\"
872 
873                 PrefixCut = 3;      // Source index location incremented of + 3
874             }
875             else
876             {
877                 PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer);
878                 PrefixLength = PrefixCut * sizeof(WCHAR);
879                 Prefix = CurDirName->Buffer;
880             }
881             break;
882         }
883 
884         case RtlPathTypeRootLocalDevice:    /* \\.     */
885         {
886             Prefix       = DeviceRootString.Buffer;
887             PrefixLength = DeviceRootString.Length;
888             Source += 3;
889             SourceLength -= 3 * sizeof(WCHAR);
890 
891             PrefixCut = 4;
892             break;
893         }
894 
895         case RtlPathTypeUnknown:
896             goto Quit;
897     }
898 
899     /* Do we have enough space for storing the full path? */
900     reqsize = PrefixLength;
901     if (reqsize + SourceLength + sizeof(WCHAR) > Size)
902     {
903         /* Not enough space, return needed size (including terminating '\0') */
904         reqsize += SourceLength + sizeof(WCHAR);
905         goto Quit;
906     }
907 
908     /*
909      * Build the full path
910      */
911     /* Copy the path's prefix */
912     if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength);
913     /* Copy the remaining part of the path */
914     RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
915 
916     /* Some cleanup */
917     Prefix = NULL;
918     if (EnvVarValue.Buffer)
919     {
920         RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer);
921         EnvVarValue.Buffer = NULL;
922     }
923 
924     /*
925      * Finally, put the path in canonical form (remove redundant . and ..,
926      * (back)slashes...) and retrieve the length of the full path name
927      * (without its terminating null character) (in chars).
928      */
929     reqsize = RtlpCollapsePath(Buffer, /* Size, reqsize, */ PrefixCut, SkipTrailingPathSeparators);
930 
931     /* Find the file part, which is present after the last path separator */
932     if (ShortName)
933     {
934         ptr = wcsrchr(Buffer, L'\\');
935         if (ptr) ++ptr; // Skip it
936 
937         /*
938          * For UNC paths, the file part is after the \\share\dir part of the path.
939          */
940         PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3);
941 
942         if (ptr && *ptr && (ptr >= Buffer + PrefixCut))
943         {
944             *ShortName = ptr;
945         }
946         else
947         {
948             /* Zero-out the short name */
949             *ShortName = NULL;
950         }
951     }
952 
953 Quit:
954     /* Release PEB lock */
955     RtlReleasePebLock();
956 
957     return reqsize;
958 }
959 
960 NTSTATUS
961 NTAPI
962 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath,
963                               OUT PUNICODE_STRING NtPath,
964                               OUT PCWSTR *PartName,
965                               OUT PRTL_RELATIVE_NAME_U RelativeName)
966 {
967     ULONG DosLength;
968     PWSTR NewBuffer, p;
969 
970     /* Validate the input */
971     if (!DosPath) return STATUS_OBJECT_NAME_INVALID;
972 
973     /* Validate the DOS length */
974     DosLength = DosPath->Length;
975     if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
976 
977     /* Make space for the new path */
978     NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
979                                 0,
980                                 DosLength + sizeof(UNICODE_NULL));
981     if (!NewBuffer) return STATUS_NO_MEMORY;
982 
983     /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
984     RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length);
985     RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length,
986                   DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR),
987                   DosPath->Length - RtlpDosDevicesPrefix.Length);
988     NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL;
989 
990     /* Did the caller send a relative name? */
991     if (RelativeName)
992     {
993         /* Zero initialize it */
994         RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
995         RelativeName->ContainingDirectory = NULL;
996         RelativeName->CurDirRef = 0;
997     }
998 
999     /* Did the caller request a partial name? */
1000     if (PartName)
1001     {
1002         /* Loop from the back until we find a path separator */
1003         p = &NewBuffer[DosLength / sizeof(WCHAR)];
1004         while (--p > NewBuffer)
1005         {
1006             /* We found a path separator, move past it */
1007             if (*p == OBJ_NAME_PATH_SEPARATOR)
1008             {
1009                 ++p;
1010                 break;
1011             }
1012         }
1013 
1014         /* Check whether a separator was found and if something remains */
1015         if ((p > NewBuffer) && *p)
1016         {
1017             /* What follows the path separator is the partial name */
1018             *PartName = p;
1019         }
1020         else
1021         {
1022             /* The path ends with a path separator, no partial name */
1023             *PartName = NULL;
1024         }
1025     }
1026 
1027     /* Build the final NT path string */
1028     NtPath->Buffer = NewBuffer;
1029     NtPath->Length = (USHORT)DosLength;
1030     NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL);
1031     return STATUS_SUCCESS;
1032 }
1033 
1034 NTSTATUS
1035 NTAPI
1036 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative,
1037                                          IN PCUNICODE_STRING DosName,
1038                                          OUT PUNICODE_STRING NtName,
1039                                          OUT PCWSTR *PartName,
1040                                          OUT PRTL_RELATIVE_NAME_U RelativeName)
1041 {
1042     WCHAR BigBuffer[MAX_PATH + 1];
1043     PWCHAR PrefixBuffer, NewBuffer, Buffer;
1044     ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length;
1045     UNICODE_STRING CapturedDosName, PartNameString, FullPath;
1046     BOOLEAN QuickPath;
1047     RTL_PATH_TYPE InputPathType, BufferPathType;
1048     NTSTATUS Status;
1049     BOOLEAN NameInvalid;
1050     PCURDIR CurrentDirectory;
1051 
1052     /* Assume MAX_PATH for now */
1053     DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
1054             HaveRelative, DosName, NtName, PartName, RelativeName);
1055     MaxLength = sizeof(BigBuffer);
1056 
1057     /* Validate the input */
1058     if (!DosName) return STATUS_OBJECT_NAME_INVALID;
1059 
1060     /* Capture input string */
1061     CapturedDosName = *DosName;
1062 
1063     /* Check for the presence or absence of the NT prefix "\\?\" form */
1064     // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
1065     if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) ||
1066         (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) ||
1067         (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) ||
1068         (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) ||
1069         (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3]))
1070     {
1071         /* NT prefix not present */
1072 
1073         /* Quick path won't be used */
1074         QuickPath = FALSE;
1075 
1076         /* Use the static buffer */
1077         Buffer = BigBuffer;
1078         MaxLength += RtlpDosDevicesUncPrefix.Length;
1079 
1080         /* Allocate a buffer to hold the path */
1081         NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength);
1082         DPRINT("MaxLength: %lx\n", MaxLength);
1083         if (!NewBuffer) return STATUS_NO_MEMORY;
1084     }
1085     else
1086     {
1087         /* NT prefix present */
1088 
1089         /* Use the optimized path after acquiring the lock */
1090         QuickPath = TRUE;
1091         NewBuffer = NULL;
1092     }
1093 
1094     /* Lock the PEB and check if the quick path can be used */
1095     RtlAcquirePebLock();
1096     if (QuickPath)
1097     {
1098         /* Some simple fixups will get us the correct path */
1099         DPRINT("Quick path\n");
1100         Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName,
1101                                                NtName,
1102                                                PartName,
1103                                                RelativeName);
1104 
1105         /* Release the lock, we're done here */
1106         RtlReleasePebLock();
1107         return Status;
1108     }
1109 
1110     /* Call the main function to get the full path name and length */
1111     PathLength = RtlGetFullPathName_Ustr(&CapturedDosName,
1112                                          MAX_PATH * sizeof(WCHAR),
1113                                          Buffer,
1114                                          PartName,
1115                                          &NameInvalid,
1116                                          &InputPathType);
1117     if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR))))
1118     {
1119         /* Invalid name, fail */
1120         DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength);
1121         RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1122         RtlReleasePebLock();
1123         return STATUS_OBJECT_NAME_INVALID;
1124     }
1125 
1126     /* Start by assuming the path starts with \??\ (DOS Devices Path) */
1127     PrefixLength = RtlpDosDevicesPrefix.Length;
1128     PrefixBuffer = RtlpDosDevicesPrefix.Buffer;
1129     PrefixCut = 0;
1130 
1131     /* Check where it really is */
1132     BufferPathType = RtlDetermineDosPathNameType_U(Buffer);
1133     DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType);
1134     switch (BufferPathType)
1135     {
1136         /* It's actually a UNC path in \??\UNC\ */
1137         case RtlPathTypeUncAbsolute:
1138             PrefixLength = RtlpDosDevicesUncPrefix.Length;
1139             PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer;
1140             PrefixCut = 2;
1141             break;
1142 
1143         case RtlPathTypeLocalDevice:
1144             /* We made a good guess, go with it but skip the \??\ */
1145             PrefixCut = 4;
1146             break;
1147 
1148         case RtlPathTypeDriveAbsolute:
1149         case RtlPathTypeDriveRelative:
1150         case RtlPathTypeRooted:
1151         case RtlPathTypeRelative:
1152             /* Our guess was good, roll with it */
1153             break;
1154 
1155         /* Nothing else is expected */
1156         default:
1157             ASSERT(FALSE);
1158     }
1159 
1160     /* Now copy the prefix and the buffer */
1161     RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength);
1162     RtlCopyMemory((PCHAR)NewBuffer + PrefixLength,
1163                   Buffer + PrefixCut,
1164                   PathLength - (PrefixCut * sizeof(WCHAR)));
1165 
1166     /* Compute the length */
1167     Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR);
1168     LengthChars = Length / sizeof(WCHAR);
1169 
1170     /* Setup the actual NT path string and terminate it */
1171     NtName->Buffer = NewBuffer;
1172     NtName->Length = (USHORT)Length;
1173     NtName->MaximumLength = (USHORT)MaxLength;
1174     NewBuffer[LengthChars] = UNICODE_NULL;
1175     DPRINT("New buffer: %S\n", NewBuffer);
1176     DPRINT("NT Name: %wZ\n", NtName);
1177 
1178     /* Check if a partial name was requested */
1179     if ((PartName) && (*PartName))
1180     {
1181         /* Convert to Unicode */
1182         Status = RtlInitUnicodeStringEx(&PartNameString, *PartName);
1183         if (NT_SUCCESS(Status))
1184         {
1185             /* Set the partial name */
1186             *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))];
1187         }
1188         else
1189         {
1190             /* Fail */
1191             RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1192             RtlReleasePebLock();
1193             return Status;
1194         }
1195     }
1196 
1197     /* Check if a relative name was asked for */
1198     if (RelativeName)
1199     {
1200         /* Setup the structure */
1201         RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0);
1202         RelativeName->ContainingDirectory = NULL;
1203         RelativeName->CurDirRef = NULL;
1204 
1205         /* Check if the input path itself was relative */
1206         if (InputPathType == RtlPathTypeRelative)
1207         {
1208             /* Get current directory */
1209             CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory);
1210             if (CurrentDirectory->Handle)
1211             {
1212                 Status = RtlInitUnicodeStringEx(&FullPath, Buffer);
1213                 if (!NT_SUCCESS(Status))
1214                 {
1215                     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
1216                     RtlReleasePebLock();
1217                     return Status;
1218                 }
1219 
1220                 /* If current directory is bigger than full path, there's no way */
1221                 if (CurrentDirectory->DosPath.Length > FullPath.Length)
1222                 {
1223                     RtlReleasePebLock();
1224                     return Status;
1225                 }
1226 
1227                 /* File is in current directory */
1228                 if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE))
1229                 {
1230                     /* Make relative name string */
1231                     RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR));
1232                     RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length);
1233                     /* If relative name starts with \, skip it */
1234                     if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1235                     {
1236                         RelativeName->RelativeName.Buffer++;
1237                         RelativeName->RelativeName.Length -= sizeof(WCHAR);
1238                     }
1239                     RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length;
1240                     DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName));
1241 
1242                     if (!HaveRelative)
1243                     {
1244                         RelativeName->ContainingDirectory = CurrentDirectory->Handle;
1245                         return Status;
1246                     }
1247 
1248                     /* Give back current directory data & reference counter */
1249                     RelativeName->CurDirRef = RtlpCurDirRef;
1250                     if (RelativeName->CurDirRef)
1251                     {
1252                         InterlockedIncrement(&RtlpCurDirRef->RefCount);
1253                     }
1254 
1255                     RelativeName->ContainingDirectory = CurrentDirectory->Handle;
1256                 }
1257             }
1258         }
1259     }
1260 
1261     /* Done */
1262     RtlReleasePebLock();
1263     return STATUS_SUCCESS;
1264 }
1265 
1266 NTSTATUS
1267 NTAPI
1268 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative,
1269                                       IN PCWSTR DosName,
1270                                       OUT PUNICODE_STRING NtName,
1271                                       OUT PCWSTR *PartName,
1272                                       OUT PRTL_RELATIVE_NAME_U RelativeName)
1273 {
1274     NTSTATUS Status;
1275     UNICODE_STRING NameString;
1276 
1277     /* Create the unicode name */
1278     Status = RtlInitUnicodeStringEx(&NameString, DosName);
1279     if (NT_SUCCESS(Status))
1280     {
1281         /* Call the unicode function */
1282         Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative,
1283                                                           &NameString,
1284                                                           NtName,
1285                                                           PartName,
1286                                                           RelativeName);
1287     }
1288 
1289     /* Return status */
1290     return Status;
1291 }
1292 
1293 BOOLEAN
1294 NTAPI
1295 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName,
1296                                         OUT PUNICODE_STRING NtName,
1297                                         OUT PCWSTR *PartName,
1298                                         OUT PRTL_RELATIVE_NAME_U RelativeName)
1299 {
1300     /* Call the internal function */
1301     ASSERT(RelativeName);
1302     return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE,
1303                                                                DosName,
1304                                                                NtName,
1305                                                                PartName,
1306                                                                RelativeName));
1307 }
1308 
1309 BOOLEAN
1310 NTAPI
1311 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName,
1312                          IN BOOLEAN SucceedIfBusy)
1313 {
1314     BOOLEAN Result;
1315     RTL_RELATIVE_NAME_U RelativeName;
1316     UNICODE_STRING NtPathName;
1317     PVOID Buffer;
1318     OBJECT_ATTRIBUTES ObjectAttributes;
1319     NTSTATUS Status;
1320     FILE_BASIC_INFORMATION BasicInformation;
1321 
1322     /* Get the NT Path */
1323     Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName,
1324                                                      &NtPathName,
1325                                                      NULL,
1326                                                      &RelativeName);
1327     if (!Result) return FALSE;
1328 
1329     /* Save the buffer */
1330     Buffer = NtPathName.Buffer;
1331 
1332     /* Check if we have a relative name */
1333     if (RelativeName.RelativeName.Length)
1334     {
1335         /* Use it */
1336         NtPathName = RelativeName.RelativeName;
1337     }
1338     else
1339     {
1340         /* Otherwise ignore it */
1341         RelativeName.ContainingDirectory = NULL;
1342     }
1343 
1344     /* Initialize the object attributes */
1345     InitializeObjectAttributes(&ObjectAttributes,
1346                                &NtPathName,
1347                                OBJ_CASE_INSENSITIVE,
1348                                RelativeName.ContainingDirectory,
1349                                NULL);
1350 
1351     /* Query the attributes and free the buffer now */
1352     Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation);
1353     RtlReleaseRelativeName(&RelativeName);
1354     RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1355 
1356     /* Check if we failed */
1357     if (!NT_SUCCESS(Status))
1358     {
1359         /* Check if we failed because the file is in use */
1360         if ((Status == STATUS_SHARING_VIOLATION) ||
1361             (Status == STATUS_ACCESS_DENIED))
1362         {
1363             /* Check if the caller wants this to be considered OK */
1364             Result = SucceedIfBusy ? TRUE : FALSE;
1365         }
1366         else
1367         {
1368             /* A failure because the file didn't exist */
1369             Result = FALSE;
1370         }
1371     }
1372     else
1373     {
1374         /* The file exists */
1375         Result = TRUE;
1376     }
1377 
1378     /* Return the result */
1379     return Result;
1380 }
1381 
1382 BOOLEAN
1383 NTAPI
1384 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName)
1385 {
1386     /* Call the updated API */
1387     return RtlDoesFileExists_UstrEx(FileName, TRUE);
1388 }
1389 
1390 BOOLEAN
1391 NTAPI
1392 RtlDoesFileExists_UEx(IN PCWSTR FileName,
1393                       IN BOOLEAN SucceedIfBusy)
1394 {
1395     UNICODE_STRING NameString;
1396 
1397     /* Create the unicode name*/
1398     if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName)))
1399     {
1400         /* Call the unicode function */
1401         return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy);
1402     }
1403 
1404     /* Fail */
1405     return FALSE;
1406 }
1407 
1408 /* PUBLIC FUNCTIONS ***********************************************************/
1409 
1410 /*
1411  * @implemented
1412  */
1413 VOID
1414 NTAPI
1415 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName)
1416 {
1417     /* Check if a directory reference was grabbed */
1418     if (RelativeName->CurDirRef)
1419     {
1420         /* Decrease reference count */
1421         if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount))
1422         {
1423             /* If no one uses it any longer, close handle & free */
1424             NtClose(RelativeName->CurDirRef->Handle);
1425             RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef);
1426         }
1427         RelativeName->CurDirRef = NULL;
1428     }
1429 }
1430 
1431 /*
1432  * @implemented
1433  */
1434 ULONG
1435 NTAPI
1436 RtlGetLongestNtPathLength(VOID)
1437 {
1438     /*
1439      * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1440      * a mapped network drive), which is accessed through the DOS Global?? path.
1441      * This is, and has always been equal to, 269 characters, except in Wine
1442      * which claims this is 277. Go figure.
1443      */
1444     return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL);
1445 }
1446 
1447 /*
1448  * @implemented
1449  * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1450  *        (with a 'e' instead of a 'a' in "Seperators").
1451  */
1452 NTSTATUS
1453 NTAPI
1454 RtlGetLengthWithoutTrailingPathSeparators(IN  ULONG Flags,
1455                                           IN  PCUNICODE_STRING PathString,
1456                                           OUT PULONG Length)
1457 {
1458     ULONG NumChars;
1459 
1460     /* Parameters validation */
1461     if (Length == NULL) return STATUS_INVALID_PARAMETER;
1462 
1463     *Length = 0;
1464 
1465     if (PathString == NULL) return STATUS_INVALID_PARAMETER;
1466 
1467     /* No flags are supported yet */
1468     if (Flags != 0) return STATUS_INVALID_PARAMETER;
1469 
1470     NumChars = PathString->Length / sizeof(WCHAR);
1471 
1472     /*
1473      * Notice that we skip the last character, therefore:
1474      * - if we have: "some/path/f" we test for: "some/path/"
1475      * - if we have: "some/path/"  we test for: "some/path"
1476      * - if we have: "s" we test for: ""
1477      * - if we have: "" then NumChars was already zero and we aren't there
1478      */
1479 
1480     while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1]))
1481     {
1482         --NumChars;
1483     }
1484 
1485     *Length = NumChars;
1486     return STATUS_SUCCESS;
1487 }
1488 
1489 /*
1490  * @implemented
1491  */
1492 RTL_PATH_TYPE
1493 NTAPI
1494 RtlDetermineDosPathNameType_U(IN PCWSTR Path)
1495 {
1496     DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
1497 
1498     /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1499     if (IS_PATH_SEPARATOR(Path[0]))
1500     {
1501         if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted;                /* \x             */
1502         if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x            */
1503         if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice;            /* \\.\x or \\?\x */
1504         if (Path[3]) return RtlPathTypeUncAbsolute;                               /* \\.x or \\?x   */
1505         return RtlPathTypeRootLocalDevice;                                        /* \\. or \\?     */
1506     }
1507     else
1508     {
1509         if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative;          /* x              */
1510         if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute;          /* x:\            */
1511         return RtlPathTypeDriveRelative;                                          /* x:             */
1512     }
1513 }
1514 
1515 /*
1516  * @implemented
1517  */
1518 ULONG
1519 NTAPI
1520 RtlIsDosDeviceName_U(IN PCWSTR Path)
1521 {
1522     UNICODE_STRING PathString;
1523     NTSTATUS Status;
1524 
1525     /* Build the string */
1526     Status = RtlInitUnicodeStringEx(&PathString, Path);
1527     if (!NT_SUCCESS(Status)) return 0;
1528 
1529     /*
1530      * Returns 0 if name is not valid DOS device name, or DWORD with
1531      * offset in bytes to DOS device name from beginning of buffer in high word
1532      * and size in bytes of DOS device name in low word
1533      */
1534      return RtlIsDosDeviceName_Ustr(&PathString);
1535 }
1536 
1537 /*
1538  * @implemented
1539  */
1540 ULONG
1541 NTAPI
1542 RtlGetCurrentDirectory_U(
1543     _In_ ULONG MaximumLength,
1544     _Out_bytecap_(MaximumLength) PWSTR Buffer)
1545 {
1546     ULONG Length, Bytes;
1547     PCURDIR CurDir;
1548     PWSTR CurDirName;
1549     DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
1550 
1551     /* Lock the PEB to get the current directory */
1552     RtlAcquirePebLock();
1553     CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
1554 
1555     /* Get the buffer and character length */
1556     CurDirName = CurDir->DosPath.Buffer;
1557     Length = CurDir->DosPath.Length / sizeof(WCHAR);
1558     ASSERT((CurDirName != NULL) && (Length > 0));
1559 
1560     /*
1561      * DosPath.Buffer should always have a trailing slash. There is an assert
1562      * below which checks for this.
1563      *
1564      * This function either returns x:\ for a root (keeping the original buffer)
1565      * or it returns x:\path\foo for a directory (replacing the trailing slash
1566      * with a NULL.
1567      */
1568     Bytes = Length * sizeof(WCHAR);
1569     if ((Length <= 1) || (CurDirName[Length - 2] == L':'))
1570     {
1571         /* Check if caller does not have enough space */
1572         if (MaximumLength <= Bytes)
1573         {
1574             /* Call has no space for it, fail, add the trailing slash */
1575             RtlReleasePebLock();
1576             return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR);
1577         }
1578     }
1579     else
1580     {
1581         /* Check if caller does not have enough space */
1582         if (MaximumLength < Bytes)
1583         {
1584             /* Call has no space for it, fail */
1585             RtlReleasePebLock();
1586             return Bytes;
1587         }
1588     }
1589 
1590     /* Copy the buffer since we seem to have space */
1591     RtlCopyMemory(Buffer, CurDirName, Bytes);
1592 
1593     /* The buffer should end with a path separator */
1594     ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR);
1595 
1596     /* Again check for our two cases (drive root vs path) */
1597     if ((Length <= 1) || (Buffer[Length - 2] != L':'))
1598     {
1599         /* Replace the trailing slash with a null */
1600         Buffer[Length - 1] = UNICODE_NULL;
1601         --Length;
1602     }
1603     else
1604     {
1605         /* Append the null char since there's no trailing slash */
1606         Buffer[Length] = UNICODE_NULL;
1607     }
1608 
1609     /* Release PEB lock */
1610     RtlReleasePebLock();
1611     DPRINT("CurrentDirectory %S\n", Buffer);
1612     return Length * sizeof(WCHAR);
1613 }
1614 
1615 /*
1616  * @implemented
1617  */
1618 NTSTATUS
1619 NTAPI
1620 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path)
1621 {
1622     PCURDIR CurDir;
1623     NTSTATUS Status;
1624     RTL_PATH_TYPE PathType;
1625     IO_STATUS_BLOCK IoStatusBlock;
1626     UNICODE_STRING FullPath, NtName;
1627     PRTLP_CURDIR_REF OldCurDir = NULL;
1628     OBJECT_ATTRIBUTES ObjectAttributes;
1629     FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo;
1630     ULONG SavedLength, CharLength, FullPathLength;
1631     HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL;
1632 
1633     DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
1634 
1635     /* Initialize for failure case */
1636     RtlInitEmptyUnicodeString(&NtName, NULL, 0);
1637 
1638     /* Can't set current directory on DOS device */
1639     if (RtlIsDosDeviceName_Ustr(Path))
1640     {
1641         return STATUS_NOT_A_DIRECTORY;
1642     }
1643 
1644     /* Get current directory */
1645     RtlAcquirePebLock();
1646     CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
1647 
1648     /* Check if we have to drop current handle */
1649     if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE)
1650     {
1651         OldHandle = CurDir->Handle;
1652         CurDir->Handle = NULL;
1653     }
1654 
1655     /* Allocate a buffer for full path (using max possible length */
1656     FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength);
1657     if (!FullPath.Buffer)
1658     {
1659         Status = STATUS_NO_MEMORY;
1660         goto Leave;
1661     }
1662 
1663     /* Init string */
1664     FullPath.Length = 0;
1665     FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
1666 
1667     /* Get new directory full path */
1668     FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType);
1669     if (!FullPathLength)
1670     {
1671         Status = STATUS_OBJECT_NAME_INVALID;
1672         goto Leave;
1673     }
1674 
1675     SavedLength = FullPath.MaximumLength;
1676     CharLength = FullPathLength / sizeof(WCHAR);
1677 
1678     if (FullPathLength > FullPath.MaximumLength)
1679     {
1680         Status = STATUS_NAME_TOO_LONG;
1681         goto Leave;
1682     }
1683 
1684     /* Translate it to NT name */
1685     if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL))
1686     {
1687         Status = STATUS_OBJECT_NAME_INVALID;
1688         goto Leave;
1689     }
1690 
1691    InitializeObjectAttributes(&ObjectAttributes, &NtName,
1692                               OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
1693                               NULL, NULL);
1694 
1695     /* If previous current directory was removable, then check it for dropping */
1696     if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS)
1697     {
1698         /* Get back normal handle */
1699         CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS);
1700         CurDir->Handle = NULL;
1701 
1702         /* Get device information */
1703         Status = NtQueryVolumeInformationFile(CurDirHandle,
1704                                               &IoStatusBlock,
1705                                               &FileFsDeviceInfo,
1706                                               sizeof(FileFsDeviceInfo),
1707                                               FileFsDeviceInformation);
1708         /* Retry without taking care of removable device */
1709         if (!NT_SUCCESS(Status))
1710         {
1711             Status = RtlSetCurrentDirectory_U(Path);
1712             goto Leave;
1713         }
1714     }
1715     else
1716     {
1717         /* Open directory */
1718         Status = NtOpenFile(&CurDirHandle,
1719                             SYNCHRONIZE | FILE_TRAVERSE,
1720                             &ObjectAttributes,
1721                             &IoStatusBlock,
1722                             FILE_SHARE_READ | FILE_SHARE_WRITE,
1723                             FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
1724         if (!NT_SUCCESS(Status)) goto Leave;
1725 
1726         /* Get device information */
1727         Status = NtQueryVolumeInformationFile(CurDirHandle,
1728                                               &IoStatusBlock,
1729                                               &FileFsDeviceInfo,
1730                                               sizeof(FileFsDeviceInfo),
1731                                               FileFsDeviceInformation);
1732         if (!NT_SUCCESS(Status)) goto Leave;
1733     }
1734 
1735     /* If device is removable, mark handle */
1736     if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA)
1737     {
1738         CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE);
1739     }
1740 
1741     FullPath.Length = (USHORT)FullPathLength;
1742 
1743     /* If full path isn't \ terminated, do it */
1744     if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR)
1745     {
1746         if ((CharLength + 1) * sizeof(WCHAR) > SavedLength)
1747         {
1748             Status = STATUS_NAME_TOO_LONG;
1749             goto Leave;
1750         }
1751 
1752         FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR;
1753         FullPath.Buffer[CharLength + 1] = UNICODE_NULL;
1754         FullPath.Length += sizeof(WCHAR);
1755     }
1756 
1757     /* If we have previous current directory with only us as reference, save it */
1758     if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1)
1759     {
1760         OldCurDirHandle = RtlpCurDirRef->Handle;
1761     }
1762     else
1763     {
1764         /* Allocate new current directory struct saving previous one */
1765         OldCurDir = RtlpCurDirRef;
1766         RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF));
1767         if (!RtlpCurDirRef)
1768         {
1769             RtlpCurDirRef = OldCurDir;
1770             OldCurDir = NULL;
1771             Status = STATUS_NO_MEMORY;
1772             goto Leave;
1773         }
1774 
1775         /* Set reference to 1 (us) */
1776         RtlpCurDirRef->RefCount = 1;
1777     }
1778 
1779     /* Save new data */
1780     CurDir->Handle = CurDirHandle;
1781     RtlpCurDirRef->Handle = CurDirHandle;
1782     CurDirHandle = NULL;
1783 
1784     /* Copy full path */
1785     RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR));
1786     CurDir->DosPath.Length = FullPath.Length;
1787 
1788     Status = STATUS_SUCCESS;
1789 
1790 Leave:
1791     RtlReleasePebLock();
1792 
1793     if (FullPath.Buffer)
1794     {
1795         RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer);
1796     }
1797 
1798     if (NtName.Buffer)
1799     {
1800         RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer);
1801     }
1802 
1803     if (CurDirHandle) NtClose(CurDirHandle);
1804 
1805     if (OldHandle) NtClose(OldHandle);
1806 
1807     if (OldCurDirHandle) NtClose(OldCurDirHandle);
1808 
1809     if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0)
1810     {
1811         NtClose(OldCurDir->Handle);
1812         RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir);
1813     }
1814 
1815     return Status;
1816 }
1817 
1818 /*
1819  * @implemented
1820  */
1821 ULONG
1822 NTAPI
1823 RtlGetFullPathName_UEx(
1824     _In_ PWSTR FileName,
1825     _In_ ULONG BufferLength,
1826     _Out_writes_bytes_(BufferLength) PWSTR Buffer,
1827     _Out_opt_ PWSTR *FilePart,
1828     _Out_opt_ RTL_PATH_TYPE *InputPathType)
1829 {
1830     UNICODE_STRING FileNameString;
1831     NTSTATUS status;
1832 
1833     if (InputPathType)
1834         *InputPathType = 0;
1835 
1836     /* Build the string */
1837     status = RtlInitUnicodeStringEx(&FileNameString, FileName);
1838     if (!NT_SUCCESS(status)) return 0;
1839 
1840     /* Call the extended function */
1841     return RtlGetFullPathName_Ustr(
1842         &FileNameString,
1843         BufferLength,
1844         Buffer,
1845         (PCWSTR*)FilePart,
1846         NULL,
1847         InputPathType);
1848 }
1849 
1850 /******************************************************************
1851  *    RtlGetFullPathName_U  (NTDLL.@)
1852  *
1853  * Returns the number of bytes written to buffer (not including the
1854  * terminating NULL) if the function succeeds, or the required number of bytes
1855  * (including the terminating NULL) if the buffer is too small.
1856  *
1857  * file_part will point to the filename part inside buffer (except if we use
1858  * DOS device name, in which case file_in_buf is NULL)
1859  *
1860  * @implemented
1861  */
1862 
1863 /*
1864  * @implemented
1865  */
1866 ULONG
1867 NTAPI
1868 RtlGetFullPathName_U(
1869     _In_ PCWSTR FileName,
1870     _In_ ULONG Size,
1871     _Out_z_bytecap_(Size) PWSTR Buffer,
1872     _Out_opt_ PWSTR *ShortName)
1873 {
1874     RTL_PATH_TYPE PathType;
1875 
1876     /* Call the extended function */
1877     return RtlGetFullPathName_UEx((PWSTR)FileName,
1878                                    Size,
1879                                    Buffer,
1880                                    ShortName,
1881                                    &PathType);
1882 }
1883 
1884 /*
1885  * @implemented
1886  */
1887 BOOLEAN
1888 NTAPI
1889 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,
1890                              OUT PUNICODE_STRING NtName,
1891                              OUT PCWSTR *PartName,
1892                              OUT PRTL_RELATIVE_NAME_U RelativeName)
1893 {
1894     /* Call the internal function */
1895     return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE,
1896                                                             DosName,
1897                                                             NtName,
1898                                                             PartName,
1899                                                             RelativeName));
1900 }
1901 
1902 /*
1903  * @implemented
1904  */
1905 NTSTATUS
1906 NTAPI
1907 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName,
1908                                         OUT PUNICODE_STRING NtName,
1909                                         OUT PCWSTR *PartName,
1910                                         OUT PRTL_RELATIVE_NAME_U RelativeName)
1911 {
1912     /* Call the internal function */
1913     return RtlpDosPathNameToRelativeNtPathName_U(FALSE,
1914                                                  DosName,
1915                                                  NtName,
1916                                                  PartName,
1917                                                  RelativeName);
1918 }
1919 
1920 /*
1921  * @implemented
1922  */
1923 BOOLEAN
1924 NTAPI
1925 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName,
1926                                      OUT PUNICODE_STRING NtName,
1927                                      OUT PCWSTR *PartName,
1928                                      OUT PRTL_RELATIVE_NAME_U RelativeName)
1929 {
1930     /* Call the internal function */
1931     ASSERT(RelativeName);
1932     return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE,
1933                                                             DosName,
1934                                                             NtName,
1935                                                             PartName,
1936                                                             RelativeName));
1937 }
1938 
1939 /*
1940  * @implemented
1941  */
1942 NTSTATUS
1943 NTAPI
1944 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName,
1945                                                 OUT PUNICODE_STRING NtName,
1946                                                 OUT PCWSTR *PartName,
1947                                                 OUT PRTL_RELATIVE_NAME_U RelativeName)
1948 {
1949     /* Call the internal function */
1950     ASSERT(RelativeName);
1951     return RtlpDosPathNameToRelativeNtPathName_U(TRUE,
1952                                                  DosName,
1953                                                  NtName,
1954                                                  PartName,
1955                                                  RelativeName);
1956 }
1957 
1958 /*
1959  * @implemented
1960  */
1961 NTSTATUS NTAPI RtlNtPathNameToDosPathName(IN ULONG Flags,
1962                                           IN OUT PRTL_UNICODE_STRING_BUFFER Path,
1963                                           OUT PULONG PathType,
1964                                           PULONG Unknown)
1965 {
1966     PCUNICODE_STRING UsePrefix = NULL, AlternatePrefix = NULL;
1967 
1968     if (PathType)
1969         *PathType = 0;
1970 
1971     if (!Path || Flags)
1972         return STATUS_INVALID_PARAMETER;
1973 
1974     /* The initial check is done on Path->String */
1975     if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix, &Path->String, TRUE))
1976     {
1977         UsePrefix = &RtlpDosDevicesUncPrefix;
1978         AlternatePrefix = &RtlpDoubleSlashPrefix;
1979         if (PathType)
1980             *PathType = RTL_CONVERTED_UNC_PATH;
1981     }
1982     else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix, &Path->String, FALSE))
1983     {
1984         UsePrefix = &RtlpDosDevicesPrefix;
1985         if (PathType)
1986             *PathType = RTL_CONVERTED_NT_PATH;
1987     }
1988 
1989     if (UsePrefix)
1990     {
1991         NTSTATUS Status;
1992 
1993         USHORT Len = Path->String.Length - UsePrefix->Length;
1994         if (AlternatePrefix)
1995             Len += AlternatePrefix->Length;
1996 
1997         Status = RtlEnsureBufferSize(0, &Path->ByteBuffer, Len);
1998         if (!NT_SUCCESS(Status))
1999             return Status;
2000 
2001         if (Len + sizeof(UNICODE_NULL) <= Path->ByteBuffer.Size)
2002         {
2003             /* Then, the contents of Path->ByteBuffer are always used... */
2004             if (AlternatePrefix)
2005             {
2006                 memcpy(Path->ByteBuffer.Buffer, AlternatePrefix->Buffer, AlternatePrefix->Length);
2007                 memmove(Path->ByteBuffer.Buffer + AlternatePrefix->Length, Path->ByteBuffer.Buffer + UsePrefix->Length,
2008                     Len - AlternatePrefix->Length);
2009             }
2010             else
2011             {
2012                 memmove(Path->ByteBuffer.Buffer, Path->ByteBuffer.Buffer + UsePrefix->Length, Len);
2013             }
2014             Path->String.Buffer = (PWSTR)Path->ByteBuffer.Buffer;
2015             Path->String.Length = Len;
2016             Path->String.MaximumLength = Path->ByteBuffer.Size;
2017             Path->String.Buffer[Len / sizeof(WCHAR)] = UNICODE_NULL;
2018         }
2019         return STATUS_SUCCESS;
2020     }
2021 
2022     if (PathType)
2023     {
2024         switch (RtlDetermineDosPathNameType_Ustr(&Path->String))
2025         {
2026         case RtlPathTypeUncAbsolute:
2027         case RtlPathTypeDriveAbsolute:
2028         case RtlPathTypeLocalDevice:
2029         case RtlPathTypeRootLocalDevice:
2030             *PathType = RTL_UNCHANGED_DOS_PATH;
2031             break;
2032         case RtlPathTypeUnknown:
2033         case RtlPathTypeDriveRelative:
2034         case RtlPathTypeRooted:
2035         case RtlPathTypeRelative:
2036             *PathType = RTL_UNCHANGED_UNK_PATH;
2037             break;
2038         }
2039     }
2040 
2041     return STATUS_SUCCESS;
2042 }
2043 
2044 /*
2045  * @implemented
2046  */
2047 ULONG
2048 NTAPI
2049 RtlDosSearchPath_U(IN PCWSTR Path,
2050                    IN PCWSTR FileName,
2051                    IN PCWSTR Extension,
2052                    IN ULONG Size,
2053                    IN PWSTR Buffer,
2054                    OUT PWSTR *PartName)
2055 {
2056     NTSTATUS Status;
2057     ULONG ExtensionLength, Length, FileNameLength, PathLength;
2058     UNICODE_STRING TempString;
2059     PWCHAR NewBuffer, BufferStart;
2060     PCWSTR p;
2061 
2062     /* Check if this is an absolute path */
2063     if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative)
2064     {
2065         /* Check if the file exists */
2066         if (RtlDoesFileExists_UEx(FileName, TRUE))
2067         {
2068             /* Get the full name, which does the DOS lookup */
2069             return RtlGetFullPathName_U(FileName, Size, Buffer, PartName);
2070         }
2071 
2072         /* Doesn't exist, so fail */
2073         return 0;
2074     }
2075 
2076     /* Scan the filename */
2077     p = FileName;
2078     while (*p)
2079     {
2080         /* Looking for an extension */
2081         if (*p == L'.')
2082         {
2083             /* No extension string needed -- it's part of the filename */
2084             Extension = NULL;
2085             break;
2086         }
2087 
2088         /* Next character */
2089         p++;
2090     }
2091 
2092     /* Do we have an extension? */
2093     if (!Extension)
2094     {
2095         /* Nope, don't worry about one */
2096         ExtensionLength = 0;
2097     }
2098     else
2099     {
2100         /* Build a temporary string to get the extension length */
2101         Status = RtlInitUnicodeStringEx(&TempString, Extension);
2102         if (!NT_SUCCESS(Status)) return 0;
2103         ExtensionLength = TempString.Length;
2104     }
2105 
2106     /* Build a temporary string to get the path length */
2107     Status = RtlInitUnicodeStringEx(&TempString, Path);
2108     if (!NT_SUCCESS(Status)) return 0;
2109     PathLength = TempString.Length;
2110 
2111     /* Build a temporary string to get the filename length */
2112     Status = RtlInitUnicodeStringEx(&TempString, FileName);
2113     if (!NT_SUCCESS(Status)) return 0;
2114     FileNameLength = TempString.Length;
2115 
2116     /* Allocate the buffer for the new string name */
2117     NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2118                                 0,
2119                                 FileNameLength +
2120                                 ExtensionLength +
2121                                 PathLength +
2122                                 3 * sizeof(WCHAR));
2123     if (!NewBuffer)
2124     {
2125         /* Fail the call */
2126         DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
2127                  __FUNCTION__);
2128         return 0;
2129     }
2130 
2131     /* Final loop to build the path */
2132     while (TRUE)
2133     {
2134         /* Check if we have a valid character */
2135         BufferStart = NewBuffer;
2136         if (*Path)
2137         {
2138             /* Loop as long as there's no semicolon */
2139             while (*Path != L';')
2140             {
2141                 /* Copy the next character */
2142                 *BufferStart++ = *Path++;
2143                 if (!*Path) break;
2144             }
2145 
2146             /* We found a semi-colon, to stop path processing on this loop */
2147             if (*Path == L';') ++Path;
2148         }
2149 
2150         /* Add a terminating slash if needed */
2151         if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR))
2152         {
2153             *BufferStart++ = OBJ_NAME_PATH_SEPARATOR;
2154         }
2155 
2156         /* Bail out if we reached the end */
2157         if (!*Path) Path = NULL;
2158 
2159         /* Copy the file name and check if an extension is needed */
2160         RtlCopyMemory(BufferStart, FileName, FileNameLength);
2161         if (ExtensionLength)
2162         {
2163             /* Copy the extension too */
2164             RtlCopyMemory((PCHAR)BufferStart + FileNameLength,
2165                           Extension,
2166                           ExtensionLength + sizeof(WCHAR));
2167         }
2168         else
2169         {
2170             /* Just NULL-terminate */
2171             *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL;
2172         }
2173 
2174         /* Now, does this file exist? */
2175         if (RtlDoesFileExists_UEx(NewBuffer, FALSE))
2176         {
2177             /* Call the full-path API to get the length */
2178             Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName);
2179             break;
2180         }
2181 
2182         /* If we got here, path doesn't exist, so fail the call */
2183         Length = 0;
2184         if (!Path) break;
2185     }
2186 
2187     /* Free the allocation and return the length */
2188     RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer);
2189     return Length;
2190 }
2191 
2192 /*
2193  * @implemented
2194  */
2195 NTSTATUS
2196 NTAPI
2197 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName,
2198                           IN PUNICODE_STRING StaticString,
2199                           IN PUNICODE_STRING DynamicString,
2200                           IN PUNICODE_STRING *StringUsed,
2201                           IN PSIZE_T FilePartSize,
2202                           OUT PBOOLEAN NameInvalid,
2203                           OUT RTL_PATH_TYPE* PathType,
2204                           OUT PSIZE_T LengthNeeded)
2205 {
2206     NTSTATUS Status;
2207     PWCHAR StaticBuffer;
2208     PCWCH ShortName;
2209     ULONG Length;
2210     USHORT StaticLength;
2211     UNICODE_STRING TempDynamicString;
2212 
2213     /* Initialize all our locals */
2214     ShortName = NULL;
2215     StaticBuffer = NULL;
2216     TempDynamicString.Buffer = NULL;
2217 
2218     /* Initialize the input parameters */
2219     if (StringUsed) *StringUsed = NULL;
2220     if (LengthNeeded) *LengthNeeded = 0;
2221     if (FilePartSize) *FilePartSize = 0;
2222 
2223     /* Check for invalid parameters */
2224     if ((DynamicString) && !(StringUsed) && (StaticString))
2225     {
2226         return STATUS_INVALID_PARAMETER;
2227     }
2228 
2229     /* Check if we did not get an input string */
2230     if (!StaticString)
2231     {
2232         /* Allocate one */
2233         StaticLength = MAX_PATH * sizeof(WCHAR);
2234         StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR);
2235         if (!StaticBuffer) return STATUS_NO_MEMORY;
2236     }
2237     else
2238     {
2239         /* Use the one we received */
2240         StaticBuffer = StaticString->Buffer;
2241         StaticLength = StaticString->MaximumLength;
2242     }
2243 
2244     /* Call the lower-level function */
2245     Length = RtlGetFullPathName_Ustr(FileName,
2246                                      StaticLength,
2247                                      StaticBuffer,
2248                                      &ShortName,
2249                                      NameInvalid,
2250                                      PathType);
2251     DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer);
2252     if (!Length)
2253     {
2254         /* Fail if it failed */
2255         DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
2256                  __FUNCTION__,
2257                  __LINE__);
2258         Status = STATUS_OBJECT_NAME_INVALID;
2259         goto Quickie;
2260     }
2261 
2262     /* Check if it fits inside our static string */
2263     if ((StaticString) && (Length < StaticLength))
2264     {
2265         /* Set the final length */
2266         StaticString->Length = (USHORT)Length;
2267 
2268         /* Set the file part size */
2269         if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
2270 
2271         /* Return the static string if requested */
2272         if (StringUsed) *StringUsed = StaticString;
2273 
2274         /* We are done with success */
2275         Status = STATUS_SUCCESS;
2276         goto Quickie;
2277     }
2278 
2279     /* Did we not have an input dynamic string ?*/
2280     if (!DynamicString)
2281     {
2282         /* Return the length we need */
2283         if (LengthNeeded) *LengthNeeded = Length;
2284 
2285         /* And fail such that the caller can try again */
2286         Status = STATUS_BUFFER_TOO_SMALL;
2287         goto Quickie;
2288     }
2289 
2290     /* Check if it fits in our static buffer */
2291     if ((StaticBuffer) && (Length < StaticLength))
2292     {
2293         /* NULL-terminate it */
2294         StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
2295 
2296         /* Set the settings for the dynamic string the caller sent */
2297         DynamicString->MaximumLength = StaticLength;
2298         DynamicString->Length = (USHORT)Length;
2299         DynamicString->Buffer = StaticBuffer;
2300 
2301         /* Set the part size */
2302         if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0;
2303 
2304         /* Return the dynamic string if requested */
2305         if (StringUsed) *StringUsed = DynamicString;
2306 
2307         /* Do not free the static buffer on exit, and return success */
2308         StaticBuffer = NULL;
2309         Status = STATUS_SUCCESS;
2310         goto Quickie;
2311     }
2312 
2313     /* Now try again under the PEB lock */
2314     RtlAcquirePebLock();
2315     Length = RtlGetFullPathName_Ustr(FileName,
2316                                      StaticLength,
2317                                      StaticBuffer,
2318                                      &ShortName,
2319                                      NameInvalid,
2320                                      PathType);
2321     if (!Length)
2322     {
2323         /* It failed */
2324         DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2325                  __FUNCTION__, __LINE__);
2326         Status = STATUS_OBJECT_NAME_INVALID;
2327         goto Release;
2328     }
2329 
2330     /* Check if it fits inside our static string now */
2331     if ((StaticString) && (Length < StaticLength))
2332     {
2333         /* Set the final length */
2334         StaticString->Length = (USHORT)Length;
2335 
2336         /* Set the file part size */
2337         if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
2338 
2339         /* Return the static string if requested */
2340         if (StringUsed) *StringUsed = StaticString;
2341 
2342         /* We are done with success */
2343         Status = STATUS_SUCCESS;
2344         goto Release;
2345     }
2346 
2347     /* Check if the path won't even fit in a real string */
2348     if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
2349     {
2350         /* Name is way too long, fail */
2351         Status = STATUS_NAME_TOO_LONG;
2352         goto Release;
2353     }
2354 
2355     /* Allocate the string to hold the path name now */
2356     TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR),
2357                                                         TAG_USTR);
2358     if (!TempDynamicString.Buffer)
2359     {
2360         /* Out of memory, fail */
2361         Status = STATUS_NO_MEMORY;
2362         goto Release;
2363     }
2364 
2365     /* Add space for a NULL terminator, and now check the full path */
2366     TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
2367     Length = RtlGetFullPathName_Ustr(FileName,
2368                                      Length,
2369                                      TempDynamicString.Buffer,
2370                                      &ShortName,
2371                                      NameInvalid,
2372                                      PathType);
2373     if (!Length)
2374     {
2375         /* Some path error, so fail out */
2376         DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2377                  __FUNCTION__, __LINE__);
2378         Status = STATUS_OBJECT_NAME_INVALID;
2379         goto Release;
2380     }
2381 
2382     /* It should fit in the string we just allocated */
2383     ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR)));
2384     if (Length > TempDynamicString.MaximumLength)
2385     {
2386         /* This is really weird and would mean some kind of race */
2387         Status = STATUS_INTERNAL_ERROR;
2388         goto Release;
2389     }
2390 
2391     /* Return the file part size */
2392     if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0;
2393 
2394     /* Terminate the whole string now */
2395     TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
2396 
2397     /* Finalize the string and return it to the user */
2398     DynamicString->Buffer = TempDynamicString.Buffer;
2399     DynamicString->Length = (USHORT)Length;
2400     DynamicString->MaximumLength = TempDynamicString.MaximumLength;
2401     if (StringUsed) *StringUsed = DynamicString;
2402 
2403     /* Return success and make sure we don't free the buffer on exit */
2404     TempDynamicString.Buffer = NULL;
2405     Status = STATUS_SUCCESS;
2406 
2407 Release:
2408     /* Release the PEB lock */
2409     RtlReleasePebLock();
2410 
2411 Quickie:
2412     /* Free any buffers we should be freeing */
2413     DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer);
2414     if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer))
2415     {
2416         RtlpFreeMemory(StaticBuffer, TAG_USTR);
2417     }
2418     if (TempDynamicString.Buffer)
2419     {
2420         RtlpFreeMemory(TempDynamicString.Buffer, TAG_USTR);
2421     }
2422 
2423     /* Print out any unusual errors */
2424     if ((NT_ERROR(Status)) &&
2425         (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
2426     {
2427         DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2428                 __FUNCTION__, FileName, Status);
2429     }
2430 
2431     /* Return, we're all done */
2432     return Status;
2433 }
2434 
2435 /*
2436  * @implemented
2437  */
2438 NTSTATUS
2439 NTAPI
2440 RtlDosSearchPath_Ustr(IN ULONG Flags,
2441                       IN PUNICODE_STRING PathString,
2442                       IN PUNICODE_STRING FileNameString,
2443                       IN PUNICODE_STRING ExtensionString,
2444                       IN PUNICODE_STRING CallerBuffer,
2445                       IN OUT PUNICODE_STRING DynamicString OPTIONAL,
2446                       OUT PUNICODE_STRING* FullNameOut OPTIONAL,
2447                       OUT PSIZE_T FilePartSize OPTIONAL,
2448                       OUT PSIZE_T LengthNeeded OPTIONAL)
2449 {
2450     WCHAR StaticCandidateBuffer[MAX_PATH];
2451     UNICODE_STRING StaticCandidateString;
2452     NTSTATUS Status;
2453     RTL_PATH_TYPE PathType;
2454     PWCHAR p, End, CandidateEnd, SegmentEnd;
2455     SIZE_T SegmentSize, ByteCount, PathSize, MaxPathSize = 0;
2456     USHORT NamePlusExtLength, WorstCaseLength, ExtensionLength = 0;
2457     PUNICODE_STRING FullIsolatedPath;
2458     DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2459             Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString);
2460 
2461     /* Initialize the input string */
2462     RtlInitEmptyUnicodeString(&StaticCandidateString,
2463                               StaticCandidateBuffer,
2464                               sizeof(StaticCandidateBuffer));
2465 
2466     /* Initialize optional arguments */
2467     if (FullNameOut ) *FullNameOut  = NULL;
2468     if (FilePartSize) *FilePartSize = 0;
2469     if (LengthNeeded) *LengthNeeded = 0;
2470     if (DynamicString)
2471     {
2472         DynamicString->Length = DynamicString->MaximumLength = 0;
2473         DynamicString->Buffer = NULL;
2474     }
2475 
2476     /* Check for invalid parameters */
2477     if ((Flags & ~7) ||
2478         !(PathString) ||
2479         !(FileNameString) ||
2480         ((CallerBuffer) && (DynamicString) && !(FullNameOut)))
2481     {
2482         /* Fail */
2483         DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__);
2484         Status = STATUS_INVALID_PARAMETER;
2485         goto Quickie;
2486     }
2487 
2488     /* First check what kind of path this is */
2489     PathType = RtlDetermineDosPathNameType_Ustr(FileNameString);
2490 
2491     /* Check if the caller wants to prevent relative .\ and ..\ paths */
2492     if ((Flags & 2) &&
2493          (PathType == RtlPathTypeRelative) &&
2494          (FileNameString->Length >= (2 * sizeof(WCHAR))) &&
2495          (FileNameString->Buffer[0] == L'.') &&
2496          ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) ||
2497           ((FileNameString->Buffer[1] == L'.') &&
2498            ((FileNameString->Length >= (3 * sizeof(WCHAR))) &&
2499            (IS_PATH_SEPARATOR(FileNameString->Buffer[2]))))))
2500     {
2501         /* Yes, and this path is like that, so make it seem unknown */
2502         PathType = RtlPathTypeUnknown;
2503     }
2504 
2505     /* Now check relative vs non-relative paths */
2506     if (PathType == RtlPathTypeRelative)
2507     {
2508         /* Does the caller want SxS? */
2509         if (Flags & 1)
2510         {
2511             /* Apply the SxS magic */
2512             FullIsolatedPath = NULL;
2513             Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE,
2514                                                               FileNameString,
2515                                                               ExtensionString,
2516                                                               CallerBuffer,
2517                                                               DynamicString,
2518                                                               &FullIsolatedPath,
2519                                                               NULL,
2520                                                               FilePartSize,
2521                                                               LengthNeeded);
2522             if (NT_SUCCESS(Status))
2523             {
2524                 /* We found the SxS path, return it */
2525                 if (FullNameOut) *FullNameOut = FullIsolatedPath;
2526                 goto Quickie;
2527             }
2528             else if (Status != STATUS_SXS_KEY_NOT_FOUND)
2529             {
2530                 /* Critical SxS error, fail */
2531                 DbgPrint("%s: Failing because call to "
2532                          "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2533                           "status 0x%08lx\n",
2534                          __FUNCTION__,
2535                          FileNameString,
2536                          Status);
2537                 goto Quickie;
2538             }
2539         }
2540 
2541         /* No SxS key found, or not requested, check if there's an extension */
2542         if (ExtensionString)
2543         {
2544             /* Save the extension length, and check if there's a file name */
2545             ExtensionLength = ExtensionString->Length;
2546             if (FileNameString->Length)
2547             {
2548                 /* Start parsing the file name */
2549                 End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)];
2550                 while (End > FileNameString->Buffer)
2551                 {
2552                     /* If we find a path separator, there's no extension */
2553                     if (IS_PATH_SEPARATOR(*--End)) break;
2554 
2555                     /* Otherwise, did we find an extension dot? */
2556                     if (*End == L'.')
2557                     {
2558                         /* Ignore what the caller sent it, use the filename's */
2559                         ExtensionString = NULL;
2560                         ExtensionLength = 0;
2561                         break;
2562                     }
2563                 }
2564             }
2565         }
2566 
2567         /* Check if we got a path */
2568         if (PathString->Length)
2569         {
2570             /* Start parsing the path name, looking for path separators */
2571             End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)];
2572             p = End;
2573             while ((p > PathString->Buffer) && (*--p == L';'))
2574             {
2575                 /* This is the size of the path -- handle a trailing slash */
2576                 PathSize = End - p - 1;
2577                 if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
2578 
2579                 /* Check if we found a bigger path than before */
2580                 if (PathSize > MaxPathSize) MaxPathSize = PathSize;
2581 
2582                 /* Keep going with the path after this path separator */
2583                 End = p;
2584             }
2585 
2586             /* This is the trailing path, run the same code as above */
2587             PathSize = End - p;
2588             if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
2589             if (PathSize > MaxPathSize) MaxPathSize = PathSize;
2590 
2591             /* Finally, convert the largest path size into WCHAR */
2592             MaxPathSize *= sizeof(WCHAR);
2593         }
2594 
2595         /* Use the extension, the file name, and the largest path as the size */
2596         WorstCaseLength = ExtensionLength +
2597                           FileNameString->Length +
2598                           (USHORT)MaxPathSize +
2599                           sizeof(UNICODE_NULL);
2600         if (WorstCaseLength > UNICODE_STRING_MAX_BYTES)
2601         {
2602             /* It has to fit in a registry string, if not, fail here */
2603             DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2604                      "worst case file name length is %Iu bytes\n",
2605                      __FUNCTION__,
2606                      WorstCaseLength);
2607             Status = STATUS_NAME_TOO_LONG;
2608             goto Quickie;
2609         }
2610 
2611         /* Scan the path now, to see if we can find the file */
2612         p = PathString->Buffer;
2613         End = &p[PathString->Length / sizeof(WCHAR)];
2614         while (p < End)
2615         {
2616             /* Find out where this path ends */
2617             for (SegmentEnd = p;
2618                  ((SegmentEnd != End) && (*SegmentEnd != L';'));
2619                  SegmentEnd++);
2620 
2621             /* Compute the size of this path */
2622             ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR);
2623 
2624             /* Handle trailing slash if there isn't one */
2625             if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1))))
2626             {
2627                 /* Add space for one */
2628                 SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR);
2629             }
2630 
2631             /* Now check if our initial static buffer is too small */
2632             if (StaticCandidateString.MaximumLength <
2633                 (SegmentSize + ExtensionLength + FileNameString->Length))
2634             {
2635                 /* At this point we should've been using our static buffer */
2636                 ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer);
2637                 if (StaticCandidateString.Buffer != StaticCandidateBuffer)
2638                 {
2639                     /* Something is really messed up if this was the dynamic string */
2640                     DbgPrint("%s: internal error #1; "
2641                              "CandidateString.Buffer = %p; "
2642                              "StaticCandidateBuffer = %p\n",
2643                             __FUNCTION__,
2644                             StaticCandidateString.Buffer,
2645                             StaticCandidateBuffer);
2646                     Status = STATUS_INTERNAL_ERROR;
2647                     goto Quickie;
2648                 }
2649 
2650                 /* We checked before that the maximum possible size shoudl fit! */
2651                 ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) <
2652                         UNICODE_STRING_MAX_BYTES);
2653                 if ((SegmentSize + ExtensionLength + FileNameString->Length) >
2654                     (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR)))
2655                 {
2656                     /* For some reason it's not fitting anymore. Something messed up */
2657                     DbgPrint("%s: internal error #2; SegmentSize = %u, "
2658                              "FileName->Length = %u, DefaultExtensionLength = %u\n",
2659                              __FUNCTION__,
2660                              SegmentSize,
2661                              FileNameString->Length,
2662                              ExtensionLength);
2663                     Status = STATUS_INTERNAL_ERROR;
2664                     goto Quickie;
2665                 }
2666 
2667                 /* Now allocate the dynamic string */
2668                 StaticCandidateString.MaximumLength = FileNameString->Length +
2669                                                       WorstCaseLength;
2670                 StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength,
2671                                                                         TAG_USTR);
2672                 if (!StaticCandidateString.Buffer)
2673                 {
2674                     /* Out of memory, fail */
2675                     DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2676                              __FUNCTION__,
2677                              StaticCandidateString.MaximumLength);
2678                     Status = STATUS_NO_MEMORY;
2679                     goto Quickie;
2680                 }
2681             }
2682 
2683             /* Copy the path in the string */
2684             RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount);
2685 
2686             /* Get to the end of the string, and add the trailing slash if missing */
2687             CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)];
2688             if ((SegmentSize) && (SegmentSize != ByteCount))
2689             {
2690                 *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR;
2691             }
2692 
2693             /* Copy the filename now */
2694             RtlCopyMemory(CandidateEnd,
2695                           FileNameString->Buffer,
2696                           FileNameString->Length);
2697             CandidateEnd += (FileNameString->Length / sizeof(WCHAR));
2698 
2699             /* Check if there was an extension */
2700             if (ExtensionString)
2701             {
2702                 /* Copy the extension too */
2703                 RtlCopyMemory(CandidateEnd,
2704                               ExtensionString->Buffer,
2705                               ExtensionString->Length);
2706                           CandidateEnd += (ExtensionString->Length / sizeof(WCHAR));
2707             }
2708 
2709             /* We are done, terminate it */
2710             *CandidateEnd = UNICODE_NULL;
2711 
2712             /* Now set the final length of the string so it becomes valid */
2713             StaticCandidateString.Length = (USHORT)(CandidateEnd -
2714                                             StaticCandidateString.Buffer) *
2715                                            sizeof(WCHAR);
2716 
2717             /* Check if this file exists */
2718             DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer);
2719             if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE))
2720             {
2721                 /* Awesome, it does, now get the full path */
2722                 Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
2723                                                    CallerBuffer,
2724                                                    DynamicString,
2725                                                    (PUNICODE_STRING*)FullNameOut,
2726                                                    FilePartSize,
2727                                                    NULL,
2728                                                    &PathType,
2729                                                    LengthNeeded);
2730                 if (!(NT_SUCCESS(Status)) &&
2731                     ((Status != STATUS_NO_SUCH_FILE) &&
2732                      (Status != STATUS_BUFFER_TOO_SMALL)))
2733                 {
2734                     DbgPrint("%s: Failing because we thought we found %wZ on "
2735                              "the search path, but RtlGetfullPathNameUStrEx() "
2736                              "returned %08lx\n",
2737                              __FUNCTION__,
2738                              &StaticCandidateString,
2739                              Status);
2740                 }
2741                 DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
2742                 goto Quickie;
2743             }
2744             else
2745             {
2746                 /* Otherwise, move to the next path */
2747                 if (SegmentEnd != End)
2748                 {
2749                     /* Handle the case of the path separator trailing */
2750                     p = SegmentEnd + 1;
2751                 }
2752                 else
2753                 {
2754                     p = SegmentEnd;
2755                 }
2756             }
2757         }
2758 
2759         /* Loop finished and we didn't break out -- fail */
2760         Status = STATUS_NO_SUCH_FILE;
2761     }
2762     else
2763     {
2764         /* We have a full path, so check if it does exist */
2765         DPRINT("%wZ\n", FileNameString);
2766         if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE))
2767         {
2768             /* It doesn't exist, did we have an extension? */
2769             if (!(ExtensionString) || !(ExtensionString->Length))
2770             {
2771                 /* No extension, so just fail */
2772                 Status = STATUS_NO_SUCH_FILE;
2773                 goto Quickie;
2774             }
2775 
2776             /* There was an extension, check if the filename already had one */
2777             if (!(Flags & 4) && (FileNameString->Length))
2778             {
2779                 /* Parse the filename */
2780                 p = FileNameString->Buffer;
2781                 End = &p[FileNameString->Length / sizeof(WCHAR)];
2782                 while (End > p)
2783                 {
2784                     /* If there's a path separator, there's no extension */
2785                     if (IS_PATH_SEPARATOR(*--End)) break;
2786 
2787                     /* Othwerwise, did we find an extension dot? */
2788                     if (*End == L'.')
2789                     {
2790                         /* File already had an extension, so fail */
2791                         Status = STATUS_NO_SUCH_FILE;
2792                         goto Quickie;
2793                     }
2794                 }
2795             }
2796 
2797             /* So there is an extension, we'll try again by adding it */
2798             NamePlusExtLength = FileNameString->Length +
2799                                 ExtensionString->Length +
2800                                 sizeof(UNICODE_NULL);
2801             if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES)
2802             {
2803                 /* It won't fit in any kind of valid string, so fail */
2804                 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2805                          __FUNCTION__,
2806                          NamePlusExtLength);
2807                 Status = STATUS_NAME_TOO_LONG;
2808                 goto Quickie;
2809             }
2810 
2811             /* Fill it fit in our temporary string? */
2812             if (NamePlusExtLength > StaticCandidateString.MaximumLength)
2813             {
2814                 /* It won't fit anymore, allocate a dynamic string for it */
2815                 StaticCandidateString.MaximumLength = NamePlusExtLength;
2816                 StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength,
2817                                                                         TAG_USTR);
2818                 if (!StaticCandidateString.Buffer)
2819                 {
2820                     /* Ran out of memory, so fail */
2821                     DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2822                              __FUNCTION__);
2823                     Status = STATUS_NO_MEMORY;
2824                     goto Quickie;
2825                 }
2826             }
2827 
2828             /* Copy the filename */
2829             RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
2830 
2831             /* Copy the extension */
2832             RtlAppendUnicodeStringToString(&StaticCandidateString,
2833                                            ExtensionString);
2834 
2835             DPRINT("SB: %wZ\n", &StaticCandidateString);
2836 
2837             /* And check if this file now exists */
2838             if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE))
2839             {
2840                 /* Still no joy, fail out */
2841                 Status = STATUS_NO_SUCH_FILE;
2842                 goto Quickie;
2843             }
2844 
2845             /* File was found, get the final full path */
2846             Status = RtlGetFullPathName_UstrEx(&StaticCandidateString,
2847                                                CallerBuffer,
2848                                                DynamicString,
2849                                                (PUNICODE_STRING*)FullNameOut,
2850                                                FilePartSize,
2851                                                NULL,
2852                                                &PathType,
2853                                                LengthNeeded);
2854             if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE))
2855             {
2856                 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2857                          "failed with status %08lx\n",
2858                          __FUNCTION__,
2859                          &StaticCandidateString,
2860                          Status);
2861             }
2862             DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
2863         }
2864         else
2865         {
2866             /* File was found on the first try, get the final full path */
2867             Status = RtlGetFullPathName_UstrEx(FileNameString,
2868                                                CallerBuffer,
2869                                                DynamicString,
2870                                                (PUNICODE_STRING*)FullNameOut,
2871                                                FilePartSize,
2872                                                NULL,
2873                                                &PathType,
2874                                                LengthNeeded);
2875             if (!(NT_SUCCESS(Status)) &&
2876                 ((Status != STATUS_NO_SUCH_FILE) &&
2877                 (Status != STATUS_BUFFER_TOO_SMALL)))
2878             {
2879                 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2880                          "failed with status %08lx\n",
2881                          __FUNCTION__,
2882                          FileNameString,
2883                          Status);
2884             }
2885             DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer);
2886         }
2887     }
2888 
2889 Quickie:
2890     /* Anything that was not an error, turn into STATUS_SUCCESS */
2891     if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS;
2892 
2893     /* Check if we had a dynamic string */
2894     if ((StaticCandidateString.Buffer) &&
2895         (StaticCandidateString.Buffer != StaticCandidateBuffer))
2896     {
2897         /* Free it */
2898         RtlFreeUnicodeString(&StaticCandidateString);
2899     }
2900 
2901     /* Return the status */
2902     return Status;
2903 }
2904 
2905 /*
2906  * @implemented
2907  */
2908 BOOLEAN
2909 NTAPI
2910 RtlDoesFileExists_U(IN PCWSTR FileName)
2911 {
2912     /* Call the new function */
2913     return RtlDoesFileExists_UEx(FileName, TRUE);
2914 }
2915 
2916 /* EOF */
2917