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 const UNICODE_STRING DeviceRootString
= RTL_CONSTANT_STRING(L
"\\\\.\\");
36 const UNICODE_STRING RtlpDosDevicesUncPrefix
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
37 const UNICODE_STRING RtlpWin32NtRootSlash
= RTL_CONSTANT_STRING(L
"\\\\?\\");
38 const UNICODE_STRING RtlpDosSlashCONDevice
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
39 const UNICODE_STRING RtlpDosDevicesPrefix
= RTL_CONSTANT_STRING(L
"\\??\\");
41 const UNICODE_STRING RtlpDosLPTDevice
= RTL_CONSTANT_STRING(L
"LPT");
42 const UNICODE_STRING RtlpDosCOMDevice
= RTL_CONSTANT_STRING(L
"COM");
43 const UNICODE_STRING RtlpDosPRNDevice
= RTL_CONSTANT_STRING(L
"PRN");
44 const UNICODE_STRING RtlpDosAUXDevice
= RTL_CONSTANT_STRING(L
"AUX");
45 const UNICODE_STRING RtlpDosCONDevice
= RTL_CONSTANT_STRING(L
"CON");
46 const UNICODE_STRING RtlpDosNULDevice
= RTL_CONSTANT_STRING(L
"NUL");
48 PRTLP_CURDIR_REF RtlpCurDirRef
;
50 /* PRIVATE FUNCTIONS **********************************************************/
54 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
59 Path
= PathString
->Buffer
;
60 Chars
= PathString
->Length
/ sizeof(WCHAR
);
62 /* Return if there are no characters */
63 if (!Chars
) return RtlPathTypeRelative
;
66 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
67 * actually check for the path length before touching the characters
69 if (IS_PATH_SEPARATOR(Path
[0]))
71 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
72 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
73 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
74 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
75 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
79 if ((Chars
< 2) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
80 if ((Chars
< 3) || !(IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveRelative
; /* x: */
81 return RtlPathTypeDriveAbsolute
; /* x:\ */
87 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
89 UNICODE_STRING PathCopy
;
91 USHORT PathChars
, ColonCount
= 0;
92 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
95 /* Validate the input */
96 if (!PathString
) return 0;
98 /* Check what type of path this is */
99 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
101 /* Fail for UNC or unknown paths */
102 case RtlPathTypeUnknown
:
103 case RtlPathTypeUncAbsolute
:
106 /* Make special check for the CON device */
107 case RtlPathTypeLocalDevice
:
108 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
110 /* This should return 0x80006 */
111 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
119 /* Make a copy of the string */
120 PathCopy
= *PathString
;
121 OriginalLength
= PathString
->Length
;
123 /* Return if there's no characters */
124 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
125 if (!PathChars
) return 0;
127 /* Check for drive path and truncate */
128 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
130 /* Fixup the lengths */
131 PathCopy
.Length
-= sizeof(WCHAR
);
132 if (!--PathChars
) return 0;
134 /* Remember this for later */
138 /* Check for extension or space, and truncate */
141 /* Stop if we hit something else than a space or period */
142 c
= PathCopy
.Buffer
[PathChars
- 1];
143 if ((c
!= L
'.') && (c
!= L
' ')) break;
145 /* Fixup the lengths */
146 PathCopy
.Length
-= sizeof(WCHAR
);
148 /* Remember this for later */
150 } while (--PathChars
);
152 /* Anything still left? */
155 /* Loop from the end */
156 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
157 End
>= PathCopy
.Buffer
;
160 /* Check if the character is a path or drive separator */
162 if (IS_PATH_SEPARATOR(c
) || ((c
== L
':') && (End
== PathCopy
.Buffer
+ 1)))
164 /* Get the next lower case character */
166 c
= RtlpDowncaseUnicodeChar(*End
);
168 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
169 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
170 ((c
== L
'l') || (c
== L
'c') || (c
== L
'p') || (c
== L
'a') || (c
== L
'n')))
172 /* Calculate the offset */
173 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
175 /* Build the final string */
176 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
177 PathCopy
.Buffer
= End
;
179 /* Save new amount of chars in the path */
180 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
191 /* Get the next lower case character and check if it's a DOS device */
192 c
= RtlpDowncaseUnicodeChar(*PathCopy
.Buffer
);
193 if ((c
!= L
'l') && (c
!= L
'c') && (c
!= L
'p') && (c
!= L
'a') && (c
!= L
'n'))
195 /* Not LPT, COM, PRN, AUX, or NUL */
200 /* Now skip past any extra extension or drive letter characters */
201 Start
= PathCopy
.Buffer
;
202 End
= &Start
[PathChars
];
206 if ((c
== L
'.') || (c
== L
':')) break;
210 /* And then go backwards to get rid of spaces */
211 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == L
' ')) --Start
;
213 /* Finally see how many characters are left, and that's our size */
214 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
215 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
217 /* Check if this is a COM or LPT port, which has a digit after it */
218 if ((PathChars
== 4) &&
219 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != L
'0')))
221 /* Don't compare the number part, just check for LPT or COM */
222 PathCopy
.Length
-= sizeof(WCHAR
);
223 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
224 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
227 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
228 return MAKELONG(ReturnLength
, ReturnOffset
);
231 else if ((PathChars
== 3) &&
232 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
233 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
234 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
235 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
237 /* Otherwise this was something like AUX, NUL, PRN, or CON */
238 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
239 return MAKELONG(ReturnLength
, ReturnOffset
);
242 /* Otherwise, this is not a valid DOS device */
248 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
250 OUT PBOOLEAN NameInvalid
)
255 /* Allocate a large enough buffer */
256 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
262 /* Copy the filename */
263 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
265 /* And add a dot at the end */
266 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
267 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
269 /* Check if the file exists or not */
270 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
272 /* Get rid of the buffer now */
273 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
277 /* Assume the name is ok, but fail the call */
278 *NameInvalid
= FALSE
;
279 Status
= STATUS_NO_MEMORY
;
282 /* Return the status */
288 /******************************************************************
289 * RtlpCollapsePath (from WINE)
291 * Helper for RtlGetFullPathName_U
293 * 1) Converts slashes into backslashes and gets rid of duplicated ones;
294 * 2) Gets rid of . and .. components in the path.
296 * Returns the full path length without its terminating NULL character.
299 RtlpCollapsePath(PWSTR Path
, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark
, BOOLEAN SkipTrailingPathSeparators
)
303 // FIXME: Do not suppose NULL-terminated strings!!
305 ULONG PathLength
= wcslen(Path
);
306 PWSTR EndBuffer
= Path
+ PathLength
; // Path + PathBufferSize / sizeof(WCHAR);
309 /* Convert slashes into backslashes */
310 for (p
= Path
; *p
; p
++)
312 if (*p
== L
'/') *p
= L
'\\';
315 /* Collapse duplicate backslashes */
316 next
= Path
+ max( 1, mark
);
317 for (p
= next
; *p
; p
++)
319 if (*p
!= L
'\\' || next
[-1] != L
'\\') *next
++ = *p
;
321 *next
= UNICODE_NULL
;
331 case UNICODE_NULL
: /* final . */
332 if (p
> Path
+ mark
) p
--;
337 case L
'\\': /* .\ component */
339 // ASSERT(EndPath - next == wcslen(next));
340 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
341 EndPath
-= (next
- p
);
345 if (p
[2] == L
'\\') /* ..\ component */
351 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
353 // ASSERT(EndPath - next == wcslen(next));
354 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
355 EndPath
-= (next
- p
);
358 else if (p
[2] == UNICODE_NULL
) /* final .. */
363 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
364 if (p
> Path
+ mark
) p
--;
374 /* Skip to the next component */
375 while (*p
&& *p
!= L
'\\') p
++;
378 /* Remove last dot in previous dir name */
379 if (p
> Path
+ mark
&& p
[-1] == L
'.')
381 // ASSERT(EndPath - p == wcslen(p));
382 RtlMoveMemory(p
- 1, p
, (EndPath
- p
+ 1) * sizeof(WCHAR
));
392 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
393 if (SkipTrailingPathSeparators
)
395 while (p
> Path
+ mark
&& IS_PATH_SEPARATOR(p
[-1])) p
--;
398 /* Remove trailing spaces and dots (for all the path) */
399 while (p
> Path
&& (p
[-1] == L
' ' || p
[-1] == L
'.')) p
--;
402 * Zero-out the discarded buffer zone, starting just after
403 * the path string and going up to the end of the buffer.
404 * It also NULL-terminate the path string.
406 ASSERT(EndBuffer
>= p
);
407 RtlZeroMemory(p
, (EndBuffer
- p
+ 1) * sizeof(WCHAR
));
409 /* Return the real path length */
410 PathLength
= (p
- Path
);
411 // ASSERT(PathLength == wcslen(Path));
412 return (PathLength
* sizeof(WCHAR
));
415 /******************************************************************
416 * RtlpSkipUNCPrefix (from WINE)
418 * Helper for RtlGetFullPathName_U
420 * Skips the \\share\dir part of a file name and returns the new position
421 * (which can point on the last backslash of "dir\").
424 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer
)
426 PCWSTR UncPath
= FileNameBuffer
+ 2;
427 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer
);
429 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* share name */
430 while (IS_PATH_SEPARATOR(*UncPath
)) UncPath
++;
431 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* dir name */
432 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
434 return (UncPath
- FileNameBuffer
);
439 RtlpApplyLengthFunction(IN ULONG Flags
,
441 IN PVOID UnicodeStringOrUnicodeStringBuffer
,
442 IN PVOID LengthFunction
)
445 return STATUS_NOT_IMPLEMENTED
;
450 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags
,
452 OUT PULONG LengthOut
)
455 return STATUS_NOT_IMPLEMENTED
;
460 RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName
,
461 IN PUNICODE_STRING a2
,
462 IN PUNICODE_STRING a3
)
465 return STATUS_NOT_IMPLEMENTED
;
470 RtlGetFullPathName_Ustr(
471 _In_ PUNICODE_STRING FileName
,
473 _Out_z_bytecap_(Size
) PWSTR Buffer
,
474 _Out_opt_ PCWSTR
*ShortName
,
475 _Out_opt_ PBOOLEAN InvalidName
,
476 _Out_ RTL_PATH_TYPE
*PathType
)
479 PWCHAR FileNameBuffer
;
480 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
481 BOOLEAN SkipTrailingPathSeparators
;
488 PCUNICODE_STRING CurDirName
;
489 UNICODE_STRING EnvVarName
, EnvVarValue
;
490 WCHAR EnvVarNameBuffer
[4];
492 ULONG PrefixCut
= 0; // Where the path really starts (after the skipped prefix)
493 PWCHAR Prefix
= NULL
; // pointer to the string to be inserted as the new path prefix
494 ULONG PrefixLength
= 0;
499 /* For now, assume the name is valid */
500 DPRINT("Filename: %wZ\n", FileName
);
501 DPRINT("Size and buffer: %lx %p\n", Size
, Buffer
);
502 if (InvalidName
) *InvalidName
= FALSE
;
504 /* Handle initial path type and failure case */
505 *PathType
= RtlPathTypeUnknown
;
506 if ((FileName
->Length
== 0) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
508 /* Break filename into component parts */
509 FileNameBuffer
= FileName
->Buffer
;
510 FileNameLength
= FileName
->Length
;
511 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
513 /* Kill trailing spaces */
514 c
= FileNameBuffer
[FileNameChars
- 1];
515 while ((FileNameLength
!= 0) && (c
== L
' '))
517 /* Keep going, ignoring the spaces */
518 FileNameLength
-= sizeof(WCHAR
);
519 if (FileNameLength
!= 0) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
522 /* Check if anything is left */
523 if (FileNameLength
== 0) return 0;
526 * Check whether we'll need to skip trailing path separators in the
527 * computed full path name. If the original file name already contained
528 * trailing separators, then we keep them in the full path name. On the
529 * other hand, if the original name didn't contain any trailing separators
530 * then we'll skip it in the full path name.
532 SkipTrailingPathSeparators
= !IS_PATH_SEPARATOR(FileNameBuffer
[FileNameChars
- 1]);
534 /* Check if this is a DOS name */
535 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
536 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
539 /* Zero out the short name */
540 if (ShortName
) *ShortName
= NULL
;
542 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
543 DosLengthOffset
= HIWORD(DosLength
);
544 DosLength
= LOWORD(DosLength
);
546 /* Do we have a DOS length, and does the caller want validity? */
547 if (InvalidName
&& (DosLengthOffset
!= 0))
550 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
552 /* If the check failed, or the name is invalid, fail here */
553 if (!NT_SUCCESS(Status
)) return 0;
554 if (*InvalidName
) return 0;
557 /* Add the size of the device root and check if it fits in the size */
558 FullLength
= DosLength
+ DeviceRootString
.Length
;
559 if (FullLength
< Size
)
561 /* Add the device string */
562 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
564 /* Now add the DOS device name */
565 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
566 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
570 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
574 /* Otherwise, there's no space, so return the buffer size needed */
575 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
576 return FullLength
+ sizeof(UNICODE_NULL
);
579 /* Zero-out the destination buffer. FileName must be different from Buffer */
580 RtlZeroMemory(Buffer
, Size
);
582 /* Get the path type */
583 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
587 /**********************************************
588 ** CODE REWRITING IS HAPPENING THERE **
589 **********************************************/
590 Source
= FileNameBuffer
;
591 SourceLength
= FileNameLength
;
592 EnvVarValue
.Buffer
= NULL
;
594 /* Lock the PEB to get the current directory */
596 CurDirName
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
600 case RtlPathTypeUncAbsolute
: /* \\foo */
602 PrefixCut
= RtlpSkipUNCPrefix(FileNameBuffer
);
606 case RtlPathTypeLocalDevice
: /* \\.\foo */
612 case RtlPathTypeDriveAbsolute
: /* c:\foo */
614 ASSERT(FileNameBuffer
[1] == L
':');
615 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer
[2]));
617 // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
618 Prefix
= FileNameBuffer
;
619 PrefixLength
= 3 * sizeof(WCHAR
);
621 SourceLength
-= 3 * sizeof(WCHAR
);
627 case RtlPathTypeDriveRelative
: /* c:foo */
629 WCHAR CurDrive
, NewDrive
;
632 SourceLength
-= 2 * sizeof(WCHAR
);
634 CurDrive
= RtlpUpcaseUnicodeChar(CurDirName
->Buffer
[0]);
635 NewDrive
= RtlpUpcaseUnicodeChar(FileNameBuffer
[0]);
637 if ((NewDrive
!= CurDrive
) || CurDirName
->Buffer
[1] != L
':')
639 EnvVarNameBuffer
[0] = L
'=';
640 EnvVarNameBuffer
[1] = NewDrive
;
641 EnvVarNameBuffer
[2] = L
':';
642 EnvVarNameBuffer
[3] = UNICODE_NULL
;
644 EnvVarName
.Length
= 3 * sizeof(WCHAR
);
645 EnvVarName
.MaximumLength
= EnvVarName
.Length
+ sizeof(WCHAR
);
646 EnvVarName
.Buffer
= EnvVarNameBuffer
;
648 // FIXME: Is it possible to use the user-given buffer ?
649 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
650 EnvVarValue
.Length
= 0;
651 EnvVarValue
.MaximumLength
= (USHORT
)Size
;
652 EnvVarValue
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
653 if (EnvVarValue
.Buffer
== NULL
)
660 Status
= RtlQueryEnvironmentVariable_U(NULL
, &EnvVarName
, &EnvVarValue
);
666 * FIXME: Win2k seems to check that the environment
667 * variable actually points to an existing directory.
668 * If not, root of the drive is used (this seems also
669 * to be the only place in RtlGetFullPathName that the
670 * existence of a part of a path is checked).
672 EnvVarValue
.Buffer
[EnvVarValue
.Length
/ sizeof(WCHAR
)] = L
'\\';
673 Prefix
= EnvVarValue
.Buffer
;
674 PrefixLength
= EnvVarValue
.Length
+ sizeof(WCHAR
); /* Append trailing '\\' */
677 case STATUS_BUFFER_TOO_SMALL
:
678 reqsize
= EnvVarValue
.Length
+ SourceLength
+ sizeof(UNICODE_NULL
);
682 DPRINT1("RtlQueryEnvironmentVariable_U returned 0x%08lx\n", Status
);
684 EnvVarNameBuffer
[0] = NewDrive
;
685 EnvVarNameBuffer
[1] = L
':';
686 EnvVarNameBuffer
[2] = L
'\\';
687 EnvVarNameBuffer
[3] = UNICODE_NULL
;
688 Prefix
= EnvVarNameBuffer
;
689 PrefixLength
= 3 * sizeof(WCHAR
);
691 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
692 EnvVarValue
.Buffer
= NULL
;
699 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
702 case RtlPathTypeRelative
: /* foo */
704 Prefix
= CurDirName
->Buffer
;
705 PrefixLength
= CurDirName
->Length
;
706 if (CurDirName
->Buffer
[1] != L
':')
708 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
717 case RtlPathTypeRooted
: /* \xxx */
719 if (CurDirName
->Buffer
[1] == L
':')
721 // The path starts with "C:\"
722 ASSERT(CurDirName
->Buffer
[1] == L
':');
723 ASSERT(IS_PATH_SEPARATOR(CurDirName
->Buffer
[2]));
725 Prefix
= CurDirName
->Buffer
;
726 PrefixLength
= 3 * sizeof(WCHAR
); // Skip "C:\"
728 PrefixCut
= 3; // Source index location incremented of + 3
732 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
733 PrefixLength
= PrefixCut
* sizeof(WCHAR
);
734 Prefix
= CurDirName
->Buffer
;
739 case RtlPathTypeRootLocalDevice
: /* \\. */
741 Prefix
= DeviceRootString
.Buffer
;
742 PrefixLength
= DeviceRootString
.Length
;
744 SourceLength
-= 3 * sizeof(WCHAR
);
750 case RtlPathTypeUnknown
:
754 /* Do we have enough space for storing the full path? */
755 reqsize
= PrefixLength
;
756 if (reqsize
+ SourceLength
+ sizeof(WCHAR
) > Size
)
758 /* Not enough space, return needed size (including terminating '\0') */
759 reqsize
+= SourceLength
+ sizeof(WCHAR
);
764 * Build the full path
766 /* Copy the path's prefix */
767 if (PrefixLength
) RtlMoveMemory(Buffer
, Prefix
, PrefixLength
);
768 /* Copy the remaining part of the path */
769 RtlMoveMemory(Buffer
+ PrefixLength
/ sizeof(WCHAR
), Source
, SourceLength
+ sizeof(WCHAR
));
773 if (EnvVarValue
.Buffer
)
775 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
776 EnvVarValue
.Buffer
= NULL
;
780 * Finally, put the path in canonical form (remove redundant . and ..,
781 * (back)slashes...) and retrieve the length of the full path name
782 * (without its terminating null character) (in chars).
784 reqsize
= RtlpCollapsePath(Buffer
, /* Size, reqsize, */ PrefixCut
, SkipTrailingPathSeparators
);
786 /* Find the file part, which is present after the last path separator */
789 ptr
= wcsrchr(Buffer
, L
'\\');
790 if (ptr
) ++ptr
; // Skip it
793 * For UNC paths, the file part is after the \\share\dir part of the path.
795 PrefixCut
= (*PathType
== RtlPathTypeUncAbsolute
? PrefixCut
: 3);
797 if (ptr
&& *ptr
&& (ptr
>= Buffer
+ PrefixCut
))
803 /* Zero-out the short name */
809 /* Release PEB lock */
817 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
818 OUT PUNICODE_STRING NtPath
,
819 OUT PCWSTR
*PartName
,
820 OUT PRTL_RELATIVE_NAME_U RelativeName
)
825 /* Validate the input */
826 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
828 /* Validate the DOS length */
829 DosLength
= DosPath
->Length
;
830 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
832 /* Make space for the new path */
833 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
835 DosLength
+ sizeof(UNICODE_NULL
));
836 if (!NewBuffer
) return STATUS_NO_MEMORY
;
838 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
839 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
840 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
841 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
842 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
843 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
845 /* Did the caller send a relative name? */
848 /* Zero initialize it */
849 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
850 RelativeName
->ContainingDirectory
= NULL
;
851 RelativeName
->CurDirRef
= 0;
854 /* Did the caller request a partial name? */
857 /* Loop from the back until we find a path separator */
858 p
= &NewBuffer
[DosLength
/ sizeof(WCHAR
)];
859 while (--p
> NewBuffer
)
861 /* We found a path separator, move past it */
862 if (*p
== OBJ_NAME_PATH_SEPARATOR
)
869 /* Check whether a separator was found and if something remains */
870 if ((p
> NewBuffer
) && *p
)
872 /* What follows the path separator is the partial name */
877 /* The path ends with a path separator, no partial name */
882 /* Build the final NT path string */
883 NtPath
->Buffer
= NewBuffer
;
884 NtPath
->Length
= (USHORT
)DosLength
;
885 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
886 return STATUS_SUCCESS
;
891 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
892 IN PCUNICODE_STRING DosName
,
893 OUT PUNICODE_STRING NtName
,
894 OUT PCWSTR
*PartName
,
895 OUT PRTL_RELATIVE_NAME_U RelativeName
)
897 WCHAR BigBuffer
[MAX_PATH
+ 1];
898 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
899 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
900 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
902 RTL_PATH_TYPE InputPathType
, BufferPathType
;
905 PCURDIR CurrentDirectory
;
907 /* Assume MAX_PATH for now */
908 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
909 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
910 MaxLength
= sizeof(BigBuffer
);
912 /* Validate the input */
913 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
915 /* Capture input string */
916 CapturedDosName
= *DosName
;
918 /* Check for the presence or absence of the NT prefix "\\?\" form */
919 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
920 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
921 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
922 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
923 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
924 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
926 /* NT prefix not present */
928 /* Quick path won't be used */
931 /* Use the static buffer */
933 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
935 /* Allocate a buffer to hold the path */
936 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
937 DPRINT("MaxLength: %lx\n", MaxLength
);
938 if (!NewBuffer
) return STATUS_NO_MEMORY
;
942 /* NT prefix present */
944 /* Use the optimized path after acquiring the lock */
949 /* Lock the PEB and check if the quick path can be used */
953 /* Some simple fixups will get us the correct path */
954 DPRINT("Quick path\n");
955 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
960 /* Release the lock, we're done here */
965 /* Call the main function to get the full path name and length */
966 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
967 MAX_PATH
* sizeof(WCHAR
),
972 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
974 /* Invalid name, fail */
975 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
976 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
978 return STATUS_OBJECT_NAME_INVALID
;
981 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
982 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
983 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
986 /* Check where it really is */
987 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
988 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
989 switch (BufferPathType
)
991 /* It's actually a UNC path in \??\UNC\ */
992 case RtlPathTypeUncAbsolute
:
993 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
994 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
998 case RtlPathTypeLocalDevice
:
999 /* We made a good guess, go with it but skip the \??\ */
1003 case RtlPathTypeDriveAbsolute
:
1004 case RtlPathTypeDriveRelative
:
1005 case RtlPathTypeRooted
:
1006 case RtlPathTypeRelative
:
1007 /* Our guess was good, roll with it */
1010 /* Nothing else is expected */
1015 /* Now copy the prefix and the buffer */
1016 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
1017 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
1019 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
1021 /* Compute the length */
1022 Length
= PathLength
+ PrefixLength
- PrefixCut
* sizeof(WCHAR
);
1023 LengthChars
= Length
/ sizeof(WCHAR
);
1025 /* Setup the actual NT path string and terminate it */
1026 NtName
->Buffer
= NewBuffer
;
1027 NtName
->Length
= (USHORT
)Length
;
1028 NtName
->MaximumLength
= (USHORT
)MaxLength
;
1029 NewBuffer
[LengthChars
] = UNICODE_NULL
;
1030 DPRINT("New buffer: %S\n", NewBuffer
);
1031 DPRINT("NT Name: %wZ\n", NtName
);
1033 /* Check if a partial name was requested */
1034 if ((PartName
) && (*PartName
))
1036 /* Convert to Unicode */
1037 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
1038 if (NT_SUCCESS(Status
))
1040 /* Set the partial name */
1041 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
1046 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1047 RtlReleasePebLock();
1052 /* Check if a relative name was asked for */
1055 /* Setup the structure */
1056 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
1057 RelativeName
->ContainingDirectory
= NULL
;
1058 RelativeName
->CurDirRef
= NULL
;
1060 /* Check if the input path itself was relative */
1061 if (InputPathType
== RtlPathTypeRelative
)
1063 /* Get current directory */
1064 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
1065 if (CurrentDirectory
->Handle
)
1067 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
1068 if (!NT_SUCCESS(Status
))
1070 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1071 RtlReleasePebLock();
1075 /* If current directory is bigger than full path, there's no way */
1076 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
1078 RtlReleasePebLock();
1082 /* File is in current directory */
1083 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
1085 /* Make relative name string */
1086 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ PrefixLength
+ FullPath
.Length
- PrefixCut
* sizeof(WCHAR
));
1087 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
1088 /* If relative name starts with \, skip it */
1089 if (RelativeName
->RelativeName
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1091 RelativeName
->RelativeName
.Buffer
++;
1092 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
1094 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
1095 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
1099 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1103 /* Give back current directory data & reference counter */
1104 RelativeName
->CurDirRef
= RtlpCurDirRef
;
1105 if (RelativeName
->CurDirRef
)
1107 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
1110 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1117 RtlReleasePebLock();
1118 return STATUS_SUCCESS
;
1123 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
1125 OUT PUNICODE_STRING NtName
,
1126 OUT PCWSTR
*PartName
,
1127 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1130 UNICODE_STRING NameString
;
1132 /* Create the unicode name */
1133 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
1134 if (NT_SUCCESS(Status
))
1136 /* Call the unicode function */
1137 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
1150 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
1151 OUT PUNICODE_STRING NtName
,
1152 OUT PCWSTR
*PartName
,
1153 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1155 /* Call the internal function */
1156 ASSERT(RelativeName
);
1157 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
1166 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1167 IN BOOLEAN SucceedIfBusy
)
1170 RTL_RELATIVE_NAME_U RelativeName
;
1171 UNICODE_STRING NtPathName
;
1173 OBJECT_ATTRIBUTES ObjectAttributes
;
1175 FILE_BASIC_INFORMATION BasicInformation
;
1177 /* Get the NT Path */
1178 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1182 if (!Result
) return FALSE
;
1184 /* Save the buffer */
1185 Buffer
= NtPathName
.Buffer
;
1187 /* Check if we have a relative name */
1188 if (RelativeName
.RelativeName
.Length
)
1191 NtPathName
= RelativeName
.RelativeName
;
1195 /* Otherwise ignore it */
1196 RelativeName
.ContainingDirectory
= NULL
;
1199 /* Initialize the object attributes */
1200 InitializeObjectAttributes(&ObjectAttributes
,
1202 OBJ_CASE_INSENSITIVE
,
1203 RelativeName
.ContainingDirectory
,
1206 /* Query the attributes and free the buffer now */
1207 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1208 RtlReleaseRelativeName(&RelativeName
);
1209 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1211 /* Check if we failed */
1212 if (!NT_SUCCESS(Status
))
1214 /* Check if we failed because the file is in use */
1215 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1216 (Status
== STATUS_ACCESS_DENIED
))
1218 /* Check if the caller wants this to be considered OK */
1219 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1223 /* A failure because the file didn't exist */
1229 /* The file exists */
1233 /* Return the result */
1239 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1241 /* Call the updated API */
1242 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1247 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1248 IN BOOLEAN SucceedIfBusy
)
1250 UNICODE_STRING NameString
;
1252 /* Create the unicode name*/
1253 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1255 /* Call the unicode function */
1256 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
1263 /* PUBLIC FUNCTIONS ***********************************************************/
1270 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
1272 /* Check if a directory reference was grabbed */
1273 if (RelativeName
->CurDirRef
)
1275 /* Decrease reference count */
1276 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
1278 /* If no one uses it any longer, close handle & free */
1279 NtClose(RelativeName
->CurDirRef
->Handle
);
1280 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
1282 RelativeName
->CurDirRef
= NULL
;
1291 RtlGetLongestNtPathLength(VOID
)
1294 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1295 * a mapped network drive), which is accessed through the DOS Global?? path.
1296 * This is, and has always been equal to, 269 characters, except in Wine
1297 * which claims this is 277. Go figure.
1299 return MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
/ sizeof(WCHAR
) + sizeof(ANSI_NULL
);
1304 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1305 * (with a 'e' instead of a 'a' in "Seperators").
1309 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags
,
1310 IN PCUNICODE_STRING PathString
,
1315 /* Parameters validation */
1316 if (Length
== NULL
) return STATUS_INVALID_PARAMETER
;
1320 if (PathString
== NULL
) return STATUS_INVALID_PARAMETER
;
1322 /* No flags are supported yet */
1323 if (Flags
!= 0) return STATUS_INVALID_PARAMETER
;
1325 NumChars
= PathString
->Length
/ sizeof(WCHAR
);
1328 * Notice that we skip the last character, therefore:
1329 * - if we have: "some/path/f" we test for: "some/path/"
1330 * - if we have: "some/path/" we test for: "some/path"
1331 * - if we have: "s" we test for: ""
1332 * - if we have: "" then NumChars was already zero and we aren't there
1335 while (NumChars
> 0 && IS_PATH_SEPARATOR(PathString
->Buffer
[NumChars
- 1]))
1341 return STATUS_SUCCESS
;
1349 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
1351 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
1353 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1354 if (IS_PATH_SEPARATOR(Path
[0]))
1356 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
1357 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
1358 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
1359 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
1360 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
1364 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
1365 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
1366 return RtlPathTypeDriveRelative
; /* x: */
1375 RtlIsDosDeviceName_U(IN PCWSTR Path
)
1377 UNICODE_STRING PathString
;
1380 /* Build the string */
1381 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
1382 if (!NT_SUCCESS(Status
)) return 0;
1385 * Returns 0 if name is not valid DOS device name, or DWORD with
1386 * offset in bytes to DOS device name from beginning of buffer in high word
1387 * and size in bytes of DOS device name in low word
1389 return RtlIsDosDeviceName_Ustr(&PathString
);
1397 RtlGetCurrentDirectory_U(
1398 _In_ ULONG MaximumLength
,
1399 _Out_bytecap_(MaximumLength
) PWSTR Buffer
)
1401 ULONG Length
, Bytes
;
1404 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
1406 /* Lock the PEB to get the current directory */
1407 RtlAcquirePebLock();
1408 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1410 /* Get the buffer and character length */
1411 CurDirName
= CurDir
->DosPath
.Buffer
;
1412 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
1413 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
1416 * DosPath.Buffer should always have a trailing slash. There is an assert
1417 * below which checks for this.
1419 * This function either returns x:\ for a root (keeping the original buffer)
1420 * or it returns x:\path\foo for a directory (replacing the trailing slash
1423 Bytes
= Length
* sizeof(WCHAR
);
1424 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
1426 /* Check if caller does not have enough space */
1427 if (MaximumLength
<= Bytes
)
1429 /* Call has no space for it, fail, add the trailing slash */
1430 RtlReleasePebLock();
1431 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1436 /* Check if caller does not have enough space */
1437 if (MaximumLength
< Bytes
)
1439 /* Call has no space for it, fail */
1440 RtlReleasePebLock();
1445 /* Copy the buffer since we seem to have space */
1446 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
1448 /* The buffer should end with a path separator */
1449 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
1451 /* Again check for our two cases (drive root vs path) */
1452 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
1454 /* Replace the trailing slash with a null */
1455 Buffer
[Length
- 1] = UNICODE_NULL
;
1460 /* Append the null char since there's no trailing slash */
1461 Buffer
[Length
] = UNICODE_NULL
;
1464 /* Release PEB lock */
1465 RtlReleasePebLock();
1466 DPRINT("CurrentDirectory %S\n", Buffer
);
1467 return Length
* sizeof(WCHAR
);
1475 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1479 RTL_PATH_TYPE PathType
;
1480 IO_STATUS_BLOCK IoStatusBlock
;
1481 UNICODE_STRING FullPath
, NtName
;
1482 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1483 OBJECT_ATTRIBUTES ObjectAttributes
;
1484 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1485 ULONG SavedLength
, CharLength
, FullPathLength
;
1486 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1488 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1490 /* Initialize for failure case */
1491 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1493 /* Can't set current directory on DOS device */
1494 if (RtlIsDosDeviceName_Ustr(Path
))
1496 return STATUS_NOT_A_DIRECTORY
;
1499 /* Get current directory */
1500 RtlAcquirePebLock();
1501 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1503 /* Check if we have to drop current handle */
1504 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1506 OldHandle
= CurDir
->Handle
;
1507 CurDir
->Handle
= NULL
;
1510 /* Allocate a buffer for full path (using max possible length */
1511 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1512 if (!FullPath
.Buffer
)
1514 Status
= STATUS_NO_MEMORY
;
1519 FullPath
.Length
= 0;
1520 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1522 /* Get new directory full path */
1523 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1524 if (!FullPathLength
)
1526 Status
= STATUS_OBJECT_NAME_INVALID
;
1530 SavedLength
= FullPath
.MaximumLength
;
1531 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1533 if (FullPathLength
> FullPath
.MaximumLength
)
1535 Status
= STATUS_NAME_TOO_LONG
;
1539 /* Translate it to NT name */
1540 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1542 Status
= STATUS_OBJECT_NAME_INVALID
;
1546 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1547 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1550 /* If previous current directory was removable, then check it for dropping */
1551 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1553 /* Get back normal handle */
1554 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1555 CurDir
->Handle
= NULL
;
1557 /* Get device information */
1558 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1561 sizeof(FileFsDeviceInfo
),
1562 FileFsDeviceInformation
);
1563 /* Retry without taking care of removable device */
1564 if (!NT_SUCCESS(Status
))
1566 Status
= RtlSetCurrentDirectory_U(Path
);
1572 /* Open directory */
1573 Status
= NtOpenFile(&CurDirHandle
,
1574 SYNCHRONIZE
| FILE_TRAVERSE
,
1577 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1578 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1579 if (!NT_SUCCESS(Status
)) goto Leave
;
1581 /* Get device information */
1582 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1585 sizeof(FileFsDeviceInfo
),
1586 FileFsDeviceInformation
);
1587 if (!NT_SUCCESS(Status
)) goto Leave
;
1590 /* If device is removable, mark handle */
1591 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1593 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1596 FullPath
.Length
= (USHORT
)FullPathLength
;
1598 /* If full path isn't \ terminated, do it */
1599 if (FullPath
.Buffer
[CharLength
- 1] != OBJ_NAME_PATH_SEPARATOR
)
1601 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1603 Status
= STATUS_NAME_TOO_LONG
;
1607 FullPath
.Buffer
[CharLength
] = OBJ_NAME_PATH_SEPARATOR
;
1608 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1609 FullPath
.Length
+= sizeof(WCHAR
);
1612 /* If we have previous current directory with only us as reference, save it */
1613 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1615 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1619 /* Allocate new current directory struct saving previous one */
1620 OldCurDir
= RtlpCurDirRef
;
1621 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1624 RtlpCurDirRef
= OldCurDir
;
1626 Status
= STATUS_NO_MEMORY
;
1630 /* Set reference to 1 (us) */
1631 RtlpCurDirRef
->RefCount
= 1;
1635 CurDir
->Handle
= CurDirHandle
;
1636 RtlpCurDirRef
->Handle
= CurDirHandle
;
1637 CurDirHandle
= NULL
;
1639 /* Copy full path */
1640 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1641 CurDir
->DosPath
.Length
= FullPath
.Length
;
1643 Status
= STATUS_SUCCESS
;
1646 RtlReleasePebLock();
1648 if (FullPath
.Buffer
)
1650 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1655 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1658 if (CurDirHandle
) NtClose(CurDirHandle
);
1660 if (OldHandle
) NtClose(OldHandle
);
1662 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1664 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1666 NtClose(OldCurDir
->Handle
);
1667 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1674 /******************************************************************
1675 * RtlGetFullPathName_U (NTDLL.@)
1677 * Returns the number of bytes written to buffer (not including the
1678 * terminating NULL) if the function succeeds, or the required number of bytes
1679 * (including the terminating NULL) if the buffer is too small.
1681 * file_part will point to the filename part inside buffer (except if we use
1682 * DOS device name, in which case file_in_buf is NULL)
1692 RtlGetFullPathName_U(
1693 _In_ PCWSTR FileName
,
1695 _Out_z_bytecap_(Size
) PWSTR Buffer
,
1696 _Out_opt_ PWSTR
*ShortName
)
1699 UNICODE_STRING FileNameString
;
1700 RTL_PATH_TYPE PathType
;
1702 /* Build the string */
1703 Status
= RtlInitUnicodeStringEx(&FileNameString
, FileName
);
1704 if (!NT_SUCCESS(Status
)) return 0;
1706 /* Call the extended function */
1707 return RtlGetFullPathName_Ustr(&FileNameString
,
1720 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1721 OUT PUNICODE_STRING NtName
,
1722 OUT PCWSTR
*PartName
,
1723 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1725 /* Call the internal function */
1726 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1738 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1739 OUT PUNICODE_STRING NtName
,
1740 OUT PCWSTR
*PartName
,
1741 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1743 /* Call the internal function */
1744 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1756 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1757 OUT PUNICODE_STRING NtName
,
1758 OUT PCWSTR
*PartName
,
1759 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1761 /* Call the internal function */
1762 ASSERT(RelativeName
);
1763 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1775 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1776 OUT PUNICODE_STRING NtName
,
1777 OUT PCWSTR
*PartName
,
1778 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1780 /* Call the internal function */
1781 ASSERT(RelativeName
);
1782 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1793 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1795 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1796 return STATUS_NOT_IMPLEMENTED
;
1804 RtlDosSearchPath_U(IN PCWSTR Path
,
1806 IN PCWSTR Extension
,
1809 OUT PWSTR
*PartName
)
1812 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1813 UNICODE_STRING TempString
;
1814 PWCHAR NewBuffer
, BufferStart
;
1817 /* Check if this is an absolute path */
1818 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1820 /* Check if the file exists */
1821 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1823 /* Get the full name, which does the DOS lookup */
1824 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1827 /* Doesn't exist, so fail */
1831 /* Scan the filename */
1835 /* Looking for an extension */
1838 /* No extension string needed -- it's part of the filename */
1843 /* Next character */
1847 /* Do we have an extension? */
1850 /* Nope, don't worry about one */
1851 ExtensionLength
= 0;
1855 /* Build a temporary string to get the extension length */
1856 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1857 if (!NT_SUCCESS(Status
)) return 0;
1858 ExtensionLength
= TempString
.Length
;
1861 /* Build a temporary string to get the path length */
1862 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1863 if (!NT_SUCCESS(Status
)) return 0;
1864 PathLength
= TempString
.Length
;
1866 /* Build a temporary string to get the filename length */
1867 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1868 if (!NT_SUCCESS(Status
)) return 0;
1869 FileNameLength
= TempString
.Length
;
1871 /* Allocate the buffer for the new string name */
1872 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1881 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1886 /* Final loop to build the path */
1889 /* Check if we have a valid character */
1890 BufferStart
= NewBuffer
;
1893 /* Loop as long as there's no semicolon */
1894 while (*Path
!= L
';')
1896 /* Copy the next character */
1897 *BufferStart
++ = *Path
++;
1901 /* We found a semi-colon, to stop path processing on this loop */
1902 if (*Path
== L
';') ++Path
;
1905 /* Add a terminating slash if needed */
1906 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != OBJ_NAME_PATH_SEPARATOR
))
1908 *BufferStart
++ = OBJ_NAME_PATH_SEPARATOR
;
1911 /* Bail out if we reached the end */
1912 if (!*Path
) Path
= NULL
;
1914 /* Copy the file name and check if an extension is needed */
1915 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1916 if (ExtensionLength
)
1918 /* Copy the extension too */
1919 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1921 ExtensionLength
+ sizeof(WCHAR
));
1925 /* Just NULL-terminate */
1926 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1929 /* Now, does this file exist? */
1930 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1932 /* Call the full-path API to get the length */
1933 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1937 /* If we got here, path doesn't exist, so fail the call */
1942 /* Free the allocation and return the length */
1943 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1952 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
1953 IN PUNICODE_STRING StaticString
,
1954 IN PUNICODE_STRING DynamicString
,
1955 IN PUNICODE_STRING
*StringUsed
,
1956 IN PSIZE_T FilePartSize
,
1957 OUT PBOOLEAN NameInvalid
,
1958 OUT RTL_PATH_TYPE
* PathType
,
1959 OUT PSIZE_T LengthNeeded
)
1962 PWCHAR StaticBuffer
;
1965 USHORT StaticLength
;
1966 UNICODE_STRING TempDynamicString
;
1968 /* Initialize all our locals */
1970 StaticBuffer
= NULL
;
1971 TempDynamicString
.Buffer
= NULL
;
1973 /* Initialize the input parameters */
1974 if (StringUsed
) *StringUsed
= NULL
;
1975 if (LengthNeeded
) *LengthNeeded
= 0;
1976 if (FilePartSize
) *FilePartSize
= 0;
1978 /* Check for invalid parameters */
1979 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
1981 return STATUS_INVALID_PARAMETER
;
1984 /* Check if we did not get an input string */
1988 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
1989 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
1990 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
1994 /* Use the one we received */
1995 StaticBuffer
= StaticString
->Buffer
;
1996 StaticLength
= StaticString
->MaximumLength
;
1999 /* Call the lower-level function */
2000 Length
= RtlGetFullPathName_Ustr(FileName
,
2006 DPRINT("Length: %u StaticBuffer: %S\n", Length
, StaticBuffer
);
2009 /* Fail if it failed */
2010 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
2013 Status
= STATUS_OBJECT_NAME_INVALID
;
2017 /* Check if it fits inside our static string */
2018 if ((StaticString
) && (Length
< StaticLength
))
2020 /* Set the final length */
2021 StaticString
->Length
= (USHORT
)Length
;
2023 /* Set the file part size */
2024 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2026 /* Return the static string if requested */
2027 if (StringUsed
) *StringUsed
= StaticString
;
2029 /* We are done with success */
2030 Status
= STATUS_SUCCESS
;
2034 /* Did we not have an input dynamic string ?*/
2037 /* Return the length we need */
2038 if (LengthNeeded
) *LengthNeeded
= Length
;
2040 /* And fail such that the caller can try again */
2041 Status
= STATUS_BUFFER_TOO_SMALL
;
2045 /* Check if it fits in our static buffer */
2046 if ((StaticBuffer
) && (Length
< StaticLength
))
2048 /* NULL-terminate it */
2049 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2051 /* Set the settings for the dynamic string the caller sent */
2052 DynamicString
->MaximumLength
= StaticLength
;
2053 DynamicString
->Length
= (USHORT
)Length
;
2054 DynamicString
->Buffer
= StaticBuffer
;
2056 /* Set the part size */
2057 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
2059 /* Return the dynamic string if requested */
2060 if (StringUsed
) *StringUsed
= DynamicString
;
2062 /* Do not free the static buffer on exit, and return success */
2063 StaticBuffer
= NULL
;
2064 Status
= STATUS_SUCCESS
;
2068 /* Now try again under the PEB lock */
2069 RtlAcquirePebLock();
2070 Length
= RtlGetFullPathName_Ustr(FileName
,
2079 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2080 __FUNCTION__
, __LINE__
);
2081 Status
= STATUS_OBJECT_NAME_INVALID
;
2085 /* Check if it fits inside our static string now */
2086 if ((StaticString
) && (Length
< StaticLength
))
2088 /* Set the final length */
2089 StaticString
->Length
= (USHORT
)Length
;
2091 /* Set the file part size */
2092 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2094 /* Return the static string if requested */
2095 if (StringUsed
) *StringUsed
= StaticString
;
2097 /* We are done with success */
2098 Status
= STATUS_SUCCESS
;
2102 /* Check if the path won't even fit in a real string */
2103 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
2105 /* Name is way too long, fail */
2106 Status
= STATUS_NAME_TOO_LONG
;
2110 /* Allocate the string to hold the path name now */
2111 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
2113 if (!TempDynamicString
.Buffer
)
2115 /* Out of memory, fail */
2116 Status
= STATUS_NO_MEMORY
;
2120 /* Add space for a NULL terminator, and now check the full path */
2121 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
2122 Length
= RtlGetFullPathName_Ustr(FileName
,
2124 TempDynamicString
.Buffer
,
2130 /* Some path error, so fail out */
2131 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2132 __FUNCTION__
, __LINE__
);
2133 Status
= STATUS_OBJECT_NAME_INVALID
;
2137 /* It should fit in the string we just allocated */
2138 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
2139 if (Length
> TempDynamicString
.MaximumLength
)
2141 /* This is really weird and would mean some kind of race */
2142 Status
= STATUS_INTERNAL_ERROR
;
2146 /* Return the file part size */
2147 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
2149 /* Terminate the whole string now */
2150 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2152 /* Finalize the string and return it to the user */
2153 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
2154 DynamicString
->Length
= (USHORT
)Length
;
2155 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
2156 if (StringUsed
) *StringUsed
= DynamicString
;
2158 /* Return success and make sure we don't free the buffer on exit */
2159 TempDynamicString
.Buffer
= NULL
;
2160 Status
= STATUS_SUCCESS
;
2163 /* Release the PEB lock */
2164 RtlReleasePebLock();
2167 /* Free any buffers we should be freeing */
2168 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2169 if ((StaticString
) && (StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2171 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2173 if (TempDynamicString
.Buffer
)
2175 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2178 /* Print out any unusual errors */
2179 if ((NT_ERROR(Status
)) &&
2180 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2182 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2183 __FUNCTION__
, FileName
, Status
);
2186 /* Return, we're all done */
2195 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2196 IN PUNICODE_STRING PathString
,
2197 IN PUNICODE_STRING FileNameString
,
2198 IN PUNICODE_STRING ExtensionString
,
2199 IN PUNICODE_STRING CallerBuffer
,
2200 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2201 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2202 OUT PSIZE_T FilePartSize OPTIONAL
,
2203 OUT PSIZE_T LengthNeeded OPTIONAL
)
2205 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2206 UNICODE_STRING StaticCandidateString
;
2208 RTL_PATH_TYPE PathType
;
2209 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2210 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2211 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2212 PUNICODE_STRING FullIsolatedPath
;
2213 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2214 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2216 /* Initialize the input string */
2217 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2218 StaticCandidateBuffer
,
2219 sizeof(StaticCandidateBuffer
));
2221 /* Initialize optional arguments */
2222 if (FullNameOut
) *FullNameOut
= NULL
;
2223 if (FilePartSize
) *FilePartSize
= 0;
2224 if (LengthNeeded
) *LengthNeeded
= 0;
2227 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2228 DynamicString
->Buffer
= NULL
;
2231 /* Check for invalid parameters */
2234 !(FileNameString
) ||
2235 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2238 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2239 Status
= STATUS_INVALID_PARAMETER
;
2243 /* First check what kind of path this is */
2244 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2246 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2248 (PathType
== RtlPathTypeRelative
) &&
2249 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2250 (FileNameString
->Buffer
[0] == L
'.') &&
2251 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2252 ((FileNameString
->Buffer
[1] == L
'.') &&
2253 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2254 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2256 /* Yes, and this path is like that, so make it seem unknown */
2257 PathType
= RtlPathTypeUnknown
;
2260 /* Now check relative vs non-relative paths */
2261 if (PathType
== RtlPathTypeRelative
)
2263 /* Does the caller want SxS? */
2266 /* Apply the SxS magic */
2267 FullIsolatedPath
= NULL
;
2268 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2277 if (NT_SUCCESS(Status
))
2279 /* We found the SxS path, return it */
2280 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2283 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2285 /* Critical SxS error, fail */
2286 DbgPrint("%s: Failing because call to "
2287 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2296 /* No SxS key found, or not requested, check if there's an extension */
2297 if (ExtensionString
)
2299 /* Save the extension length, and check if there's a file name */
2300 ExtensionLength
= ExtensionString
->Length
;
2301 if (FileNameString
->Length
)
2303 /* Start parsing the file name */
2304 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2305 while (End
> FileNameString
->Buffer
)
2307 /* If we find a path separator, there's no extension */
2308 if (IS_PATH_SEPARATOR(*--End
)) break;
2310 /* Otherwise, did we find an extension dot? */
2313 /* Ignore what the caller sent it, use the filename's */
2314 ExtensionString
= NULL
;
2315 ExtensionLength
= 0;
2322 /* Check if we got a path */
2323 if (PathString
->Length
)
2325 /* Start parsing the path name, looking for path separators */
2326 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2328 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2330 /* This is the size of the path -- handle a trailing slash */
2331 PathSize
= End
- p
- 1;
2332 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2334 /* Check if we found a bigger path than before */
2335 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2337 /* Keep going with the path after this path separator */
2341 /* This is the trailing path, run the same code as above */
2343 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2344 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2346 /* Finally, convert the largest path size into WCHAR */
2347 MaxPathSize
*= sizeof(WCHAR
);
2350 /* Use the extension, the file name, and the largest path as the size */
2351 WorstCaseLength
= ExtensionLength
+
2352 FileNameString
->Length
+
2353 (USHORT
)MaxPathSize
+
2354 sizeof(UNICODE_NULL
);
2355 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2357 /* It has to fit in a registry string, if not, fail here */
2358 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2359 "worst case file name length is %Iu bytes\n",
2362 Status
= STATUS_NAME_TOO_LONG
;
2366 /* Scan the path now, to see if we can find the file */
2367 p
= PathString
->Buffer
;
2368 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2371 /* Find out where this path ends */
2372 for (SegmentEnd
= p
;
2373 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2376 /* Compute the size of this path */
2377 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2379 /* Handle trailing slash if there isn't one */
2380 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2382 /* Add space for one */
2383 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2386 /* Now check if our initial static buffer is too small */
2387 if (StaticCandidateString
.MaximumLength
<
2388 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2390 /* At this point we should've been using our static buffer */
2391 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2392 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2394 /* Something is really messed up if this was the dynamic string */
2395 DbgPrint("%s: internal error #1; "
2396 "CandidateString.Buffer = %p; "
2397 "StaticCandidateBuffer = %p\n",
2399 StaticCandidateString
.Buffer
,
2400 StaticCandidateBuffer
);
2401 Status
= STATUS_INTERNAL_ERROR
;
2405 /* We checked before that the maximum possible size shoudl fit! */
2406 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2407 UNICODE_STRING_MAX_BYTES
);
2408 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2409 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2411 /* For some reason it's not fitting anymore. Something messed up */
2412 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2413 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2416 FileNameString
->Length
,
2418 Status
= STATUS_INTERNAL_ERROR
;
2422 /* Now allocate the dynamic string */
2423 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2425 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2427 if (!StaticCandidateString
.Buffer
)
2429 /* Out of memory, fail */
2430 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2432 StaticCandidateString
.MaximumLength
);
2433 Status
= STATUS_NO_MEMORY
;
2438 /* Copy the path in the string */
2439 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2441 /* Get to the end of the string, and add the trailing slash if missing */
2442 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2443 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2445 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2448 /* Copy the filename now */
2449 RtlCopyMemory(CandidateEnd
,
2450 FileNameString
->Buffer
,
2451 FileNameString
->Length
);
2452 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2454 /* Check if there was an extension */
2455 if (ExtensionString
)
2457 /* Copy the extension too */
2458 RtlCopyMemory(CandidateEnd
,
2459 ExtensionString
->Buffer
,
2460 ExtensionString
->Length
);
2461 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2464 /* We are done, terminate it */
2465 *CandidateEnd
= UNICODE_NULL
;
2467 /* Now set the final length of the string so it becomes valid */
2468 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2469 StaticCandidateString
.Buffer
) *
2472 /* Check if this file exists */
2473 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2474 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2476 /* Awesome, it does, now get the full path */
2477 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2480 (PUNICODE_STRING
*)FullNameOut
,
2485 if (!(NT_SUCCESS(Status
)) &&
2486 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2487 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2489 DbgPrint("%s: Failing because we thought we found %wZ on "
2490 "the search path, but RtlGetfullPathNameUStrEx() "
2493 &StaticCandidateString
,
2496 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2501 /* Otherwise, move to the next path */
2502 if (SegmentEnd
!= End
)
2504 /* Handle the case of the path separator trailing */
2514 /* Loop finished and we didn't break out -- fail */
2515 Status
= STATUS_NO_SUCH_FILE
;
2519 /* We have a full path, so check if it does exist */
2520 DPRINT("%wZ\n", FileNameString
);
2521 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2523 /* It doesn't exist, did we have an extension? */
2524 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2526 /* No extension, so just fail */
2527 Status
= STATUS_NO_SUCH_FILE
;
2531 /* There was an extension, check if the filename already had one */
2532 if (!(Flags
& 4) && (FileNameString
->Length
))
2534 /* Parse the filename */
2535 p
= FileNameString
->Buffer
;
2536 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2539 /* If there's a path separator, there's no extension */
2540 if (IS_PATH_SEPARATOR(*--End
)) break;
2542 /* Othwerwise, did we find an extension dot? */
2545 /* File already had an extension, so fail */
2546 Status
= STATUS_NO_SUCH_FILE
;
2552 /* So there is an extension, we'll try again by adding it */
2553 NamePlusExtLength
= FileNameString
->Length
+
2554 ExtensionString
->Length
+
2555 sizeof(UNICODE_NULL
);
2556 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2558 /* It won't fit in any kind of valid string, so fail */
2559 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2562 Status
= STATUS_NAME_TOO_LONG
;
2566 /* Fill it fit in our temporary string? */
2567 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2569 /* It won't fit anymore, allocate a dynamic string for it */
2570 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2571 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2573 if (!StaticCandidateString
.Buffer
)
2575 /* Ran out of memory, so fail */
2576 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2578 Status
= STATUS_NO_MEMORY
;
2583 /* Copy the filename */
2584 RtlCopyUnicodeString(&StaticCandidateString
, FileNameString
);
2586 /* Copy the extension */
2587 RtlAppendUnicodeStringToString(&StaticCandidateString
,
2590 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2592 /* And check if this file now exists */
2593 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2595 /* Still no joy, fail out */
2596 Status
= STATUS_NO_SUCH_FILE
;
2600 /* File was found, get the final full path */
2601 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2604 (PUNICODE_STRING
*)FullNameOut
,
2609 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2611 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2612 "failed with status %08lx\n",
2614 &StaticCandidateString
,
2617 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2621 /* File was found on the first try, get the final full path */
2622 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2625 (PUNICODE_STRING
*)FullNameOut
,
2630 if (!(NT_SUCCESS(Status
)) &&
2631 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2632 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2634 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2635 "failed with status %08lx\n",
2640 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2645 /* Anything that was not an error, turn into STATUS_SUCCESS */
2646 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2648 /* Check if we had a dynamic string */
2649 if ((StaticCandidateString
.Buffer
) &&
2650 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2653 RtlFreeUnicodeString(&StaticCandidateString
);
2656 /* Return the status */
2665 RtlDoesFileExists_U(IN PCWSTR FileName
)
2667 /* Call the new function */
2668 return RtlDoesFileExists_UEx(FileName
, TRUE
);