2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: Path and current directory functions
6 * PROGRAMMERS: Wine team
11 /* INCLUDES *****************************************************************/
18 /* DEFINITONS and MACROS ******************************************************/
20 #define MAX_PFX_SIZE 16
22 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
25 /* GLOBALS ********************************************************************/
27 static const WCHAR DeviceRootW
[] = L
"\\\\.\\";
28 const UNICODE_STRING DeviceRootString
= RTL_CONSTANT_STRING(L
"\\\\.\\");
30 const UNICODE_STRING RtlpDosDevicesUncPrefix
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
31 const UNICODE_STRING RtlpWin32NtRootSlash
= RTL_CONSTANT_STRING(L
"\\\\?\\");
32 const UNICODE_STRING RtlpDosSlashCONDevice
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
33 const UNICODE_STRING RtlpDosDevicesPrefix
= RTL_CONSTANT_STRING(L
"\\??\\");
35 const UNICODE_STRING RtlpDosLPTDevice
= RTL_CONSTANT_STRING(L
"LPT");
36 const UNICODE_STRING RtlpDosCOMDevice
= RTL_CONSTANT_STRING(L
"COM");
37 const UNICODE_STRING RtlpDosPRNDevice
= RTL_CONSTANT_STRING(L
"PRN");
38 const UNICODE_STRING RtlpDosAUXDevice
= RTL_CONSTANT_STRING(L
"AUX");
39 const UNICODE_STRING RtlpDosCONDevice
= RTL_CONSTANT_STRING(L
"CON");
40 const UNICODE_STRING RtlpDosNULDevice
= RTL_CONSTANT_STRING(L
"NUL");
42 /* PRIVATE FUNCTIONS **********************************************************/
46 RtlIsDosDeviceName_Ustr(IN PUNICODE_STRING PathString
)
48 UNICODE_STRING PathCopy
;
50 ULONG PathChars
, ColonCount
= 0;
51 USHORT ReturnOffset
= 0, ReturnLength
;
54 /* Validate the input */
55 if (!PathString
) return 0;
57 /* Check what type of path this is */
58 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
60 /* Fail for UNC or unknown paths */
61 case RtlPathTypeUnknown
:
62 case RtlPathTypeUncAbsolute
:
65 /* Make special check for the CON device */
66 case RtlPathTypeLocalDevice
:
67 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
69 /* This should return 0x80006 */
70 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
78 /* Make a copy of the string */
79 PathCopy
= *PathString
;
81 /* Return if there's no characters */
82 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
83 if (!PathChars
) return 0;
85 /* Check for drive path and truncate */
86 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
88 /* Fixup the lengths */
89 PathCopy
.Length
-= sizeof(WCHAR
);
90 if (!--PathChars
) return 0;
92 /* Remember this for later */
96 /* Check for extension or space, and truncate */
97 c
= PathCopy
.Buffer
[PathChars
- 1];
100 /* Stop if we hit a space or period */
101 if ((c
!= '.') && (c
!= ' ')) break;
103 /* Fixup the lengths and get the next character */
104 PathCopy
.Length
-= sizeof(WCHAR
);
105 if (!--PathChars
) c
= PathCopy
.Buffer
[PathChars
- 1];
107 /* Remember this for later */
111 /* Anything still left? */
114 /* Loop from the end */
115 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
116 End
>= PathCopy
.Buffer
;
119 /* Check if the character is a path or drive separator */
121 if ((c
== '\\') || (c
== '/') || ((c
== ':') && (End
== PathCopy
.Buffer
+ 1)))
123 /* Get the next lower case character */
125 c
= *End
| ' '; // ' ' == ('z' - 'Z')
127 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
128 if ((End
< &PathCopy
.Buffer
[PathCopy
.Length
/ sizeof(WCHAR
)]) &&
129 ((c
== 'l') || (c
== 'c') || (c
== 'p') || (c
== 'a') || (c
== 'n')))
131 /* Calculate the offset */
132 ReturnOffset
= (PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
;
134 /* Build the final string */
135 PathCopy
.Length
-= ReturnOffset
;
136 PathCopy
.Length
-= (ColonCount
* sizeof(WCHAR
));
137 PathCopy
.Buffer
= End
;
145 /* Get the next lower case character and check if it's a DOS device */
146 c
= *PathCopy
.Buffer
| ' '; // ' ' == ('z' - 'Z')
147 if ((c
!= 'l') && (c
!= 'c') && (c
!= 'p') && (c
!= 'a') && (c
!= 'n'))
149 /* Not LPT, COM, PRN, AUX, or NUL */
154 /* Now skip past any extra extension or drive letter characters */
155 Start
= PathCopy
.Buffer
;
156 End
= &Start
[PathChars
];
160 if ((c
== '.') || (c
== ':')) break;
164 /* And then go backwards to get rid of spaces */
165 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == ' ')) --Start
;
167 /* Finally see how many characters are left, and that's our size */
168 PathChars
= Start
- PathCopy
.Buffer
;
169 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
171 /* Check if this is a COM or LPT port, which has a digit after it */
172 if ((PathChars
== 4) &&
173 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != '0')))
175 /* Don't compare the number part, just check for LPT or COM */
176 PathCopy
.Length
-= sizeof(WCHAR
);
177 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
178 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
181 ReturnLength
= sizeof(L
"COM1");
182 return MAKELONG(ReturnOffset
, ReturnLength
);
185 else if ((PathChars
== 3) &&
186 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
187 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
188 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
189 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
191 /* Otherwise this was something like AUX, NUL, PRN, or CON */
192 ReturnLength
= sizeof(L
"AUX");
193 return MAKELONG(ReturnOffset
, ReturnLength
);
196 /* Otherwise, this isn't a valid DOS device */
202 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
207 /* Validate the input */
208 if (!PathString
) return RtlPathTypeUnknown
;
210 Path
= PathString
->Buffer
;
211 Chars
= PathString
->Length
/ sizeof(WCHAR
);
214 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
215 * actually check for the path length before touching the characters
217 if ((Chars
< 1) || (IS_PATH_SEPARATOR(Path
[0])))
219 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
220 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
221 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
222 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
223 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
227 if ((Chars
< 2) || (!(Path
[0]) || (Path
[1] != L
':'))) return RtlPathTypeRelative
; /* x */
228 if ((Chars
< 3) || (IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveAbsolute
; /* x:\ */
229 return RtlPathTypeDriveRelative
; /* x: */
235 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
237 OUT PBOOLEAN NameInvalid
)
242 /* Allocate a large enough buffer */
243 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
249 /* Copy the filename */
250 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
252 /* And add a dot at the end */
253 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
254 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
256 /* Check if the file exists or not */
257 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
259 /* Get rid of the buffer now */
260 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
264 /* Assume the name is ok, but fail the call */
265 *NameInvalid
= FALSE
;
266 Status
= STATUS_NO_MEMORY
;
269 /* Return the status */
275 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName
,
278 OUT PCWSTR
*ShortName
,
279 OUT PBOOLEAN InvalidName
,
280 OUT RTL_PATH_TYPE
*PathType
)
282 PWCHAR FileNameBuffer
;
283 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
287 /* For now, assume the name is valid */
288 DPRINT("Filename: %wZ\n", FileName
);
289 DPRINT("Size and buffer: %lx %S\n", Size
, Buffer
);
290 if (InvalidName
) *InvalidName
= FALSE
;
292 /* Handle initial path type and failure case */
293 *PathType
= RtlPathTypeUnknown
;
294 if (!(Size
) || !(Buffer
) || !(FileName
) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
296 /* Break filename into component parts */
297 FileNameBuffer
= FileName
->Buffer
;
298 FileNameLength
= FileName
->Length
;
299 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
301 /* Kill trailing spaces */
302 c
= FileNameBuffer
[FileNameChars
- 1];
303 while ((FileNameLength
) && (c
== L
' '))
305 /* Keep going, ignoring the spaces */
306 FileNameLength
-= sizeof(WCHAR
);
307 if (FileNameLength
) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
310 /* Check if anything is left */
311 if (!FileNameLength
) return 0;
313 /* Check if this is a DOS name */
314 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
315 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
318 /* Zero out the short name */
319 if (ShortName
) *ShortName
= NULL
;
321 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
322 DosLengthOffset
= DosLength
>> 16;
323 DosLength
= DosLength
& 0xFFFF;
325 /* Do we have a DOS length, and does the caller want validity? */
326 if ((InvalidName
) && (DosLengthOffset
))
329 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
331 /* If the check failed, or the name is invalid, fail here */
332 if (!NT_SUCCESS(Status
)) return 0;
333 if (*InvalidName
) return 0;
336 /* Add the size of the device root and check if it fits in the size */
337 FullLength
= DosLength
+ DeviceRootString
.Length
;
338 if (FullLength
< Size
)
340 /* Add the device string */
341 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
343 /* Now add the DOS device name */
344 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
345 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
349 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
353 /* Otherwise, there's no space, so return the buffer size needed */
354 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
355 return FullLength
+ sizeof(UNICODE_NULL
);
358 /* This should work well enough for our current needs */
359 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
360 DPRINT("Path type: %lx\n", *PathType
);
362 /* This is disgusting... but avoids re-writing everything */
363 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer
, Size
, Buffer
);
364 return RtlGetFullPathName_U(FileNameBuffer
, Size
, Buffer
, (PWSTR
*)ShortName
);
369 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
370 OUT PUNICODE_STRING NtPath
,
371 OUT PCWSTR
*PartName
,
372 OUT PRTL_RELATIVE_NAME_U RelativeName
)
377 /* Validate the input */
378 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
380 /* Validate the DOS length */
381 DosLength
= DosPath
->Length
;
382 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
384 /* Make space for the new path */
385 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
387 DosLength
+ sizeof(UNICODE_NULL
));
388 if (!NewBuffer
) return STATUS_NO_MEMORY
;
390 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
391 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
392 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
393 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
394 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
395 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
397 /* Did the caller send a relative name? */
400 /* Zero initialize it */
401 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
402 RelativeName
->ContainingDirectory
= NULL
;
403 RelativeName
->CurDirRef
= 0;
406 /* Did the caller request a partial name? */
409 /* Loop from the back until we find a path separator */
410 p
= &NewBuffer
[(DosLength
- 1) / sizeof (WCHAR
)];
411 while (p
> NewBuffer
) if (*p
-- == '\\') break;
416 /* Move past it -- anything left? */
420 /* The path ends with a path separator, no part name */
425 /* What follows the path separator is the part name */
431 /* Build the final NT path string */
432 NtPath
->Length
= DosLength
;
433 NtPath
->Buffer
= NewBuffer
;
434 NtPath
->MaximumLength
= DosLength
+ sizeof(UNICODE_NULL
);
435 return STATUS_SUCCESS
;
440 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
441 IN PCUNICODE_STRING DosName
,
442 OUT PUNICODE_STRING NtName
,
443 OUT PCWSTR
*PartName
,
444 OUT PRTL_RELATIVE_NAME_U RelativeName
)
446 WCHAR BigBuffer
[MAX_PATH
+ 1];
447 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
448 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
449 UNICODE_STRING CapturedDosName
, PartNameString
;
451 RTL_PATH_TYPE InputPathType
, BufferPathType
;
455 /* Assume MAX_PATH for now */
456 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
457 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
458 MaxLength
= sizeof(BigBuffer
);
460 /* Validate the input */
461 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
463 /* Capture input string */
464 CapturedDosName
= *DosName
;
466 /* Check for \\?\\ form */
467 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
468 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
469 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
470 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
471 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
473 /* Quick path won't be used */
476 /* Use the static buffer */
478 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
480 /* Allocate a buffer to hold the path */
481 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
482 DPRINT("Length: %lx\n", MaxLength
);
483 if (!NewBuffer
) return STATUS_NO_MEMORY
;
487 /* Use the optimized path after acquiring the lock */
492 /* Lock the PEB and check if the quick path can be used */
496 /* Some simple fixups will get us the correct path */
497 DPRINT("Quick path\n");
498 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
503 /* Release the lock, we're done here */
508 /* Call the main function to get the full path name and length */
509 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
510 MAX_PATH
* sizeof(WCHAR
),
515 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
517 /* Invalid name, fail */
518 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
519 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
521 return STATUS_OBJECT_NAME_INVALID
;
524 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
525 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
526 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
529 /* Check where it really is */
530 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
531 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
532 switch (BufferPathType
)
534 /* It's actually a UNC path in \??\UNC\ */
535 case RtlPathTypeUncAbsolute
:
536 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
537 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
541 case RtlPathTypeLocalDevice
:
542 /* We made a good guess, go with it but skip the \??\ */
546 case RtlPathTypeDriveAbsolute
:
547 case RtlPathTypeDriveRelative
:
548 case RtlPathTypeRooted
:
549 case RtlPathTypeRelative
:
550 /* Our guess was good, roll with it */
553 /* Nothing else is expected */
559 /* Now copy the prefix and the buffer */
560 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
561 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
563 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
565 /* Compute the length */
566 Length
= PathLength
- PrefixCut
* sizeof(WCHAR
) + PrefixLength
;
567 LengthChars
= Length
/ sizeof(WCHAR
);
569 /* Setup the actual NT path string and terminate it */
570 NtName
->Buffer
= NewBuffer
;
571 NtName
->Length
= Length
;
572 NtName
->MaximumLength
= MaxLength
;
573 NewBuffer
[LengthChars
] = UNICODE_NULL
;
574 DPRINT("new buffer: %S\n", NewBuffer
);
575 DPRINT("NT Name: %wZ\n", NtName
);
577 /* Check if a partial name was requested */
578 if ((PartName
) && (*PartName
))
580 /* Convert to Unicode */
581 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
582 if (NT_SUCCESS(Status
))
584 /* Set the partial name */
585 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
590 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
596 /* Check if a relative name was asked for */
599 /* Setup the structure */
600 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
601 RelativeName
->ContainingDirectory
= NULL
;
602 RelativeName
->CurDirRef
= 0;
604 /* Check if the input path itself was relative */
605 if (InputPathType
== RtlPathTypeRelative
)
607 /* FIXME: HACK: Old code */
610 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
613 RtlInitUnicodeString(&us
, Buffer
);
614 us
.Length
= (cd
->DosPath
.Length
< us
.Length
) ? cd
->DosPath
.Length
: us
.Length
;
615 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
617 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - PrefixCut
) + ((InputPathType
== 1) ? 8 : 4);
618 RelativeName
->RelativeName
.Buffer
= NewBuffer
+ Length
;
619 RelativeName
->RelativeName
.Length
= NtName
->Length
- (Length
* sizeof(WCHAR
));
620 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
621 RelativeName
->ContainingDirectory
= cd
->Handle
;
629 return STATUS_SUCCESS
;
634 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
636 OUT PUNICODE_STRING NtName
,
637 OUT PCWSTR
*PartName
,
638 OUT PRTL_RELATIVE_NAME_U RelativeName
)
641 UNICODE_STRING NameString
;
643 /* Create the unicode name */
644 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
645 if (NT_SUCCESS(Status
))
647 /* Call the unicode function */
648 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
661 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
662 OUT PUNICODE_STRING NtName
,
663 OUT PCWSTR
*PartName
,
664 OUT PRTL_RELATIVE_NAME_U RelativeName
)
666 /* Call the internal function */
667 ASSERT(RelativeName
);
668 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
677 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
678 IN BOOLEAN SucceedIfBusy
)
681 RTL_RELATIVE_NAME_U RelativeName
;
682 UNICODE_STRING NtPathName
;
684 OBJECT_ATTRIBUTES ObjectAttributes
;
686 FILE_BASIC_INFORMATION BasicInformation
;
688 /* Validate the input */
689 if (!FileName
) return FALSE
;
691 /* Get the NT Path */
692 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
696 if (!Result
) return FALSE
;
698 /* Save the buffer */
699 Buffer
= NtPathName
.Buffer
;
701 /* Check if we have a relative name */
702 if (RelativeName
.RelativeName
.Length
)
705 NtPathName
= RelativeName
.RelativeName
;
709 /* Otherwise ignore it */
710 RelativeName
.ContainingDirectory
= NULL
;
713 /* Initialize the object attributes */
714 InitializeObjectAttributes(&ObjectAttributes
,
716 OBJ_CASE_INSENSITIVE
,
717 RelativeName
.ContainingDirectory
,
720 /* Query the attributes and free the buffer now */
721 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
722 RtlReleaseRelativeName(&RelativeName
);
723 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
725 /* Check if we failed */
726 if (!NT_SUCCESS(Status
))
728 /* Check if we failed because the file is in use */
729 if ((Status
== STATUS_SHARING_VIOLATION
) ||
730 (Status
== STATUS_ACCESS_DENIED
))
732 /* Check if the caller wants this to be considered OK */
733 Result
= SucceedIfBusy
? TRUE
: FALSE
;
737 /* A failure because the file didn't exist */
743 /* The file exists */
747 /* Return the result */
753 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
755 /* Call the updated API */
756 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
761 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
762 IN BOOLEAN SucceedIfBusy
)
764 UNICODE_STRING NameString
;
766 /* Create the unicode name*/
767 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
769 /* Call the unicode function */
770 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
777 /* PUBLIC FUNCTIONS ***********************************************************/
784 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
786 /* Check if a directory reference was grabbed */
787 if (RelativeName
->CurDirRef
)
789 /* FIXME: Not yet supported */
791 RelativeName
->CurDirRef
= NULL
;
800 RtlGetLongestNtPathLength(VOID
)
803 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
804 * a mapped network drive), which is accessed through the DOS Global?? path.
805 * This is, and has always been equal to, 269 characters, except in Wine
806 * which claims this is 277. Go figure.
808 return (MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
+ sizeof(ANSI_NULL
));
816 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
818 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
820 /* Validate the input */
821 if (!Path
) return RtlPathTypeUnknown
;
823 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
824 if (IS_PATH_SEPARATOR(Path
[0]))
826 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
827 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
828 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
829 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
830 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
834 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
835 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
836 return RtlPathTypeDriveRelative
; /* x: */
845 RtlIsDosDeviceName_U(IN PWSTR Path
)
847 UNICODE_STRING PathString
;
850 /* Build the string */
851 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
852 if (!NT_SUCCESS(Status
)) return 0;
855 * Returns 0 if name is not valid DOS device name, or DWORD with
856 * offset in bytes to DOS device name from beginning of buffer in high word
857 * and size in bytes of DOS device name in low word
859 return RtlIsDosDeviceName_Ustr(&PathString
);
867 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
873 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
875 /* Lock the PEB to get the current directory */
877 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
879 /* Get the buffer and character length */
880 CurDirName
= CurDir
->DosPath
.Buffer
;
881 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
882 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
884 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
885 Bytes
= Length
* sizeof(WCHAR
);
886 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
888 /* Check if caller does not have enough space */
889 if (MaximumLength
<= Bytes
)
891 /* Call has no space for it, fail, add the trailing slash */
893 return Bytes
+ sizeof(L
'\\');
898 /* Check if caller does not have enough space */
899 if (MaximumLength
<= Bytes
)
901 /* Call has no space for it, fail */
907 /* Copy the buffer since we seem to have space */
908 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
910 /* The buffer should end with a path separator */
911 ASSERT(Buffer
[Length
- 1] == L
'\\');
913 /* Again check for our two cases (drive root vs path) */
914 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
916 /* Replace the trailing slash with a null */
917 Buffer
[Length
- 1] = UNICODE_NULL
;
922 /* Append the null char since there's no trailing slash */
923 Buffer
[Length
] = UNICODE_NULL
;
926 /* Release PEB lock */
928 DPRINT("CurrentDirectory %S\n", Buffer
);
929 return Length
* sizeof(WCHAR
);
936 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
939 FILE_FS_DEVICE_INFORMATION device_info
;
940 OBJECT_ATTRIBUTES Attr
;
941 IO_STATUS_BLOCK iosb
;
945 HANDLE handle
= NULL
;
948 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
952 RtlAcquirePebLock ();
954 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
956 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
958 RtlReleasePebLock ();
959 return STATUS_OBJECT_NAME_INVALID
;
962 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
964 InitializeObjectAttributes (&Attr
,
966 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
970 Status
= ZwOpenFile (&handle
,
971 SYNCHRONIZE
| FILE_TRAVERSE
,
974 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
975 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
977 if (!NT_SUCCESS(Status
))
979 RtlFreeUnicodeString( &full
);
980 RtlReleasePebLock ();
984 /* don't keep the directory handle open on removable media */
985 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
986 sizeof(device_info
), FileFsDeviceInformation
)) &&
987 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
989 DPRINT1("don't keep the directory handle open on removable media\n");
998 /* append trailing \ if missing */
999 size
= full
.Length
/ sizeof(WCHAR
);
1001 ptr
+= 4; /* skip \??\ prefix */
1004 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
1005 * So the nullterm is replaced with \
1008 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
1010 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
1011 cd
->DosPath
.Buffer
[size
] = 0;
1012 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
1014 RtlFreeUnicodeString( &full
);
1015 RtlReleasePebLock();
1017 return STATUS_SUCCESS
;
1021 /******************************************************************
1024 * Helper for RtlGetFullPathName_U.
1025 * Get rid of . and .. components in the path.
1027 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
1031 /* convert every / into a \ */
1032 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
1034 /* collapse duplicate backslashes */
1035 next
= path
+ max( 1, mark
);
1036 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
1046 case '\\': /* .\ component */
1048 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1050 case 0: /* final . */
1051 if (p
> path
+ mark
) p
--;
1055 if (p
[2] == '\\') /* ..\ component */
1058 if (p
> path
+ mark
)
1061 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1063 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1066 else if (!p
[2]) /* final .. */
1068 if (p
> path
+ mark
)
1071 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1072 if (p
> path
+ mark
) p
--;
1080 /* skip to the next component */
1081 while (*p
&& *p
!= '\\') p
++;
1084 /* remove last dot in previous dir name */
1085 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
1090 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
1091 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
1097 /******************************************************************
1100 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
1102 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
1105 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
1106 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1107 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
1108 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1113 /******************************************************************
1114 * get_full_path_helper
1116 * Helper for RtlGetFullPathName_U
1117 * Note: name and buffer are allowed to point to the same memory spot
1119 static ULONG
get_full_path_helper(
1124 ULONG reqsize
= 0, mark
= 0, dep
= 0, deplen
;
1125 LPWSTR ins_str
= NULL
;
1127 const UNICODE_STRING
* cd
;
1130 /* return error if name only consists of spaces */
1131 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
1132 if (!*ptr
) return 0;
1134 RtlAcquirePebLock();
1136 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
1137 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
1139 switch (RtlDetermineDosPathNameType_U(name
))
1141 case RtlPathTypeUncAbsolute
: /* \\foo */
1142 ptr
= skip_unc_prefix( name
);
1143 mark
= (ptr
- name
);
1146 case RtlPathTypeLocalDevice
: /* \\.\foo */
1150 case RtlPathTypeDriveAbsolute
: /* c:\foo */
1151 reqsize
= sizeof(WCHAR
);
1152 tmp
[0] = towupper(name
[0]);
1158 case RtlPathTypeDriveRelative
: /* c:foo */
1160 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
1162 UNICODE_STRING var
, val
;
1168 var
.Length
= 3 * sizeof(WCHAR
);
1169 var
.MaximumLength
= 4 * sizeof(WCHAR
);
1172 val
.MaximumLength
= size
;
1173 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
1174 if (val
.Buffer
== NULL
)
1180 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
1182 case STATUS_SUCCESS
:
1183 /* FIXME: Win2k seems to check that the environment variable actually points
1184 * to an existing directory. If not, root of the drive is used
1185 * (this seems also to be the only spot in RtlGetFullPathName that the
1186 * existence of a part of a path is checked)
1189 case STATUS_BUFFER_TOO_SMALL
:
1190 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
1191 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
1192 ins_str
= val
.Buffer
;
1194 case STATUS_VARIABLE_NOT_FOUND
:
1195 reqsize
= 3 * sizeof(WCHAR
);
1200 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1203 DPRINT1("Unsupported status code\n");
1204 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1212 case RtlPathTypeRelative
: /* foo */
1213 reqsize
= cd
->Length
;
1214 ins_str
= cd
->Buffer
;
1215 if (cd
->Buffer
[1] != ':')
1217 ptr
= skip_unc_prefix( cd
->Buffer
);
1218 mark
= ptr
- cd
->Buffer
;
1223 case RtlPathTypeRooted
: /* \xxx */
1225 if (name
[0] == '/') /* may be a Unix path */
1227 const WCHAR
*ptr
= name
;
1228 int drive
= find_drive_root( &ptr
);
1231 reqsize
= 3 * sizeof(WCHAR
);
1232 tmp
[0] = 'A' + drive
;
1242 if (cd
->Buffer
[1] == ':')
1244 reqsize
= 2 * sizeof(WCHAR
);
1245 tmp
[0] = cd
->Buffer
[0];
1252 ptr
= skip_unc_prefix( cd
->Buffer
);
1253 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
1254 mark
= reqsize
/ sizeof(WCHAR
);
1255 ins_str
= cd
->Buffer
;
1259 case RtlPathTypeRootLocalDevice
: /* \\. */
1260 reqsize
= 4 * sizeof(WCHAR
);
1270 case RtlPathTypeUnknown
:
1274 /* enough space ? */
1275 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
1276 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
1278 /* not enough space, return need size (including terminating '\0') */
1279 reqsize
+= deplen
+ sizeof(WCHAR
);
1283 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
1284 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
1287 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
1288 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
1290 collapse_path( buffer
, mark
);
1291 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
1294 RtlReleasePebLock();
1299 /******************************************************************
1300 * RtlGetFullPathName_U (NTDLL.@)
1302 * Returns the number of bytes written to buffer (not including the
1303 * terminating NULL) if the function succeeds, or the required number of bytes
1304 * (including the terminating NULL) if the buffer is too small.
1306 * file_part will point to the filename part inside buffer (except if we use
1307 * DOS device name, in which case file_in_buf is NULL)
1311 ULONG NTAPI
RtlGetFullPathName_U(
1321 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
1323 if (!name
|| !*name
) return 0;
1325 if (file_part
) *file_part
= NULL
;
1327 /* check for DOS device name */
1328 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
1331 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
1332 DWORD sz
= LOWORD(dosdev
); /* in bytes */
1334 if (8 + sz
+ 2 > size
) return sz
+ 10;
1335 wcscpy(buffer
, DeviceRootW
);
1336 memmove(buffer
+ 4, name
+ offset
, sz
);
1337 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
1338 /* file_part isn't set in this case */
1342 reqsize
= get_full_path_helper(name
, buffer
, size
);
1343 if (!reqsize
) return 0;
1346 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
1347 if (tmp
== NULL
) return 0;
1348 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
1349 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
1351 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1352 return reqsize
+ sizeof(WCHAR
);
1354 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
1355 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1358 /* find file part */
1359 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
1369 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1370 OUT PUNICODE_STRING NtName
,
1371 OUT PCWSTR
*PartName
,
1372 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1374 /* Call the internal function */
1375 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1387 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1388 OUT PUNICODE_STRING NtName
,
1389 OUT PCWSTR
*PartName
,
1390 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1392 /* Call the internal function */
1393 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1405 RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName
,
1406 OUT PUNICODE_STRING NtName
,
1407 OUT PCWSTR
*PartName
,
1408 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1410 /* Call the internal function */
1411 ASSERT(RelativeName
);
1412 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1424 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PWSTR DosName
,
1425 OUT PUNICODE_STRING NtName
,
1426 OUT PCWSTR
*PartName
,
1427 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1429 /* Call the internal function */
1430 ASSERT(RelativeName
);
1431 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1442 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1444 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1445 return STATUS_NOT_IMPLEMENTED
;
1453 RtlDosSearchPath_U(IN PCWSTR Path
,
1455 IN PCWSTR Extension
,
1458 OUT PWSTR
*PartName
)
1461 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1462 UNICODE_STRING TempString
;
1463 PWCHAR NewBuffer
, BufferStart
;
1466 /* Validate the input */
1467 if (!(Path
) || !(FileName
)) return 0;
1469 /* Check if this is an absolute path */
1470 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1472 /* Check if the file exists */
1473 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1475 /* Get the full name, which does the DOS lookup */
1476 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1479 /* Doesn't exist, so fail */
1483 /* Scan the filename */
1487 /* Looking for an extension */
1490 /* No extension string needed -- it's part of the filename */
1495 /* Next character */
1499 /* Do we have an extension? */
1502 /* Nope, don't worry about one */
1503 ExtensionLength
= 0;
1507 /* Build a temporary string to get the extension length */
1508 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1509 if (!NT_SUCCESS(Status
)) return 0;
1510 ExtensionLength
= TempString
.Length
;
1513 /* Build a temporary string to get the path length */
1514 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1515 if (!NT_SUCCESS(Status
)) return 0;
1516 PathLength
= TempString
.Length
;
1518 /* Build a temporary string to get the filename length */
1519 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1520 if (!NT_SUCCESS(Status
)) return 0;
1521 FileNameLength
= TempString
.Length
;
1523 /* Allocate the buffer for the new string name */
1524 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1533 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1538 /* Final loop to build the path */
1541 /* Check if we have a valid character */
1542 BufferStart
= NewBuffer
;
1545 /* Loop as long as there's no semicolon */
1546 while (*Path
!= ';')
1548 /* Copy the next character */
1549 *BufferStart
++ = *Path
++;
1553 /* We found a semi-colon, to stop path processing on this loop */
1554 if (*Path
== ';') ++Path
;
1557 /* Add a terminating slash if needed */
1558 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != '\\'))
1560 *BufferStart
++ = '\\';
1563 /* Bail out if we reached the end */
1564 if (!*Path
) Path
= NULL
;
1566 /* Copy the file name and check if an extension is needed */
1567 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1568 if (ExtensionLength
)
1570 /* Copy the extension too */
1571 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1573 ExtensionLength
+ sizeof(WCHAR
));
1577 /* Just NULL-terminate */
1578 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1581 /* Now, does this file exist? */
1582 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1584 /* Call the full-path API to get the length */
1585 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1589 /* If we got here, path doesn't exist, so fail the call */
1594 /* Free the allocation and return the length */
1595 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1604 RtlDoesFileExists_U(IN PCWSTR FileName
)
1606 /* Call the new function */
1607 return RtlDoesFileExists_UEx(FileName
, TRUE
);