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
9 * Alex Ionescu (alex.ionescu@reactos.org)
10 * Pierre Schweitzer (pierre@reactos.org)
13 /* INCLUDES *****************************************************************/
20 /* DEFINITONS and MACROS ******************************************************/
22 #define MAX_PFX_SIZE 16
24 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
26 #define RTL_CURDIR_IS_REMOVABLE 0x1
27 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2
28 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
29 C_ASSERT(RTL_CURDIR_ALL_FLAGS
== OBJ_HANDLE_TAGBITS
);
32 /* GLOBALS ********************************************************************/
34 static const WCHAR DeviceRootW
[] = L
"\\\\.\\";
35 const UNICODE_STRING DeviceRootString
= RTL_CONSTANT_STRING(L
"\\\\.\\");
37 const UNICODE_STRING RtlpDosDevicesUncPrefix
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
38 const UNICODE_STRING RtlpWin32NtRootSlash
= RTL_CONSTANT_STRING(L
"\\\\?\\");
39 const UNICODE_STRING RtlpDosSlashCONDevice
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
40 const UNICODE_STRING RtlpDosDevicesPrefix
= RTL_CONSTANT_STRING(L
"\\??\\");
42 const UNICODE_STRING RtlpDosLPTDevice
= RTL_CONSTANT_STRING(L
"LPT");
43 const UNICODE_STRING RtlpDosCOMDevice
= RTL_CONSTANT_STRING(L
"COM");
44 const UNICODE_STRING RtlpDosPRNDevice
= RTL_CONSTANT_STRING(L
"PRN");
45 const UNICODE_STRING RtlpDosAUXDevice
= RTL_CONSTANT_STRING(L
"AUX");
46 const UNICODE_STRING RtlpDosCONDevice
= RTL_CONSTANT_STRING(L
"CON");
47 const UNICODE_STRING RtlpDosNULDevice
= RTL_CONSTANT_STRING(L
"NUL");
49 PRTLP_CURDIR_REF RtlpCurDirRef
;
51 /* PRIVATE FUNCTIONS **********************************************************/
55 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
57 UNICODE_STRING PathCopy
;
59 USHORT PathChars
, ColonCount
= 0;
60 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
63 /* Validate the input */
64 if (!PathString
) return 0;
66 /* Check what type of path this is */
67 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
69 /* Fail for UNC or unknown paths */
70 case RtlPathTypeUnknown
:
71 case RtlPathTypeUncAbsolute
:
74 /* Make special check for the CON device */
75 case RtlPathTypeLocalDevice
:
76 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
78 /* This should return 0x80006 */
79 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
87 /* Make a copy of the string */
88 PathCopy
= *PathString
;
89 OriginalLength
= PathString
->Length
;
91 /* Return if there's no characters */
92 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
93 if (!PathChars
) return 0;
95 /* Check for drive path and truncate */
96 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
98 /* Fixup the lengths */
99 PathCopy
.Length
-= sizeof(WCHAR
);
100 if (!--PathChars
) return 0;
102 /* Remember this for later */
106 /* Check for extension or space, and truncate */
109 /* Stop if we hit something else than a space or period */
110 c
= PathCopy
.Buffer
[PathChars
- 1];
111 if ((c
!= '.') && (c
!= ' ')) break;
113 /* Fixup the lengths */
114 PathCopy
.Length
-= sizeof(WCHAR
);
116 /* Remember this for later */
118 } while (--PathChars
);
120 /* Anything still left? */
123 /* Loop from the end */
124 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
125 End
>= PathCopy
.Buffer
;
128 /* Check if the character is a path or drive separator */
130 if ((c
== '\\') || (c
== '/') || ((c
== ':') && (End
== PathCopy
.Buffer
+ 1)))
132 /* Get the next lower case character */
134 c
= *End
| ' '; // ' ' == ('z' - 'Z')
136 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
137 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
138 ((c
== 'l') || (c
== 'c') || (c
== 'p') || (c
== 'a') || (c
== 'n')))
140 /* Calculate the offset */
141 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
143 /* Build the final string */
144 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
145 PathCopy
.Buffer
= End
;
147 /* Save new amount of chars in the path */
148 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
159 /* Get the next lower case character and check if it's a DOS device */
160 c
= *PathCopy
.Buffer
| ' '; // ' ' == ('z' - 'Z')
161 if ((c
!= 'l') && (c
!= 'c') && (c
!= 'p') && (c
!= 'a') && (c
!= 'n'))
163 /* Not LPT, COM, PRN, AUX, or NUL */
168 /* Now skip past any extra extension or drive letter characters */
169 Start
= PathCopy
.Buffer
;
170 End
= &Start
[PathChars
];
174 if ((c
== '.') || (c
== ':')) break;
178 /* And then go backwards to get rid of spaces */
179 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == ' ')) --Start
;
181 /* Finally see how many characters are left, and that's our size */
182 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
183 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
185 /* Check if this is a COM or LPT port, which has a digit after it */
186 if ((PathChars
== 4) &&
187 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != '0')))
189 /* Don't compare the number part, just check for LPT or COM */
190 PathCopy
.Length
-= sizeof(WCHAR
);
191 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
192 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
195 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
196 return MAKELONG(ReturnLength
, ReturnOffset
);
199 else if ((PathChars
== 3) &&
200 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
201 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
202 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
203 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
205 /* Otherwise this was something like AUX, NUL, PRN, or CON */
206 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
207 return MAKELONG(ReturnLength
, ReturnOffset
);
210 /* Otherwise, this is not a valid DOS device */
216 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
221 /* Validate the input */
222 if (!PathString
) return RtlPathTypeUnknown
;
224 Path
= PathString
->Buffer
;
225 Chars
= PathString
->Length
/ sizeof(WCHAR
);
227 /* Return if there are no characters */
228 if (!Chars
) return RtlPathTypeUnknown
;
231 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
232 * actually check for the path length before touching the characters
234 if ((Chars
< 1) || (IS_PATH_SEPARATOR(Path
[0])))
236 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
237 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
238 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
239 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
240 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
244 if ((Chars
< 2) || (!(Path
[0]) || (Path
[1] != L
':'))) return RtlPathTypeRelative
; /* x */
245 if ((Chars
< 3) || (IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveAbsolute
; /* x:\ */
246 return RtlPathTypeDriveRelative
; /* x: */
252 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
254 OUT PBOOLEAN NameInvalid
)
259 /* Allocate a large enough buffer */
260 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
266 /* Copy the filename */
267 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
269 /* And add a dot at the end */
270 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
271 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
273 /* Check if the file exists or not */
274 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
276 /* Get rid of the buffer now */
277 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
281 /* Assume the name is ok, but fail the call */
282 *NameInvalid
= FALSE
;
283 Status
= STATUS_NO_MEMORY
;
286 /* Return the status */
292 RtlGetFullPathName_Ustr(IN PUNICODE_STRING FileName
,
295 OUT PCWSTR
*ShortName
,
296 OUT PBOOLEAN InvalidName
,
297 OUT RTL_PATH_TYPE
*PathType
)
299 PWCHAR FileNameBuffer
;
300 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
304 /* For now, assume the name is valid */
305 DPRINT("Filename: %wZ\n", FileName
);
306 DPRINT("Size and buffer: %lx %S\n", Size
, Buffer
);
307 if (InvalidName
) *InvalidName
= FALSE
;
309 /* Handle initial path type and failure case */
310 *PathType
= RtlPathTypeUnknown
;
311 if (!(Size
) || !(Buffer
) || !(FileName
) ||
312 !(FileName
->Length
) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
314 /* Break filename into component parts */
315 FileNameBuffer
= FileName
->Buffer
;
316 FileNameLength
= FileName
->Length
;
317 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
319 /* Kill trailing spaces */
320 c
= FileNameBuffer
[FileNameChars
- 1];
321 while ((FileNameLength
) && (c
== L
' '))
323 /* Keep going, ignoring the spaces */
324 FileNameLength
-= sizeof(WCHAR
);
325 if (FileNameLength
) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
328 /* Check if anything is left */
329 if (!FileNameLength
) return 0;
331 /* Check if this is a DOS name */
332 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
333 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
336 /* Zero out the short name */
337 if (ShortName
) *ShortName
= NULL
;
339 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
340 DosLengthOffset
= DosLength
>> 16;
341 DosLength
= DosLength
& 0xFFFF;
343 /* Do we have a DOS length, and does the caller want validity? */
344 if ((InvalidName
) && (DosLengthOffset
))
347 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
349 /* If the check failed, or the name is invalid, fail here */
350 if (!NT_SUCCESS(Status
)) return 0;
351 if (*InvalidName
) return 0;
354 /* Add the size of the device root and check if it fits in the size */
355 FullLength
= DosLength
+ DeviceRootString
.Length
;
356 if (FullLength
< Size
)
358 /* Add the device string */
359 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
361 /* Now add the DOS device name */
362 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
363 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
367 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
371 /* Otherwise, there's no space, so return the buffer size needed */
372 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
373 return FullLength
+ sizeof(UNICODE_NULL
);
376 /* This should work well enough for our current needs */
377 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
378 DPRINT("Path type: %lx\n", *PathType
);
380 /* This is disgusting... but avoids re-writing everything */
381 DPRINT("Calling old API with %s and %lx and %S\n", FileNameBuffer
, Size
, Buffer
);
382 return RtlGetFullPathName_U(FileNameBuffer
, Size
, Buffer
, (PWSTR
*)ShortName
);
387 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
388 OUT PUNICODE_STRING NtPath
,
389 OUT PCWSTR
*PartName
,
390 OUT PRTL_RELATIVE_NAME_U RelativeName
)
395 /* Validate the input */
396 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
398 /* Validate the DOS length */
399 DosLength
= DosPath
->Length
;
400 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
402 /* Make space for the new path */
403 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
405 DosLength
+ sizeof(UNICODE_NULL
));
406 if (!NewBuffer
) return STATUS_NO_MEMORY
;
408 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
409 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
410 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
411 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
412 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
413 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
415 /* Did the caller send a relative name? */
418 /* Zero initialize it */
419 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
420 RelativeName
->ContainingDirectory
= NULL
;
421 RelativeName
->CurDirRef
= 0;
424 /* Did the caller request a partial name? */
427 /* Loop from the back until we find a path separator */
428 p
= &NewBuffer
[(DosLength
- 1) / sizeof (WCHAR
)];
429 while (p
> NewBuffer
) if (*p
-- == '\\') break;
434 /* Move past it -- anything left? */
438 /* The path ends with a path separator, no part name */
443 /* What follows the path separator is the part name */
449 /* Build the final NT path string */
450 NtPath
->Length
= (USHORT
)DosLength
;
451 NtPath
->Buffer
= NewBuffer
;
452 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
453 return STATUS_SUCCESS
;
458 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
459 IN PCUNICODE_STRING DosName
,
460 OUT PUNICODE_STRING NtName
,
461 OUT PCWSTR
*PartName
,
462 OUT PRTL_RELATIVE_NAME_U RelativeName
)
464 WCHAR BigBuffer
[MAX_PATH
+ 1];
465 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
466 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
467 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
469 RTL_PATH_TYPE InputPathType
, BufferPathType
;
472 PCURDIR CurrentDirectory
;
474 /* Assume MAX_PATH for now */
475 DPRINT("Relative: %lx DosName: %wZ NtName: %wZ, PartName: %p, RelativeName: %p\n",
476 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
477 MaxLength
= sizeof(BigBuffer
);
479 /* Validate the input */
480 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
482 /* Capture input string */
483 CapturedDosName
= *DosName
;
485 /* Check for \\?\\ form */
486 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
487 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
488 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
489 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
490 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
492 /* Quick path won't be used */
495 /* Use the static buffer */
497 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
499 /* Allocate a buffer to hold the path */
500 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
501 DPRINT("Length: %lx\n", MaxLength
);
502 if (!NewBuffer
) return STATUS_NO_MEMORY
;
506 /* Use the optimized path after acquiring the lock */
511 /* Lock the PEB and check if the quick path can be used */
515 /* Some simple fixups will get us the correct path */
516 DPRINT("Quick path\n");
517 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
522 /* Release the lock, we're done here */
527 /* Call the main function to get the full path name and length */
528 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
529 MAX_PATH
* sizeof(WCHAR
),
534 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
536 /* Invalid name, fail */
537 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
538 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
540 return STATUS_OBJECT_NAME_INVALID
;
543 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
544 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
545 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
548 /* Check where it really is */
549 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
550 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
551 switch (BufferPathType
)
553 /* It's actually a UNC path in \??\UNC\ */
554 case RtlPathTypeUncAbsolute
:
555 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
556 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
560 case RtlPathTypeLocalDevice
:
561 /* We made a good guess, go with it but skip the \??\ */
565 case RtlPathTypeDriveAbsolute
:
566 case RtlPathTypeDriveRelative
:
567 case RtlPathTypeRooted
:
568 case RtlPathTypeRelative
:
569 /* Our guess was good, roll with it */
572 /* Nothing else is expected */
578 /* Now copy the prefix and the buffer */
579 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
580 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
582 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
584 /* Compute the length */
585 Length
= PathLength
- PrefixCut
* sizeof(WCHAR
) + PrefixLength
;
586 LengthChars
= Length
/ sizeof(WCHAR
);
588 /* Setup the actual NT path string and terminate it */
589 NtName
->Buffer
= NewBuffer
;
590 NtName
->Length
= (USHORT
)Length
;
591 NtName
->MaximumLength
= (USHORT
)MaxLength
;
592 NewBuffer
[LengthChars
] = UNICODE_NULL
;
593 DPRINT("new buffer: %S\n", NewBuffer
);
594 DPRINT("NT Name: %wZ\n", NtName
);
596 /* Check if a partial name was requested */
597 if ((PartName
) && (*PartName
))
599 /* Convert to Unicode */
600 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
601 if (NT_SUCCESS(Status
))
603 /* Set the partial name */
604 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
609 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
615 /* Check if a relative name was asked for */
618 /* Setup the structure */
619 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
620 RelativeName
->ContainingDirectory
= NULL
;
621 RelativeName
->CurDirRef
= NULL
;
623 /* Check if the input path itself was relative */
624 if (InputPathType
== RtlPathTypeRelative
)
626 /* Get current directory */
627 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
628 if (CurrentDirectory
->Handle
)
630 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
631 if (!NT_SUCCESS(Status
))
633 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
638 /* If current directory is bigger than full path, there's no way */
639 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
645 /* File is in current directory */
646 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
648 /* Make relative name string */
649 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ FullPath
.Length
- PrefixCut
);
650 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
651 /* If relative name starts with \, skip it */
652 if (RelativeName
->RelativeName
.Buffer
[0] == L
'\\')
654 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)RelativeName
->RelativeName
.Buffer
+ sizeof(WCHAR
));
655 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
657 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
658 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
662 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
666 /* Give back current directory data & reference counter */
667 RelativeName
->CurDirRef
= RtlpCurDirRef
;
668 if (RelativeName
->CurDirRef
)
670 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
673 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
681 return STATUS_SUCCESS
;
686 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
688 OUT PUNICODE_STRING NtName
,
689 OUT PCWSTR
*PartName
,
690 OUT PRTL_RELATIVE_NAME_U RelativeName
)
693 UNICODE_STRING NameString
;
695 /* Create the unicode name */
696 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
697 if (NT_SUCCESS(Status
))
699 /* Call the unicode function */
700 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
713 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
714 OUT PUNICODE_STRING NtName
,
715 OUT PCWSTR
*PartName
,
716 OUT PRTL_RELATIVE_NAME_U RelativeName
)
718 /* Call the internal function */
719 ASSERT(RelativeName
);
720 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
729 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
730 IN BOOLEAN SucceedIfBusy
)
733 RTL_RELATIVE_NAME_U RelativeName
;
734 UNICODE_STRING NtPathName
;
736 OBJECT_ATTRIBUTES ObjectAttributes
;
738 FILE_BASIC_INFORMATION BasicInformation
;
740 /* Validate the input */
741 if (!FileName
) return FALSE
;
743 /* Get the NT Path */
744 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
748 if (!Result
) return FALSE
;
750 /* Save the buffer */
751 Buffer
= NtPathName
.Buffer
;
753 /* Check if we have a relative name */
754 if (RelativeName
.RelativeName
.Length
)
757 NtPathName
= RelativeName
.RelativeName
;
761 /* Otherwise ignore it */
762 RelativeName
.ContainingDirectory
= NULL
;
765 /* Initialize the object attributes */
766 InitializeObjectAttributes(&ObjectAttributes
,
768 OBJ_CASE_INSENSITIVE
,
769 RelativeName
.ContainingDirectory
,
772 /* Query the attributes and free the buffer now */
773 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
774 RtlReleaseRelativeName(&RelativeName
);
775 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
777 /* Check if we failed */
778 if (!NT_SUCCESS(Status
))
780 /* Check if we failed because the file is in use */
781 if ((Status
== STATUS_SHARING_VIOLATION
) ||
782 (Status
== STATUS_ACCESS_DENIED
))
784 /* Check if the caller wants this to be considered OK */
785 Result
= SucceedIfBusy
? TRUE
: FALSE
;
789 /* A failure because the file didn't exist */
795 /* The file exists */
799 /* Return the result */
805 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
807 /* Call the updated API */
808 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
813 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
814 IN BOOLEAN SucceedIfBusy
)
816 UNICODE_STRING NameString
;
818 /* Create the unicode name*/
819 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
821 /* Call the unicode function */
822 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
829 /* PUBLIC FUNCTIONS ***********************************************************/
836 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
838 /* Check if a directory reference was grabbed */
839 if (RelativeName
->CurDirRef
)
841 /* Decrease reference count */
842 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
844 /* If no one uses it any longer, close handle & free */
845 NtClose(RelativeName
->CurDirRef
->Handle
);
846 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
848 RelativeName
->CurDirRef
= NULL
;
857 RtlGetLongestNtPathLength(VOID
)
860 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
861 * a mapped network drive), which is accessed through the DOS Global?? path.
862 * This is, and has always been equal to, 269 characters, except in Wine
863 * which claims this is 277. Go figure.
865 return (MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
+ sizeof(ANSI_NULL
));
873 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
875 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
877 /* Validate the input */
878 if (!Path
) return RtlPathTypeUnknown
;
880 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
881 if (IS_PATH_SEPARATOR(Path
[0]))
883 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
884 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
885 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
886 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
887 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
891 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
892 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
893 return RtlPathTypeDriveRelative
; /* x: */
902 RtlIsDosDeviceName_U(IN PCWSTR Path
)
904 UNICODE_STRING PathString
;
907 /* Build the string */
908 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
909 if (!NT_SUCCESS(Status
)) return 0;
912 * Returns 0 if name is not valid DOS device name, or DWORD with
913 * offset in bytes to DOS device name from beginning of buffer in high word
914 * and size in bytes of DOS device name in low word
916 return RtlIsDosDeviceName_Ustr(&PathString
);
924 RtlGetCurrentDirectory_U(IN ULONG MaximumLength
,
930 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
932 /* Lock the PEB to get the current directory */
934 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
936 /* Get the buffer and character length */
937 CurDirName
= CurDir
->DosPath
.Buffer
;
938 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
939 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
942 * DosPath.Buffer should always have a trailing slash. There is an assert
943 * below which checks for this.
945 * This function either returns x:\ for a root (keeping the original buffer)
946 * or it returns x:\path\foo for a directory (replacing the trailing slash
949 Bytes
= Length
* sizeof(WCHAR
);
950 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
952 /* Check if caller does not have enough space */
953 if (MaximumLength
<= Bytes
)
955 /* Call has no space for it, fail, add the trailing slash */
957 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
962 /* Check if caller does not have enough space */
963 if (MaximumLength
< Bytes
)
965 /* Call has no space for it, fail */
971 /* Copy the buffer since we seem to have space */
972 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
974 /* The buffer should end with a path separator */
975 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
977 /* Again check for our two cases (drive root vs path) */
978 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
980 /* Replace the trailing slash with a null */
981 Buffer
[Length
- 1] = UNICODE_NULL
;
986 /* Append the null char since there's no trailing slash */
987 Buffer
[Length
] = UNICODE_NULL
;
990 /* Release PEB lock */
992 DPRINT("CurrentDirectory %S\n", Buffer
);
993 return Length
* sizeof(WCHAR
);
1001 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1005 RTL_PATH_TYPE PathType
;
1006 IO_STATUS_BLOCK IoStatusBlock
;
1007 UNICODE_STRING FullPath
, NtName
;
1008 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1009 OBJECT_ATTRIBUTES ObjectAttributes
;
1010 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1011 ULONG SavedLength
, CharLength
, FullPathLength
;
1012 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1014 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1016 /* Initialize for failure case */
1017 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1019 /* Can't set current directory on DOS device */
1020 if (RtlIsDosDeviceName_Ustr(Path
))
1022 return STATUS_NOT_A_DIRECTORY
;
1025 /* Get current directory */
1026 RtlAcquirePebLock();
1027 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1029 /* Check if we have to drop current handle */
1030 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1032 OldHandle
= CurDir
->Handle
;
1033 CurDir
->Handle
= NULL
;
1036 /* Allocate a buffer for full path (using max possible length */
1037 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1038 if (!FullPath
.Buffer
)
1040 Status
= STATUS_NO_MEMORY
;
1045 FullPath
.Length
= 0;
1046 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1048 /* Get new directory full path */
1049 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1050 if (!FullPathLength
)
1052 Status
= STATUS_OBJECT_NAME_INVALID
;
1056 SavedLength
= FullPath
.MaximumLength
;
1057 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1059 if (FullPathLength
> FullPath
.MaximumLength
)
1061 Status
= STATUS_NAME_TOO_LONG
;
1065 /* Translate it to NT name */
1066 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1068 Status
= STATUS_OBJECT_NAME_INVALID
;
1072 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1073 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1076 /* If previous current directory was removable, then check it for dropping */
1077 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1079 /* Get back normal handle */
1080 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1081 CurDir
->Handle
= NULL
;
1083 /* Get device information */
1084 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1087 sizeof(FileFsDeviceInfo
),
1088 FileFsDeviceInformation
);
1089 /* Retry without taking care of removable device */
1090 if (!NT_SUCCESS(Status
))
1092 Status
= RtlSetCurrentDirectory_U(Path
);
1098 /* Open directory */
1099 Status
= NtOpenFile(&CurDirHandle
,
1100 SYNCHRONIZE
| FILE_TRAVERSE
,
1103 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1104 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1105 if (!NT_SUCCESS(Status
)) goto Leave
;
1107 /* Get device information */
1108 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1111 sizeof(FileFsDeviceInfo
),
1112 FileFsDeviceInformation
);
1113 if (!NT_SUCCESS(Status
)) goto Leave
;
1116 /* If device is removable, mark handle */
1117 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1119 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1122 FullPath
.Length
= (USHORT
)FullPathLength
;
1124 /* If full path isn't \ terminated, do it */
1125 if (FullPath
.Buffer
[CharLength
- 1] != L
'\\')
1127 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1129 Status
= STATUS_NAME_TOO_LONG
;
1133 FullPath
.Buffer
[CharLength
] = L
'\\';
1134 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1135 FullPath
.Length
+= sizeof(WCHAR
);
1138 /* If we have previous current directory with only us as reference, save it */
1139 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1141 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1145 /* Allocate new current directory struct saving previous one */
1146 OldCurDir
= RtlpCurDirRef
;
1147 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1150 RtlpCurDirRef
= OldCurDir
;
1152 Status
= STATUS_NO_MEMORY
;
1156 /* Set reference to 1 (us) */
1157 RtlpCurDirRef
->RefCount
= 1;
1161 CurDir
->Handle
= CurDirHandle
;
1162 RtlpCurDirRef
->Handle
= CurDirHandle
;
1163 CurDirHandle
= NULL
;
1165 /* Copy full path */
1166 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1167 CurDir
->DosPath
.Length
= FullPath
.Length
;
1169 Status
= STATUS_SUCCESS
;
1172 RtlReleasePebLock();
1174 if (FullPath
.Buffer
)
1176 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1181 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1184 if (CurDirHandle
) NtClose(CurDirHandle
);
1186 if (OldHandle
) NtClose(OldHandle
);
1188 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1190 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1192 NtClose(OldCurDir
->Handle
);
1193 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1200 /******************************************************************
1203 * Helper for RtlGetFullPathName_U.
1204 * Get rid of . and .. components in the path.
1206 void FORCEINLINE
collapse_path( WCHAR
*path
, UINT mark
)
1210 /* convert every / into a \ */
1211 for (p
= path
; *p
; p
++) if (*p
== '/') *p
= '\\';
1213 /* collapse duplicate backslashes */
1214 next
= path
+ max( 1, mark
);
1215 for (p
= next
; *p
; p
++) if (*p
!= '\\' || next
[-1] != '\\') *next
++ = *p
;
1225 case '\\': /* .\ component */
1227 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1229 case 0: /* final . */
1230 if (p
> path
+ mark
) p
--;
1234 if (p
[2] == '\\') /* ..\ component */
1237 if (p
> path
+ mark
)
1240 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1242 memmove( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
1245 else if (!p
[2]) /* final .. */
1247 if (p
> path
+ mark
)
1250 while (p
> path
+ mark
&& p
[-1] != '\\') p
--;
1251 if (p
> path
+ mark
) p
--;
1259 /* skip to the next component */
1260 while (*p
&& *p
!= '\\') p
++;
1263 /* remove last dot in previous dir name */
1264 if (p
> path
+ mark
&& p
[-1] == '.') memmove( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
1269 /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
1270 while (p
> path
+ mark
&& (p
[-1] == ' ' || p
[-1] == '.')) p
--;
1276 /******************************************************************
1279 * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
1281 static const WCHAR
*skip_unc_prefix( const WCHAR
*ptr
)
1284 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* share name */
1285 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1286 while (*ptr
&& !IS_PATH_SEPARATOR(*ptr
)) ptr
++; /* dir name */
1287 while (IS_PATH_SEPARATOR(*ptr
)) ptr
++;
1292 /******************************************************************
1293 * get_full_path_helper
1295 * Helper for RtlGetFullPathName_U
1296 * Note: name and buffer are allowed to point to the same memory spot
1298 static ULONG
get_full_path_helper(
1303 SIZE_T reqsize
= 0, mark
= 0, dep
= 0, deplen
;
1304 LPWSTR ins_str
= NULL
;
1306 const UNICODE_STRING
* cd
;
1309 /* return error if name only consists of spaces */
1310 for (ptr
= name
; *ptr
; ptr
++) if (*ptr
!= ' ') break;
1311 if (!*ptr
) return 0;
1313 RtlAcquirePebLock();
1315 //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath;
1316 cd
= &NtCurrentTeb()->ProcessEnvironmentBlock
->ProcessParameters
->CurrentDirectory
.DosPath
;
1318 switch (RtlDetermineDosPathNameType_U(name
))
1320 case RtlPathTypeUncAbsolute
: /* \\foo */
1321 ptr
= skip_unc_prefix( name
);
1322 mark
= (ptr
- name
);
1325 case RtlPathTypeLocalDevice
: /* \\.\foo */
1329 case RtlPathTypeDriveAbsolute
: /* c:\foo */
1330 reqsize
= sizeof(WCHAR
);
1331 tmp
[0] = towupper(name
[0]);
1337 case RtlPathTypeDriveRelative
: /* c:foo */
1339 if (towupper(name
[0]) != towupper(cd
->Buffer
[0]) || cd
->Buffer
[1] != ':')
1341 UNICODE_STRING var
, val
;
1347 var
.Length
= 3 * sizeof(WCHAR
);
1348 var
.MaximumLength
= 4 * sizeof(WCHAR
);
1351 val
.MaximumLength
= (USHORT
)size
;
1352 val
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, size
);
1353 if (val
.Buffer
== NULL
)
1359 switch (RtlQueryEnvironmentVariable_U(NULL
, &var
, &val
))
1361 case STATUS_SUCCESS
:
1362 /* FIXME: Win2k seems to check that the environment variable actually points
1363 * to an existing directory. If not, root of the drive is used
1364 * (this seems also to be the only spot in RtlGetFullPathName that the
1365 * existence of a part of a path is checked)
1368 case STATUS_BUFFER_TOO_SMALL
:
1369 reqsize
= val
.Length
+ sizeof(WCHAR
); /* append trailing '\\' */
1370 val
.Buffer
[val
.Length
/ sizeof(WCHAR
)] = '\\';
1371 ins_str
= val
.Buffer
;
1373 case STATUS_VARIABLE_NOT_FOUND
:
1374 reqsize
= 3 * sizeof(WCHAR
);
1379 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1382 DPRINT1("Unsupported status code\n");
1383 RtlFreeHeap(RtlGetProcessHeap(), 0, val
.Buffer
);
1391 case RtlPathTypeRelative
: /* foo */
1392 reqsize
= cd
->Length
;
1393 ins_str
= cd
->Buffer
;
1394 if (cd
->Buffer
[1] != ':')
1396 ptr
= skip_unc_prefix( cd
->Buffer
);
1397 mark
= ptr
- cd
->Buffer
;
1402 case RtlPathTypeRooted
: /* \xxx */
1404 if (name
[0] == '/') /* may be a Unix path */
1406 const WCHAR
*ptr
= name
;
1407 int drive
= find_drive_root( &ptr
);
1410 reqsize
= 3 * sizeof(WCHAR
);
1411 tmp
[0] = 'A' + drive
;
1421 if (cd
->Buffer
[1] == ':')
1423 reqsize
= 2 * sizeof(WCHAR
);
1424 tmp
[0] = cd
->Buffer
[0];
1431 ptr
= skip_unc_prefix( cd
->Buffer
);
1432 reqsize
= (ptr
- cd
->Buffer
) * sizeof(WCHAR
);
1433 mark
= reqsize
/ sizeof(WCHAR
);
1434 ins_str
= cd
->Buffer
;
1438 case RtlPathTypeRootLocalDevice
: /* \\. */
1439 reqsize
= 4 * sizeof(WCHAR
);
1449 case RtlPathTypeUnknown
:
1453 /* enough space ? */
1454 deplen
= wcslen(name
+ dep
) * sizeof(WCHAR
);
1455 if (reqsize
+ deplen
+ sizeof(WCHAR
) > size
)
1457 /* not enough space, return need size (including terminating '\0') */
1458 reqsize
+= deplen
+ sizeof(WCHAR
);
1462 memmove(buffer
+ reqsize
/ sizeof(WCHAR
), name
+ dep
, deplen
+ sizeof(WCHAR
));
1463 if (reqsize
) memcpy(buffer
, ins_str
, reqsize
);
1466 if (ins_str
!= tmp
&& ins_str
!= cd
->Buffer
)
1467 RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str
);
1469 collapse_path( buffer
, (ULONG
)mark
);
1470 reqsize
= wcslen(buffer
) * sizeof(WCHAR
);
1473 RtlReleasePebLock();
1474 return (ULONG
)reqsize
;
1478 /******************************************************************
1479 * RtlGetFullPathName_U (NTDLL.@)
1481 * Returns the number of bytes written to buffer (not including the
1482 * terminating NULL) if the function succeeds, or the required number of bytes
1483 * (including the terminating NULL) if the buffer is too small.
1485 * file_part will point to the filename part inside buffer (except if we use
1486 * DOS device name, in which case file_in_buf is NULL)
1490 ULONG NTAPI
RtlGetFullPathName_U(
1500 DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name
, size
, buffer
, file_part
);
1502 if (!name
|| !*name
) return 0;
1504 if (file_part
) *file_part
= NULL
;
1506 /* check for DOS device name */
1507 dosdev
= RtlIsDosDeviceName_U((WCHAR
*)name
);
1510 DWORD offset
= HIWORD(dosdev
) / sizeof(WCHAR
); /* get it in WCHARs, not bytes */
1511 DWORD sz
= LOWORD(dosdev
); /* in bytes */
1513 if (8 + sz
+ 2 > size
) return sz
+ 10;
1514 wcscpy(buffer
, DeviceRootW
);
1515 memmove(buffer
+ 4, name
+ offset
, sz
);
1516 buffer
[4 + sz
/ sizeof(WCHAR
)] = '\0';
1517 /* file_part isn't set in this case */
1521 reqsize
= get_full_path_helper(name
, buffer
, size
);
1522 if (!reqsize
) return 0;
1525 LPWSTR tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize
);
1526 if (tmp
== NULL
) return 0;
1527 reqsize
= get_full_path_helper(name
, tmp
, reqsize
);
1528 if (reqsize
+ sizeof(WCHAR
) > size
) /* it may have worked the second time */
1530 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1531 return reqsize
+ sizeof(WCHAR
);
1533 memcpy( buffer
, tmp
, reqsize
+ sizeof(WCHAR
) );
1534 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1537 /* find file part */
1538 if (file_part
&& (ptr
= wcsrchr(buffer
, '\\')) != NULL
&& ptr
>= buffer
+ 2 && *++ptr
)
1548 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1549 OUT PUNICODE_STRING NtName
,
1550 OUT PCWSTR
*PartName
,
1551 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1553 /* Call the internal function */
1554 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1566 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1567 OUT PUNICODE_STRING NtName
,
1568 OUT PCWSTR
*PartName
,
1569 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1571 /* Call the internal function */
1572 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1584 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1585 OUT PUNICODE_STRING NtName
,
1586 OUT PCWSTR
*PartName
,
1587 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1589 /* Call the internal function */
1590 ASSERT(RelativeName
);
1591 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1603 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1604 OUT PUNICODE_STRING NtName
,
1605 OUT PCWSTR
*PartName
,
1606 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1608 /* Call the internal function */
1609 ASSERT(RelativeName
);
1610 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1621 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1623 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1624 return STATUS_NOT_IMPLEMENTED
;
1632 RtlDosSearchPath_U(IN PCWSTR Path
,
1634 IN PCWSTR Extension
,
1637 OUT PWSTR
*PartName
)
1640 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1641 UNICODE_STRING TempString
;
1642 PWCHAR NewBuffer
, BufferStart
;
1645 /* Validate the input */
1646 if (!(Path
) || !(FileName
)) return 0;
1648 /* Check if this is an absolute path */
1649 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1651 /* Check if the file exists */
1652 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1654 /* Get the full name, which does the DOS lookup */
1655 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1658 /* Doesn't exist, so fail */
1662 /* Scan the filename */
1666 /* Looking for an extension */
1669 /* No extension string needed -- it's part of the filename */
1674 /* Next character */
1678 /* Do we have an extension? */
1681 /* Nope, don't worry about one */
1682 ExtensionLength
= 0;
1686 /* Build a temporary string to get the extension length */
1687 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1688 if (!NT_SUCCESS(Status
)) return 0;
1689 ExtensionLength
= TempString
.Length
;
1692 /* Build a temporary string to get the path length */
1693 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1694 if (!NT_SUCCESS(Status
)) return 0;
1695 PathLength
= TempString
.Length
;
1697 /* Build a temporary string to get the filename length */
1698 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1699 if (!NT_SUCCESS(Status
)) return 0;
1700 FileNameLength
= TempString
.Length
;
1702 /* Allocate the buffer for the new string name */
1703 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1712 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1717 /* Final loop to build the path */
1720 /* Check if we have a valid character */
1721 BufferStart
= NewBuffer
;
1724 /* Loop as long as there's no semicolon */
1725 while (*Path
!= ';')
1727 /* Copy the next character */
1728 *BufferStart
++ = *Path
++;
1732 /* We found a semi-colon, to stop path processing on this loop */
1733 if (*Path
== ';') ++Path
;
1736 /* Add a terminating slash if needed */
1737 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != '\\'))
1739 *BufferStart
++ = '\\';
1742 /* Bail out if we reached the end */
1743 if (!*Path
) Path
= NULL
;
1745 /* Copy the file name and check if an extension is needed */
1746 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1747 if (ExtensionLength
)
1749 /* Copy the extension too */
1750 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1752 ExtensionLength
+ sizeof(WCHAR
));
1756 /* Just NULL-terminate */
1757 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1760 /* Now, does this file exist? */
1761 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1763 /* Call the full-path API to get the length */
1764 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1768 /* If we got here, path doesn't exist, so fail the call */
1773 /* Free the allocation and return the length */
1774 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1783 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
1784 IN PUNICODE_STRING StaticString
,
1785 IN PUNICODE_STRING DynamicString
,
1786 IN PUNICODE_STRING
*StringUsed
,
1787 IN PSIZE_T FilePartSize
,
1788 OUT PBOOLEAN NameInvalid
,
1789 OUT RTL_PATH_TYPE
* PathType
,
1790 OUT PSIZE_T LengthNeeded
)
1793 PWCHAR StaticBuffer
;
1796 USHORT StaticLength
;
1797 UNICODE_STRING TempDynamicString
;
1799 /* Initialize all our locals */
1801 StaticBuffer
= NULL
;
1802 TempDynamicString
.Buffer
= NULL
;
1804 /* Initialize the input parameters */
1805 if (StringUsed
) *StringUsed
= NULL
;
1806 if (LengthNeeded
) *LengthNeeded
= 0;
1807 if (FilePartSize
) *FilePartSize
= 0;
1809 /* Check for invalid parameters */
1810 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
1812 return STATUS_INVALID_PARAMETER
;
1815 /* Check if we did not get an input string */
1819 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
1820 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
1821 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
1825 /* Use the one we received */
1826 StaticBuffer
= StaticString
->Buffer
;
1827 StaticLength
= StaticString
->MaximumLength
;
1830 /* Call the lower-level function */
1831 Length
= RtlGetFullPathName_Ustr(FileName
,
1837 DPRINT("Length: %d StaticBuffer: %S\n", Length
, StaticBuffer
);
1840 /* Fail if it failed */
1841 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
1844 Status
= STATUS_OBJECT_NAME_INVALID
;
1848 /* Check if it fits inside our static string */
1849 if ((StaticString
) && (Length
< StaticLength
))
1851 /* Set the final length */
1852 StaticString
->Length
= (USHORT
)Length
;
1854 /* Set the file part size */
1855 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
1857 /* Return the static string if requested */
1858 if (StringUsed
) *StringUsed
= StaticString
;
1860 /* We are done with success */
1861 Status
= STATUS_SUCCESS
;
1865 /* Did we not have an input dynamic string ?*/
1868 /* Return the length we need */
1869 if (LengthNeeded
) *LengthNeeded
= Length
;
1871 /* And fail such that the caller can try again */
1872 Status
= STATUS_BUFFER_TOO_SMALL
;
1876 /* Check if it fits in our static buffer */
1877 if ((StaticBuffer
) && (Length
< StaticLength
))
1879 /* NULL-terminate it */
1880 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1882 /* Set the settings for the dynamic string the caller sent */
1883 DynamicString
->MaximumLength
= StaticLength
;
1884 DynamicString
->Length
= (USHORT
)Length
;
1885 DynamicString
->Buffer
= StaticBuffer
;
1887 /* Set the part size */
1888 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
1890 /* Return the dynamic string if requested */
1891 if (StringUsed
) *StringUsed
= DynamicString
;
1893 /* Do not free the static buffer on exit, and return success */
1894 StaticBuffer
= NULL
;
1895 Status
= STATUS_SUCCESS
;
1899 /* Now try again under the PEB lock */
1900 RtlAcquirePebLock();
1901 Length
= RtlGetFullPathName_Ustr(FileName
,
1910 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
1911 __FUNCTION__
, __LINE__
);
1912 Status
= STATUS_OBJECT_NAME_INVALID
;
1916 /* Check if it fits inside our static string now */
1917 if ((StaticString
) && (Length
< StaticLength
))
1919 /* Set the final length */
1920 StaticString
->Length
= (USHORT
)Length
;
1922 /* Set the file part size */
1923 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
1925 /* Return the static string if requested */
1926 if (StringUsed
) *StringUsed
= StaticString
;
1928 /* We are done with success */
1929 Status
= STATUS_SUCCESS
;
1933 /* Check if the path won't even fit in a real string */
1934 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
1936 /* Name is way too long, fail */
1937 Status
= STATUS_NAME_TOO_LONG
;
1941 /* Allocate the string to hold the path name now */
1942 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
1944 if (!TempDynamicString
.Buffer
)
1946 /* Out of memory, fail */
1947 Status
= STATUS_NO_MEMORY
;
1951 /* Add space for a NULL terminator, and now check the full path */
1952 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
1953 Length
= RtlGetFullPathName_Ustr(FileName
,
1955 TempDynamicString
.Buffer
,
1961 /* Some path error, so fail out */
1962 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
1963 __FUNCTION__
, __LINE__
);
1964 Status
= STATUS_OBJECT_NAME_INVALID
;
1968 /* It should fit in the string we just allocated */
1969 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
1970 if (Length
> TempDynamicString
.MaximumLength
)
1972 /* This is really weird and would mean some kind of race */
1973 Status
= STATUS_INTERNAL_ERROR
;
1977 /* Return the file part size */
1978 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
1980 /* Terminate the whole string now */
1981 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1983 /* Finalize the string and return it to the user */
1984 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
1985 DynamicString
->Length
= (USHORT
)Length
;
1986 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
1987 if (StringUsed
) *StringUsed
= DynamicString
;
1989 /* Return success and make sure we don't free the buffer on exit */
1990 TempDynamicString
.Buffer
= NULL
;
1991 Status
= STATUS_SUCCESS
;
1994 /* Release the PEB lock */
1995 RtlReleasePebLock();
1998 /* Free any buffers we should be freeing */
1999 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2000 if ((StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2002 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2004 if (TempDynamicString
.Buffer
)
2006 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2009 /* Print out any unusual errors */
2010 if ((NT_ERROR(Status
)) &&
2011 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2013 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2014 __FUNCTION__
, FileName
, Status
);
2017 /* Return, we're all done */
2026 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2027 IN PUNICODE_STRING PathString
,
2028 IN PUNICODE_STRING FileNameString
,
2029 IN PUNICODE_STRING ExtensionString
,
2030 IN PUNICODE_STRING CallerBuffer
,
2031 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2032 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2033 OUT PSIZE_T FilePartSize OPTIONAL
,
2034 OUT PSIZE_T LengthNeeded OPTIONAL
)
2036 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2037 UNICODE_STRING StaticCandidateString
;
2039 RTL_PATH_TYPE PathType
;
2040 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2041 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2042 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2043 PUNICODE_STRING FullIsolatedPath
;
2044 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2045 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2047 /* Initialize the input string */
2048 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2049 StaticCandidateBuffer
,
2050 sizeof(StaticCandidateBuffer
));
2052 /* Initialize optional arguments */
2053 if (FullNameOut
) *FullNameOut
= NULL
;
2054 if (FilePartSize
) *FilePartSize
= 0;
2057 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2058 DynamicString
->Buffer
= NULL
;
2061 /* Check for invalid parameters */
2064 !(FileNameString
) ||
2065 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2068 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2069 Status
= STATUS_INVALID_PARAMETER
;
2073 /* First check what kind of path this is */
2074 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2076 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2078 (PathType
== RtlPathTypeRelative
) &&
2079 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2080 (FileNameString
->Buffer
[0] == L
'.') &&
2081 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2082 ((FileNameString
->Buffer
[1] == L
'.') &&
2083 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2084 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2086 /* Yes, and this path is like that, so make it seem unknown */
2087 PathType
= RtlPathTypeUnknown
;
2090 /* Now check relative vs non-relative paths */
2091 if (PathType
== RtlPathTypeRelative
)
2093 /* Does the caller want SxS? */
2096 /* Apply the SxS magic */
2097 FullIsolatedPath
= NULL
;
2098 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2107 if (NT_SUCCESS(Status
))
2109 /* We found the SxS path, return it */
2110 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2113 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2115 /* Critical SxS error, fail */
2116 DbgPrint("%s: Failing because call to "
2117 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2126 /* No SxS key found, or not requested, check if there's an extension */
2127 if (ExtensionString
)
2129 /* Save the extension length, and check if there's a file name */
2130 ExtensionLength
= ExtensionString
->Length
;
2131 if (FileNameString
->Length
)
2133 /* Start parsing the file name */
2134 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2135 while (End
> FileNameString
->Buffer
)
2137 /* If we find a path separator, there's no extension */
2138 if (IS_PATH_SEPARATOR(*--End
)) break;
2140 /* Otherwise, did we find an extension dot? */
2143 /* Ignore what the caller sent it, use the filename's */
2144 ExtensionString
= NULL
;
2145 ExtensionLength
= 0;
2152 /* Check if we got a path */
2153 if (PathString
->Length
)
2155 /* Start parsing the path name, looking for path separators */
2156 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2158 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2160 /* This is the size of the path -- handle a trailing slash */
2161 PathSize
= End
- p
- 1;
2162 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2164 /* Check if we found a bigger path than before */
2165 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2167 /* Keep going with the path after this path separator */
2171 /* This is the trailing path, run the same code as above */
2173 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2174 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2176 /* Finally, convert the largest path size into WCHAR */
2177 MaxPathSize
*= sizeof(WCHAR
);
2180 /* Use the extension, the file name, and the largest path as the size */
2181 WorstCaseLength
= ExtensionLength
+
2182 FileNameString
->Length
+
2183 (USHORT
)MaxPathSize
+
2184 sizeof(UNICODE_NULL
);
2185 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2187 /* It has to fit in a registry string, if not, fail here */
2188 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2189 "worst case file name length is %Iu bytes\n",
2192 Status
= STATUS_NAME_TOO_LONG
;
2196 /* Scan the path now, to see if we can find the file */
2197 p
= PathString
->Buffer
;
2198 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2201 /* Find out where this path ends */
2202 for (SegmentEnd
= p
;
2203 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2206 /* Compute the size of this path */
2207 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2209 /* Handle trailing slash if there isn't one */
2210 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2212 /* Add space for one */
2213 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2216 /* Now check if our initial static buffer is too small */
2217 if (StaticCandidateString
.MaximumLength
<
2218 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2220 /* At this point we should've been using our static buffer */
2221 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2222 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2224 /* Something is really messed up if this was the dynamic string */
2225 DbgPrint("%s: internal error #1; "
2226 "CandidateString.Buffer = %p; "
2227 "StaticCandidateBuffer = %p\n",
2229 StaticCandidateString
.Buffer
,
2230 StaticCandidateBuffer
);
2231 Status
= STATUS_INTERNAL_ERROR
;
2235 /* We checked before that the maximum possible size shoudl fit! */
2236 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2237 UNICODE_STRING_MAX_BYTES
);
2238 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2239 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2241 /* For some reason it's not fitting anymore. Something messed up */
2242 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2243 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2246 FileNameString
->Length
,
2248 Status
= STATUS_INTERNAL_ERROR
;
2252 /* Now allocate the dynamic string */
2253 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2255 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2257 if (!StaticCandidateString
.Buffer
)
2259 /* Out of memory, fail */
2260 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2262 StaticCandidateString
.MaximumLength
);
2263 Status
= STATUS_NO_MEMORY
;
2268 /* Copy the path in the string */
2269 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2271 /* Get to the end of the string, and add the trailing slash if missing */
2272 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2273 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2275 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2278 /* Copy the filename now */
2279 RtlCopyMemory(CandidateEnd
,
2280 FileNameString
->Buffer
,
2281 FileNameString
->Length
);
2282 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2284 /* Check if there was an extension */
2285 if (ExtensionString
)
2287 /* Copy the extension too */
2288 RtlCopyMemory(CandidateEnd
,
2289 ExtensionString
->Buffer
,
2290 ExtensionString
->Length
);
2291 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2294 /* We are done, terminate it */
2295 *CandidateEnd
= UNICODE_NULL
;
2297 /* Now set the final length of the string so it becomes valid */
2298 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2299 StaticCandidateString
.Buffer
) *
2302 /* Check if this file exists */
2303 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2304 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2306 /* Awesome, it does, now get the full path */
2307 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2310 (PUNICODE_STRING
*)FullNameOut
,
2315 if (!(NT_SUCCESS(Status
)) &&
2316 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2317 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2319 DbgPrint("%s: Failing because we thought we found %wZ on "
2320 "the search path, but RtlGetfullPathNameUStrEx() "
2325 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2330 /* Otherwise, move to the next path */
2331 if (SegmentEnd
!= End
)
2333 /* Handle the case of the path separator trailing */
2343 /* Loop finished and we didn't break out -- fail */
2344 Status
= STATUS_NO_SUCH_FILE
;
2348 /* We have a full path, so check if it does exist */
2349 DPRINT("%wZ\n", FileNameString
);
2350 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2352 /* It doesn't exist, did we have an extension? */
2353 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2355 /* No extension, so just fail */
2356 Status
= STATUS_NO_SUCH_FILE
;
2360 /* There was an extension, check if the filename already had one */
2361 if (!(Flags
& 4) && (FileNameString
->Length
))
2363 /* Parse the filename */
2364 p
= FileNameString
->Buffer
;
2365 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2368 /* If there's a path separator, there's no extension */
2369 if (IS_PATH_SEPARATOR(*--End
)) break;
2371 /* Othwerwise, did we find an extension dot? */
2374 /* File already had an extension, so fail */
2375 Status
= STATUS_NO_SUCH_FILE
;
2381 /* So there is an extension, we'll try again by adding it */
2382 NamePlusExtLength
= FileNameString
->Length
+
2383 ExtensionString
->Length
+
2384 sizeof(UNICODE_NULL
);
2385 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2387 /* It won't fit in any kind of valid string, so fail */
2388 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2391 Status
= STATUS_NAME_TOO_LONG
;
2395 /* Fill it fit in our temporary string? */
2396 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2398 /* It won't fit anymore, allocate a dynamic string for it */
2399 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2400 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2402 if (!StaticCandidateString
.Buffer
)
2404 /* Ran out of memory, so fail */
2405 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2407 Status
= STATUS_NO_MEMORY
;
2412 /* Copy the filename */
2413 RtlCopyMemory(StaticCandidateString
.Buffer
,
2414 FileNameString
->Buffer
,
2415 FileNameString
->Length
);
2417 /* Copy the extension */
2418 RtlCopyMemory(&StaticCandidateString
.Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)],
2419 ExtensionString
->Buffer
,
2420 ExtensionString
->Length
);
2422 /* Now NULL-terminate */
2423 StaticCandidateString
.Buffer
[StaticCandidateString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2425 /* Finalize the length of the string to make it valid */
2426 StaticCandidateString
.Length
= FileNameString
->Length
+ ExtensionString
->Length
;
2427 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2429 /* And check if this file now exists */
2430 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2432 /* Still no joy, fail out */
2433 Status
= STATUS_NO_SUCH_FILE
;
2437 /* File was found, get the final full path */
2438 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2441 (PUNICODE_STRING
*)FullNameOut
,
2446 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2448 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2449 "failed with status %08lx\n",
2451 &StaticCandidateString
,
2454 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2458 /* File was found on the first try, get the final full path */
2459 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2462 (PUNICODE_STRING
*)FullNameOut
,
2467 if (!(NT_SUCCESS(Status
)) &&
2468 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2469 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2471 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2472 "failed with status %08lx\n",
2477 DPRINT("STatus: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2482 /* Anything that was not an error, turn into STATUS_SUCCESS */
2483 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2485 /* Check if we had a dynamic string */
2486 if ((StaticCandidateString
.Buffer
) &&
2487 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2490 RtlFreeUnicodeString(&StaticCandidateString
);
2493 /* Return the status */
2502 RtlDoesFileExists_U(IN PCWSTR FileName
)
2504 /* Call the new function */
2505 return RtlDoesFileExists_UEx(FileName
, TRUE
);