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 PRTLP_CURDIR_REF RtlpCurDirRef
;
44 /* PRIVATE FUNCTIONS **********************************************************/
48 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
50 UNICODE_STRING PathCopy
;
52 USHORT PathChars
, ColonCount
= 0;
53 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
56 /* Validate the input */
57 if (!PathString
) return 0;
59 /* Check what type of path this is */
60 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
62 /* Fail for UNC or unknown paths */
63 case RtlPathTypeUnknown
:
64 case RtlPathTypeUncAbsolute
:
67 /* Make special check for the CON device */
68 case RtlPathTypeLocalDevice
:
69 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
71 /* This should return 0x80006 */
72 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
80 /* Make a copy of the string */
81 PathCopy
= *PathString
;
82 OriginalLength
= PathString
->Length
;
84 /* Return if there's no characters */
85 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
86 if (!PathChars
) return 0;
88 /* Check for drive path and truncate */
89 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
91 /* Fixup the lengths */
92 PathCopy
.Length
-= sizeof(WCHAR
);
93 if (!--PathChars
) return 0;
95 /* Remember this for later */
99 /* Check for extension or space, and truncate */
102 /* Stop if we hit something else than a space or period */
103 c
= PathCopy
.Buffer
[PathChars
- 1];
104 if ((c
!= '.') && (c
!= ' ')) break;
106 /* Fixup the lengths */
107 PathCopy
.Length
-= sizeof(WCHAR
);
109 /* Remember this for later */
111 } while (--PathChars
);
113 /* Anything still left? */
116 /* Loop from the end */
117 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
118 End
>= PathCopy
.Buffer
;
121 /* Check if the character is a path or drive separator */
123 if ((c
== '\\') || (c
== '/') || ((c
== ':') && (End
== PathCopy
.Buffer
+ 1)))
125 /* Get the next lower case character */
127 c
= *End
| ' '; // ' ' == ('z' - 'Z')
129 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
130 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
131 ((c
== 'l') || (c
== 'c') || (c
== 'p') || (c
== 'a') || (c
== 'n')))
133 /* Calculate the offset */
134 ReturnOffset
= (PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
;
136 /* Build the final string */
137 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
138 PathCopy
.Buffer
= End
;
140 /* Save new amount of chars in the path */
141 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
152 /* Get the next lower case character and check if it's a DOS device */
153 c
= *PathCopy
.Buffer
| ' '; // ' ' == ('z' - 'Z')
154 if ((c
!= 'l') && (c
!= 'c') && (c
!= 'p') && (c
!= 'a') && (c
!= 'n'))
156 /* Not LPT, COM, PRN, AUX, or NUL */
161 /* Now skip past any extra extension or drive letter characters */
162 Start
= PathCopy
.Buffer
;
163 End
= &Start
[PathChars
];
167 if ((c
== '.') || (c
== ':')) break;
171 /* And then go backwards to get rid of spaces */
172 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == ' ')) --Start
;
174 /* Finally see how many characters are left, and that's our size */
175 PathChars
= Start
- PathCopy
.Buffer
;
176 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
178 /* Check if this is a COM or LPT port, which has a digit after it */
179 if ((PathChars
== 4) &&
180 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != '0')))
182 /* Don't compare the number part, just check for LPT or COM */
183 PathCopy
.Length
-= sizeof(WCHAR
);
184 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
185 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
188 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
189 return MAKELONG(ReturnLength
, ReturnOffset
);
192 else if ((PathChars
== 3) &&
193 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
194 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
195 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
196 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
198 /* Otherwise this was something like AUX, NUL, PRN, or CON */
199 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
200 return MAKELONG(ReturnLength
, ReturnOffset
);
203 /* Otherwise, this is not a valid DOS device */
209 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
214 /* Validate the input */
215 if (!PathString
) return RtlPathTypeUnknown
;
217 Path
= PathString
->Buffer
;
218 Chars
= PathString
->Length
/ sizeof(WCHAR
);
220 /* Return if there are no characters */
221 if (!Chars
) return RtlPathTypeUnknown
;
224 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
225 * actually check for the path length before touching the characters
227 if ((Chars
< 1) || (IS_PATH_SEPARATOR(Path
[0])))
229 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
230 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
231 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
232 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
233 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
237 if ((Chars
< 2) || (!(Path
[0]) || (Path
[1] != L
':'))) return RtlPathTypeRelative
; /* x */
238 if ((Chars
< 3) || (IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveAbsolute
; /* x:\ */
239 return RtlPathTypeDriveRelative
; /* x: */
245 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
247 OUT PBOOLEAN NameInvalid
)
252 /* Allocate a large enough buffer */
253 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
259 /* Copy the filename */
260 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
262 /* And add a dot at the end */
263 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
264 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
266 /* Check if the file exists or not */
267 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
269 /* Get rid of the buffer now */
270 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
274 /* Assume the name is ok, but fail the call */
275 *NameInvalid
= FALSE
;
276 Status
= STATUS_NO_MEMORY
;
279 /* Return the status */
285 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName
,
288 OUT PCWSTR
*ShortName
,
289 OUT PBOOLEAN InvalidName
,
290 OUT RTL_PATH_TYPE
*PathType
)
292 PWCHAR FileNameBuffer
;
293 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
297 /* For now, assume the name is valid */
298 DPRINT("Filename: %wZ\n", FileName
);
299 DPRINT("Size and buffer: %lx %S\n", Size
, Buffer
);
300 if (InvalidName
) *InvalidName
= FALSE
;
302 /* Handle initial path type and failure case */
303 *PathType
= RtlPathTypeUnknown
;
304 if (!(Size
) || !(Buffer
) || !(FileName
) ||
305 !(FileName
->Length
) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
307 /* Break filename into component parts */
308 FileNameBuffer
= FileName
->Buffer
;
309 FileNameLength
= FileName
->Length
;
310 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
312 /* Kill trailing spaces */
313 c
= FileNameBuffer
[FileNameChars
- 1];
314 while ((FileNameLength
) && (c
== L
' '))
316 /* Keep going, ignoring the spaces */
317 FileNameLength
-= sizeof(WCHAR
);
318 if (FileNameLength
) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
321 /* Check if anything is left */
322 if (!FileNameLength
) return 0;
324 /* Check if this is a DOS name */
325 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
326 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
329 /* Zero out the short name */
330 if (ShortName
) *ShortName
= NULL
;
332 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
333 DosLengthOffset
= DosLength
>> 16;
334 DosLength
= DosLength
& 0xFFFF;
336 /* Do we have a DOS length, and does the caller want validity? */
337 if ((InvalidName
) && (DosLengthOffset
))
340 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
342 /* If the check failed, or the name is invalid, fail here */
343 if (!NT_SUCCESS(Status
)) return 0;
344 if (*InvalidName
) return 0;
347 /* Add the size of the device root and check if it fits in the size */
348 FullLength
= DosLength
+ DeviceRootString
.Length
;
349 if (FullLength
< Size
)
351 /* Add the device string */
352 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
354 /* Now add the DOS device name */
355 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
356 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
360 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
364 /* Otherwise, there's no space, so return the buffer size needed */
365 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
366 return FullLength
+ sizeof(UNICODE_NULL
);
369 /* This should work well enough for our current needs */
370 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
371 DPRINT("Path type: %lx\n", *PathType
);
373 /* This is disgusting... but avoids re-writing everything */
374 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer
, Size
, Buffer
);
375 return RtlGetFullPathName_U(FileNameBuffer
, Size
, Buffer
, (PWSTR
*)ShortName
);
380 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
381 OUT PUNICODE_STRING NtPath
,
382 OUT PCWSTR
*PartName
,
383 OUT PRTL_RELATIVE_NAME_U RelativeName
)
388 /* Validate the input */
389 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
391 /* Validate the DOS length */
392 DosLength
= DosPath
->Length
;
393 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
395 /* Make space for the new path */
396 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
398 DosLength
+ sizeof(UNICODE_NULL
));
399 if (!NewBuffer
) return STATUS_NO_MEMORY
;
401 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
402 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
403 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
404 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
405 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
406 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
408 /* Did the caller send a relative name? */
411 /* Zero initialize it */
412 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
413 RelativeName
->ContainingDirectory
= NULL
;
414 RelativeName
->CurDirRef
= 0;
417 /* Did the caller request a partial name? */
420 /* Loop from the back until we find a path separator */
421 p
= &NewBuffer
[(DosLength
- 1) / sizeof (WCHAR
)];
422 while (p
> NewBuffer
) if (*p
-- == '\\') break;
427 /* Move past it -- anything left? */
431 /* The path ends with a path separator, no part name */
436 /* What follows the path separator is the part name */
442 /* Build the final NT path string */
443 NtPath
->Length
= (USHORT
)DosLength
;
444 NtPath
->Buffer
= NewBuffer
;
445 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
446 return STATUS_SUCCESS
;
451 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
452 IN PCUNICODE_STRING DosName
,
453 OUT PUNICODE_STRING NtName
,
454 OUT PCWSTR
*PartName
,
455 OUT PRTL_RELATIVE_NAME_U RelativeName
)
457 WCHAR BigBuffer
[MAX_PATH
+ 1];
458 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
459 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
460 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
462 RTL_PATH_TYPE InputPathType
, BufferPathType
;
465 PCURDIR CurrentDirectory
;
467 /* Assume MAX_PATH for now */
468 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
469 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
470 MaxLength
= sizeof(BigBuffer
);
472 /* Validate the input */
473 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
475 /* Capture input string */
476 CapturedDosName
= *DosName
;
478 /* Check for \\?\\ form */
479 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
480 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
481 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
482 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
483 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
485 /* Quick path won't be used */
488 /* Use the static buffer */
490 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
492 /* Allocate a buffer to hold the path */
493 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
494 DPRINT("Length: %lx\n", MaxLength
);
495 if (!NewBuffer
) return STATUS_NO_MEMORY
;
499 /* Use the optimized path after acquiring the lock */
504 /* Lock the PEB and check if the quick path can be used */
508 /* Some simple fixups will get us the correct path */
509 DPRINT("Quick path\n");
510 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
515 /* Release the lock, we're done here */
520 /* Call the main function to get the full path name and length */
521 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
522 MAX_PATH
* sizeof(WCHAR
),
527 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
529 /* Invalid name, fail */
530 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
531 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
533 return STATUS_OBJECT_NAME_INVALID
;
536 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
537 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
538 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
541 /* Check where it really is */
542 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
543 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
544 switch (BufferPathType
)
546 /* It's actually a UNC path in \??\UNC\ */
547 case RtlPathTypeUncAbsolute
:
548 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
549 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
553 case RtlPathTypeLocalDevice
:
554 /* We made a good guess, go with it but skip the \??\ */
558 case RtlPathTypeDriveAbsolute
:
559 case RtlPathTypeDriveRelative
:
560 case RtlPathTypeRooted
:
561 case RtlPathTypeRelative
:
562 /* Our guess was good, roll with it */
565 /* Nothing else is expected */
571 /* Now copy the prefix and the buffer */
572 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
573 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
575 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
577 /* Compute the length */
578 Length
= PathLength
- PrefixCut
* sizeof(WCHAR
) + PrefixLength
;
579 LengthChars
= Length
/ sizeof(WCHAR
);
581 /* Setup the actual NT path string and terminate it */
582 NtName
->Buffer
= NewBuffer
;
583 NtName
->Length
= (USHORT
)Length
;
584 NtName
->MaximumLength
= (USHORT
)MaxLength
;
585 NewBuffer
[LengthChars
] = UNICODE_NULL
;
586 DPRINT("new buffer: %S\n", NewBuffer
);
587 DPRINT("NT Name: %wZ\n", NtName
);
589 /* Check if a partial name was requested */
590 if ((PartName
) && (*PartName
))
592 /* Convert to Unicode */
593 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
594 if (NT_SUCCESS(Status
))
596 /* Set the partial name */
597 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
602 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
608 /* Check if a relative name was asked for */
611 /* Setup the structure */
612 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
613 RelativeName
->ContainingDirectory
= NULL
;
614 RelativeName
->CurDirRef
= NULL
;
616 /* Check if the input path itself was relative */
617 if (InputPathType
== RtlPathTypeRelative
)
619 /* Get current directory */
620 CurrentDirectory
= (PCURDIR
)&(NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
);
621 if (CurrentDirectory
->Handle
)
623 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
624 if (!NT_SUCCESS(Status
))
626 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
631 /* If current directory is bigger than full path, there's no way */
632 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
638 /* File is in current directory */
639 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
641 /* Make relative name string */
642 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ FullPath
.Length
- PrefixCut
);
643 RelativeName
->RelativeName
.Length
= PathLength
- FullPath
.Length
;
644 /* If relative name starts with \, skip it */
645 if (RelativeName
->RelativeName
.Buffer
[0] == L
'\\')
647 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)RelativeName
->RelativeName
.Buffer
+ sizeof(WCHAR
));
648 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
650 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
651 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
655 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
659 /* Give back current directory data & reference counter */
660 RelativeName
->CurDirRef
= RtlpCurDirRef
;
661 if (RelativeName
->CurDirRef
)
663 /* FIXME: Increment reference count */
666 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
674 return STATUS_SUCCESS
;
679 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
681 OUT PUNICODE_STRING NtName
,
682 OUT PCWSTR
*PartName
,
683 OUT PRTL_RELATIVE_NAME_U RelativeName
)
686 UNICODE_STRING NameString
;
688 /* Create the unicode name */
689 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
690 if (NT_SUCCESS(Status
))
692 /* Call the unicode function */
693 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
706 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
707 OUT PUNICODE_STRING NtName
,
708 OUT PCWSTR
*PartName
,
709 OUT PRTL_RELATIVE_NAME_U RelativeName
)
711 /* Call the internal function */
712 ASSERT(RelativeName
);
713 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
722 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
723 IN BOOLEAN SucceedIfBusy
)
726 RTL_RELATIVE_NAME_U RelativeName
;
727 UNICODE_STRING NtPathName
;
729 OBJECT_ATTRIBUTES ObjectAttributes
;
731 FILE_BASIC_INFORMATION BasicInformation
;
733 /* Validate the input */
734 if (!FileName
) return FALSE
;
736 /* Get the NT Path */
737 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
741 if (!Result
) return FALSE
;
743 /* Save the buffer */
744 Buffer
= NtPathName
.Buffer
;
746 /* Check if we have a relative name */
747 if (RelativeName
.RelativeName
.Length
)
750 NtPathName
= RelativeName
.RelativeName
;
754 /* Otherwise ignore it */
755 RelativeName
.ContainingDirectory
= NULL
;
758 /* Initialize the object attributes */
759 InitializeObjectAttributes(&ObjectAttributes
,
761 OBJ_CASE_INSENSITIVE
,
762 RelativeName
.ContainingDirectory
,
765 /* Query the attributes and free the buffer now */
766 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
767 RtlReleaseRelativeName(&RelativeName
);
768 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
770 /* Check if we failed */
771 if (!NT_SUCCESS(Status
))
773 /* Check if we failed because the file is in use */
774 if ((Status
== STATUS_SHARING_VIOLATION
) ||
775 (Status
== STATUS_ACCESS_DENIED
))
777 /* Check if the caller wants this to be considered OK */
778 Result
= SucceedIfBusy
? TRUE
: FALSE
;
782 /* A failure because the file didn't exist */
788 /* The file exists */
792 /* Return the result */
798 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
800 /* Call the updated API */
801 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
806 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
807 IN BOOLEAN SucceedIfBusy
)
809 UNICODE_STRING NameString
;
811 /* Create the unicode name*/
812 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
814 /* Call the unicode function */
815 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
822 /* PUBLIC FUNCTIONS ***********************************************************/
829 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
831 /* Check if a directory reference was grabbed */
832 if (RelativeName
->CurDirRef
)
834 /* FIXME: Not yet supported */
836 RelativeName
->CurDirRef
= NULL
;
845 RtlGetLongestNtPathLength(VOID
)
848 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
849 * a mapped network drive), which is accessed through the DOS Global?? path.
850 * This is, and has always been equal to, 269 characters, except in Wine
851 * which claims this is 277. Go figure.
853 return (MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
+ sizeof(ANSI_NULL
));
861 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
863 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
865 /* Validate the input */
866 if (!Path
) return RtlPathTypeUnknown
;
868 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
869 if (IS_PATH_SEPARATOR(Path
[0]))
871 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
872 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
873 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
874 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
875 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
879 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
880 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
881 return RtlPathTypeDriveRelative
; /* x: */
890 RtlIsDosDeviceName_U(IN PCWSTR Path
)
892 UNICODE_STRING PathString
;
895 /* Build the string */
896 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
897 if (!NT_SUCCESS(Status
)) return 0;
900 * Returns 0 if name is not valid DOS device name, or DWORD with
901 * offset in bytes to DOS device name from beginning of buffer in high word
902 * and size in bytes of DOS device name in low word
904 return RtlIsDosDeviceName_Ustr(&PathString
);
912 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
918 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
920 /* Lock the PEB to get the current directory */
922 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
924 /* Get the buffer and character length */
925 CurDirName
= CurDir
->DosPath
.Buffer
;
926 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
927 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
930 * DosPath.Buffer should always have a trailing slash. There is an assert
931 * below which checks for this.
933 * This function either returns x:\ for a root (keeping the original buffer)
934 * or it returns x:\path\foo for a directory (replacing the trailing slash
937 Bytes
= Length
* sizeof(WCHAR
);
938 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
940 /* Check if caller does not have enough space */
941 if (MaximumLength
<= Bytes
)
943 /* Call has no space for it, fail, add the trailing slash */
945 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
950 /* Check if caller does not have enough space */
951 if (MaximumLength
< Bytes
)
953 /* Call has no space for it, fail */
959 /* Copy the buffer since we seem to have space */
960 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
962 /* The buffer should end with a path separator */
963 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
965 /* Again check for our two cases (drive root vs path) */
966 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
968 /* Replace the trailing slash with a null */
969 Buffer
[Length
- 1] = UNICODE_NULL
;
974 /* Append the null char since there's no trailing slash */
975 Buffer
[Length
] = UNICODE_NULL
;
978 /* Release PEB lock */
980 DPRINT("CurrentDirectory %S\n", Buffer
);
981 return Length
* sizeof(WCHAR
);
988 RtlSetCurrentDirectory_U(PUNICODE_STRING dir
)
991 FILE_FS_DEVICE_INFORMATION device_info
;
992 OBJECT_ATTRIBUTES Attr
;
993 IO_STATUS_BLOCK iosb
;
997 HANDLE handle
= NULL
;
1000 DPRINT("RtlSetCurrentDirectory %wZ\n", dir
);
1004 RtlAcquirePebLock ();
1006 cd
= (PCURDIR
)&NtCurrentPeb ()->ProcessParameters
->CurrentDirectory
.DosPath
;
1008 if (!RtlDosPathNameToNtPathName_U (dir
->Buffer
, &full
, 0, 0))
1010 RtlReleasePebLock ();
1011 return STATUS_OBJECT_NAME_INVALID
;
1014 DPRINT("RtlSetCurrentDirectory: full %wZ\n",&full
);
1016 InitializeObjectAttributes (&Attr
,
1018 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1022 Status
= ZwOpenFile (&handle
,
1023 SYNCHRONIZE
| FILE_TRAVERSE
,
1026 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1027 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1029 if (!NT_SUCCESS(Status
))
1031 RtlFreeUnicodeString( &full
);
1032 RtlReleasePebLock ();
1036 /* don't keep the directory handle open on removable media */
1037 if (NT_SUCCESS(ZwQueryVolumeInformationFile( handle
, &iosb
, &device_info
,
1038 sizeof(device_info
), FileFsDeviceInformation
)) &&
1039 (device_info
.Characteristics
& FILE_REMOVABLE_MEDIA
))
1041 DPRINT1("don't keep the directory handle open on removable media\n");
1047 ZwClose(cd
->Handle
);
1048 cd
->Handle
= handle
;
1050 /* append trailing \ if missing */
1051 size
= full
.Length
/ sizeof(WCHAR
);
1053 ptr
+= 4; /* skip \??\ prefix */
1056 /* This is ok because RtlDosPathNameToNtPathName_U returns a nullterminated string.
1057 * So the nullterm is replaced with \
1060 if (size
&& ptr
[size
- 1] != '\\') ptr
[size
++] = '\\';
1062 memcpy( cd
->DosPath
.Buffer
, ptr
, size
* sizeof(WCHAR
));
1063 cd
->DosPath
.Buffer
[size
] = 0;
1064 cd
->DosPath
.Length
= size
* sizeof(WCHAR
);
1066 RtlFreeUnicodeString( &full
);
1067 RtlReleasePebLock();
1069 return STATUS_SUCCESS
;
1073 /******************************************************************
1076 * Helper for RtlGetFullPathName_U.
1077 * Get rid of . and .. components in the path.
1079 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
1083 /* convert every / into a \ */
1084 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
1086 /* collapse duplicate backslashes */
1087 next
= path
+ max( 1, mark
);
1088 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
1098 case '\\': /* .\ component */
1100 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1102 case 0: /* final . */
1103 if (p
> path
+ mark
) p
--;
1107 if (p
[2] == '\\') /* ..\ component */
1110 if (p
> path
+ mark
)
1113 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1115 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1118 else if (!p
[2]) /* final .. */
1120 if (p
> path
+ mark
)
1123 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1124 if (p
> path
+ mark
) p
--;
1132 /* skip to the next component */
1133 while (*p
&& *p
!= '\\') p
++;
1136 /* remove last dot in previous dir name */
1137 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
1142 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
1143 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
1149 /******************************************************************
1152 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
1154 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
1157 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
1158 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1159 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
1160 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1165 /******************************************************************
1166 * get_full_path_helper
1168 * Helper for RtlGetFullPathName_U
1169 * Note: name and buffer are allowed to point to the same memory spot
1171 static ULONG
get_full_path_helper(
1176 SIZE_T reqsize
= 0, mark
= 0, dep
= 0, deplen
;
1177 LPWSTR ins_str
= NULL
;
1179 const UNICODE_STRING
* cd
;
1182 /* return error if name only consists of spaces */
1183 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
1184 if (!*ptr
) return 0;
1186 RtlAcquirePebLock();
1188 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
1189 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
1191 switch (RtlDetermineDosPathNameType_U(name
))
1193 case RtlPathTypeUncAbsolute
: /* \\foo */
1194 ptr
= skip_unc_prefix( name
);
1195 mark
= (ptr
- name
);
1198 case RtlPathTypeLocalDevice
: /* \\.\foo */
1202 case RtlPathTypeDriveAbsolute
: /* c:\foo */
1203 reqsize
= sizeof(WCHAR
);
1204 tmp
[0] = towupper(name
[0]);
1210 case RtlPathTypeDriveRelative
: /* c:foo */
1212 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
1214 UNICODE_STRING var
, val
;
1220 var
.Length
= 3 * sizeof(WCHAR
);
1221 var
.MaximumLength
= 4 * sizeof(WCHAR
);
1224 val
.MaximumLength
= (USHORT
)size
;
1225 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
1226 if (val
.Buffer
== NULL
)
1232 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
1234 case STATUS_SUCCESS
:
1235 /* FIXME: Win2k seems to check that the environment variable actually points
1236 * to an existing directory. If not, root of the drive is used
1237 * (this seems also to be the only spot in RtlGetFullPathName that the
1238 * existence of a part of a path is checked)
1241 case STATUS_BUFFER_TOO_SMALL
:
1242 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
1243 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
1244 ins_str
= val
.Buffer
;
1246 case STATUS_VARIABLE_NOT_FOUND
:
1247 reqsize
= 3 * sizeof(WCHAR
);
1252 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1255 DPRINT1("Unsupported status code\n");
1256 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1264 case RtlPathTypeRelative
: /* foo */
1265 reqsize
= cd
->Length
;
1266 ins_str
= cd
->Buffer
;
1267 if (cd
->Buffer
[1] != ':')
1269 ptr
= skip_unc_prefix( cd
->Buffer
);
1270 mark
= ptr
- cd
->Buffer
;
1275 case RtlPathTypeRooted
: /* \xxx */
1277 if (name
[0] == '/') /* may be a Unix path */
1279 const WCHAR
*ptr
= name
;
1280 int drive
= find_drive_root( &ptr
);
1283 reqsize
= 3 * sizeof(WCHAR
);
1284 tmp
[0] = 'A' + drive
;
1294 if (cd
->Buffer
[1] == ':')
1296 reqsize
= 2 * sizeof(WCHAR
);
1297 tmp
[0] = cd
->Buffer
[0];
1304 ptr
= skip_unc_prefix( cd
->Buffer
);
1305 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
1306 mark
= reqsize
/ sizeof(WCHAR
);
1307 ins_str
= cd
->Buffer
;
1311 case RtlPathTypeRootLocalDevice
: /* \\. */
1312 reqsize
= 4 * sizeof(WCHAR
);
1322 case RtlPathTypeUnknown
:
1326 /* enough space ? */
1327 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
1328 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
1330 /* not enough space, return need size (including terminating '\0') */
1331 reqsize
+= deplen
+ sizeof(WCHAR
);
1335 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
1336 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
1339 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
1340 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
1342 collapse_path( buffer
, (ULONG
)mark
);
1343 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
1346 RtlReleasePebLock();
1347 return (ULONG
)reqsize
;
1351 /******************************************************************
1352 * RtlGetFullPathName_U (NTDLL.@)
1354 * Returns the number of bytes written to buffer (not including the
1355 * terminating NULL) if the function succeeds, or the required number of bytes
1356 * (including the terminating NULL) if the buffer is too small.
1358 * file_part will point to the filename part inside buffer (except if we use
1359 * DOS device name, in which case file_in_buf is NULL)
1363 ULONG NTAPI
RtlGetFullPathName_U(
1373 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
1375 if (!name
|| !*name
) return 0;
1377 if (file_part
) *file_part
= NULL
;
1379 /* check for DOS device name */
1380 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
1383 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
1384 DWORD sz
= LOWORD(dosdev
); /* in bytes */
1386 if (8 + sz
+ 2 > size
) return sz
+ 10;
1387 wcscpy(buffer
, DeviceRootW
);
1388 memmove(buffer
+ 4, name
+ offset
, sz
);
1389 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
1390 /* file_part isn't set in this case */
1394 reqsize
= get_full_path_helper(name
, buffer
, size
);
1395 if (!reqsize
) return 0;
1398 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
1399 if (tmp
== NULL
) return 0;
1400 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
1401 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
1403 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1404 return reqsize
+ sizeof(WCHAR
);
1406 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
1407 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1410 /* find file part */
1411 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
1421 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1422 OUT PUNICODE_STRING NtName
,
1423 OUT PCWSTR
*PartName
,
1424 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1426 /* Call the internal function */
1427 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1439 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1440 OUT PUNICODE_STRING NtName
,
1441 OUT PCWSTR
*PartName
,
1442 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1444 /* Call the internal function */
1445 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1457 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1458 OUT PUNICODE_STRING NtName
,
1459 OUT PCWSTR
*PartName
,
1460 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1462 /* Call the internal function */
1463 ASSERT(RelativeName
);
1464 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1476 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1477 OUT PUNICODE_STRING NtName
,
1478 OUT PCWSTR
*PartName
,
1479 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1481 /* Call the internal function */
1482 ASSERT(RelativeName
);
1483 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1494 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1496 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1497 return STATUS_NOT_IMPLEMENTED
;
1505 RtlDosSearchPath_U(IN PCWSTR Path
,
1507 IN PCWSTR Extension
,
1510 OUT PWSTR
*PartName
)
1513 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1514 UNICODE_STRING TempString
;
1515 PWCHAR NewBuffer
, BufferStart
;
1518 /* Validate the input */
1519 if (!(Path
) || !(FileName
)) return 0;
1521 /* Check if this is an absolute path */
1522 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1524 /* Check if the file exists */
1525 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1527 /* Get the full name, which does the DOS lookup */
1528 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1531 /* Doesn't exist, so fail */
1535 /* Scan the filename */
1539 /* Looking for an extension */
1542 /* No extension string needed -- it's part of the filename */
1547 /* Next character */
1551 /* Do we have an extension? */
1554 /* Nope, don't worry about one */
1555 ExtensionLength
= 0;
1559 /* Build a temporary string to get the extension length */
1560 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1561 if (!NT_SUCCESS(Status
)) return 0;
1562 ExtensionLength
= TempString
.Length
;
1565 /* Build a temporary string to get the path length */
1566 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1567 if (!NT_SUCCESS(Status
)) return 0;
1568 PathLength
= TempString
.Length
;
1570 /* Build a temporary string to get the filename length */
1571 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1572 if (!NT_SUCCESS(Status
)) return 0;
1573 FileNameLength
= TempString
.Length
;
1575 /* Allocate the buffer for the new string name */
1576 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1585 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1590 /* Final loop to build the path */
1593 /* Check if we have a valid character */
1594 BufferStart
= NewBuffer
;
1597 /* Loop as long as there's no semicolon */
1598 while (*Path
!= ';')
1600 /* Copy the next character */
1601 *BufferStart
++ = *Path
++;
1605 /* We found a semi-colon, to stop path processing on this loop */
1606 if (*Path
== ';') ++Path
;
1609 /* Add a terminating slash if needed */
1610 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != '\\'))
1612 *BufferStart
++ = '\\';
1615 /* Bail out if we reached the end */
1616 if (!*Path
) Path
= NULL
;
1618 /* Copy the file name and check if an extension is needed */
1619 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1620 if (ExtensionLength
)
1622 /* Copy the extension too */
1623 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1625 ExtensionLength
+ sizeof(WCHAR
));
1629 /* Just NULL-terminate */
1630 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1633 /* Now, does this file exist? */
1634 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1636 /* Call the full-path API to get the length */
1637 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1641 /* If we got here, path doesn't exist, so fail the call */
1646 /* Free the allocation and return the length */
1647 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1656 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
1657 IN PUNICODE_STRING StaticString
,
1658 IN PUNICODE_STRING DynamicString
,
1659 IN PUNICODE_STRING
*StringUsed
,
1660 IN PSIZE_T FilePartSize
,
1661 OUT PBOOLEAN NameInvalid
,
1662 OUT RTL_PATH_TYPE
* PathType
,
1663 OUT PULONG LengthNeeded
)
1666 PWCHAR StaticBuffer
;
1669 USHORT StaticLength
;
1670 UNICODE_STRING TempDynamicString
;
1672 /* Initialize all our locals */
1674 StaticBuffer
= NULL
;
1675 TempDynamicString
.Buffer
= NULL
;
1677 /* Initialize the input parameters */
1678 if (StringUsed
) *StringUsed
= NULL
;
1679 if (LengthNeeded
) *LengthNeeded
= 0;
1680 if (FilePartSize
) *FilePartSize
= 0;
1682 /* Check for invalid parameters */
1683 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
1685 return STATUS_INVALID_PARAMETER
;
1688 /* Check if we did not get an input string */
1692 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
1693 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
1694 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
1698 /* Use the one we received */
1699 StaticBuffer
= StaticString
->Buffer
;
1700 StaticLength
= StaticString
->MaximumLength
;
1703 /* Call the lower-level function */
1704 Length
= RtlGetFullPathName_Ustr(FileName
,
1710 DPRINT("Length: %d StaticBuffer: %S\n", Length
, StaticBuffer
);
1713 /* Fail if it failed */
1714 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
1717 Status
= STATUS_OBJECT_NAME_INVALID
;
1721 /* Check if it fits inside our static string */
1722 if ((StaticString
) && (Length
< StaticLength
))
1724 /* Set the final length */
1725 StaticString
->Length
= Length
;
1727 /* Set the file part size */
1728 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
1730 /* Return the static string if requested */
1731 if (StringUsed
) *StringUsed
= StaticString
;
1733 /* We are done with success */
1734 Status
= STATUS_SUCCESS
;
1738 /* Did we not have an input dynamic string ?*/
1741 /* Return the length we need */
1742 if (LengthNeeded
) *LengthNeeded
= Length
;
1744 /* And fail such that the caller can try again */
1745 Status
= STATUS_BUFFER_TOO_SMALL
;
1749 /* Check if it fits in our static buffer */
1750 if ((StaticBuffer
) && (Length
< StaticLength
))
1752 /* NULL-terminate it */
1753 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1755 /* Set the settings for the dynamic string the caller sent */
1756 DynamicString
->MaximumLength
= StaticLength
;
1757 DynamicString
->Length
= Length
;
1758 DynamicString
->Buffer
= StaticBuffer
;
1760 /* Set the part size */
1761 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
1763 /* Return the dynamic string if requested */
1764 if (StringUsed
) *StringUsed
= DynamicString
;
1766 /* Do not free the static buffer on exit, and return success */
1767 StaticBuffer
= NULL
;
1768 Status
= STATUS_SUCCESS
;
1772 /* Now try again under the PEB lock */
1773 RtlAcquirePebLock();
1774 Length
= RtlGetFullPathName_Ustr(FileName
,
1783 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
1784 __FUNCTION__
, __LINE__
);
1785 Status
= STATUS_OBJECT_NAME_INVALID
;
1789 /* Check if it fits inside our static string now */
1790 if ((StaticString
) && (Length
< StaticLength
))
1792 /* Set the final length */
1793 StaticString
->Length
= Length
;
1795 /* Set the file part size */
1796 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
1798 /* Return the static string if requested */
1799 if (StringUsed
) *StringUsed
= StaticString
;
1801 /* We are done with success */
1802 Status
= STATUS_SUCCESS
;
1806 /* Check if the path won't even fit in a real string */
1807 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
1809 /* Name is way too long, fail */
1810 Status
= STATUS_NAME_TOO_LONG
;
1814 /* Allocate the string to hold the path name now */
1815 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
1817 if (!TempDynamicString
.Buffer
)
1819 /* Out of memory, fail */
1820 Status
= STATUS_NO_MEMORY
;
1824 /* Add space for a NULL terminator, and now check the full path */
1825 TempDynamicString
.MaximumLength
= Length
+ sizeof(UNICODE_NULL
);
1826 Length
= RtlGetFullPathName_Ustr(FileName
,
1828 TempDynamicString
.Buffer
,
1834 /* Some path error, so fail out */
1835 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
1836 __FUNCTION__
, __LINE__
);
1837 Status
= STATUS_OBJECT_NAME_INVALID
;
1841 /* It should fit in the string we just allocated */
1842 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
1843 if (Length
> TempDynamicString
.MaximumLength
)
1845 /* This is really weird and would mean some kind of race */
1846 Status
= STATUS_INTERNAL_ERROR
;
1850 /* Return the file part size */
1851 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
1853 /* Terminate the whole string now */
1854 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1856 /* Finalize the string and return it to the user */
1857 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
1858 DynamicString
->Length
= Length
;
1859 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
1860 if (StringUsed
) *StringUsed
= DynamicString
;
1862 /* Return success and make sure we don't free the buffer on exit */
1863 TempDynamicString
.Buffer
= NULL
;
1864 Status
= STATUS_SUCCESS
;
1867 /* Release the PEB lock */
1868 RtlReleasePebLock();
1871 /* Free any buffers we should be freeing */
1872 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
1873 if ((StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
1875 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
1877 if (TempDynamicString
.Buffer
)
1879 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
1882 /* Print out any unusual errors */
1883 if ((NT_ERROR(Status
)) &&
1884 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
1886 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
1887 __FUNCTION__
, FileName
, Status
);
1890 /* Return, we're all done */
1899 RtlDosSearchPath_Ustr(IN ULONG Flags
,
1900 IN PUNICODE_STRING PathString
,
1901 IN PUNICODE_STRING FileNameString
,
1902 IN PUNICODE_STRING ExtensionString
,
1903 IN PUNICODE_STRING CallerBuffer
,
1904 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
1905 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
1906 OUT PULONG FilePartSize OPTIONAL
,
1907 OUT PULONG LengthNeeded OPTIONAL
)
1909 WCHAR StaticCandidateBuffer
[MAX_PATH
];
1910 UNICODE_STRING StaticCandidateString
;
1912 RTL_PATH_TYPE PathType
;
1913 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
1914 ULONG SegmentSize
, NamePlusExtLength
, PathSize
, MaxPathSize
= 0, WorstCaseLength
, ByteCount
;
1915 USHORT ExtensionLength
= 0;
1916 PUNICODE_STRING FullIsolatedPath
;
1917 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
1918 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
1920 /* Initialize the input string */
1921 RtlInitEmptyUnicodeString(&StaticCandidateString
,
1922 StaticCandidateBuffer
,
1923 sizeof(StaticCandidateBuffer
));
1925 /* Initialize optional arguments */
1926 if (FullNameOut
) *FullNameOut
= NULL
;
1927 if (FilePartSize
) *FilePartSize
= 0;
1930 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
1931 DynamicString
->Buffer
= NULL
;
1934 /* Check for invalid parameters */
1937 !(FileNameString
) ||
1938 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
1941 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
1942 Status
= STATUS_INVALID_PARAMETER
;
1946 /* First check what kind of path this is */
1947 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
1949 /* Check if the caller wants to prevent relative .\ and ..\ paths */
1951 (PathType
== RtlPathTypeRelative
) &&
1952 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
1953 (FileNameString
->Buffer
[0] == L
'.') &&
1954 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
1955 ((FileNameString
->Buffer
[1] == L
'.') &&
1956 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
1957 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
1959 /* Yes, and this path is like that, so make it seem unknown */
1960 PathType
= RtlPathTypeUnknown
;
1963 /* Now check relative vs non-relative paths */
1964 if (PathType
== RtlPathTypeRelative
)
1966 /* Does the caller want SxS? */
1969 /* Apply the SxS magic */
1970 FullIsolatedPath
= NULL
;
1971 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
1980 if (NT_SUCCESS(Status
))
1982 /* We found the SxS path, return it */
1983 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
1986 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
1988 /* Critical SxS error, fail */
1989 DbgPrint("%s: Failing because call to "
1990 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
1999 /* No SxS key found, or not requested, check if there's an extension */
2000 if (ExtensionString
)
2002 /* Save the extension length, and check if there's a file name */
2003 ExtensionLength
= ExtensionString
->Length
;
2004 if (FileNameString
->Length
)
2006 /* Start parsing the file name */
2007 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2008 while (End
> FileNameString
->Buffer
)
2010 /* If we find a path separator, there's no extension */
2011 if (IS_PATH_SEPARATOR(*--End
)) break;
2013 /* Otherwise, did we find an extension dot? */
2016 /* Ignore what the caller sent it, use the filename's */
2017 ExtensionString
= NULL
;
2018 ExtensionLength
= 0;
2025 /* Check if we got a path */
2026 if (PathString
->Length
)
2028 /* Start parsing the path name, looking for path separators */
2029 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2031 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2033 /* This is the size of the path -- handle a trailing slash */
2034 PathSize
= End
- p
- 1;
2035 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2037 /* Check if we found a bigger path than before */
2038 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2040 /* Keep going with the path after this path separator */
2044 /* This is the trailing path, run the same code as above */
2046 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2047 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2049 /* Finally, convert the largest path size into WCHAR */
2050 MaxPathSize
*= sizeof(WCHAR
);
2053 /* Use the extension, the file name, and the largest path as the size */
2054 WorstCaseLength
= ExtensionLength
+
2055 FileNameString
->Length
+
2057 sizeof(UNICODE_NULL
);
2058 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2060 /* It has to fit in a registry string, if not, fail here */
2061 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2062 "worst case file name length is %Iu bytes\n",
2065 Status
= STATUS_NAME_TOO_LONG
;
2069 /* Scan the path now, to see if we can find the file */
2070 p
= PathString
->Buffer
;
2071 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2074 /* Find out where this path ends */
2075 for (SegmentEnd
= p
;
2076 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2079 /* Compute the size of this path */
2080 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2082 /* Handle trailing slash if there isn't one */
2083 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2085 /* Add space for one */
2086 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2089 /* Now check if our initial static buffer is too small */
2090 if (StaticCandidateString
.MaximumLength
<
2091 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2093 /* At this point we should've been using our static buffer */
2094 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2095 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2097 /* Something is really messed up if this was the dynamic string */
2098 DbgPrint("%s: internal error #1; "
2099 "CandidateString.Buffer = %p; "
2100 "StaticCandidateBuffer = %p\n",
2102 StaticCandidateString
.Buffer
,
2103 StaticCandidateBuffer
);
2104 Status
= STATUS_INTERNAL_ERROR
;
2108 /* We checked before that the maximum possible size shoudl fit! */
2109 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2110 UNICODE_STRING_MAX_BYTES
);
2111 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2112 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2114 /* For some reason it's not fitting anymore. Something messed up */
2115 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2116 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2119 FileNameString
->Length
,
2121 Status
= STATUS_INTERNAL_ERROR
;
2125 /* Now allocate the dynamic string */
2126 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2128 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2130 if (!StaticCandidateString
.Buffer
)
2132 /* Out of memory, fail */
2133 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2135 StaticCandidateString
.MaximumLength
);
2136 Status
= STATUS_NO_MEMORY
;
2141 /* Copy the path in the string */
2142 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2144 /* Get to the end of the string, and add the trailing slash if missing */
2145 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2146 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2148 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2151 /* Copy the filename now */
2152 RtlCopyMemory(CandidateEnd
,
2153 FileNameString
->Buffer
,
2154 FileNameString
->Length
);
2155 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2157 /* Check if there was an extension */
2158 if (ExtensionString
)
2160 /* Copy the extension too */
2161 RtlCopyMemory(CandidateEnd
,
2162 ExtensionString
->Buffer
,
2163 ExtensionString
->Length
);
2164 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2167 /* We are done, terminate it */
2168 *CandidateEnd
= UNICODE_NULL
;
2170 /* Now set the final length of the string so it becomes valid */
2171 StaticCandidateString
.Length
= (CandidateEnd
-
2172 StaticCandidateString
.Buffer
) *
2175 /* Check if this file exists */
2176 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2177 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2179 /* Awesome, it does, now get the full path */
2180 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2183 (PUNICODE_STRING
*)FullNameOut
,
2188 if (!(NT_SUCCESS(Status
)) &&
2189 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2190 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2192 DbgPrint("%s: Failing because we thought we found %wZ on "
2193 "the search path, but RtlGetfullPathNameUStrEx() "
2198 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2203 /* Otherwise, move to the next path */
2204 if (SegmentEnd
!= End
)
2206 /* Handle the case of the path separator trailing */
2216 /* Loop finished and we didn't break out -- fail */
2217 Status
= STATUS_NO_SUCH_FILE
;
2221 /* We have a full path, so check if it does exist */
2222 DPRINT("%wZ\n", FileNameString
);
2223 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2225 /* It doesn't exist, did we have an extension? */
2226 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2228 /* No extension, so just fail */
2229 Status
= STATUS_NO_SUCH_FILE
;
2233 /* There was an extension, check if the filename already had one */
2234 if (!(Flags
& 4) && (FileNameString
->Length
))
2236 /* Parse the filename */
2237 p
= FileNameString
->Buffer
;
2238 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2241 /* If there's a path separator, there's no extension */
2242 if (IS_PATH_SEPARATOR(*--End
)) break;
2244 /* Othwerwise, did we find an extension dot? */
2247 /* File already had an extension, so fail */
2248 Status
= STATUS_NO_SUCH_FILE
;
2254 /* So there is an extension, we'll try again by adding it */
2255 NamePlusExtLength
= FileNameString
->Length
+
2256 ExtensionString
->Length
+
2257 sizeof(UNICODE_NULL
);
2258 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2260 /* It won't fit in any kind of valid string, so fail */
2261 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2264 Status
= STATUS_NAME_TOO_LONG
;
2268 /* Fill it fit in our temporary string? */
2269 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2271 /* It won't fit anymore, allocate a dynamic string for it */
2272 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2273 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2275 if (!StaticCandidateString
.Buffer
)
2277 /* Ran out of memory, so fail */
2278 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2280 Status
= STATUS_NO_MEMORY
;
2285 /* Copy the filename */
2286 RtlCopyMemory(StaticCandidateString
.Buffer
,
2287 FileNameString
->Buffer
,
2288 FileNameString
->Length
);
2290 /* Copy the extension */
2291 RtlCopyMemory(&StaticCandidateString
.Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)],
2292 ExtensionString
->Buffer
,
2293 ExtensionString
->Length
);
2295 /* Now NULL-terminate */
2296 StaticCandidateString
.Buffer
[StaticCandidateString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2298 /* Finalize the length of the string to make it valid */
2299 StaticCandidateString
.Length
= FileNameString
->Length
+ ExtensionString
->Length
;
2300 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2302 /* And check if this file now exists */
2303 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2305 /* Still no joy, fail out */
2306 Status
= STATUS_NO_SUCH_FILE
;
2310 /* File was found, get the final full path */
2311 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2314 (PUNICODE_STRING
*)FullNameOut
,
2319 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2321 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2322 "failed with status %08lx\n",
2324 &StaticCandidateString
,
2327 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2331 /* File was found on the first try, get the final full path */
2332 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2335 (PUNICODE_STRING
*)FullNameOut
,
2340 if (!(NT_SUCCESS(Status
)) &&
2341 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2342 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2344 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2345 "failed with status %08lx\n",
2350 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2355 /* Anything that was not an error, turn into STATUS_SUCCESS */
2356 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2358 /* Check if we had a dynamic string */
2359 if ((StaticCandidateString
.Buffer
) &&
2360 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2363 RtlFreeUnicodeString(&StaticCandidateString
);
2366 /* Return the status */
2375 RtlDoesFileExists_U(IN PCWSTR FileName
)
2377 /* Call the new function */
2378 return RtlDoesFileExists_UEx(FileName
, TRUE
);