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
);
213 /* Return if there are no characters */
214 if (!Chars
) return RtlPathTypeUnknown
;
217 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
218 * actually check for the path length before touching the characters
220 if ((Chars
< 1) || (IS_PATH_SEPARATOR(Path
[0])))
222 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
223 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
224 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
225 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
226 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
230 if ((Chars
< 2) || (!(Path
[0]) || (Path
[1] != L
':'))) return RtlPathTypeRelative
; /* x */
231 if ((Chars
< 3) || (IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveAbsolute
; /* x:\ */
232 return RtlPathTypeDriveRelative
; /* x: */
238 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
240 OUT PBOOLEAN NameInvalid
)
245 /* Allocate a large enough buffer */
246 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
252 /* Copy the filename */
253 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
255 /* And add a dot at the end */
256 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
257 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
259 /* Check if the file exists or not */
260 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
262 /* Get rid of the buffer now */
263 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
267 /* Assume the name is ok, but fail the call */
268 *NameInvalid
= FALSE
;
269 Status
= STATUS_NO_MEMORY
;
272 /* Return the status */
278 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName
,
281 OUT PCWSTR
*ShortName
,
282 OUT PBOOLEAN InvalidName
,
283 OUT RTL_PATH_TYPE
*PathType
)
285 PWCHAR FileNameBuffer
;
286 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
290 /* For now, assume the name is valid */
291 DPRINT("Filename: %wZ\n", FileName
);
292 DPRINT("Size and buffer: %lx %S\n", Size
, Buffer
);
293 if (InvalidName
) *InvalidName
= FALSE
;
295 /* Handle initial path type and failure case */
296 *PathType
= RtlPathTypeUnknown
;
297 if (!(Size
) || !(Buffer
) || !(FileName
) ||
298 !(FileName
->Length
) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
300 /* Break filename into component parts */
301 FileNameBuffer
= FileName
->Buffer
;
302 FileNameLength
= FileName
->Length
;
303 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
305 /* Kill trailing spaces */
306 c
= FileNameBuffer
[FileNameChars
- 1];
307 while ((FileNameLength
) && (c
== L
' '))
309 /* Keep going, ignoring the spaces */
310 FileNameLength
-= sizeof(WCHAR
);
311 if (FileNameLength
) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
314 /* Check if anything is left */
315 if (!FileNameLength
) return 0;
317 /* Check if this is a DOS name */
318 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
319 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
322 /* Zero out the short name */
323 if (ShortName
) *ShortName
= NULL
;
325 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
326 DosLengthOffset
= DosLength
>> 16;
327 DosLength
= DosLength
& 0xFFFF;
329 /* Do we have a DOS length, and does the caller want validity? */
330 if ((InvalidName
) && (DosLengthOffset
))
333 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
335 /* If the check failed, or the name is invalid, fail here */
336 if (!NT_SUCCESS(Status
)) return 0;
337 if (*InvalidName
) return 0;
340 /* Add the size of the device root and check if it fits in the size */
341 FullLength
= DosLength
+ DeviceRootString
.Length
;
342 if (FullLength
< Size
)
344 /* Add the device string */
345 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
347 /* Now add the DOS device name */
348 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
349 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
353 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
357 /* Otherwise, there's no space, so return the buffer size needed */
358 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
359 return FullLength
+ sizeof(UNICODE_NULL
);
362 /* This should work well enough for our current needs */
363 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
364 DPRINT("Path type: %lx\n", *PathType
);
366 /* This is disgusting... but avoids re-writing everything */
367 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer
, Size
, Buffer
);
368 return RtlGetFullPathName_U(FileNameBuffer
, Size
, Buffer
, (PWSTR
*)ShortName
);
373 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
374 OUT PUNICODE_STRING NtPath
,
375 OUT PCWSTR
*PartName
,
376 OUT PRTL_RELATIVE_NAME_U RelativeName
)
381 /* Validate the input */
382 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
384 /* Validate the DOS length */
385 DosLength
= DosPath
->Length
;
386 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
388 /* Make space for the new path */
389 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
391 DosLength
+ sizeof(UNICODE_NULL
));
392 if (!NewBuffer
) return STATUS_NO_MEMORY
;
394 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
395 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
396 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
397 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
398 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
399 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
401 /* Did the caller send a relative name? */
404 /* Zero initialize it */
405 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
406 RelativeName
->ContainingDirectory
= NULL
;
407 RelativeName
->CurDirRef
= 0;
410 /* Did the caller request a partial name? */
413 /* Loop from the back until we find a path separator */
414 p
= &NewBuffer
[(DosLength
- 1) / sizeof (WCHAR
)];
415 while (p
> NewBuffer
) if (*p
-- == '\\') break;
420 /* Move past it -- anything left? */
424 /* The path ends with a path separator, no part name */
429 /* What follows the path separator is the part name */
435 /* Build the final NT path string */
436 NtPath
->Length
= DosLength
;
437 NtPath
->Buffer
= NewBuffer
;
438 NtPath
->MaximumLength
= DosLength
+ sizeof(UNICODE_NULL
);
439 return STATUS_SUCCESS
;
444 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
445 IN PCUNICODE_STRING DosName
,
446 OUT PUNICODE_STRING NtName
,
447 OUT PCWSTR
*PartName
,
448 OUT PRTL_RELATIVE_NAME_U RelativeName
)
450 WCHAR BigBuffer
[MAX_PATH
+ 1];
451 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
452 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
453 UNICODE_STRING CapturedDosName
, PartNameString
;
455 RTL_PATH_TYPE InputPathType
, BufferPathType
;
459 /* Assume MAX_PATH for now */
460 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
461 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
462 MaxLength
= sizeof(BigBuffer
);
464 /* Validate the input */
465 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
467 /* Capture input string */
468 CapturedDosName
= *DosName
;
470 /* Check for \\?\\ form */
471 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
472 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
473 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
474 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
475 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
477 /* Quick path won't be used */
480 /* Use the static buffer */
482 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
484 /* Allocate a buffer to hold the path */
485 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
486 DPRINT("Length: %lx\n", MaxLength
);
487 if (!NewBuffer
) return STATUS_NO_MEMORY
;
491 /* Use the optimized path after acquiring the lock */
496 /* Lock the PEB and check if the quick path can be used */
500 /* Some simple fixups will get us the correct path */
501 DPRINT("Quick path\n");
502 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
507 /* Release the lock, we're done here */
512 /* Call the main function to get the full path name and length */
513 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
514 MAX_PATH
* sizeof(WCHAR
),
519 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
521 /* Invalid name, fail */
522 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
523 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
525 return STATUS_OBJECT_NAME_INVALID
;
528 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
529 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
530 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
533 /* Check where it really is */
534 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
535 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
536 switch (BufferPathType
)
538 /* It's actually a UNC path in \??\UNC\ */
539 case RtlPathTypeUncAbsolute
:
540 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
541 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
545 case RtlPathTypeLocalDevice
:
546 /* We made a good guess, go with it but skip the \??\ */
550 case RtlPathTypeDriveAbsolute
:
551 case RtlPathTypeDriveRelative
:
552 case RtlPathTypeRooted
:
553 case RtlPathTypeRelative
:
554 /* Our guess was good, roll with it */
557 /* Nothing else is expected */
563 /* Now copy the prefix and the buffer */
564 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
565 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
567 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
569 /* Compute the length */
570 Length
= PathLength
- PrefixCut
* sizeof(WCHAR
) + PrefixLength
;
571 LengthChars
= Length
/ sizeof(WCHAR
);
573 /* Setup the actual NT path string and terminate it */
574 NtName
->Buffer
= NewBuffer
;
575 NtName
->Length
= Length
;
576 NtName
->MaximumLength
= MaxLength
;
577 NewBuffer
[LengthChars
] = UNICODE_NULL
;
578 DPRINT("new buffer: %S\n", NewBuffer
);
579 DPRINT("NT Name: %wZ\n", NtName
);
581 /* Check if a partial name was requested */
582 if ((PartName
) && (*PartName
))
584 /* Convert to Unicode */
585 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
586 if (NT_SUCCESS(Status
))
588 /* Set the partial name */
589 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
594 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
600 /* Check if a relative name was asked for */
603 /* Setup the structure */
604 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
605 RelativeName
->ContainingDirectory
= NULL
;
606 RelativeName
->CurDirRef
= 0;
608 /* Check if the input path itself was relative */
609 if (InputPathType
== RtlPathTypeRelative
)
611 /* FIXME: HACK: Old code */
614 cd
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
617 RtlInitUnicodeString(&us
, Buffer
);
618 us
.Length
= (cd
->DosPath
.Length
< us
.Length
) ? cd
->DosPath
.Length
: us
.Length
;
619 if (RtlEqualUnicodeString(&us
, &cd
->DosPath
, TRUE
))
621 Length
= ((cd
->DosPath
.Length
/ sizeof(WCHAR
)) - PrefixCut
) + ((InputPathType
== 1) ? 8 : 4);
622 RelativeName
->RelativeName
.Buffer
= NewBuffer
+ Length
;
623 RelativeName
->RelativeName
.Length
= NtName
->Length
- (Length
* sizeof(WCHAR
));
624 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
625 RelativeName
->ContainingDirectory
= cd
->Handle
;
633 return STATUS_SUCCESS
;
638 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
640 OUT PUNICODE_STRING NtName
,
641 OUT PCWSTR
*PartName
,
642 OUT PRTL_RELATIVE_NAME_U RelativeName
)
645 UNICODE_STRING NameString
;
647 /* Create the unicode name */
648 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
649 if (NT_SUCCESS(Status
))
651 /* Call the unicode function */
652 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
665 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
666 OUT PUNICODE_STRING NtName
,
667 OUT PCWSTR
*PartName
,
668 OUT PRTL_RELATIVE_NAME_U RelativeName
)
670 /* Call the internal function */
671 ASSERT(RelativeName
);
672 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
681 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
682 IN BOOLEAN SucceedIfBusy
)
685 RTL_RELATIVE_NAME_U RelativeName
;
686 UNICODE_STRING NtPathName
;
688 OBJECT_ATTRIBUTES ObjectAttributes
;
690 FILE_BASIC_INFORMATION BasicInformation
;
692 /* Validate the input */
693 if (!FileName
) return FALSE
;
695 /* Get the NT Path */
696 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
700 if (!Result
) return FALSE
;
702 /* Save the buffer */
703 Buffer
= NtPathName
.Buffer
;
705 /* Check if we have a relative name */
706 if (RelativeName
.RelativeName
.Length
)
709 NtPathName
= RelativeName
.RelativeName
;
713 /* Otherwise ignore it */
714 RelativeName
.ContainingDirectory
= NULL
;
717 /* Initialize the object attributes */
718 InitializeObjectAttributes(&ObjectAttributes
,
720 OBJ_CASE_INSENSITIVE
,
721 RelativeName
.ContainingDirectory
,
724 /* Query the attributes and free the buffer now */
725 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
726 RtlReleaseRelativeName(&RelativeName
);
727 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
729 /* Check if we failed */
730 if (!NT_SUCCESS(Status
))
732 /* Check if we failed because the file is in use */
733 if ((Status
== STATUS_SHARING_VIOLATION
) ||
734 (Status
== STATUS_ACCESS_DENIED
))
736 /* Check if the caller wants this to be considered OK */
737 Result
= SucceedIfBusy
? TRUE
: FALSE
;
741 /* A failure because the file didn't exist */
747 /* The file exists */
751 /* Return the result */
757 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
759 /* Call the updated API */
760 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
765 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
766 IN BOOLEAN SucceedIfBusy
)
768 UNICODE_STRING NameString
;
770 /* Create the unicode name*/
771 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
773 /* Call the unicode function */
774 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
781 /* PUBLIC FUNCTIONS ***********************************************************/
788 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
790 /* Check if a directory reference was grabbed */
791 if (RelativeName
->CurDirRef
)
793 /* FIXME: Not yet supported */
795 RelativeName
->CurDirRef
= NULL
;
804 RtlGetLongestNtPathLength(VOID
)
807 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
808 * a mapped network drive), which is accessed through the DOS Global?? path.
809 * This is, and has always been equal to, 269 characters, except in Wine
810 * which claims this is 277. Go figure.
812 return (MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
+ sizeof(ANSI_NULL
));
820 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
822 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
824 /* Validate the input */
825 if (!Path
) return RtlPathTypeUnknown
;
827 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
828 if (IS_PATH_SEPARATOR(Path
[0]))
830 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
831 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
832 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
833 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
834 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
838 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
839 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
840 return RtlPathTypeDriveRelative
; /* x: */
849 RtlIsDosDeviceName_U(IN PWSTR Path
)
851 UNICODE_STRING PathString
;
854 /* Build the string */
855 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
856 if (!NT_SUCCESS(Status
)) return 0;
859 * Returns 0 if name is not valid DOS device name, or DWORD with
860 * offset in bytes to DOS device name from beginning of buffer in high word
861 * and size in bytes of DOS device name in low word
863 return RtlIsDosDeviceName_Ustr(&PathString
);
871 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
877 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
879 /* Lock the PEB to get the current directory */
881 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
883 /* Get the buffer and character length */
884 CurDirName
= CurDir
->DosPath
.Buffer
;
885 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
886 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
888 /* Check for x:\ vs x:\path\foo (note the trailing slash) */
889 Bytes
= Length
* sizeof(WCHAR
);
890 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
892 /* Check if caller does not have enough space */
893 if (MaximumLength
<= Bytes
)
895 /* Call has no space for it, fail, add the trailing slash */
897 return Bytes
+ sizeof(L
'\\');
902 /* Check if caller does not have enough space */
903 if (MaximumLength
<= Bytes
)
905 /* Call has no space for it, fail */
911 /* Copy the buffer since we seem to have space */
912 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
914 /* The buffer should end with a path separator */
915 ASSERT(Buffer
[Length
- 1] == L
'\\');
917 /* Again check for our two cases (drive root vs path) */
918 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
920 /* Replace the trailing slash with a null */
921 Buffer
[Length
- 1] = UNICODE_NULL
;
926 /* Append the null char since there's no trailing slash */
927 Buffer
[Length
] = UNICODE_NULL
;
930 /* Release PEB lock */
932 DPRINT("CurrentDirectory %S\n", Buffer
);
933 return Length
* sizeof(WCHAR
);
940 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
943 FILE_FS_DEVICE_INFORMATION device_info
;
944 OBJECT_ATTRIBUTES Attr
;
945 IO_STATUS_BLOCK iosb
;
949 HANDLE handle
= NULL
;
952 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
956 RtlAcquirePebLock ();
958 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
960 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
962 RtlReleasePebLock ();
963 return STATUS_OBJECT_NAME_INVALID
;
966 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
968 InitializeObjectAttributes (&Attr
,
970 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
974 Status
= ZwOpenFile (&handle
,
975 SYNCHRONIZE
| FILE_TRAVERSE
,
978 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
979 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
981 if (!NT_SUCCESS(Status
))
983 RtlFreeUnicodeString( &full
);
984 RtlReleasePebLock ();
988 /* don't keep the directory handle open on removable media */
989 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
990 sizeof(device_info
), FileFsDeviceInformation
)) &&
991 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
993 DPRINT1("don't keep the directory handle open on removable media\n");
1000 cd
->Handle
= handle
;
1002 /* append trailing \ if missing */
1003 size
= full
.Length
/ sizeof(WCHAR
);
1005 ptr
+= 4; /* skip \??\ prefix */
1008 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
1009 * So the nullterm is replaced with \
1012 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
1014 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
1015 cd
->DosPath
.Buffer
[size
] = 0;
1016 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
1018 RtlFreeUnicodeString( &full
);
1019 RtlReleasePebLock();
1021 return STATUS_SUCCESS
;
1025 /******************************************************************
1028 * Helper for RtlGetFullPathName_U.
1029 * Get rid of . and .. components in the path.
1031 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
1035 /* convert every / into a \ */
1036 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
1038 /* collapse duplicate backslashes */
1039 next
= path
+ max( 1, mark
);
1040 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
1050 case '\\': /* .\ component */
1052 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1054 case 0: /* final . */
1055 if (p
> path
+ mark
) p
--;
1059 if (p
[2] == '\\') /* ..\ component */
1062 if (p
> path
+ mark
)
1065 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1067 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1070 else if (!p
[2]) /* final .. */
1072 if (p
> path
+ mark
)
1075 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1076 if (p
> path
+ mark
) p
--;
1084 /* skip to the next component */
1085 while (*p
&& *p
!= '\\') p
++;
1088 /* remove last dot in previous dir name */
1089 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
1094 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
1095 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
1101 /******************************************************************
1104 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
1106 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
1109 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
1110 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1111 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
1112 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1117 /******************************************************************
1118 * get_full_path_helper
1120 * Helper for RtlGetFullPathName_U
1121 * Note: name and buffer are allowed to point to the same memory spot
1123 static ULONG
get_full_path_helper(
1128 ULONG reqsize
= 0, mark
= 0, dep
= 0, deplen
;
1129 LPWSTR ins_str
= NULL
;
1131 const UNICODE_STRING
* cd
;
1134 /* return error if name only consists of spaces */
1135 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
1136 if (!*ptr
) return 0;
1138 RtlAcquirePebLock();
1140 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
1141 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
1143 switch (RtlDetermineDosPathNameType_U(name
))
1145 case RtlPathTypeUncAbsolute
: /* \\foo */
1146 ptr
= skip_unc_prefix( name
);
1147 mark
= (ptr
- name
);
1150 case RtlPathTypeLocalDevice
: /* \\.\foo */
1154 case RtlPathTypeDriveAbsolute
: /* c:\foo */
1155 reqsize
= sizeof(WCHAR
);
1156 tmp
[0] = towupper(name
[0]);
1162 case RtlPathTypeDriveRelative
: /* c:foo */
1164 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
1166 UNICODE_STRING var
, val
;
1172 var
.Length
= 3 * sizeof(WCHAR
);
1173 var
.MaximumLength
= 4 * sizeof(WCHAR
);
1176 val
.MaximumLength
= size
;
1177 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
1178 if (val
.Buffer
== NULL
)
1184 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
1186 case STATUS_SUCCESS
:
1187 /* FIXME: Win2k seems to check that the environment variable actually points
1188 * to an existing directory. If not, root of the drive is used
1189 * (this seems also to be the only spot in RtlGetFullPathName that the
1190 * existence of a part of a path is checked)
1193 case STATUS_BUFFER_TOO_SMALL
:
1194 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
1195 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
1196 ins_str
= val
.Buffer
;
1198 case STATUS_VARIABLE_NOT_FOUND
:
1199 reqsize
= 3 * sizeof(WCHAR
);
1204 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1207 DPRINT1("Unsupported status code\n");
1208 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1216 case RtlPathTypeRelative
: /* foo */
1217 reqsize
= cd
->Length
;
1218 ins_str
= cd
->Buffer
;
1219 if (cd
->Buffer
[1] != ':')
1221 ptr
= skip_unc_prefix( cd
->Buffer
);
1222 mark
= ptr
- cd
->Buffer
;
1227 case RtlPathTypeRooted
: /* \xxx */
1229 if (name
[0] == '/') /* may be a Unix path */
1231 const WCHAR
*ptr
= name
;
1232 int drive
= find_drive_root( &ptr
);
1235 reqsize
= 3 * sizeof(WCHAR
);
1236 tmp
[0] = 'A' + drive
;
1246 if (cd
->Buffer
[1] == ':')
1248 reqsize
= 2 * sizeof(WCHAR
);
1249 tmp
[0] = cd
->Buffer
[0];
1256 ptr
= skip_unc_prefix( cd
->Buffer
);
1257 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
1258 mark
= reqsize
/ sizeof(WCHAR
);
1259 ins_str
= cd
->Buffer
;
1263 case RtlPathTypeRootLocalDevice
: /* \\. */
1264 reqsize
= 4 * sizeof(WCHAR
);
1274 case RtlPathTypeUnknown
:
1278 /* enough space ? */
1279 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
1280 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
1282 /* not enough space, return need size (including terminating '\0') */
1283 reqsize
+= deplen
+ sizeof(WCHAR
);
1287 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
1288 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
1291 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
1292 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
1294 collapse_path( buffer
, mark
);
1295 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
1298 RtlReleasePebLock();
1303 /******************************************************************
1304 * RtlGetFullPathName_U (NTDLL.@)
1306 * Returns the number of bytes written to buffer (not including the
1307 * terminating NULL) if the function succeeds, or the required number of bytes
1308 * (including the terminating NULL) if the buffer is too small.
1310 * file_part will point to the filename part inside buffer (except if we use
1311 * DOS device name, in which case file_in_buf is NULL)
1315 ULONG NTAPI
RtlGetFullPathName_U(
1325 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
1327 if (!name
|| !*name
) return 0;
1329 if (file_part
) *file_part
= NULL
;
1331 /* check for DOS device name */
1332 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
1335 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
1336 DWORD sz
= LOWORD(dosdev
); /* in bytes */
1338 if (8 + sz
+ 2 > size
) return sz
+ 10;
1339 wcscpy(buffer
, DeviceRootW
);
1340 memmove(buffer
+ 4, name
+ offset
, sz
);
1341 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
1342 /* file_part isn't set in this case */
1346 reqsize
= get_full_path_helper(name
, buffer
, size
);
1347 if (!reqsize
) return 0;
1350 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
1351 if (tmp
== NULL
) return 0;
1352 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
1353 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
1355 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1356 return reqsize
+ sizeof(WCHAR
);
1358 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
1359 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1362 /* find file part */
1363 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
1373 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1374 OUT PUNICODE_STRING NtName
,
1375 OUT PCWSTR
*PartName
,
1376 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1378 /* Call the internal function */
1379 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1391 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1392 OUT PUNICODE_STRING NtName
,
1393 OUT PCWSTR
*PartName
,
1394 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1396 /* Call the internal function */
1397 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1409 RtlDosPathNameToRelativeNtPathName_U(IN PWSTR DosName
,
1410 OUT PUNICODE_STRING NtName
,
1411 OUT PCWSTR
*PartName
,
1412 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1414 /* Call the internal function */
1415 ASSERT(RelativeName
);
1416 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1428 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PWSTR DosName
,
1429 OUT PUNICODE_STRING NtName
,
1430 OUT PCWSTR
*PartName
,
1431 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1433 /* Call the internal function */
1434 ASSERT(RelativeName
);
1435 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1446 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1448 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1449 return STATUS_NOT_IMPLEMENTED
;
1457 RtlDosSearchPath_U(IN PCWSTR Path
,
1459 IN PCWSTR Extension
,
1462 OUT PWSTR
*PartName
)
1465 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1466 UNICODE_STRING TempString
;
1467 PWCHAR NewBuffer
, BufferStart
;
1470 /* Validate the input */
1471 if (!(Path
) || !(FileName
)) return 0;
1473 /* Check if this is an absolute path */
1474 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1476 /* Check if the file exists */
1477 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1479 /* Get the full name, which does the DOS lookup */
1480 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1483 /* Doesn't exist, so fail */
1487 /* Scan the filename */
1491 /* Looking for an extension */
1494 /* No extension string needed -- it's part of the filename */
1499 /* Next character */
1503 /* Do we have an extension? */
1506 /* Nope, don't worry about one */
1507 ExtensionLength
= 0;
1511 /* Build a temporary string to get the extension length */
1512 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1513 if (!NT_SUCCESS(Status
)) return 0;
1514 ExtensionLength
= TempString
.Length
;
1517 /* Build a temporary string to get the path length */
1518 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1519 if (!NT_SUCCESS(Status
)) return 0;
1520 PathLength
= TempString
.Length
;
1522 /* Build a temporary string to get the filename length */
1523 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1524 if (!NT_SUCCESS(Status
)) return 0;
1525 FileNameLength
= TempString
.Length
;
1527 /* Allocate the buffer for the new string name */
1528 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1537 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1542 /* Final loop to build the path */
1545 /* Check if we have a valid character */
1546 BufferStart
= NewBuffer
;
1549 /* Loop as long as there's no semicolon */
1550 while (*Path
!= ';')
1552 /* Copy the next character */
1553 *BufferStart
++ = *Path
++;
1557 /* We found a semi-colon, to stop path processing on this loop */
1558 if (*Path
== ';') ++Path
;
1561 /* Add a terminating slash if needed */
1562 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != '\\'))
1564 *BufferStart
++ = '\\';
1567 /* Bail out if we reached the end */
1568 if (!*Path
) Path
= NULL
;
1570 /* Copy the file name and check if an extension is needed */
1571 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1572 if (ExtensionLength
)
1574 /* Copy the extension too */
1575 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1577 ExtensionLength
+ sizeof(WCHAR
));
1581 /* Just NULL-terminate */
1582 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1585 /* Now, does this file exist? */
1586 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1588 /* Call the full-path API to get the length */
1589 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1593 /* If we got here, path doesn't exist, so fail the call */
1598 /* Free the allocation and return the length */
1599 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1608 RtlDoesFileExists_U(IN PCWSTR FileName
)
1610 /* Call the new function */
1611 return RtlDoesFileExists_UEx(FileName
, TRUE
);