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