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