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 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
60 Path
= PathString
->Buffer
;
61 Chars
= PathString
->Length
/ sizeof(WCHAR
);
63 /* Return if there are no characters */
64 if (!Chars
) return RtlPathTypeRelative
;
67 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
68 * actually check for the path length before touching the characters
70 if (IS_PATH_SEPARATOR(Path
[0]))
72 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
73 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
74 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
75 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
76 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
80 if ((Chars
< 2) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
81 if ((Chars
< 3) || !(IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveRelative
; /* x: */
82 return RtlPathTypeDriveAbsolute
; /* x:\ */
88 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
90 UNICODE_STRING PathCopy
;
92 USHORT PathChars
, ColonCount
= 0;
93 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
96 /* Validate the input */
97 if (!PathString
) return 0;
99 /* Check what type of path this is */
100 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
102 /* Fail for UNC or unknown paths */
103 case RtlPathTypeUnknown
:
104 case RtlPathTypeUncAbsolute
:
107 /* Make special check for the CON device */
108 case RtlPathTypeLocalDevice
:
109 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
111 /* This should return 0x80006 */
112 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
120 /* Make a copy of the string */
121 PathCopy
= *PathString
;
122 OriginalLength
= PathString
->Length
;
124 /* Return if there's no characters */
125 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
126 if (!PathChars
) return 0;
128 /* Check for drive path and truncate */
129 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
131 /* Fixup the lengths */
132 PathCopy
.Length
-= sizeof(WCHAR
);
133 if (!--PathChars
) return 0;
135 /* Remember this for later */
139 /* Check for extension or space, and truncate */
142 /* Stop if we hit something else than a space or period */
143 c
= PathCopy
.Buffer
[PathChars
- 1];
144 if ((c
!= L
'.') && (c
!= L
' ')) break;
146 /* Fixup the lengths */
147 PathCopy
.Length
-= sizeof(WCHAR
);
149 /* Remember this for later */
151 } while (--PathChars
);
153 /* Anything still left? */
156 /* Loop from the end */
157 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
158 End
>= PathCopy
.Buffer
;
161 /* Check if the character is a path or drive separator */
163 if (IS_PATH_SEPARATOR(c
) || ((c
== L
':') && (End
== PathCopy
.Buffer
+ 1)))
165 /* Get the next lower case character */
167 c
= RtlDowncaseUnicodeChar(*End
);
169 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
170 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
171 ((c
== L
'l') || (c
== L
'c') || (c
== L
'p') || (c
== L
'a') || (c
== L
'n')))
173 /* Calculate the offset */
174 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
176 /* Build the final string */
177 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
178 PathCopy
.Buffer
= End
;
180 /* Save new amount of chars in the path */
181 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
192 /* Get the next lower case character and check if it's a DOS device */
193 c
= RtlDowncaseUnicodeChar(*PathCopy
.Buffer
);
194 if ((c
!= L
'l') && (c
!= L
'c') && (c
!= L
'p') && (c
!= L
'a') && (c
!= L
'n'))
196 /* Not LPT, COM, PRN, AUX, or NUL */
201 /* Now skip past any extra extension or drive letter characters */
202 Start
= PathCopy
.Buffer
;
203 End
= &Start
[PathChars
];
207 if ((c
== L
'.') || (c
== L
':')) break;
211 /* And then go backwards to get rid of spaces */
212 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == L
' ')) --Start
;
214 /* Finally see how many characters are left, and that's our size */
215 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
216 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
218 /* Check if this is a COM or LPT port, which has a digit after it */
219 if ((PathChars
== 4) &&
220 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != L
'0')))
222 /* Don't compare the number part, just check for LPT or COM */
223 PathCopy
.Length
-= sizeof(WCHAR
);
224 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
225 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
228 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
229 return MAKELONG(ReturnLength
, ReturnOffset
);
232 else if ((PathChars
== 3) &&
233 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
234 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
235 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
236 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
238 /* Otherwise this was something like AUX, NUL, PRN, or CON */
239 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
240 return MAKELONG(ReturnLength
, ReturnOffset
);
243 /* Otherwise, this is not a valid DOS device */
249 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
251 OUT PBOOLEAN NameInvalid
)
256 /* Allocate a large enough buffer */
257 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
263 /* Copy the filename */
264 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
266 /* And add a dot at the end */
267 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
268 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
270 /* Check if the file exists or not */
271 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
273 /* Get rid of the buffer now */
274 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
278 /* Assume the name is ok, but fail the call */
279 *NameInvalid
= FALSE
;
280 Status
= STATUS_NO_MEMORY
;
283 /* Return the status */
289 /******************************************************************
290 * RtlpCollapsePath (from WINE)
292 * Helper for RtlGetFullPathName_U
294 * 1) Converts slashes into backslashes and gets rid of duplicated ones;
295 * 2) Gets rid of . and .. components in the path.
297 * Returns the full path length without its terminating NULL character.
300 RtlpCollapsePath(PWSTR Path
, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark
, BOOLEAN SkipTrailingPathSeparators
)
304 // FIXME: Do not suppose NULL-terminated strings!!
306 ULONG PathLength
= wcslen(Path
);
307 PWSTR EndBuffer
= Path
+ PathLength
; // Path + PathBufferSize / sizeof(WCHAR);
310 /* Convert slashes into backslashes */
311 for (p
= Path
; *p
; p
++)
313 if (*p
== L
'/') *p
= L
'\\';
316 /* Collapse duplicate backslashes */
317 next
= Path
+ max( 1, mark
);
318 for (p
= next
; *p
; p
++)
320 if (*p
!= L
'\\' || next
[-1] != L
'\\') *next
++ = *p
;
322 *next
= UNICODE_NULL
;
332 case UNICODE_NULL
: /* final . */
333 if (p
> Path
+ mark
) p
--;
338 case L
'\\': /* .\ component */
340 // ASSERT(EndPath - next == wcslen(next));
341 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
342 EndPath
-= (next
- p
);
346 if (p
[2] == L
'\\') /* ..\ component */
352 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
354 // ASSERT(EndPath - next == wcslen(next));
355 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
356 EndPath
-= (next
- p
);
359 else if (p
[2] == UNICODE_NULL
) /* final .. */
364 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
365 if (p
> Path
+ mark
) p
--;
375 /* Skip to the next component */
376 while (*p
&& *p
!= L
'\\') p
++;
379 /* Remove last dot in previous dir name */
380 if (p
> Path
+ mark
&& p
[-1] == L
'.')
382 // ASSERT(EndPath - p == wcslen(p));
383 RtlMoveMemory(p
- 1, p
, (EndPath
- p
+ 1) * sizeof(WCHAR
));
393 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
394 if (SkipTrailingPathSeparators
)
396 while (p
> Path
+ mark
&& IS_PATH_SEPARATOR(p
[-1])) p
--;
399 /* Remove trailing spaces and dots (for all the path) */
400 while (p
> Path
&& (p
[-1] == L
' ' || p
[-1] == L
'.')) p
--;
403 * Zero-out the discarded buffer zone, starting just after
404 * the path string and going up to the end of the buffer.
405 * It also NULL-terminate the path string.
407 ASSERT(EndBuffer
>= p
);
408 RtlZeroMemory(p
, (EndBuffer
- p
+ 1) * sizeof(WCHAR
));
410 /* Return the real path length */
411 PathLength
= (p
- Path
);
412 // ASSERT(PathLength == wcslen(Path));
413 return (PathLength
* sizeof(WCHAR
));
416 /******************************************************************
417 * RtlpSkipUNCPrefix (from WINE)
419 * Helper for RtlGetFullPathName_U
421 * Skips the \\share\dir part of a file name and returns the new position
422 * (which can point on the last backslash of "dir\").
425 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer
)
427 PCWSTR UncPath
= FileNameBuffer
+ 2;
428 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer
);
430 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* share name */
431 while (IS_PATH_SEPARATOR(*UncPath
)) UncPath
++;
432 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* dir name */
433 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
435 return (UncPath
- FileNameBuffer
);
440 RtlpApplyLengthFunction(IN ULONG Flags
,
442 IN PVOID UnicodeStringOrUnicodeStringBuffer
,
443 IN PVOID LengthFunction
)
446 return STATUS_NOT_IMPLEMENTED
;
451 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags
,
453 OUT PULONG LengthOut
)
456 return STATUS_NOT_IMPLEMENTED
;
461 RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName
,
462 IN PUNICODE_STRING a2
,
463 IN PUNICODE_STRING a3
)
466 return STATUS_NOT_IMPLEMENTED
;
471 RtlGetFullPathName_Ustr(
472 _In_ PUNICODE_STRING FileName
,
474 _Out_z_bytecap_(Size
) PWSTR Buffer
,
475 _Out_opt_ PCWSTR
*ShortName
,
476 _Out_opt_ PBOOLEAN InvalidName
,
477 _Out_ RTL_PATH_TYPE
*PathType
)
480 PWCHAR FileNameBuffer
;
481 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
482 BOOLEAN SkipTrailingPathSeparators
;
489 PCUNICODE_STRING CurDirName
;
490 UNICODE_STRING EnvVarName
, EnvVarValue
;
491 WCHAR EnvVarNameBuffer
[4];
493 ULONG PrefixCut
= 0; // Where the path really starts (after the skipped prefix)
494 PWCHAR Prefix
= NULL
; // pointer to the string to be inserted as the new path prefix
495 ULONG PrefixLength
= 0;
500 /* For now, assume the name is valid */
501 DPRINT("Filename: %wZ\n", FileName
);
502 DPRINT("Size and buffer: %lx %p\n", Size
, Buffer
);
503 if (InvalidName
) *InvalidName
= FALSE
;
505 /* Handle initial path type and failure case */
506 *PathType
= RtlPathTypeUnknown
;
507 if ((FileName
->Length
== 0) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
509 /* Break filename into component parts */
510 FileNameBuffer
= FileName
->Buffer
;
511 FileNameLength
= FileName
->Length
;
512 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
514 /* Kill trailing spaces */
515 c
= FileNameBuffer
[FileNameChars
- 1];
516 while ((FileNameLength
!= 0) && (c
== L
' '))
518 /* Keep going, ignoring the spaces */
519 FileNameLength
-= sizeof(WCHAR
);
520 if (FileNameLength
!= 0) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
523 /* Check if anything is left */
524 if (FileNameLength
== 0) return 0;
527 * Check whether we'll need to skip trailing path separators in the
528 * computed full path name. If the original file name already contained
529 * trailing separators, then we keep them in the full path name. On the
530 * other hand, if the original name didn't contain any trailing separators
531 * then we'll skip it in the full path name.
533 SkipTrailingPathSeparators
= !IS_PATH_SEPARATOR(FileNameBuffer
[FileNameChars
- 1]);
535 /* Check if this is a DOS name */
536 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
537 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
540 /* Zero out the short name */
541 if (ShortName
) *ShortName
= NULL
;
543 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
544 DosLengthOffset
= HIWORD(DosLength
);
545 DosLength
= LOWORD(DosLength
);
547 /* Do we have a DOS length, and does the caller want validity? */
548 if (InvalidName
&& (DosLengthOffset
!= 0))
551 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
553 /* If the check failed, or the name is invalid, fail here */
554 if (!NT_SUCCESS(Status
)) return 0;
555 if (*InvalidName
) return 0;
558 /* Add the size of the device root and check if it fits in the size */
559 FullLength
= DosLength
+ DeviceRootString
.Length
;
560 if (FullLength
< Size
)
562 /* Add the device string */
563 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
565 /* Now add the DOS device name */
566 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
567 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
571 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
575 /* Otherwise, there's no space, so return the buffer size needed */
576 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
577 return FullLength
+ sizeof(UNICODE_NULL
);
580 /* Zero-out the destination buffer. FileName must be different from Buffer */
581 RtlZeroMemory(Buffer
, Size
);
583 /* Get the path type */
584 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
588 /**********************************************
589 ** CODE REWRITING IS HAPPENING THERE **
590 **********************************************/
591 Source
= FileNameBuffer
;
592 SourceLength
= FileNameLength
;
593 EnvVarValue
.Buffer
= NULL
;
595 /* Lock the PEB to get the current directory */
597 CurDirName
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
601 case RtlPathTypeUncAbsolute
: /* \\foo */
602 PrefixCut
= RtlpSkipUNCPrefix(FileNameBuffer
);
605 case RtlPathTypeLocalDevice
: /* \\.\foo */
609 case RtlPathTypeDriveAbsolute
: /* c:\foo */
610 ASSERT(FileNameBuffer
[1] == L
':');
611 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer
[2]));
613 Prefix
= FileNameBuffer
;
614 PrefixLength
= 3 * sizeof(WCHAR
);
616 SourceLength
-= 3 * sizeof(WCHAR
);
621 case RtlPathTypeDriveRelative
: /* c:foo */
623 SourceLength
-= 2 * sizeof(WCHAR
);
624 if (RtlUpcaseUnicodeChar(FileNameBuffer
[0]) != RtlUpcaseUnicodeChar(CurDirName
->Buffer
[0]) ||
625 CurDirName
->Buffer
[1] != L
':')
627 EnvVarNameBuffer
[0] = L
'=';
628 EnvVarNameBuffer
[1] = FileNameBuffer
[0];
629 EnvVarNameBuffer
[2] = L
':';
630 EnvVarNameBuffer
[3] = UNICODE_NULL
;
632 EnvVarName
.Length
= 3 * sizeof(WCHAR
);
633 EnvVarName
.MaximumLength
= EnvVarName
.Length
+ sizeof(WCHAR
);
634 EnvVarName
.Buffer
= EnvVarNameBuffer
;
636 // FIXME: Is it possible to use the user-given buffer ?
637 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
638 EnvVarValue
.Length
= 0;
639 EnvVarValue
.MaximumLength
= (USHORT
)Size
;
640 EnvVarValue
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
641 if (EnvVarValue
.Buffer
== NULL
)
648 Status
= RtlQueryEnvironmentVariable_U(NULL
, &EnvVarName
, &EnvVarValue
);
654 * FIXME: Win2k seems to check that the environment
655 * variable actually points to an existing directory.
656 * If not, root of the drive is used (this seems also
657 * to be the only place in RtlGetFullPathName that the
658 * existence of a part of a path is checked).
660 EnvVarValue
.Buffer
[EnvVarValue
.Length
/ sizeof(WCHAR
)] = L
'\\';
661 Prefix
= EnvVarValue
.Buffer
;
662 PrefixLength
= EnvVarValue
.Length
+ sizeof(WCHAR
); /* Append trailing '\\' */
665 case STATUS_BUFFER_TOO_SMALL
:
666 reqsize
= EnvVarValue
.Length
+ SourceLength
+ sizeof(UNICODE_NULL
);
670 DPRINT1("RtlQueryEnvironmentVariable_U returned 0x%08lx\n", Status
);
672 EnvVarNameBuffer
[0] = FileNameBuffer
[0];
673 EnvVarNameBuffer
[1] = L
':';
674 EnvVarNameBuffer
[2] = L
'\\';
675 EnvVarNameBuffer
[3] = UNICODE_NULL
;
676 Prefix
= EnvVarNameBuffer
;
677 PrefixLength
= 3 * sizeof(WCHAR
);
679 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
680 EnvVarValue
.Buffer
= NULL
;
687 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
689 case RtlPathTypeRelative
: /* foo */
690 Prefix
= CurDirName
->Buffer
;
691 PrefixLength
= CurDirName
->Length
;
692 if (CurDirName
->Buffer
[1] != L
':')
694 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
702 case RtlPathTypeRooted
: /* \xxx */
703 if (CurDirName
->Buffer
[1] == L
':')
705 // The path starts with "C:\"
706 ASSERT(CurDirName
->Buffer
[1] == L
':');
707 ASSERT(IS_PATH_SEPARATOR(CurDirName
->Buffer
[2]));
709 Prefix
= CurDirName
->Buffer
;
710 PrefixLength
= 3 * sizeof(WCHAR
); // Skip "C:\"
712 PrefixCut
= 3; // Source index location incremented of + 3
716 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
717 PrefixLength
= PrefixCut
* sizeof(WCHAR
);
718 Prefix
= CurDirName
->Buffer
;
722 case RtlPathTypeRootLocalDevice
: /* \\. */
723 Prefix
= DeviceRootString
.Buffer
;
724 PrefixLength
= DeviceRootString
.Length
;
726 SourceLength
-= 3 * sizeof(WCHAR
);
731 case RtlPathTypeUnknown
:
735 /* Do we have enough space for storing the full path? */
736 reqsize
= PrefixLength
;
737 if (reqsize
+ SourceLength
+ sizeof(WCHAR
) > Size
)
739 /* Not enough space, return needed size (including terminating '\0') */
740 reqsize
+= SourceLength
+ sizeof(WCHAR
);
745 * Build the full path
747 /* Copy the path's prefix */
748 if (PrefixLength
) RtlMoveMemory(Buffer
, Prefix
, PrefixLength
);
749 /* Copy the remaining part of the path */
750 RtlMoveMemory(Buffer
+ PrefixLength
/ sizeof(WCHAR
), Source
, SourceLength
+ sizeof(WCHAR
));
754 if (EnvVarValue
.Buffer
)
756 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
757 EnvVarValue
.Buffer
= NULL
;
761 * Finally, put the path in canonical form (remove redundant . and ..,
762 * (back)slashes...) and retrieve the length of the full path name
763 * (without its terminating null character) (in chars).
765 reqsize
= RtlpCollapsePath(Buffer
, /* Size, reqsize, */ PrefixCut
, SkipTrailingPathSeparators
);
767 /* Find the file part, which is present after the last path separator */
770 ptr
= wcsrchr(Buffer
, L
'\\');
771 if (ptr
) ++ptr
; // Skip it
774 * For UNC paths, the file part is after the \\share\dir part of the path.
776 PrefixCut
= (*PathType
== RtlPathTypeUncAbsolute
? PrefixCut
: 3);
778 if (ptr
&& *ptr
&& (ptr
>= Buffer
+ PrefixCut
))
784 /* Zero-out the short name */
790 /* Release PEB lock */
798 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
799 OUT PUNICODE_STRING NtPath
,
800 OUT PCWSTR
*PartName
,
801 OUT PRTL_RELATIVE_NAME_U RelativeName
)
806 /* Validate the input */
807 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
809 /* Validate the DOS length */
810 DosLength
= DosPath
->Length
;
811 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
813 /* Make space for the new path */
814 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
816 DosLength
+ sizeof(UNICODE_NULL
));
817 if (!NewBuffer
) return STATUS_NO_MEMORY
;
819 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
820 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
821 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
822 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
823 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
824 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
826 /* Did the caller send a relative name? */
829 /* Zero initialize it */
830 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
831 RelativeName
->ContainingDirectory
= NULL
;
832 RelativeName
->CurDirRef
= 0;
835 /* Did the caller request a partial name? */
838 /* Loop from the back until we find a path separator */
839 p
= &NewBuffer
[DosLength
/ sizeof(WCHAR
)];
840 while (--p
> NewBuffer
)
842 /* We found a path separator, move past it */
843 if (*p
== OBJ_NAME_PATH_SEPARATOR
)
850 /* Check whether a separator was found and if something remains */
851 if ((p
> NewBuffer
) && *p
)
853 /* What follows the path separator is the partial name */
858 /* The path ends with a path separator, no partial name */
863 /* Build the final NT path string */
864 NtPath
->Buffer
= NewBuffer
;
865 NtPath
->Length
= (USHORT
)DosLength
;
866 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
867 return STATUS_SUCCESS
;
872 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
873 IN PCUNICODE_STRING DosName
,
874 OUT PUNICODE_STRING NtName
,
875 OUT PCWSTR
*PartName
,
876 OUT PRTL_RELATIVE_NAME_U RelativeName
)
878 WCHAR BigBuffer
[MAX_PATH
+ 1];
879 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
880 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
881 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
883 RTL_PATH_TYPE InputPathType
, BufferPathType
;
886 PCURDIR CurrentDirectory
;
888 /* Assume MAX_PATH for now */
889 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
890 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
891 MaxLength
= sizeof(BigBuffer
);
893 /* Validate the input */
894 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
896 /* Capture input string */
897 CapturedDosName
= *DosName
;
899 /* Check for the presence or absence of the NT prefix "\\?\" form */
900 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
901 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
902 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
903 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
904 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
905 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
907 /* NT prefix not present */
909 /* Quick path won't be used */
912 /* Use the static buffer */
914 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
916 /* Allocate a buffer to hold the path */
917 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
918 DPRINT("MaxLength: %lx\n", MaxLength
);
919 if (!NewBuffer
) return STATUS_NO_MEMORY
;
923 /* NT prefix present */
925 /* Use the optimized path after acquiring the lock */
930 /* Lock the PEB and check if the quick path can be used */
934 /* Some simple fixups will get us the correct path */
935 DPRINT("Quick path\n");
936 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
941 /* Release the lock, we're done here */
946 /* Call the main function to get the full path name and length */
947 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
948 MAX_PATH
* sizeof(WCHAR
),
953 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
955 /* Invalid name, fail */
956 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
957 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
959 return STATUS_OBJECT_NAME_INVALID
;
962 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
963 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
964 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
967 /* Check where it really is */
968 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
969 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
970 switch (BufferPathType
)
972 /* It's actually a UNC path in \??\UNC\ */
973 case RtlPathTypeUncAbsolute
:
974 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
975 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
979 case RtlPathTypeLocalDevice
:
980 /* We made a good guess, go with it but skip the \??\ */
984 case RtlPathTypeDriveAbsolute
:
985 case RtlPathTypeDriveRelative
:
986 case RtlPathTypeRooted
:
987 case RtlPathTypeRelative
:
988 /* Our guess was good, roll with it */
991 /* Nothing else is expected */
996 /* Now copy the prefix and the buffer */
997 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
998 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
1000 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
1002 /* Compute the length */
1003 Length
= PathLength
+ PrefixLength
- PrefixCut
* sizeof(WCHAR
);
1004 LengthChars
= Length
/ sizeof(WCHAR
);
1006 /* Setup the actual NT path string and terminate it */
1007 NtName
->Buffer
= NewBuffer
;
1008 NtName
->Length
= (USHORT
)Length
;
1009 NtName
->MaximumLength
= (USHORT
)MaxLength
;
1010 NewBuffer
[LengthChars
] = UNICODE_NULL
;
1011 DPRINT("New buffer: %S\n", NewBuffer
);
1012 DPRINT("NT Name: %wZ\n", NtName
);
1014 /* Check if a partial name was requested */
1015 if ((PartName
) && (*PartName
))
1017 /* Convert to Unicode */
1018 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
1019 if (NT_SUCCESS(Status
))
1021 /* Set the partial name */
1022 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
1027 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1028 RtlReleasePebLock();
1033 /* Check if a relative name was asked for */
1036 /* Setup the structure */
1037 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
1038 RelativeName
->ContainingDirectory
= NULL
;
1039 RelativeName
->CurDirRef
= NULL
;
1041 /* Check if the input path itself was relative */
1042 if (InputPathType
== RtlPathTypeRelative
)
1044 /* Get current directory */
1045 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
1046 if (CurrentDirectory
->Handle
)
1048 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
1049 if (!NT_SUCCESS(Status
))
1051 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1052 RtlReleasePebLock();
1056 /* If current directory is bigger than full path, there's no way */
1057 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
1059 RtlReleasePebLock();
1063 /* File is in current directory */
1064 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
1066 /* Make relative name string */
1067 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ PrefixLength
+ FullPath
.Length
- PrefixCut
* sizeof(WCHAR
));
1068 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
1069 /* If relative name starts with \, skip it */
1070 if (RelativeName
->RelativeName
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1072 RelativeName
->RelativeName
.Buffer
++;
1073 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
1075 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
1076 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
1080 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1084 /* Give back current directory data & reference counter */
1085 RelativeName
->CurDirRef
= RtlpCurDirRef
;
1086 if (RelativeName
->CurDirRef
)
1088 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
1091 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1098 RtlReleasePebLock();
1099 return STATUS_SUCCESS
;
1104 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
1106 OUT PUNICODE_STRING NtName
,
1107 OUT PCWSTR
*PartName
,
1108 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1111 UNICODE_STRING NameString
;
1113 /* Create the unicode name */
1114 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
1115 if (NT_SUCCESS(Status
))
1117 /* Call the unicode function */
1118 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
1131 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
1132 OUT PUNICODE_STRING NtName
,
1133 OUT PCWSTR
*PartName
,
1134 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1136 /* Call the internal function */
1137 ASSERT(RelativeName
);
1138 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
1147 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1148 IN BOOLEAN SucceedIfBusy
)
1151 RTL_RELATIVE_NAME_U RelativeName
;
1152 UNICODE_STRING NtPathName
;
1154 OBJECT_ATTRIBUTES ObjectAttributes
;
1156 FILE_BASIC_INFORMATION BasicInformation
;
1158 /* Get the NT Path */
1159 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1163 if (!Result
) return FALSE
;
1165 /* Save the buffer */
1166 Buffer
= NtPathName
.Buffer
;
1168 /* Check if we have a relative name */
1169 if (RelativeName
.RelativeName
.Length
)
1172 NtPathName
= RelativeName
.RelativeName
;
1176 /* Otherwise ignore it */
1177 RelativeName
.ContainingDirectory
= NULL
;
1180 /* Initialize the object attributes */
1181 InitializeObjectAttributes(&ObjectAttributes
,
1183 OBJ_CASE_INSENSITIVE
,
1184 RelativeName
.ContainingDirectory
,
1187 /* Query the attributes and free the buffer now */
1188 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1189 RtlReleaseRelativeName(&RelativeName
);
1190 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1192 /* Check if we failed */
1193 if (!NT_SUCCESS(Status
))
1195 /* Check if we failed because the file is in use */
1196 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1197 (Status
== STATUS_ACCESS_DENIED
))
1199 /* Check if the caller wants this to be considered OK */
1200 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1204 /* A failure because the file didn't exist */
1210 /* The file exists */
1214 /* Return the result */
1220 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1222 /* Call the updated API */
1223 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1228 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1229 IN BOOLEAN SucceedIfBusy
)
1231 UNICODE_STRING NameString
;
1233 /* Create the unicode name*/
1234 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1236 /* Call the unicode function */
1237 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
1244 /* PUBLIC FUNCTIONS ***********************************************************/
1251 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
1253 /* Check if a directory reference was grabbed */
1254 if (RelativeName
->CurDirRef
)
1256 /* Decrease reference count */
1257 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
1259 /* If no one uses it any longer, close handle & free */
1260 NtClose(RelativeName
->CurDirRef
->Handle
);
1261 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
1263 RelativeName
->CurDirRef
= NULL
;
1272 RtlGetLongestNtPathLength(VOID
)
1275 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1276 * a mapped network drive), which is accessed through the DOS Global?? path.
1277 * This is, and has always been equal to, 269 characters, except in Wine
1278 * which claims this is 277. Go figure.
1280 return MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
/ sizeof(WCHAR
) + sizeof(ANSI_NULL
);
1285 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1286 * (with a 'e' instead of a 'a' in "Seperators").
1290 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags
,
1291 IN PCUNICODE_STRING PathString
,
1296 /* Parameters validation */
1297 if (Length
== NULL
) return STATUS_INVALID_PARAMETER
;
1301 if (PathString
== NULL
) return STATUS_INVALID_PARAMETER
;
1303 /* No flags are supported yet */
1304 if (Flags
!= 0) return STATUS_INVALID_PARAMETER
;
1306 NumChars
= PathString
->Length
/ sizeof(WCHAR
);
1309 * Notice that we skip the last character, therefore:
1310 * - if we have: "some/path/f" we test for: "some/path/"
1311 * - if we have: "some/path/" we test for: "some/path"
1312 * - if we have: "s" we test for: ""
1313 * - if we have: "" then NumChars was already zero and we aren't there
1316 while (NumChars
> 0 && IS_PATH_SEPARATOR(PathString
->Buffer
[NumChars
- 1]))
1322 return STATUS_SUCCESS
;
1330 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
1332 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
1334 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1335 if (IS_PATH_SEPARATOR(Path
[0]))
1337 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
1338 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
1339 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
1340 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
1341 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
1345 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
1346 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
1347 return RtlPathTypeDriveRelative
; /* x: */
1356 RtlIsDosDeviceName_U(IN PCWSTR Path
)
1358 UNICODE_STRING PathString
;
1361 /* Build the string */
1362 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
1363 if (!NT_SUCCESS(Status
)) return 0;
1366 * Returns 0 if name is not valid DOS device name, or DWORD with
1367 * offset in bytes to DOS device name from beginning of buffer in high word
1368 * and size in bytes of DOS device name in low word
1370 return RtlIsDosDeviceName_Ustr(&PathString
);
1378 RtlGetCurrentDirectory_U(
1379 _In_ ULONG MaximumLength
,
1380 _Out_bytecap_(MaximumLength
) PWSTR Buffer
)
1382 ULONG Length
, Bytes
;
1385 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
1387 /* Lock the PEB to get the current directory */
1388 RtlAcquirePebLock();
1389 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1391 /* Get the buffer and character length */
1392 CurDirName
= CurDir
->DosPath
.Buffer
;
1393 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
1394 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
1397 * DosPath.Buffer should always have a trailing slash. There is an assert
1398 * below which checks for this.
1400 * This function either returns x:\ for a root (keeping the original buffer)
1401 * or it returns x:\path\foo for a directory (replacing the trailing slash
1404 Bytes
= Length
* sizeof(WCHAR
);
1405 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
1407 /* Check if caller does not have enough space */
1408 if (MaximumLength
<= Bytes
)
1410 /* Call has no space for it, fail, add the trailing slash */
1411 RtlReleasePebLock();
1412 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1417 /* Check if caller does not have enough space */
1418 if (MaximumLength
< Bytes
)
1420 /* Call has no space for it, fail */
1421 RtlReleasePebLock();
1426 /* Copy the buffer since we seem to have space */
1427 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
1429 /* The buffer should end with a path separator */
1430 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
1432 /* Again check for our two cases (drive root vs path) */
1433 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
1435 /* Replace the trailing slash with a null */
1436 Buffer
[Length
- 1] = UNICODE_NULL
;
1441 /* Append the null char since there's no trailing slash */
1442 Buffer
[Length
] = UNICODE_NULL
;
1445 /* Release PEB lock */
1446 RtlReleasePebLock();
1447 DPRINT("CurrentDirectory %S\n", Buffer
);
1448 return Length
* sizeof(WCHAR
);
1456 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1460 RTL_PATH_TYPE PathType
;
1461 IO_STATUS_BLOCK IoStatusBlock
;
1462 UNICODE_STRING FullPath
, NtName
;
1463 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1464 OBJECT_ATTRIBUTES ObjectAttributes
;
1465 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1466 ULONG SavedLength
, CharLength
, FullPathLength
;
1467 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1469 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1471 /* Initialize for failure case */
1472 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1474 /* Can't set current directory on DOS device */
1475 if (RtlIsDosDeviceName_Ustr(Path
))
1477 return STATUS_NOT_A_DIRECTORY
;
1480 /* Get current directory */
1481 RtlAcquirePebLock();
1482 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1484 /* Check if we have to drop current handle */
1485 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1487 OldHandle
= CurDir
->Handle
;
1488 CurDir
->Handle
= NULL
;
1491 /* Allocate a buffer for full path (using max possible length */
1492 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1493 if (!FullPath
.Buffer
)
1495 Status
= STATUS_NO_MEMORY
;
1500 FullPath
.Length
= 0;
1501 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1503 /* Get new directory full path */
1504 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1505 if (!FullPathLength
)
1507 Status
= STATUS_OBJECT_NAME_INVALID
;
1511 SavedLength
= FullPath
.MaximumLength
;
1512 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1514 if (FullPathLength
> FullPath
.MaximumLength
)
1516 Status
= STATUS_NAME_TOO_LONG
;
1520 /* Translate it to NT name */
1521 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1523 Status
= STATUS_OBJECT_NAME_INVALID
;
1527 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1528 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1531 /* If previous current directory was removable, then check it for dropping */
1532 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1534 /* Get back normal handle */
1535 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1536 CurDir
->Handle
= NULL
;
1538 /* Get device information */
1539 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1542 sizeof(FileFsDeviceInfo
),
1543 FileFsDeviceInformation
);
1544 /* Retry without taking care of removable device */
1545 if (!NT_SUCCESS(Status
))
1547 Status
= RtlSetCurrentDirectory_U(Path
);
1553 /* Open directory */
1554 Status
= NtOpenFile(&CurDirHandle
,
1555 SYNCHRONIZE
| FILE_TRAVERSE
,
1558 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1559 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1560 if (!NT_SUCCESS(Status
)) goto Leave
;
1562 /* Get device information */
1563 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1566 sizeof(FileFsDeviceInfo
),
1567 FileFsDeviceInformation
);
1568 if (!NT_SUCCESS(Status
)) goto Leave
;
1571 /* If device is removable, mark handle */
1572 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1574 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1577 FullPath
.Length
= (USHORT
)FullPathLength
;
1579 /* If full path isn't \ terminated, do it */
1580 if (FullPath
.Buffer
[CharLength
- 1] != OBJ_NAME_PATH_SEPARATOR
)
1582 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1584 Status
= STATUS_NAME_TOO_LONG
;
1588 FullPath
.Buffer
[CharLength
] = OBJ_NAME_PATH_SEPARATOR
;
1589 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1590 FullPath
.Length
+= sizeof(WCHAR
);
1593 /* If we have previous current directory with only us as reference, save it */
1594 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1596 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1600 /* Allocate new current directory struct saving previous one */
1601 OldCurDir
= RtlpCurDirRef
;
1602 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1605 RtlpCurDirRef
= OldCurDir
;
1607 Status
= STATUS_NO_MEMORY
;
1611 /* Set reference to 1 (us) */
1612 RtlpCurDirRef
->RefCount
= 1;
1616 CurDir
->Handle
= CurDirHandle
;
1617 RtlpCurDirRef
->Handle
= CurDirHandle
;
1618 CurDirHandle
= NULL
;
1620 /* Copy full path */
1621 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1622 CurDir
->DosPath
.Length
= FullPath
.Length
;
1624 Status
= STATUS_SUCCESS
;
1627 RtlReleasePebLock();
1629 if (FullPath
.Buffer
)
1631 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1636 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1639 if (CurDirHandle
) NtClose(CurDirHandle
);
1641 if (OldHandle
) NtClose(OldHandle
);
1643 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1645 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1647 NtClose(OldCurDir
->Handle
);
1648 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1655 /******************************************************************
1656 * RtlGetFullPathName_U (NTDLL.@)
1658 * Returns the number of bytes written to buffer (not including the
1659 * terminating NULL) if the function succeeds, or the required number of bytes
1660 * (including the terminating NULL) if the buffer is too small.
1662 * file_part will point to the filename part inside buffer (except if we use
1663 * DOS device name, in which case file_in_buf is NULL)
1673 RtlGetFullPathName_U(
1674 _In_ PCWSTR FileName
,
1676 _Out_z_bytecap_(Size
) PWSTR Buffer
,
1677 _Out_opt_ PWSTR
*ShortName
)
1680 UNICODE_STRING FileNameString
;
1681 RTL_PATH_TYPE PathType
;
1683 /* Build the string */
1684 Status
= RtlInitUnicodeStringEx(&FileNameString
, FileName
);
1685 if (!NT_SUCCESS(Status
)) return 0;
1687 /* Call the extended function */
1688 return RtlGetFullPathName_Ustr(&FileNameString
,
1701 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1702 OUT PUNICODE_STRING NtName
,
1703 OUT PCWSTR
*PartName
,
1704 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1706 /* Call the internal function */
1707 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1719 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1720 OUT PUNICODE_STRING NtName
,
1721 OUT PCWSTR
*PartName
,
1722 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1724 /* Call the internal function */
1725 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1737 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1738 OUT PUNICODE_STRING NtName
,
1739 OUT PCWSTR
*PartName
,
1740 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1742 /* Call the internal function */
1743 ASSERT(RelativeName
);
1744 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1756 RtlDosPathNameToRelativeNtPathName_U_WithStatus(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 RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1774 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1776 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1777 return STATUS_NOT_IMPLEMENTED
;
1785 RtlDosSearchPath_U(IN PCWSTR Path
,
1787 IN PCWSTR Extension
,
1790 OUT PWSTR
*PartName
)
1793 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1794 UNICODE_STRING TempString
;
1795 PWCHAR NewBuffer
, BufferStart
;
1798 /* Check if this is an absolute path */
1799 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1801 /* Check if the file exists */
1802 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1804 /* Get the full name, which does the DOS lookup */
1805 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1808 /* Doesn't exist, so fail */
1812 /* Scan the filename */
1816 /* Looking for an extension */
1819 /* No extension string needed -- it's part of the filename */
1824 /* Next character */
1828 /* Do we have an extension? */
1831 /* Nope, don't worry about one */
1832 ExtensionLength
= 0;
1836 /* Build a temporary string to get the extension length */
1837 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1838 if (!NT_SUCCESS(Status
)) return 0;
1839 ExtensionLength
= TempString
.Length
;
1842 /* Build a temporary string to get the path length */
1843 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1844 if (!NT_SUCCESS(Status
)) return 0;
1845 PathLength
= TempString
.Length
;
1847 /* Build a temporary string to get the filename length */
1848 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1849 if (!NT_SUCCESS(Status
)) return 0;
1850 FileNameLength
= TempString
.Length
;
1852 /* Allocate the buffer for the new string name */
1853 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1862 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1867 /* Final loop to build the path */
1870 /* Check if we have a valid character */
1871 BufferStart
= NewBuffer
;
1874 /* Loop as long as there's no semicolon */
1875 while (*Path
!= L
';')
1877 /* Copy the next character */
1878 *BufferStart
++ = *Path
++;
1882 /* We found a semi-colon, to stop path processing on this loop */
1883 if (*Path
== L
';') ++Path
;
1886 /* Add a terminating slash if needed */
1887 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != OBJ_NAME_PATH_SEPARATOR
))
1889 *BufferStart
++ = OBJ_NAME_PATH_SEPARATOR
;
1892 /* Bail out if we reached the end */
1893 if (!*Path
) Path
= NULL
;
1895 /* Copy the file name and check if an extension is needed */
1896 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1897 if (ExtensionLength
)
1899 /* Copy the extension too */
1900 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1902 ExtensionLength
+ sizeof(WCHAR
));
1906 /* Just NULL-terminate */
1907 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1910 /* Now, does this file exist? */
1911 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1913 /* Call the full-path API to get the length */
1914 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1918 /* If we got here, path doesn't exist, so fail the call */
1923 /* Free the allocation and return the length */
1924 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1933 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
1934 IN PUNICODE_STRING StaticString
,
1935 IN PUNICODE_STRING DynamicString
,
1936 IN PUNICODE_STRING
*StringUsed
,
1937 IN PSIZE_T FilePartSize
,
1938 OUT PBOOLEAN NameInvalid
,
1939 OUT RTL_PATH_TYPE
* PathType
,
1940 OUT PSIZE_T LengthNeeded
)
1943 PWCHAR StaticBuffer
;
1946 USHORT StaticLength
;
1947 UNICODE_STRING TempDynamicString
;
1949 /* Initialize all our locals */
1951 StaticBuffer
= NULL
;
1952 TempDynamicString
.Buffer
= NULL
;
1954 /* Initialize the input parameters */
1955 if (StringUsed
) *StringUsed
= NULL
;
1956 if (LengthNeeded
) *LengthNeeded
= 0;
1957 if (FilePartSize
) *FilePartSize
= 0;
1959 /* Check for invalid parameters */
1960 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
1962 return STATUS_INVALID_PARAMETER
;
1965 /* Check if we did not get an input string */
1969 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
1970 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
1971 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
1975 /* Use the one we received */
1976 StaticBuffer
= StaticString
->Buffer
;
1977 StaticLength
= StaticString
->MaximumLength
;
1980 /* Call the lower-level function */
1981 Length
= RtlGetFullPathName_Ustr(FileName
,
1987 DPRINT("Length: %u StaticBuffer: %S\n", Length
, StaticBuffer
);
1990 /* Fail if it failed */
1991 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
1994 Status
= STATUS_OBJECT_NAME_INVALID
;
1998 /* Check if it fits inside our static string */
1999 if ((StaticString
) && (Length
< StaticLength
))
2001 /* Set the final length */
2002 StaticString
->Length
= (USHORT
)Length
;
2004 /* Set the file part size */
2005 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2007 /* Return the static string if requested */
2008 if (StringUsed
) *StringUsed
= StaticString
;
2010 /* We are done with success */
2011 Status
= STATUS_SUCCESS
;
2015 /* Did we not have an input dynamic string ?*/
2018 /* Return the length we need */
2019 if (LengthNeeded
) *LengthNeeded
= Length
;
2021 /* And fail such that the caller can try again */
2022 Status
= STATUS_BUFFER_TOO_SMALL
;
2026 /* Check if it fits in our static buffer */
2027 if ((StaticBuffer
) && (Length
< StaticLength
))
2029 /* NULL-terminate it */
2030 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2032 /* Set the settings for the dynamic string the caller sent */
2033 DynamicString
->MaximumLength
= StaticLength
;
2034 DynamicString
->Length
= (USHORT
)Length
;
2035 DynamicString
->Buffer
= StaticBuffer
;
2037 /* Set the part size */
2038 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
2040 /* Return the dynamic string if requested */
2041 if (StringUsed
) *StringUsed
= DynamicString
;
2043 /* Do not free the static buffer on exit, and return success */
2044 StaticBuffer
= NULL
;
2045 Status
= STATUS_SUCCESS
;
2049 /* Now try again under the PEB lock */
2050 RtlAcquirePebLock();
2051 Length
= RtlGetFullPathName_Ustr(FileName
,
2060 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2061 __FUNCTION__
, __LINE__
);
2062 Status
= STATUS_OBJECT_NAME_INVALID
;
2066 /* Check if it fits inside our static string now */
2067 if ((StaticString
) && (Length
< StaticLength
))
2069 /* Set the final length */
2070 StaticString
->Length
= (USHORT
)Length
;
2072 /* Set the file part size */
2073 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2075 /* Return the static string if requested */
2076 if (StringUsed
) *StringUsed
= StaticString
;
2078 /* We are done with success */
2079 Status
= STATUS_SUCCESS
;
2083 /* Check if the path won't even fit in a real string */
2084 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
2086 /* Name is way too long, fail */
2087 Status
= STATUS_NAME_TOO_LONG
;
2091 /* Allocate the string to hold the path name now */
2092 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
2094 if (!TempDynamicString
.Buffer
)
2096 /* Out of memory, fail */
2097 Status
= STATUS_NO_MEMORY
;
2101 /* Add space for a NULL terminator, and now check the full path */
2102 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
2103 Length
= RtlGetFullPathName_Ustr(FileName
,
2105 TempDynamicString
.Buffer
,
2111 /* Some path error, so fail out */
2112 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2113 __FUNCTION__
, __LINE__
);
2114 Status
= STATUS_OBJECT_NAME_INVALID
;
2118 /* It should fit in the string we just allocated */
2119 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
2120 if (Length
> TempDynamicString
.MaximumLength
)
2122 /* This is really weird and would mean some kind of race */
2123 Status
= STATUS_INTERNAL_ERROR
;
2127 /* Return the file part size */
2128 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
2130 /* Terminate the whole string now */
2131 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2133 /* Finalize the string and return it to the user */
2134 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
2135 DynamicString
->Length
= (USHORT
)Length
;
2136 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
2137 if (StringUsed
) *StringUsed
= DynamicString
;
2139 /* Return success and make sure we don't free the buffer on exit */
2140 TempDynamicString
.Buffer
= NULL
;
2141 Status
= STATUS_SUCCESS
;
2144 /* Release the PEB lock */
2145 RtlReleasePebLock();
2148 /* Free any buffers we should be freeing */
2149 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2150 if ((StaticString
) && (StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2152 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2154 if (TempDynamicString
.Buffer
)
2156 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2159 /* Print out any unusual errors */
2160 if ((NT_ERROR(Status
)) &&
2161 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2163 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2164 __FUNCTION__
, FileName
, Status
);
2167 /* Return, we're all done */
2176 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2177 IN PUNICODE_STRING PathString
,
2178 IN PUNICODE_STRING FileNameString
,
2179 IN PUNICODE_STRING ExtensionString
,
2180 IN PUNICODE_STRING CallerBuffer
,
2181 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2182 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2183 OUT PSIZE_T FilePartSize OPTIONAL
,
2184 OUT PSIZE_T LengthNeeded OPTIONAL
)
2186 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2187 UNICODE_STRING StaticCandidateString
;
2189 RTL_PATH_TYPE PathType
;
2190 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2191 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2192 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2193 PUNICODE_STRING FullIsolatedPath
;
2194 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2195 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2197 /* Initialize the input string */
2198 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2199 StaticCandidateBuffer
,
2200 sizeof(StaticCandidateBuffer
));
2202 /* Initialize optional arguments */
2203 if (FullNameOut
) *FullNameOut
= NULL
;
2204 if (FilePartSize
) *FilePartSize
= 0;
2205 if (LengthNeeded
) *LengthNeeded
= 0;
2208 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2209 DynamicString
->Buffer
= NULL
;
2212 /* Check for invalid parameters */
2215 !(FileNameString
) ||
2216 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2219 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2220 Status
= STATUS_INVALID_PARAMETER
;
2224 /* First check what kind of path this is */
2225 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2227 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2229 (PathType
== RtlPathTypeRelative
) &&
2230 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2231 (FileNameString
->Buffer
[0] == L
'.') &&
2232 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2233 ((FileNameString
->Buffer
[1] == L
'.') &&
2234 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2235 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2237 /* Yes, and this path is like that, so make it seem unknown */
2238 PathType
= RtlPathTypeUnknown
;
2241 /* Now check relative vs non-relative paths */
2242 if (PathType
== RtlPathTypeRelative
)
2244 /* Does the caller want SxS? */
2247 /* Apply the SxS magic */
2248 FullIsolatedPath
= NULL
;
2249 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2258 if (NT_SUCCESS(Status
))
2260 /* We found the SxS path, return it */
2261 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2264 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2266 /* Critical SxS error, fail */
2267 DbgPrint("%s: Failing because call to "
2268 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2277 /* No SxS key found, or not requested, check if there's an extension */
2278 if (ExtensionString
)
2280 /* Save the extension length, and check if there's a file name */
2281 ExtensionLength
= ExtensionString
->Length
;
2282 if (FileNameString
->Length
)
2284 /* Start parsing the file name */
2285 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2286 while (End
> FileNameString
->Buffer
)
2288 /* If we find a path separator, there's no extension */
2289 if (IS_PATH_SEPARATOR(*--End
)) break;
2291 /* Otherwise, did we find an extension dot? */
2294 /* Ignore what the caller sent it, use the filename's */
2295 ExtensionString
= NULL
;
2296 ExtensionLength
= 0;
2303 /* Check if we got a path */
2304 if (PathString
->Length
)
2306 /* Start parsing the path name, looking for path separators */
2307 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2309 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2311 /* This is the size of the path -- handle a trailing slash */
2312 PathSize
= End
- p
- 1;
2313 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2315 /* Check if we found a bigger path than before */
2316 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2318 /* Keep going with the path after this path separator */
2322 /* This is the trailing path, run the same code as above */
2324 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2325 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2327 /* Finally, convert the largest path size into WCHAR */
2328 MaxPathSize
*= sizeof(WCHAR
);
2331 /* Use the extension, the file name, and the largest path as the size */
2332 WorstCaseLength
= ExtensionLength
+
2333 FileNameString
->Length
+
2334 (USHORT
)MaxPathSize
+
2335 sizeof(UNICODE_NULL
);
2336 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2338 /* It has to fit in a registry string, if not, fail here */
2339 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2340 "worst case file name length is %Iu bytes\n",
2343 Status
= STATUS_NAME_TOO_LONG
;
2347 /* Scan the path now, to see if we can find the file */
2348 p
= PathString
->Buffer
;
2349 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2352 /* Find out where this path ends */
2353 for (SegmentEnd
= p
;
2354 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2357 /* Compute the size of this path */
2358 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2360 /* Handle trailing slash if there isn't one */
2361 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2363 /* Add space for one */
2364 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2367 /* Now check if our initial static buffer is too small */
2368 if (StaticCandidateString
.MaximumLength
<
2369 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2371 /* At this point we should've been using our static buffer */
2372 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2373 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2375 /* Something is really messed up if this was the dynamic string */
2376 DbgPrint("%s: internal error #1; "
2377 "CandidateString.Buffer = %p; "
2378 "StaticCandidateBuffer = %p\n",
2380 StaticCandidateString
.Buffer
,
2381 StaticCandidateBuffer
);
2382 Status
= STATUS_INTERNAL_ERROR
;
2386 /* We checked before that the maximum possible size shoudl fit! */
2387 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2388 UNICODE_STRING_MAX_BYTES
);
2389 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2390 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2392 /* For some reason it's not fitting anymore. Something messed up */
2393 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2394 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2397 FileNameString
->Length
,
2399 Status
= STATUS_INTERNAL_ERROR
;
2403 /* Now allocate the dynamic string */
2404 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2406 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2408 if (!StaticCandidateString
.Buffer
)
2410 /* Out of memory, fail */
2411 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2413 StaticCandidateString
.MaximumLength
);
2414 Status
= STATUS_NO_MEMORY
;
2419 /* Copy the path in the string */
2420 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2422 /* Get to the end of the string, and add the trailing slash if missing */
2423 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2424 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2426 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2429 /* Copy the filename now */
2430 RtlCopyMemory(CandidateEnd
,
2431 FileNameString
->Buffer
,
2432 FileNameString
->Length
);
2433 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2435 /* Check if there was an extension */
2436 if (ExtensionString
)
2438 /* Copy the extension too */
2439 RtlCopyMemory(CandidateEnd
,
2440 ExtensionString
->Buffer
,
2441 ExtensionString
->Length
);
2442 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2445 /* We are done, terminate it */
2446 *CandidateEnd
= UNICODE_NULL
;
2448 /* Now set the final length of the string so it becomes valid */
2449 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2450 StaticCandidateString
.Buffer
) *
2453 /* Check if this file exists */
2454 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2455 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2457 /* Awesome, it does, now get the full path */
2458 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2461 (PUNICODE_STRING
*)FullNameOut
,
2466 if (!(NT_SUCCESS(Status
)) &&
2467 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2468 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2470 DbgPrint("%s: Failing because we thought we found %wZ on "
2471 "the search path, but RtlGetfullPathNameUStrEx() "
2474 &StaticCandidateString
,
2477 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2482 /* Otherwise, move to the next path */
2483 if (SegmentEnd
!= End
)
2485 /* Handle the case of the path separator trailing */
2495 /* Loop finished and we didn't break out -- fail */
2496 Status
= STATUS_NO_SUCH_FILE
;
2500 /* We have a full path, so check if it does exist */
2501 DPRINT("%wZ\n", FileNameString
);
2502 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2504 /* It doesn't exist, did we have an extension? */
2505 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2507 /* No extension, so just fail */
2508 Status
= STATUS_NO_SUCH_FILE
;
2512 /* There was an extension, check if the filename already had one */
2513 if (!(Flags
& 4) && (FileNameString
->Length
))
2515 /* Parse the filename */
2516 p
= FileNameString
->Buffer
;
2517 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2520 /* If there's a path separator, there's no extension */
2521 if (IS_PATH_SEPARATOR(*--End
)) break;
2523 /* Othwerwise, did we find an extension dot? */
2526 /* File already had an extension, so fail */
2527 Status
= STATUS_NO_SUCH_FILE
;
2533 /* So there is an extension, we'll try again by adding it */
2534 NamePlusExtLength
= FileNameString
->Length
+
2535 ExtensionString
->Length
+
2536 sizeof(UNICODE_NULL
);
2537 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2539 /* It won't fit in any kind of valid string, so fail */
2540 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2543 Status
= STATUS_NAME_TOO_LONG
;
2547 /* Fill it fit in our temporary string? */
2548 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2550 /* It won't fit anymore, allocate a dynamic string for it */
2551 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2552 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2554 if (!StaticCandidateString
.Buffer
)
2556 /* Ran out of memory, so fail */
2557 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2559 Status
= STATUS_NO_MEMORY
;
2564 /* Copy the filename */
2565 RtlCopyUnicodeString(&StaticCandidateString
, FileNameString
);
2567 /* Copy the extension */
2568 RtlAppendUnicodeStringToString(&StaticCandidateString
,
2571 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2573 /* And check if this file now exists */
2574 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2576 /* Still no joy, fail out */
2577 Status
= STATUS_NO_SUCH_FILE
;
2581 /* File was found, get the final full path */
2582 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2585 (PUNICODE_STRING
*)FullNameOut
,
2590 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2592 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2593 "failed with status %08lx\n",
2595 &StaticCandidateString
,
2598 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2602 /* File was found on the first try, get the final full path */
2603 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2606 (PUNICODE_STRING
*)FullNameOut
,
2611 if (!(NT_SUCCESS(Status
)) &&
2612 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2613 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2615 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2616 "failed with status %08lx\n",
2621 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2626 /* Anything that was not an error, turn into STATUS_SUCCESS */
2627 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2629 /* Check if we had a dynamic string */
2630 if ((StaticCandidateString
.Buffer
) &&
2631 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2634 RtlFreeUnicodeString(&StaticCandidateString
);
2637 /* Return the status */
2646 RtlDoesFileExists_U(IN PCWSTR FileName
)
2648 /* Call the new function */
2649 return RtlDoesFileExists_UEx(FileName
, TRUE
);