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 const UNICODE_STRING RtlpDoubleSlashPrefix
= RTL_CONSTANT_STRING(L
"\\\\");
50 static const UNICODE_STRING RtlpDefaultExtension
= RTL_CONSTANT_STRING(L
".DLL");
51 static const UNICODE_STRING RtlpDotLocal
= RTL_CONSTANT_STRING(L
".Local\\");
52 static const UNICODE_STRING RtlpPathDividers
= RTL_CONSTANT_STRING(L
"\\/");
55 PRTLP_CURDIR_REF RtlpCurDirRef
;
57 /* PRIVATE FUNCTIONS **********************************************************/
61 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
66 Path
= PathString
->Buffer
;
67 Chars
= PathString
->Length
/ sizeof(WCHAR
);
69 /* Return if there are no characters */
70 if (!Chars
) return RtlPathTypeRelative
;
73 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
74 * actually check for the path length before touching the characters
76 if (IS_PATH_SEPARATOR(Path
[0]))
78 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
79 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
80 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
81 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
82 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
86 if ((Chars
< 2) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
87 if ((Chars
< 3) || !(IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveRelative
; /* x: */
88 return RtlPathTypeDriveAbsolute
; /* x:\ */
94 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
96 UNICODE_STRING PathCopy
;
98 USHORT PathChars
, ColonCount
= 0;
99 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
102 /* Validate the input */
103 if (!PathString
) return 0;
105 /* Check what type of path this is */
106 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
108 /* Fail for UNC or unknown paths */
109 case RtlPathTypeUnknown
:
110 case RtlPathTypeUncAbsolute
:
113 /* Make special check for the CON device */
114 case RtlPathTypeLocalDevice
:
115 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
117 /* This should return 0x80006 */
118 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
126 /* Make a copy of the string */
127 PathCopy
= *PathString
;
128 OriginalLength
= PathString
->Length
;
130 /* Return if there's no characters */
131 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
132 if (!PathChars
) return 0;
134 /* Check for drive path and truncate */
135 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
137 /* Fixup the lengths */
138 PathCopy
.Length
-= sizeof(WCHAR
);
139 if (!--PathChars
) return 0;
141 /* Remember this for later */
145 /* Check for extension or space, and truncate */
148 /* Stop if we hit something else than a space or period */
149 c
= PathCopy
.Buffer
[PathChars
- 1];
150 if ((c
!= L
'.') && (c
!= L
' ')) break;
152 /* Fixup the lengths */
153 PathCopy
.Length
-= sizeof(WCHAR
);
155 /* Remember this for later */
157 } while (--PathChars
);
159 /* Anything still left? */
162 /* Loop from the end */
163 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
164 End
>= PathCopy
.Buffer
;
167 /* Check if the character is a path or drive separator */
169 if (IS_PATH_SEPARATOR(c
) || ((c
== L
':') && (End
== PathCopy
.Buffer
+ 1)))
171 /* Get the next lower case character */
173 c
= RtlpDowncaseUnicodeChar(*End
);
175 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
176 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
177 ((c
== L
'l') || (c
== L
'c') || (c
== L
'p') || (c
== L
'a') || (c
== L
'n')))
179 /* Calculate the offset */
180 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
182 /* Build the final string */
183 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
184 PathCopy
.Buffer
= End
;
186 /* Save new amount of chars in the path */
187 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
198 /* Get the next lower case character and check if it's a DOS device */
199 c
= RtlpDowncaseUnicodeChar(*PathCopy
.Buffer
);
200 if ((c
!= L
'l') && (c
!= L
'c') && (c
!= L
'p') && (c
!= L
'a') && (c
!= L
'n'))
202 /* Not LPT, COM, PRN, AUX, or NUL */
207 /* Now skip past any extra extension or drive letter characters */
208 Start
= PathCopy
.Buffer
;
209 End
= &Start
[PathChars
];
213 if ((c
== L
'.') || (c
== L
':')) break;
217 /* And then go backwards to get rid of spaces */
218 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == L
' ')) --Start
;
220 /* Finally see how many characters are left, and that's our size */
221 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
222 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
224 /* Check if this is a COM or LPT port, which has a digit after it */
225 if ((PathChars
== 4) &&
226 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != L
'0')))
228 /* Don't compare the number part, just check for LPT or COM */
229 PathCopy
.Length
-= sizeof(WCHAR
);
230 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
231 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
234 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
235 return MAKELONG(ReturnLength
, ReturnOffset
);
238 else if ((PathChars
== 3) &&
239 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
240 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
241 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
242 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
244 /* Otherwise this was something like AUX, NUL, PRN, or CON */
245 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
246 return MAKELONG(ReturnLength
, ReturnOffset
);
249 /* Otherwise, this is not a valid DOS device */
255 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
257 OUT PBOOLEAN NameInvalid
)
262 /* Allocate a large enough buffer */
263 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
269 /* Copy the filename */
270 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
272 /* And add a dot at the end */
273 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
274 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
276 /* Check if the file exists or not */
277 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
279 /* Get rid of the buffer now */
280 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
284 /* Assume the name is ok, but fail the call */
285 *NameInvalid
= FALSE
;
286 Status
= STATUS_NO_MEMORY
;
289 /* Return the status */
295 /******************************************************************
296 * RtlpCollapsePath (from WINE)
298 * Helper for RtlGetFullPathName_U
300 * 1) Converts slashes into backslashes and gets rid of duplicated ones;
301 * 2) Gets rid of . and .. components in the path.
303 * Returns the full path length without its terminating NULL character.
306 RtlpCollapsePath(PWSTR Path
, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark
, BOOLEAN SkipTrailingPathSeparators
)
310 // FIXME: Do not suppose NULL-terminated strings!!
312 SIZE_T PathLength
= wcslen(Path
);
313 PWSTR EndBuffer
= Path
+ PathLength
; // Path + PathBufferSize / sizeof(WCHAR);
316 /* Convert slashes into backslashes */
317 for (p
= Path
; *p
; p
++)
319 if (*p
== L
'/') *p
= L
'\\';
322 /* Collapse duplicate backslashes */
323 next
= Path
+ max( 1, mark
);
324 for (p
= next
; *p
; p
++)
326 if (*p
!= L
'\\' || next
[-1] != L
'\\') *next
++ = *p
;
328 *next
= UNICODE_NULL
;
338 case UNICODE_NULL
: /* final . */
339 if (p
> Path
+ mark
) p
--;
344 case L
'\\': /* .\ component */
346 // ASSERT(EndPath - next == wcslen(next));
347 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
348 EndPath
-= (next
- p
);
352 if (p
[2] == L
'\\') /* ..\ component */
358 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
360 // ASSERT(EndPath - next == wcslen(next));
361 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
362 EndPath
-= (next
- p
);
365 else if (p
[2] == UNICODE_NULL
) /* final .. */
370 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
371 if (p
> Path
+ mark
) p
--;
381 /* Skip to the next component */
382 while (*p
&& *p
!= L
'\\') p
++;
385 /* Remove last dot in previous dir name */
386 if (p
> Path
+ mark
&& p
[-1] == L
'.')
388 // ASSERT(EndPath - p == wcslen(p));
389 RtlMoveMemory(p
- 1, p
, (EndPath
- p
+ 1) * sizeof(WCHAR
));
399 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
400 if (SkipTrailingPathSeparators
)
402 while (p
> Path
+ mark
&& IS_PATH_SEPARATOR(p
[-1])) p
--;
405 /* Remove trailing spaces and dots (for all the path) */
406 while (p
> Path
&& (p
[-1] == L
' ' || p
[-1] == L
'.')) p
--;
409 * Zero-out the discarded buffer zone, starting just after
410 * the path string and going up to the end of the buffer.
411 * It also NULL-terminate the path string.
413 ASSERT(EndBuffer
>= p
);
414 RtlZeroMemory(p
, (EndBuffer
- p
+ 1) * sizeof(WCHAR
));
416 /* Return the real path length */
417 PathLength
= (p
- Path
);
418 // ASSERT(PathLength == wcslen(Path));
419 return (PathLength
* sizeof(WCHAR
));
422 /******************************************************************
423 * RtlpSkipUNCPrefix (from WINE)
425 * Helper for RtlGetFullPathName_U
427 * Skips the \\share\dir part of a file name and returns the new position
428 * (which can point on the last backslash of "dir\").
431 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer
)
433 PCWSTR UncPath
= FileNameBuffer
+ 2;
434 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer
);
436 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* share name */
437 while (IS_PATH_SEPARATOR(*UncPath
)) UncPath
++;
438 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* dir name */
439 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
441 return (UncPath
- FileNameBuffer
);
446 RtlpApplyLengthFunction(IN ULONG Flags
,
448 IN PVOID UnicodeStringOrUnicodeStringBuffer
,
449 IN PVOID LengthFunction
)
452 return STATUS_NOT_IMPLEMENTED
;
457 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags
,
459 OUT PULONG LengthOut
)
462 return STATUS_NOT_IMPLEMENTED
;
467 RtlComputePrivatizedDllName_U(
468 _In_ PUNICODE_STRING DllName
,
469 _Inout_ PUNICODE_STRING RealName
,
470 _Inout_ PUNICODE_STRING LocalName
)
472 static const UNICODE_STRING ExtensionChar
= RTL_CONSTANT_STRING(L
".");
475 UNICODE_STRING ImagePathName
, DllNameOnly
, CopyRealName
, CopyLocalName
;
476 BOOLEAN HasExtension
;
479 C_ASSERT(sizeof(UNICODE_NULL
) == sizeof(WCHAR
));
481 CopyRealName
= *RealName
;
482 CopyLocalName
= *LocalName
;
485 /* Get the image path */
486 ImagePathName
= RtlGetCurrentPeb()->ProcessParameters
->ImagePathName
;
488 /* Check if it's not normalized */
489 if (!(RtlGetCurrentPeb()->ProcessParameters
->Flags
& RTL_USER_PROCESS_PARAMETERS_NORMALIZED
))
492 ImagePathName
.Buffer
= (PWSTR
)((ULONG_PTR
)ImagePathName
.Buffer
+ (ULONG_PTR
)RtlGetCurrentPeb()->ProcessParameters
);
496 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END
,
497 DllName
, &RtlpPathDividers
, &Position
)))
499 DllNameOnly
= *DllName
;
503 /* Just keep the dll name, ignore path components */
504 Position
+= sizeof(WCHAR
);
505 DllNameOnly
.Buffer
= DllName
->Buffer
+ Position
/ sizeof(WCHAR
);
506 DllNameOnly
.Length
= DllName
->Length
- Position
;
507 DllNameOnly
.MaximumLength
= DllName
->MaximumLength
- Position
;
510 if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END
,
511 &DllNameOnly
, &ExtensionChar
, &Position
)))
516 HasExtension
= Position
> 1;
518 /* First we create the c:\path\processname.exe.Local\something.dll path */
519 RequiredSize
= ImagePathName
.Length
+ RtlpDotLocal
.Length
+ DllNameOnly
.Length
+
520 (HasExtension
? 0 : RtlpDefaultExtension
.Length
) + sizeof(UNICODE_NULL
);
522 /* This is not going to work out */
523 if (RequiredSize
> UNICODE_STRING_MAX_BYTES
)
524 return STATUS_NAME_TOO_LONG
;
526 /* We need something extra */
527 if (RequiredSize
> CopyLocalName
.MaximumLength
)
529 CopyLocalName
.Buffer
= RtlpAllocateStringMemory(RequiredSize
, TAG_USTR
);
530 if (CopyLocalName
.Buffer
== NULL
)
531 return STATUS_NO_MEMORY
;
532 CopyLocalName
.MaximumLength
= RequiredSize
;
534 /* Now build the entire path */
535 CopyLocalName
.Length
= 0;
536 Status
= RtlAppendUnicodeStringToString(&CopyLocalName
, &ImagePathName
);
537 ASSERT(NT_SUCCESS(Status
));
538 if (NT_SUCCESS(Status
))
540 Status
= RtlAppendUnicodeStringToString(&CopyLocalName
, &RtlpDotLocal
);
541 ASSERT(NT_SUCCESS(Status
));
543 if (NT_SUCCESS(Status
))
545 Status
= RtlAppendUnicodeStringToString(&CopyLocalName
, &DllNameOnly
);
546 ASSERT(NT_SUCCESS(Status
));
548 /* Do we need to append an extension? */
549 if (NT_SUCCESS(Status
) && !HasExtension
)
551 Status
= RtlAppendUnicodeStringToString(&CopyLocalName
, &RtlpDefaultExtension
);
552 ASSERT(NT_SUCCESS(Status
));
555 if (NT_SUCCESS(Status
))
557 /* then we create the c:\path\something.dll path */
558 if (NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END
,
559 &ImagePathName
, &RtlpPathDividers
, &Position
)))
561 ImagePathName
.Length
= Position
+ sizeof(WCHAR
);
564 RequiredSize
= ImagePathName
.Length
+ DllNameOnly
.Length
+
565 (HasExtension
? 0 : RtlpDefaultExtension
.Length
) + sizeof(UNICODE_NULL
);
567 if (RequiredSize
>= UNICODE_STRING_MAX_BYTES
)
569 Status
= STATUS_NAME_TOO_LONG
;
573 if (RequiredSize
> CopyRealName
.MaximumLength
)
575 CopyRealName
.Buffer
= RtlpAllocateStringMemory(RequiredSize
, TAG_USTR
);
576 if (CopyRealName
.Buffer
== NULL
)
577 Status
= STATUS_NO_MEMORY
;
578 CopyRealName
.MaximumLength
= RequiredSize
;
580 CopyRealName
.Length
= 0;
581 if (NT_SUCCESS(Status
))
583 Status
= RtlAppendUnicodeStringToString(&CopyRealName
, &ImagePathName
);
584 ASSERT(NT_SUCCESS(Status
));
586 if (NT_SUCCESS(Status
))
588 Status
= RtlAppendUnicodeStringToString(&CopyRealName
, &DllNameOnly
);
589 ASSERT(NT_SUCCESS(Status
));
591 if (NT_SUCCESS(Status
) && !HasExtension
)
593 Status
= RtlAppendUnicodeStringToString(&CopyRealName
, &RtlpDefaultExtension
);
594 ASSERT(NT_SUCCESS(Status
));
599 if (!NT_SUCCESS(Status
))
601 if (CopyRealName
.Buffer
&& CopyRealName
.Buffer
!= RealName
->Buffer
)
602 RtlpFreeStringMemory(CopyRealName
.Buffer
, TAG_USTR
);
603 if (CopyLocalName
.Buffer
&& CopyLocalName
.Buffer
!= LocalName
->Buffer
)
604 RtlpFreeStringMemory(CopyLocalName
.Buffer
, TAG_USTR
);
608 *RealName
= CopyRealName
;
609 *LocalName
= CopyLocalName
;
610 return STATUS_SUCCESS
;
615 RtlGetFullPathName_Ustr(
616 _In_ PUNICODE_STRING FileName
,
618 _Out_z_bytecap_(Size
) PWSTR Buffer
,
619 _Out_opt_ PCWSTR
*ShortName
,
620 _Out_opt_ PBOOLEAN InvalidName
,
621 _Out_ RTL_PATH_TYPE
*PathType
)
624 PWCHAR FileNameBuffer
;
625 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
626 BOOLEAN SkipTrailingPathSeparators
;
633 PCUNICODE_STRING CurDirName
;
634 UNICODE_STRING EnvVarName
, EnvVarValue
;
635 WCHAR EnvVarNameBuffer
[4];
637 ULONG PrefixCut
= 0; // Where the path really starts (after the skipped prefix)
638 PWCHAR Prefix
= NULL
; // pointer to the string to be inserted as the new path prefix
639 ULONG PrefixLength
= 0;
644 /* For now, assume the name is valid */
645 DPRINT("Filename: %wZ\n", FileName
);
646 DPRINT("Size and buffer: %lx %p\n", Size
, Buffer
);
647 if (InvalidName
) *InvalidName
= FALSE
;
649 /* Handle initial path type and failure case */
650 *PathType
= RtlPathTypeUnknown
;
651 if ((FileName
->Length
== 0) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
653 /* Break filename into component parts */
654 FileNameBuffer
= FileName
->Buffer
;
655 FileNameLength
= FileName
->Length
;
656 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
658 /* Kill trailing spaces */
659 c
= FileNameBuffer
[FileNameChars
- 1];
660 while ((FileNameLength
!= 0) && (c
== L
' '))
662 /* Keep going, ignoring the spaces */
663 FileNameLength
-= sizeof(WCHAR
);
664 if (FileNameLength
!= 0) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
667 /* Check if anything is left */
668 if (FileNameLength
== 0) return 0;
671 * Check whether we'll need to skip trailing path separators in the
672 * computed full path name. If the original file name already contained
673 * trailing separators, then we keep them in the full path name. On the
674 * other hand, if the original name didn't contain any trailing separators
675 * then we'll skip it in the full path name.
677 SkipTrailingPathSeparators
= !IS_PATH_SEPARATOR(FileNameBuffer
[FileNameChars
- 1]);
679 /* Check if this is a DOS name */
680 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
681 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
684 /* Zero out the short name */
685 if (ShortName
) *ShortName
= NULL
;
687 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
688 DosLengthOffset
= HIWORD(DosLength
);
689 DosLength
= LOWORD(DosLength
);
691 /* Do we have a DOS length, and does the caller want validity? */
692 if (InvalidName
&& (DosLengthOffset
!= 0))
695 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
697 /* If the check failed, or the name is invalid, fail here */
698 if (!NT_SUCCESS(Status
)) return 0;
699 if (*InvalidName
) return 0;
702 /* Add the size of the device root and check if it fits in the size */
703 FullLength
= DosLength
+ DeviceRootString
.Length
;
704 if (FullLength
< Size
)
706 /* Add the device string */
707 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
709 /* Now add the DOS device name */
710 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
711 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
715 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
719 /* Otherwise, there's no space, so return the buffer size needed */
720 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
721 return FullLength
+ sizeof(UNICODE_NULL
);
724 /* Zero-out the destination buffer. FileName must be different from Buffer */
725 RtlZeroMemory(Buffer
, Size
);
727 /* Get the path type */
728 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
732 /**********************************************
733 ** CODE REWRITING IS HAPPENING THERE **
734 **********************************************/
735 Source
= FileNameBuffer
;
736 SourceLength
= FileNameLength
;
737 EnvVarValue
.Buffer
= NULL
;
739 /* Lock the PEB to get the current directory */
741 CurDirName
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
745 case RtlPathTypeUncAbsolute
: /* \\foo */
747 PrefixCut
= RtlpSkipUNCPrefix(FileNameBuffer
);
751 case RtlPathTypeLocalDevice
: /* \\.\foo */
757 case RtlPathTypeDriveAbsolute
: /* c:\foo */
759 ASSERT(FileNameBuffer
[1] == L
':');
760 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer
[2]));
762 // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
763 Prefix
= FileNameBuffer
;
764 PrefixLength
= 3 * sizeof(WCHAR
);
766 SourceLength
-= 3 * sizeof(WCHAR
);
772 case RtlPathTypeDriveRelative
: /* c:foo */
774 WCHAR CurDrive
, NewDrive
;
777 SourceLength
-= 2 * sizeof(WCHAR
);
779 CurDrive
= RtlpUpcaseUnicodeChar(CurDirName
->Buffer
[0]);
780 NewDrive
= RtlpUpcaseUnicodeChar(FileNameBuffer
[0]);
782 if ((NewDrive
!= CurDrive
) || CurDirName
->Buffer
[1] != L
':')
784 EnvVarNameBuffer
[0] = L
'=';
785 EnvVarNameBuffer
[1] = NewDrive
;
786 EnvVarNameBuffer
[2] = L
':';
787 EnvVarNameBuffer
[3] = UNICODE_NULL
;
789 EnvVarName
.Length
= 3 * sizeof(WCHAR
);
790 EnvVarName
.MaximumLength
= EnvVarName
.Length
+ sizeof(WCHAR
);
791 EnvVarName
.Buffer
= EnvVarNameBuffer
;
793 // FIXME: Is it possible to use the user-given buffer ?
794 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
795 EnvVarValue
.Length
= 0;
796 EnvVarValue
.MaximumLength
= (USHORT
)Size
;
797 EnvVarValue
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
798 if (EnvVarValue
.Buffer
== NULL
)
805 Status
= RtlQueryEnvironmentVariable_U(NULL
, &EnvVarName
, &EnvVarValue
);
811 * FIXME: Win2k seems to check that the environment
812 * variable actually points to an existing directory.
813 * If not, root of the drive is used (this seems also
814 * to be the only place in RtlGetFullPathName that the
815 * existence of a part of a path is checked).
817 EnvVarValue
.Buffer
[EnvVarValue
.Length
/ sizeof(WCHAR
)] = L
'\\';
818 Prefix
= EnvVarValue
.Buffer
;
819 PrefixLength
= EnvVarValue
.Length
+ sizeof(WCHAR
); /* Append trailing '\\' */
822 case STATUS_BUFFER_TOO_SMALL
:
823 reqsize
= EnvVarValue
.Length
+ SourceLength
+ sizeof(UNICODE_NULL
);
827 DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName
, Status
);
829 EnvVarNameBuffer
[0] = NewDrive
;
830 EnvVarNameBuffer
[1] = L
':';
831 EnvVarNameBuffer
[2] = L
'\\';
832 EnvVarNameBuffer
[3] = UNICODE_NULL
;
833 Prefix
= EnvVarNameBuffer
;
834 PrefixLength
= 3 * sizeof(WCHAR
);
836 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
837 EnvVarValue
.Buffer
= NULL
;
844 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
847 case RtlPathTypeRelative
: /* foo */
849 Prefix
= CurDirName
->Buffer
;
850 PrefixLength
= CurDirName
->Length
;
851 if (CurDirName
->Buffer
[1] != L
':')
853 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
862 case RtlPathTypeRooted
: /* \xxx */
864 if (CurDirName
->Buffer
[1] == L
':')
866 // The path starts with "C:\"
867 ASSERT(CurDirName
->Buffer
[1] == L
':');
868 ASSERT(IS_PATH_SEPARATOR(CurDirName
->Buffer
[2]));
870 Prefix
= CurDirName
->Buffer
;
871 PrefixLength
= 3 * sizeof(WCHAR
); // Skip "C:\"
873 PrefixCut
= 3; // Source index location incremented of + 3
877 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
878 PrefixLength
= PrefixCut
* sizeof(WCHAR
);
879 Prefix
= CurDirName
->Buffer
;
884 case RtlPathTypeRootLocalDevice
: /* \\. */
886 Prefix
= DeviceRootString
.Buffer
;
887 PrefixLength
= DeviceRootString
.Length
;
889 SourceLength
-= 3 * sizeof(WCHAR
);
895 case RtlPathTypeUnknown
:
899 /* Do we have enough space for storing the full path? */
900 reqsize
= PrefixLength
;
901 if (reqsize
+ SourceLength
+ sizeof(WCHAR
) > Size
)
903 /* Not enough space, return needed size (including terminating '\0') */
904 reqsize
+= SourceLength
+ sizeof(WCHAR
);
909 * Build the full path
911 /* Copy the path's prefix */
912 if (PrefixLength
) RtlMoveMemory(Buffer
, Prefix
, PrefixLength
);
913 /* Copy the remaining part of the path */
914 RtlMoveMemory(Buffer
+ PrefixLength
/ sizeof(WCHAR
), Source
, SourceLength
+ sizeof(WCHAR
));
918 if (EnvVarValue
.Buffer
)
920 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
921 EnvVarValue
.Buffer
= NULL
;
925 * Finally, put the path in canonical form (remove redundant . and ..,
926 * (back)slashes...) and retrieve the length of the full path name
927 * (without its terminating null character) (in chars).
929 reqsize
= RtlpCollapsePath(Buffer
, /* Size, reqsize, */ PrefixCut
, SkipTrailingPathSeparators
);
931 /* Find the file part, which is present after the last path separator */
934 ptr
= wcsrchr(Buffer
, L
'\\');
935 if (ptr
) ++ptr
; // Skip it
938 * For UNC paths, the file part is after the \\share\dir part of the path.
940 PrefixCut
= (*PathType
== RtlPathTypeUncAbsolute
? PrefixCut
: 3);
942 if (ptr
&& *ptr
&& (ptr
>= Buffer
+ PrefixCut
))
948 /* Zero-out the short name */
954 /* Release PEB lock */
962 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
963 OUT PUNICODE_STRING NtPath
,
964 OUT PCWSTR
*PartName
,
965 OUT PRTL_RELATIVE_NAME_U RelativeName
)
970 /* Validate the input */
971 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
973 /* Validate the DOS length */
974 DosLength
= DosPath
->Length
;
975 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
977 /* Make space for the new path */
978 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
980 DosLength
+ sizeof(UNICODE_NULL
));
981 if (!NewBuffer
) return STATUS_NO_MEMORY
;
983 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
984 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
985 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
986 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
987 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
988 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
990 /* Did the caller send a relative name? */
993 /* Zero initialize it */
994 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
995 RelativeName
->ContainingDirectory
= NULL
;
996 RelativeName
->CurDirRef
= 0;
999 /* Did the caller request a partial name? */
1002 /* Loop from the back until we find a path separator */
1003 p
= &NewBuffer
[DosLength
/ sizeof(WCHAR
)];
1004 while (--p
> NewBuffer
)
1006 /* We found a path separator, move past it */
1007 if (*p
== OBJ_NAME_PATH_SEPARATOR
)
1014 /* Check whether a separator was found and if something remains */
1015 if ((p
> NewBuffer
) && *p
)
1017 /* What follows the path separator is the partial name */
1022 /* The path ends with a path separator, no partial name */
1027 /* Build the final NT path string */
1028 NtPath
->Buffer
= NewBuffer
;
1029 NtPath
->Length
= (USHORT
)DosLength
;
1030 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
1031 return STATUS_SUCCESS
;
1036 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
1037 IN PCUNICODE_STRING DosName
,
1038 OUT PUNICODE_STRING NtName
,
1039 OUT PCWSTR
*PartName
,
1040 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1042 WCHAR BigBuffer
[MAX_PATH
+ 1];
1043 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
1044 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
1045 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
1047 RTL_PATH_TYPE InputPathType
, BufferPathType
;
1049 BOOLEAN NameInvalid
;
1050 PCURDIR CurrentDirectory
;
1052 /* Assume MAX_PATH for now */
1053 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
1054 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
1055 MaxLength
= sizeof(BigBuffer
);
1057 /* Validate the input */
1058 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
1060 /* Capture input string */
1061 CapturedDosName
= *DosName
;
1063 /* Check for the presence or absence of the NT prefix "\\?\" form */
1064 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
1065 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
1066 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
1067 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
1068 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
1069 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
1071 /* NT prefix not present */
1073 /* Quick path won't be used */
1076 /* Use the static buffer */
1078 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
1080 /* Allocate a buffer to hold the path */
1081 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
1082 DPRINT("MaxLength: %lx\n", MaxLength
);
1083 if (!NewBuffer
) return STATUS_NO_MEMORY
;
1087 /* NT prefix present */
1089 /* Use the optimized path after acquiring the lock */
1094 /* Lock the PEB and check if the quick path can be used */
1095 RtlAcquirePebLock();
1098 /* Some simple fixups will get us the correct path */
1099 DPRINT("Quick path\n");
1100 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
1105 /* Release the lock, we're done here */
1106 RtlReleasePebLock();
1110 /* Call the main function to get the full path name and length */
1111 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
1112 MAX_PATH
* sizeof(WCHAR
),
1117 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
1119 /* Invalid name, fail */
1120 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
1121 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1122 RtlReleasePebLock();
1123 return STATUS_OBJECT_NAME_INVALID
;
1126 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
1127 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
1128 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
1131 /* Check where it really is */
1132 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
1133 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
1134 switch (BufferPathType
)
1136 /* It's actually a UNC path in \??\UNC\ */
1137 case RtlPathTypeUncAbsolute
:
1138 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
1139 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
1143 case RtlPathTypeLocalDevice
:
1144 /* We made a good guess, go with it but skip the \??\ */
1148 case RtlPathTypeDriveAbsolute
:
1149 case RtlPathTypeDriveRelative
:
1150 case RtlPathTypeRooted
:
1151 case RtlPathTypeRelative
:
1152 /* Our guess was good, roll with it */
1155 /* Nothing else is expected */
1160 /* Now copy the prefix and the buffer */
1161 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
1162 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
1164 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
1166 /* Compute the length */
1167 Length
= PathLength
+ PrefixLength
- PrefixCut
* sizeof(WCHAR
);
1168 LengthChars
= Length
/ sizeof(WCHAR
);
1170 /* Setup the actual NT path string and terminate it */
1171 NtName
->Buffer
= NewBuffer
;
1172 NtName
->Length
= (USHORT
)Length
;
1173 NtName
->MaximumLength
= (USHORT
)MaxLength
;
1174 NewBuffer
[LengthChars
] = UNICODE_NULL
;
1175 DPRINT("New buffer: %S\n", NewBuffer
);
1176 DPRINT("NT Name: %wZ\n", NtName
);
1178 /* Check if a partial name was requested */
1179 if ((PartName
) && (*PartName
))
1181 /* Convert to Unicode */
1182 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
1183 if (NT_SUCCESS(Status
))
1185 /* Set the partial name */
1186 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
1191 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1192 RtlReleasePebLock();
1197 /* Check if a relative name was asked for */
1200 /* Setup the structure */
1201 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
1202 RelativeName
->ContainingDirectory
= NULL
;
1203 RelativeName
->CurDirRef
= NULL
;
1205 /* Check if the input path itself was relative */
1206 if (InputPathType
== RtlPathTypeRelative
)
1208 /* Get current directory */
1209 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
1210 if (CurrentDirectory
->Handle
)
1212 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
1213 if (!NT_SUCCESS(Status
))
1215 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1216 RtlReleasePebLock();
1220 /* If current directory is bigger than full path, there's no way */
1221 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
1223 RtlReleasePebLock();
1227 /* File is in current directory */
1228 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
1230 /* Make relative name string */
1231 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ PrefixLength
+ FullPath
.Length
- PrefixCut
* sizeof(WCHAR
));
1232 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
1233 /* If relative name starts with \, skip it */
1234 if (RelativeName
->RelativeName
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1236 RelativeName
->RelativeName
.Buffer
++;
1237 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
1239 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
1240 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
1244 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1248 /* Give back current directory data & reference counter */
1249 RelativeName
->CurDirRef
= RtlpCurDirRef
;
1250 if (RelativeName
->CurDirRef
)
1252 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
1255 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1262 RtlReleasePebLock();
1263 return STATUS_SUCCESS
;
1268 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
1270 OUT PUNICODE_STRING NtName
,
1271 OUT PCWSTR
*PartName
,
1272 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1275 UNICODE_STRING NameString
;
1277 /* Create the unicode name */
1278 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
1279 if (NT_SUCCESS(Status
))
1281 /* Call the unicode function */
1282 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
1295 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
1296 OUT PUNICODE_STRING NtName
,
1297 OUT PCWSTR
*PartName
,
1298 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1300 /* Call the internal function */
1301 ASSERT(RelativeName
);
1302 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
1311 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1312 IN BOOLEAN SucceedIfBusy
)
1315 RTL_RELATIVE_NAME_U RelativeName
;
1316 UNICODE_STRING NtPathName
;
1318 OBJECT_ATTRIBUTES ObjectAttributes
;
1320 FILE_BASIC_INFORMATION BasicInformation
;
1322 /* Get the NT Path */
1323 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1327 if (!Result
) return FALSE
;
1329 /* Save the buffer */
1330 Buffer
= NtPathName
.Buffer
;
1332 /* Check if we have a relative name */
1333 if (RelativeName
.RelativeName
.Length
)
1336 NtPathName
= RelativeName
.RelativeName
;
1340 /* Otherwise ignore it */
1341 RelativeName
.ContainingDirectory
= NULL
;
1344 /* Initialize the object attributes */
1345 InitializeObjectAttributes(&ObjectAttributes
,
1347 OBJ_CASE_INSENSITIVE
,
1348 RelativeName
.ContainingDirectory
,
1351 /* Query the attributes and free the buffer now */
1352 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1353 RtlReleaseRelativeName(&RelativeName
);
1354 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1356 /* Check if we failed */
1357 if (!NT_SUCCESS(Status
))
1359 /* Check if we failed because the file is in use */
1360 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1361 (Status
== STATUS_ACCESS_DENIED
))
1363 /* Check if the caller wants this to be considered OK */
1364 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1368 /* A failure because the file didn't exist */
1374 /* The file exists */
1378 /* Return the result */
1384 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1386 /* Call the updated API */
1387 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1392 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1393 IN BOOLEAN SucceedIfBusy
)
1395 UNICODE_STRING NameString
;
1397 /* Create the unicode name*/
1398 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1400 /* Call the unicode function */
1401 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
1408 /* PUBLIC FUNCTIONS ***********************************************************/
1415 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
1417 /* Check if a directory reference was grabbed */
1418 if (RelativeName
->CurDirRef
)
1420 /* Decrease reference count */
1421 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
1423 /* If no one uses it any longer, close handle & free */
1424 NtClose(RelativeName
->CurDirRef
->Handle
);
1425 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
1427 RelativeName
->CurDirRef
= NULL
;
1436 RtlGetLongestNtPathLength(VOID
)
1439 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1440 * a mapped network drive), which is accessed through the DOS Global?? path.
1441 * This is, and has always been equal to, 269 characters, except in Wine
1442 * which claims this is 277. Go figure.
1444 return MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
/ sizeof(WCHAR
) + sizeof(ANSI_NULL
);
1449 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1450 * (with a 'e' instead of a 'a' in "Seperators").
1454 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags
,
1455 IN PCUNICODE_STRING PathString
,
1460 /* Parameters validation */
1461 if (Length
== NULL
) return STATUS_INVALID_PARAMETER
;
1465 if (PathString
== NULL
) return STATUS_INVALID_PARAMETER
;
1467 /* No flags are supported yet */
1468 if (Flags
!= 0) return STATUS_INVALID_PARAMETER
;
1470 NumChars
= PathString
->Length
/ sizeof(WCHAR
);
1473 * Notice that we skip the last character, therefore:
1474 * - if we have: "some/path/f" we test for: "some/path/"
1475 * - if we have: "some/path/" we test for: "some/path"
1476 * - if we have: "s" we test for: ""
1477 * - if we have: "" then NumChars was already zero and we aren't there
1480 while (NumChars
> 0 && IS_PATH_SEPARATOR(PathString
->Buffer
[NumChars
- 1]))
1486 return STATUS_SUCCESS
;
1494 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
1496 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
1498 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1499 if (IS_PATH_SEPARATOR(Path
[0]))
1501 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
1502 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
1503 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
1504 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
1505 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
1509 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
1510 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
1511 return RtlPathTypeDriveRelative
; /* x: */
1520 RtlIsDosDeviceName_U(IN PCWSTR Path
)
1522 UNICODE_STRING PathString
;
1525 /* Build the string */
1526 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
1527 if (!NT_SUCCESS(Status
)) return 0;
1530 * Returns 0 if name is not valid DOS device name, or DWORD with
1531 * offset in bytes to DOS device name from beginning of buffer in high word
1532 * and size in bytes of DOS device name in low word
1534 return RtlIsDosDeviceName_Ustr(&PathString
);
1542 RtlGetCurrentDirectory_U(
1543 _In_ ULONG MaximumLength
,
1544 _Out_bytecap_(MaximumLength
) PWSTR Buffer
)
1546 ULONG Length
, Bytes
;
1549 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
1551 /* Lock the PEB to get the current directory */
1552 RtlAcquirePebLock();
1553 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1555 /* Get the buffer and character length */
1556 CurDirName
= CurDir
->DosPath
.Buffer
;
1557 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
1558 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
1561 * DosPath.Buffer should always have a trailing slash. There is an assert
1562 * below which checks for this.
1564 * This function either returns x:\ for a root (keeping the original buffer)
1565 * or it returns x:\path\foo for a directory (replacing the trailing slash
1568 Bytes
= Length
* sizeof(WCHAR
);
1569 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
1571 /* Check if caller does not have enough space */
1572 if (MaximumLength
<= Bytes
)
1574 /* Call has no space for it, fail, add the trailing slash */
1575 RtlReleasePebLock();
1576 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1581 /* Check if caller does not have enough space */
1582 if (MaximumLength
< Bytes
)
1584 /* Call has no space for it, fail */
1585 RtlReleasePebLock();
1590 /* Copy the buffer since we seem to have space */
1591 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
1593 /* The buffer should end with a path separator */
1594 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
1596 /* Again check for our two cases (drive root vs path) */
1597 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
1599 /* Replace the trailing slash with a null */
1600 Buffer
[Length
- 1] = UNICODE_NULL
;
1605 /* Append the null char since there's no trailing slash */
1606 Buffer
[Length
] = UNICODE_NULL
;
1609 /* Release PEB lock */
1610 RtlReleasePebLock();
1611 DPRINT("CurrentDirectory %S\n", Buffer
);
1612 return Length
* sizeof(WCHAR
);
1620 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1624 RTL_PATH_TYPE PathType
;
1625 IO_STATUS_BLOCK IoStatusBlock
;
1626 UNICODE_STRING FullPath
, NtName
;
1627 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1628 OBJECT_ATTRIBUTES ObjectAttributes
;
1629 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1630 ULONG SavedLength
, CharLength
, FullPathLength
;
1631 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1633 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1635 /* Initialize for failure case */
1636 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1638 /* Can't set current directory on DOS device */
1639 if (RtlIsDosDeviceName_Ustr(Path
))
1641 return STATUS_NOT_A_DIRECTORY
;
1644 /* Get current directory */
1645 RtlAcquirePebLock();
1646 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1648 /* Check if we have to drop current handle */
1649 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1651 OldHandle
= CurDir
->Handle
;
1652 CurDir
->Handle
= NULL
;
1655 /* Allocate a buffer for full path (using max possible length */
1656 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1657 if (!FullPath
.Buffer
)
1659 Status
= STATUS_NO_MEMORY
;
1664 FullPath
.Length
= 0;
1665 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1667 /* Get new directory full path */
1668 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1669 if (!FullPathLength
)
1671 Status
= STATUS_OBJECT_NAME_INVALID
;
1675 SavedLength
= FullPath
.MaximumLength
;
1676 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1678 if (FullPathLength
> FullPath
.MaximumLength
)
1680 Status
= STATUS_NAME_TOO_LONG
;
1684 /* Translate it to NT name */
1685 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1687 Status
= STATUS_OBJECT_NAME_INVALID
;
1691 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1692 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1695 /* If previous current directory was removable, then check it for dropping */
1696 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1698 /* Get back normal handle */
1699 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1700 CurDir
->Handle
= NULL
;
1702 /* Get device information */
1703 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1706 sizeof(FileFsDeviceInfo
),
1707 FileFsDeviceInformation
);
1708 /* Retry without taking care of removable device */
1709 if (!NT_SUCCESS(Status
))
1711 Status
= RtlSetCurrentDirectory_U(Path
);
1717 /* Open directory */
1718 Status
= NtOpenFile(&CurDirHandle
,
1719 SYNCHRONIZE
| FILE_TRAVERSE
,
1722 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1723 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1724 if (!NT_SUCCESS(Status
)) goto Leave
;
1726 /* Get device information */
1727 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1730 sizeof(FileFsDeviceInfo
),
1731 FileFsDeviceInformation
);
1732 if (!NT_SUCCESS(Status
)) goto Leave
;
1735 /* If device is removable, mark handle */
1736 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1738 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1741 FullPath
.Length
= (USHORT
)FullPathLength
;
1743 /* If full path isn't \ terminated, do it */
1744 if (FullPath
.Buffer
[CharLength
- 1] != OBJ_NAME_PATH_SEPARATOR
)
1746 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1748 Status
= STATUS_NAME_TOO_LONG
;
1752 FullPath
.Buffer
[CharLength
] = OBJ_NAME_PATH_SEPARATOR
;
1753 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1754 FullPath
.Length
+= sizeof(WCHAR
);
1757 /* If we have previous current directory with only us as reference, save it */
1758 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1760 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1764 /* Allocate new current directory struct saving previous one */
1765 OldCurDir
= RtlpCurDirRef
;
1766 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1769 RtlpCurDirRef
= OldCurDir
;
1771 Status
= STATUS_NO_MEMORY
;
1775 /* Set reference to 1 (us) */
1776 RtlpCurDirRef
->RefCount
= 1;
1780 CurDir
->Handle
= CurDirHandle
;
1781 RtlpCurDirRef
->Handle
= CurDirHandle
;
1782 CurDirHandle
= NULL
;
1784 /* Copy full path */
1785 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1786 CurDir
->DosPath
.Length
= FullPath
.Length
;
1788 Status
= STATUS_SUCCESS
;
1791 RtlReleasePebLock();
1793 if (FullPath
.Buffer
)
1795 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1800 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1803 if (CurDirHandle
) NtClose(CurDirHandle
);
1805 if (OldHandle
) NtClose(OldHandle
);
1807 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1809 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1811 NtClose(OldCurDir
->Handle
);
1812 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1823 RtlGetFullPathName_UEx(
1824 _In_ PWSTR FileName
,
1825 _In_ ULONG BufferLength
,
1826 _Out_writes_bytes_(BufferLength
) PWSTR Buffer
,
1827 _Out_opt_ PWSTR
*FilePart
,
1828 _Out_opt_ RTL_PATH_TYPE
*InputPathType
)
1830 UNICODE_STRING FileNameString
;
1836 /* Build the string */
1837 status
= RtlInitUnicodeStringEx(&FileNameString
, FileName
);
1838 if (!NT_SUCCESS(status
)) return 0;
1840 /* Call the extended function */
1841 return RtlGetFullPathName_Ustr(
1850 /******************************************************************
1851 * RtlGetFullPathName_U (NTDLL.@)
1853 * Returns the number of bytes written to buffer (not including the
1854 * terminating NULL) if the function succeeds, or the required number of bytes
1855 * (including the terminating NULL) if the buffer is too small.
1857 * file_part will point to the filename part inside buffer (except if we use
1858 * DOS device name, in which case file_in_buf is NULL)
1868 RtlGetFullPathName_U(
1869 _In_ PCWSTR FileName
,
1871 _Out_z_bytecap_(Size
) PWSTR Buffer
,
1872 _Out_opt_ PWSTR
*ShortName
)
1874 RTL_PATH_TYPE PathType
;
1876 /* Call the extended function */
1877 return RtlGetFullPathName_UEx((PWSTR
)FileName
,
1889 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1890 OUT PUNICODE_STRING NtName
,
1891 OUT PCWSTR
*PartName
,
1892 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1894 /* Call the internal function */
1895 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1907 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1908 OUT PUNICODE_STRING NtName
,
1909 OUT PCWSTR
*PartName
,
1910 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1912 /* Call the internal function */
1913 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1925 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1926 OUT PUNICODE_STRING NtName
,
1927 OUT PCWSTR
*PartName
,
1928 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1930 /* Call the internal function */
1931 ASSERT(RelativeName
);
1932 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1944 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1945 OUT PUNICODE_STRING NtName
,
1946 OUT PCWSTR
*PartName
,
1947 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1949 /* Call the internal function */
1950 ASSERT(RelativeName
);
1951 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1961 NTSTATUS NTAPI
RtlNtPathNameToDosPathName(IN ULONG Flags
,
1962 IN OUT PRTL_UNICODE_STRING_BUFFER Path
,
1963 OUT PULONG PathType
,
1966 PCUNICODE_STRING UsePrefix
= NULL
, AlternatePrefix
= NULL
;
1972 return STATUS_INVALID_PARAMETER
;
1974 /* The initial check is done on Path->String */
1975 if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix
, &Path
->String
, TRUE
))
1977 UsePrefix
= &RtlpDosDevicesUncPrefix
;
1978 AlternatePrefix
= &RtlpDoubleSlashPrefix
;
1980 *PathType
= RTL_CONVERTED_UNC_PATH
;
1982 else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix
, &Path
->String
, FALSE
))
1984 UsePrefix
= &RtlpDosDevicesPrefix
;
1986 *PathType
= RTL_CONVERTED_NT_PATH
;
1993 USHORT Len
= Path
->String
.Length
- UsePrefix
->Length
;
1994 if (AlternatePrefix
)
1995 Len
+= AlternatePrefix
->Length
;
1997 Status
= RtlEnsureBufferSize(0, &Path
->ByteBuffer
, Len
);
1998 if (!NT_SUCCESS(Status
))
2001 if (Len
+ sizeof(UNICODE_NULL
) <= Path
->ByteBuffer
.Size
)
2003 /* Then, the contents of Path->ByteBuffer are always used... */
2004 if (AlternatePrefix
)
2006 memcpy(Path
->ByteBuffer
.Buffer
, AlternatePrefix
->Buffer
, AlternatePrefix
->Length
);
2007 memmove(Path
->ByteBuffer
.Buffer
+ AlternatePrefix
->Length
, Path
->ByteBuffer
.Buffer
+ UsePrefix
->Length
,
2008 Len
- AlternatePrefix
->Length
);
2012 memmove(Path
->ByteBuffer
.Buffer
, Path
->ByteBuffer
.Buffer
+ UsePrefix
->Length
, Len
);
2014 Path
->String
.Buffer
= (PWSTR
)Path
->ByteBuffer
.Buffer
;
2015 Path
->String
.Length
= Len
;
2016 Path
->String
.MaximumLength
= Path
->ByteBuffer
.Size
;
2017 Path
->String
.Buffer
[Len
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2019 return STATUS_SUCCESS
;
2024 switch (RtlDetermineDosPathNameType_Ustr(&Path
->String
))
2026 case RtlPathTypeUncAbsolute
:
2027 case RtlPathTypeDriveAbsolute
:
2028 case RtlPathTypeLocalDevice
:
2029 case RtlPathTypeRootLocalDevice
:
2030 *PathType
= RTL_UNCHANGED_DOS_PATH
;
2032 case RtlPathTypeUnknown
:
2033 case RtlPathTypeDriveRelative
:
2034 case RtlPathTypeRooted
:
2035 case RtlPathTypeRelative
:
2036 *PathType
= RTL_UNCHANGED_UNK_PATH
;
2041 return STATUS_SUCCESS
;
2049 RtlDosSearchPath_U(IN PCWSTR Path
,
2051 IN PCWSTR Extension
,
2054 OUT PWSTR
*PartName
)
2057 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
2058 UNICODE_STRING TempString
;
2059 PWCHAR NewBuffer
, BufferStart
;
2062 /* Check if this is an absolute path */
2063 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
2065 /* Check if the file exists */
2066 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
2068 /* Get the full name, which does the DOS lookup */
2069 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
2072 /* Doesn't exist, so fail */
2076 /* Scan the filename */
2080 /* Looking for an extension */
2083 /* No extension string needed -- it's part of the filename */
2088 /* Next character */
2092 /* Do we have an extension? */
2095 /* Nope, don't worry about one */
2096 ExtensionLength
= 0;
2100 /* Build a temporary string to get the extension length */
2101 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
2102 if (!NT_SUCCESS(Status
)) return 0;
2103 ExtensionLength
= TempString
.Length
;
2106 /* Build a temporary string to get the path length */
2107 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
2108 if (!NT_SUCCESS(Status
)) return 0;
2109 PathLength
= TempString
.Length
;
2111 /* Build a temporary string to get the filename length */
2112 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
2113 if (!NT_SUCCESS(Status
)) return 0;
2114 FileNameLength
= TempString
.Length
;
2116 /* Allocate the buffer for the new string name */
2117 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
2126 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
2131 /* Final loop to build the path */
2134 /* Check if we have a valid character */
2135 BufferStart
= NewBuffer
;
2138 /* Loop as long as there's no semicolon */
2139 while (*Path
!= L
';')
2141 /* Copy the next character */
2142 *BufferStart
++ = *Path
++;
2146 /* We found a semi-colon, to stop path processing on this loop */
2147 if (*Path
== L
';') ++Path
;
2150 /* Add a terminating slash if needed */
2151 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != OBJ_NAME_PATH_SEPARATOR
))
2153 *BufferStart
++ = OBJ_NAME_PATH_SEPARATOR
;
2156 /* Bail out if we reached the end */
2157 if (!*Path
) Path
= NULL
;
2159 /* Copy the file name and check if an extension is needed */
2160 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
2161 if (ExtensionLength
)
2163 /* Copy the extension too */
2164 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
2166 ExtensionLength
+ sizeof(WCHAR
));
2170 /* Just NULL-terminate */
2171 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
2174 /* Now, does this file exist? */
2175 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
2177 /* Call the full-path API to get the length */
2178 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
2182 /* If we got here, path doesn't exist, so fail the call */
2187 /* Free the allocation and return the length */
2188 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
2197 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
2198 IN PUNICODE_STRING StaticString
,
2199 IN PUNICODE_STRING DynamicString
,
2200 IN PUNICODE_STRING
*StringUsed
,
2201 IN PSIZE_T FilePartSize
,
2202 OUT PBOOLEAN NameInvalid
,
2203 OUT RTL_PATH_TYPE
* PathType
,
2204 OUT PSIZE_T LengthNeeded
)
2207 PWCHAR StaticBuffer
;
2210 USHORT StaticLength
;
2211 UNICODE_STRING TempDynamicString
;
2213 /* Initialize all our locals */
2215 StaticBuffer
= NULL
;
2216 TempDynamicString
.Buffer
= NULL
;
2218 /* Initialize the input parameters */
2219 if (StringUsed
) *StringUsed
= NULL
;
2220 if (LengthNeeded
) *LengthNeeded
= 0;
2221 if (FilePartSize
) *FilePartSize
= 0;
2223 /* Check for invalid parameters */
2224 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
2226 return STATUS_INVALID_PARAMETER
;
2229 /* Check if we did not get an input string */
2233 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
2234 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
2235 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
2239 /* Use the one we received */
2240 StaticBuffer
= StaticString
->Buffer
;
2241 StaticLength
= StaticString
->MaximumLength
;
2244 /* Call the lower-level function */
2245 Length
= RtlGetFullPathName_Ustr(FileName
,
2251 DPRINT("Length: %u StaticBuffer: %S\n", Length
, StaticBuffer
);
2254 /* Fail if it failed */
2255 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
2258 Status
= STATUS_OBJECT_NAME_INVALID
;
2262 /* Check if it fits inside our static string */
2263 if ((StaticString
) && (Length
< StaticLength
))
2265 /* Set the final length */
2266 StaticString
->Length
= (USHORT
)Length
;
2268 /* Set the file part size */
2269 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2271 /* Return the static string if requested */
2272 if (StringUsed
) *StringUsed
= StaticString
;
2274 /* We are done with success */
2275 Status
= STATUS_SUCCESS
;
2279 /* Did we not have an input dynamic string ?*/
2282 /* Return the length we need */
2283 if (LengthNeeded
) *LengthNeeded
= Length
;
2285 /* And fail such that the caller can try again */
2286 Status
= STATUS_BUFFER_TOO_SMALL
;
2290 /* Check if it fits in our static buffer */
2291 if ((StaticBuffer
) && (Length
< StaticLength
))
2293 /* NULL-terminate it */
2294 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2296 /* Set the settings for the dynamic string the caller sent */
2297 DynamicString
->MaximumLength
= StaticLength
;
2298 DynamicString
->Length
= (USHORT
)Length
;
2299 DynamicString
->Buffer
= StaticBuffer
;
2301 /* Set the part size */
2302 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
2304 /* Return the dynamic string if requested */
2305 if (StringUsed
) *StringUsed
= DynamicString
;
2307 /* Do not free the static buffer on exit, and return success */
2308 StaticBuffer
= NULL
;
2309 Status
= STATUS_SUCCESS
;
2313 /* Now try again under the PEB lock */
2314 RtlAcquirePebLock();
2315 Length
= RtlGetFullPathName_Ustr(FileName
,
2324 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2325 __FUNCTION__
, __LINE__
);
2326 Status
= STATUS_OBJECT_NAME_INVALID
;
2330 /* Check if it fits inside our static string now */
2331 if ((StaticString
) && (Length
< StaticLength
))
2333 /* Set the final length */
2334 StaticString
->Length
= (USHORT
)Length
;
2336 /* Set the file part size */
2337 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2339 /* Return the static string if requested */
2340 if (StringUsed
) *StringUsed
= StaticString
;
2342 /* We are done with success */
2343 Status
= STATUS_SUCCESS
;
2347 /* Check if the path won't even fit in a real string */
2348 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
2350 /* Name is way too long, fail */
2351 Status
= STATUS_NAME_TOO_LONG
;
2355 /* Allocate the string to hold the path name now */
2356 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
2358 if (!TempDynamicString
.Buffer
)
2360 /* Out of memory, fail */
2361 Status
= STATUS_NO_MEMORY
;
2365 /* Add space for a NULL terminator, and now check the full path */
2366 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
2367 Length
= RtlGetFullPathName_Ustr(FileName
,
2369 TempDynamicString
.Buffer
,
2375 /* Some path error, so fail out */
2376 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2377 __FUNCTION__
, __LINE__
);
2378 Status
= STATUS_OBJECT_NAME_INVALID
;
2382 /* It should fit in the string we just allocated */
2383 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
2384 if (Length
> TempDynamicString
.MaximumLength
)
2386 /* This is really weird and would mean some kind of race */
2387 Status
= STATUS_INTERNAL_ERROR
;
2391 /* Return the file part size */
2392 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
2394 /* Terminate the whole string now */
2395 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2397 /* Finalize the string and return it to the user */
2398 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
2399 DynamicString
->Length
= (USHORT
)Length
;
2400 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
2401 if (StringUsed
) *StringUsed
= DynamicString
;
2403 /* Return success and make sure we don't free the buffer on exit */
2404 TempDynamicString
.Buffer
= NULL
;
2405 Status
= STATUS_SUCCESS
;
2408 /* Release the PEB lock */
2409 RtlReleasePebLock();
2412 /* Free any buffers we should be freeing */
2413 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2414 if ((StaticString
) && (StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2416 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2418 if (TempDynamicString
.Buffer
)
2420 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2423 /* Print out any unusual errors */
2424 if ((NT_ERROR(Status
)) &&
2425 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2427 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2428 __FUNCTION__
, FileName
, Status
);
2431 /* Return, we're all done */
2440 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2441 IN PUNICODE_STRING PathString
,
2442 IN PUNICODE_STRING FileNameString
,
2443 IN PUNICODE_STRING ExtensionString
,
2444 IN PUNICODE_STRING CallerBuffer
,
2445 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2446 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2447 OUT PSIZE_T FilePartSize OPTIONAL
,
2448 OUT PSIZE_T LengthNeeded OPTIONAL
)
2450 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2451 UNICODE_STRING StaticCandidateString
;
2453 RTL_PATH_TYPE PathType
;
2454 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2455 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2456 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2457 PUNICODE_STRING FullIsolatedPath
;
2458 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2459 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2461 /* Initialize the input string */
2462 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2463 StaticCandidateBuffer
,
2464 sizeof(StaticCandidateBuffer
));
2466 /* Initialize optional arguments */
2467 if (FullNameOut
) *FullNameOut
= NULL
;
2468 if (FilePartSize
) *FilePartSize
= 0;
2469 if (LengthNeeded
) *LengthNeeded
= 0;
2472 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2473 DynamicString
->Buffer
= NULL
;
2476 /* Check for invalid parameters */
2479 !(FileNameString
) ||
2480 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2483 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2484 Status
= STATUS_INVALID_PARAMETER
;
2488 /* First check what kind of path this is */
2489 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2491 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2493 (PathType
== RtlPathTypeRelative
) &&
2494 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2495 (FileNameString
->Buffer
[0] == L
'.') &&
2496 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2497 ((FileNameString
->Buffer
[1] == L
'.') &&
2498 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2499 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2501 /* Yes, and this path is like that, so make it seem unknown */
2502 PathType
= RtlPathTypeUnknown
;
2505 /* Now check relative vs non-relative paths */
2506 if (PathType
== RtlPathTypeRelative
)
2508 /* Does the caller want SxS? */
2511 /* Apply the SxS magic */
2512 FullIsolatedPath
= NULL
;
2513 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2522 if (NT_SUCCESS(Status
))
2524 /* We found the SxS path, return it */
2525 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2528 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2530 /* Critical SxS error, fail */
2531 DbgPrint("%s: Failing because call to "
2532 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2541 /* No SxS key found, or not requested, check if there's an extension */
2542 if (ExtensionString
)
2544 /* Save the extension length, and check if there's a file name */
2545 ExtensionLength
= ExtensionString
->Length
;
2546 if (FileNameString
->Length
)
2548 /* Start parsing the file name */
2549 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2550 while (End
> FileNameString
->Buffer
)
2552 /* If we find a path separator, there's no extension */
2553 if (IS_PATH_SEPARATOR(*--End
)) break;
2555 /* Otherwise, did we find an extension dot? */
2558 /* Ignore what the caller sent it, use the filename's */
2559 ExtensionString
= NULL
;
2560 ExtensionLength
= 0;
2567 /* Check if we got a path */
2568 if (PathString
->Length
)
2570 /* Start parsing the path name, looking for path separators */
2571 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2573 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2575 /* This is the size of the path -- handle a trailing slash */
2576 PathSize
= End
- p
- 1;
2577 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2579 /* Check if we found a bigger path than before */
2580 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2582 /* Keep going with the path after this path separator */
2586 /* This is the trailing path, run the same code as above */
2588 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2589 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2591 /* Finally, convert the largest path size into WCHAR */
2592 MaxPathSize
*= sizeof(WCHAR
);
2595 /* Use the extension, the file name, and the largest path as the size */
2596 WorstCaseLength
= ExtensionLength
+
2597 FileNameString
->Length
+
2598 (USHORT
)MaxPathSize
+
2599 sizeof(UNICODE_NULL
);
2600 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2602 /* It has to fit in a registry string, if not, fail here */
2603 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2604 "worst case file name length is %Iu bytes\n",
2607 Status
= STATUS_NAME_TOO_LONG
;
2611 /* Scan the path now, to see if we can find the file */
2612 p
= PathString
->Buffer
;
2613 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2616 /* Find out where this path ends */
2617 for (SegmentEnd
= p
;
2618 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2621 /* Compute the size of this path */
2622 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2624 /* Handle trailing slash if there isn't one */
2625 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2627 /* Add space for one */
2628 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2631 /* Now check if our initial static buffer is too small */
2632 if (StaticCandidateString
.MaximumLength
<
2633 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2635 /* At this point we should've been using our static buffer */
2636 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2637 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2639 /* Something is really messed up if this was the dynamic string */
2640 DbgPrint("%s: internal error #1; "
2641 "CandidateString.Buffer = %p; "
2642 "StaticCandidateBuffer = %p\n",
2644 StaticCandidateString
.Buffer
,
2645 StaticCandidateBuffer
);
2646 Status
= STATUS_INTERNAL_ERROR
;
2650 /* We checked before that the maximum possible size shoudl fit! */
2651 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2652 UNICODE_STRING_MAX_BYTES
);
2653 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2654 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2656 /* For some reason it's not fitting anymore. Something messed up */
2657 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2658 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2661 FileNameString
->Length
,
2663 Status
= STATUS_INTERNAL_ERROR
;
2667 /* Now allocate the dynamic string */
2668 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2670 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2672 if (!StaticCandidateString
.Buffer
)
2674 /* Out of memory, fail */
2675 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2677 StaticCandidateString
.MaximumLength
);
2678 Status
= STATUS_NO_MEMORY
;
2683 /* Copy the path in the string */
2684 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2686 /* Get to the end of the string, and add the trailing slash if missing */
2687 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2688 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2690 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2693 /* Copy the filename now */
2694 RtlCopyMemory(CandidateEnd
,
2695 FileNameString
->Buffer
,
2696 FileNameString
->Length
);
2697 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2699 /* Check if there was an extension */
2700 if (ExtensionString
)
2702 /* Copy the extension too */
2703 RtlCopyMemory(CandidateEnd
,
2704 ExtensionString
->Buffer
,
2705 ExtensionString
->Length
);
2706 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2709 /* We are done, terminate it */
2710 *CandidateEnd
= UNICODE_NULL
;
2712 /* Now set the final length of the string so it becomes valid */
2713 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2714 StaticCandidateString
.Buffer
) *
2717 /* Check if this file exists */
2718 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2719 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2721 /* Awesome, it does, now get the full path */
2722 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2725 (PUNICODE_STRING
*)FullNameOut
,
2730 if (!(NT_SUCCESS(Status
)) &&
2731 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2732 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2734 DbgPrint("%s: Failing because we thought we found %wZ on "
2735 "the search path, but RtlGetfullPathNameUStrEx() "
2738 &StaticCandidateString
,
2741 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2746 /* Otherwise, move to the next path */
2747 if (SegmentEnd
!= End
)
2749 /* Handle the case of the path separator trailing */
2759 /* Loop finished and we didn't break out -- fail */
2760 Status
= STATUS_NO_SUCH_FILE
;
2764 /* We have a full path, so check if it does exist */
2765 DPRINT("%wZ\n", FileNameString
);
2766 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2768 /* It doesn't exist, did we have an extension? */
2769 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2771 /* No extension, so just fail */
2772 Status
= STATUS_NO_SUCH_FILE
;
2776 /* There was an extension, check if the filename already had one */
2777 if (!(Flags
& 4) && (FileNameString
->Length
))
2779 /* Parse the filename */
2780 p
= FileNameString
->Buffer
;
2781 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2784 /* If there's a path separator, there's no extension */
2785 if (IS_PATH_SEPARATOR(*--End
)) break;
2787 /* Othwerwise, did we find an extension dot? */
2790 /* File already had an extension, so fail */
2791 Status
= STATUS_NO_SUCH_FILE
;
2797 /* So there is an extension, we'll try again by adding it */
2798 NamePlusExtLength
= FileNameString
->Length
+
2799 ExtensionString
->Length
+
2800 sizeof(UNICODE_NULL
);
2801 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2803 /* It won't fit in any kind of valid string, so fail */
2804 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2807 Status
= STATUS_NAME_TOO_LONG
;
2811 /* Fill it fit in our temporary string? */
2812 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2814 /* It won't fit anymore, allocate a dynamic string for it */
2815 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2816 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2818 if (!StaticCandidateString
.Buffer
)
2820 /* Ran out of memory, so fail */
2821 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2823 Status
= STATUS_NO_MEMORY
;
2828 /* Copy the filename */
2829 RtlCopyUnicodeString(&StaticCandidateString
, FileNameString
);
2831 /* Copy the extension */
2832 RtlAppendUnicodeStringToString(&StaticCandidateString
,
2835 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2837 /* And check if this file now exists */
2838 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2840 /* Still no joy, fail out */
2841 Status
= STATUS_NO_SUCH_FILE
;
2845 /* File was found, get the final full path */
2846 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2849 (PUNICODE_STRING
*)FullNameOut
,
2854 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2856 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2857 "failed with status %08lx\n",
2859 &StaticCandidateString
,
2862 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2866 /* File was found on the first try, get the final full path */
2867 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2870 (PUNICODE_STRING
*)FullNameOut
,
2875 if (!(NT_SUCCESS(Status
)) &&
2876 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2877 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2879 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2880 "failed with status %08lx\n",
2885 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2890 /* Anything that was not an error, turn into STATUS_SUCCESS */
2891 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2893 /* Check if we had a dynamic string */
2894 if ((StaticCandidateString
.Buffer
) &&
2895 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2898 RtlFreeUnicodeString(&StaticCandidateString
);
2901 /* Return the status */
2910 RtlDoesFileExists_U(IN PCWSTR FileName
)
2912 /* Call the new function */
2913 return RtlDoesFileExists_UEx(FileName
, TRUE
);