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 PRTLP_CURDIR_REF RtlpCurDirRef
;
52 /* PRIVATE FUNCTIONS **********************************************************/
56 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
61 Path
= PathString
->Buffer
;
62 Chars
= PathString
->Length
/ sizeof(WCHAR
);
64 /* Return if there are no characters */
65 if (!Chars
) return RtlPathTypeRelative
;
68 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
69 * actually check for the path length before touching the characters
71 if (IS_PATH_SEPARATOR(Path
[0]))
73 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
74 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
75 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
76 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
77 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
81 if ((Chars
< 2) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
82 if ((Chars
< 3) || !(IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveRelative
; /* x: */
83 return RtlPathTypeDriveAbsolute
; /* x:\ */
89 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
91 UNICODE_STRING PathCopy
;
93 USHORT PathChars
, ColonCount
= 0;
94 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
97 /* Validate the input */
98 if (!PathString
) return 0;
100 /* Check what type of path this is */
101 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
103 /* Fail for UNC or unknown paths */
104 case RtlPathTypeUnknown
:
105 case RtlPathTypeUncAbsolute
:
108 /* Make special check for the CON device */
109 case RtlPathTypeLocalDevice
:
110 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
112 /* This should return 0x80006 */
113 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
121 /* Make a copy of the string */
122 PathCopy
= *PathString
;
123 OriginalLength
= PathString
->Length
;
125 /* Return if there's no characters */
126 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
127 if (!PathChars
) return 0;
129 /* Check for drive path and truncate */
130 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
132 /* Fixup the lengths */
133 PathCopy
.Length
-= sizeof(WCHAR
);
134 if (!--PathChars
) return 0;
136 /* Remember this for later */
140 /* Check for extension or space, and truncate */
143 /* Stop if we hit something else than a space or period */
144 c
= PathCopy
.Buffer
[PathChars
- 1];
145 if ((c
!= L
'.') && (c
!= L
' ')) break;
147 /* Fixup the lengths */
148 PathCopy
.Length
-= sizeof(WCHAR
);
150 /* Remember this for later */
152 } while (--PathChars
);
154 /* Anything still left? */
157 /* Loop from the end */
158 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
159 End
>= PathCopy
.Buffer
;
162 /* Check if the character is a path or drive separator */
164 if (IS_PATH_SEPARATOR(c
) || ((c
== L
':') && (End
== PathCopy
.Buffer
+ 1)))
166 /* Get the next lower case character */
168 c
= RtlpDowncaseUnicodeChar(*End
);
170 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
171 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
172 ((c
== L
'l') || (c
== L
'c') || (c
== L
'p') || (c
== L
'a') || (c
== L
'n')))
174 /* Calculate the offset */
175 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
177 /* Build the final string */
178 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
179 PathCopy
.Buffer
= End
;
181 /* Save new amount of chars in the path */
182 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
193 /* Get the next lower case character and check if it's a DOS device */
194 c
= RtlpDowncaseUnicodeChar(*PathCopy
.Buffer
);
195 if ((c
!= L
'l') && (c
!= L
'c') && (c
!= L
'p') && (c
!= L
'a') && (c
!= L
'n'))
197 /* Not LPT, COM, PRN, AUX, or NUL */
202 /* Now skip past any extra extension or drive letter characters */
203 Start
= PathCopy
.Buffer
;
204 End
= &Start
[PathChars
];
208 if ((c
== L
'.') || (c
== L
':')) break;
212 /* And then go backwards to get rid of spaces */
213 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == L
' ')) --Start
;
215 /* Finally see how many characters are left, and that's our size */
216 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
217 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
219 /* Check if this is a COM or LPT port, which has a digit after it */
220 if ((PathChars
== 4) &&
221 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != L
'0')))
223 /* Don't compare the number part, just check for LPT or COM */
224 PathCopy
.Length
-= sizeof(WCHAR
);
225 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
226 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
229 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
230 return MAKELONG(ReturnLength
, ReturnOffset
);
233 else if ((PathChars
== 3) &&
234 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
235 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
236 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
237 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
239 /* Otherwise this was something like AUX, NUL, PRN, or CON */
240 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
241 return MAKELONG(ReturnLength
, ReturnOffset
);
244 /* Otherwise, this is not a valid DOS device */
250 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
252 OUT PBOOLEAN NameInvalid
)
257 /* Allocate a large enough buffer */
258 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
264 /* Copy the filename */
265 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
267 /* And add a dot at the end */
268 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
269 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
271 /* Check if the file exists or not */
272 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
274 /* Get rid of the buffer now */
275 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
279 /* Assume the name is ok, but fail the call */
280 *NameInvalid
= FALSE
;
281 Status
= STATUS_NO_MEMORY
;
284 /* Return the status */
290 /******************************************************************
291 * RtlpCollapsePath (from WINE)
293 * Helper for RtlGetFullPathName_U
295 * 1) Converts slashes into backslashes and gets rid of duplicated ones;
296 * 2) Gets rid of . and .. components in the path.
298 * Returns the full path length without its terminating NULL character.
301 RtlpCollapsePath(PWSTR Path
, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark
, BOOLEAN SkipTrailingPathSeparators
)
305 // FIXME: Do not suppose NULL-terminated strings!!
307 ULONG PathLength
= wcslen(Path
);
308 PWSTR EndBuffer
= Path
+ PathLength
; // Path + PathBufferSize / sizeof(WCHAR);
311 /* Convert slashes into backslashes */
312 for (p
= Path
; *p
; p
++)
314 if (*p
== L
'/') *p
= L
'\\';
317 /* Collapse duplicate backslashes */
318 next
= Path
+ max( 1, mark
);
319 for (p
= next
; *p
; p
++)
321 if (*p
!= L
'\\' || next
[-1] != L
'\\') *next
++ = *p
;
323 *next
= UNICODE_NULL
;
333 case UNICODE_NULL
: /* final . */
334 if (p
> Path
+ mark
) p
--;
339 case L
'\\': /* .\ component */
341 // ASSERT(EndPath - next == wcslen(next));
342 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
343 EndPath
-= (next
- p
);
347 if (p
[2] == L
'\\') /* ..\ component */
353 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
355 // ASSERT(EndPath - next == wcslen(next));
356 RtlMoveMemory(p
, next
, (EndPath
- next
+ 1) * sizeof(WCHAR
));
357 EndPath
-= (next
- p
);
360 else if (p
[2] == UNICODE_NULL
) /* final .. */
365 while (p
> Path
+ mark
&& p
[-1] != L
'\\') p
--;
366 if (p
> Path
+ mark
) p
--;
376 /* Skip to the next component */
377 while (*p
&& *p
!= L
'\\') p
++;
380 /* Remove last dot in previous dir name */
381 if (p
> Path
+ mark
&& p
[-1] == L
'.')
383 // ASSERT(EndPath - p == wcslen(p));
384 RtlMoveMemory(p
- 1, p
, (EndPath
- p
+ 1) * sizeof(WCHAR
));
394 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
395 if (SkipTrailingPathSeparators
)
397 while (p
> Path
+ mark
&& IS_PATH_SEPARATOR(p
[-1])) p
--;
400 /* Remove trailing spaces and dots (for all the path) */
401 while (p
> Path
&& (p
[-1] == L
' ' || p
[-1] == L
'.')) p
--;
404 * Zero-out the discarded buffer zone, starting just after
405 * the path string and going up to the end of the buffer.
406 * It also NULL-terminate the path string.
408 ASSERT(EndBuffer
>= p
);
409 RtlZeroMemory(p
, (EndBuffer
- p
+ 1) * sizeof(WCHAR
));
411 /* Return the real path length */
412 PathLength
= (p
- Path
);
413 // ASSERT(PathLength == wcslen(Path));
414 return (PathLength
* sizeof(WCHAR
));
417 /******************************************************************
418 * RtlpSkipUNCPrefix (from WINE)
420 * Helper for RtlGetFullPathName_U
422 * Skips the \\share\dir part of a file name and returns the new position
423 * (which can point on the last backslash of "dir\").
426 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer
)
428 PCWSTR UncPath
= FileNameBuffer
+ 2;
429 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer
);
431 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* share name */
432 while (IS_PATH_SEPARATOR(*UncPath
)) UncPath
++;
433 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* dir name */
434 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
436 return (UncPath
- FileNameBuffer
);
441 RtlpApplyLengthFunction(IN ULONG Flags
,
443 IN PVOID UnicodeStringOrUnicodeStringBuffer
,
444 IN PVOID LengthFunction
)
447 return STATUS_NOT_IMPLEMENTED
;
452 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags
,
454 OUT PULONG LengthOut
)
457 return STATUS_NOT_IMPLEMENTED
;
462 RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName
,
463 IN PUNICODE_STRING a2
,
464 IN PUNICODE_STRING a3
)
467 return STATUS_NOT_IMPLEMENTED
;
472 RtlGetFullPathName_Ustr(
473 _In_ PUNICODE_STRING FileName
,
475 _Out_z_bytecap_(Size
) PWSTR Buffer
,
476 _Out_opt_ PCWSTR
*ShortName
,
477 _Out_opt_ PBOOLEAN InvalidName
,
478 _Out_ RTL_PATH_TYPE
*PathType
)
481 PWCHAR FileNameBuffer
;
482 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
483 BOOLEAN SkipTrailingPathSeparators
;
490 PCUNICODE_STRING CurDirName
;
491 UNICODE_STRING EnvVarName
, EnvVarValue
;
492 WCHAR EnvVarNameBuffer
[4];
494 ULONG PrefixCut
= 0; // Where the path really starts (after the skipped prefix)
495 PWCHAR Prefix
= NULL
; // pointer to the string to be inserted as the new path prefix
496 ULONG PrefixLength
= 0;
501 /* For now, assume the name is valid */
502 DPRINT("Filename: %wZ\n", FileName
);
503 DPRINT("Size and buffer: %lx %p\n", Size
, Buffer
);
504 if (InvalidName
) *InvalidName
= FALSE
;
506 /* Handle initial path type and failure case */
507 *PathType
= RtlPathTypeUnknown
;
508 if ((FileName
->Length
== 0) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
510 /* Break filename into component parts */
511 FileNameBuffer
= FileName
->Buffer
;
512 FileNameLength
= FileName
->Length
;
513 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
515 /* Kill trailing spaces */
516 c
= FileNameBuffer
[FileNameChars
- 1];
517 while ((FileNameLength
!= 0) && (c
== L
' '))
519 /* Keep going, ignoring the spaces */
520 FileNameLength
-= sizeof(WCHAR
);
521 if (FileNameLength
!= 0) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
524 /* Check if anything is left */
525 if (FileNameLength
== 0) return 0;
528 * Check whether we'll need to skip trailing path separators in the
529 * computed full path name. If the original file name already contained
530 * trailing separators, then we keep them in the full path name. On the
531 * other hand, if the original name didn't contain any trailing separators
532 * then we'll skip it in the full path name.
534 SkipTrailingPathSeparators
= !IS_PATH_SEPARATOR(FileNameBuffer
[FileNameChars
- 1]);
536 /* Check if this is a DOS name */
537 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
538 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
541 /* Zero out the short name */
542 if (ShortName
) *ShortName
= NULL
;
544 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
545 DosLengthOffset
= HIWORD(DosLength
);
546 DosLength
= LOWORD(DosLength
);
548 /* Do we have a DOS length, and does the caller want validity? */
549 if (InvalidName
&& (DosLengthOffset
!= 0))
552 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
554 /* If the check failed, or the name is invalid, fail here */
555 if (!NT_SUCCESS(Status
)) return 0;
556 if (*InvalidName
) return 0;
559 /* Add the size of the device root and check if it fits in the size */
560 FullLength
= DosLength
+ DeviceRootString
.Length
;
561 if (FullLength
< Size
)
563 /* Add the device string */
564 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
566 /* Now add the DOS device name */
567 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
568 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
572 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
576 /* Otherwise, there's no space, so return the buffer size needed */
577 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
578 return FullLength
+ sizeof(UNICODE_NULL
);
581 /* Zero-out the destination buffer. FileName must be different from Buffer */
582 RtlZeroMemory(Buffer
, Size
);
584 /* Get the path type */
585 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
589 /**********************************************
590 ** CODE REWRITING IS HAPPENING THERE **
591 **********************************************/
592 Source
= FileNameBuffer
;
593 SourceLength
= FileNameLength
;
594 EnvVarValue
.Buffer
= NULL
;
596 /* Lock the PEB to get the current directory */
598 CurDirName
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
602 case RtlPathTypeUncAbsolute
: /* \\foo */
604 PrefixCut
= RtlpSkipUNCPrefix(FileNameBuffer
);
608 case RtlPathTypeLocalDevice
: /* \\.\foo */
614 case RtlPathTypeDriveAbsolute
: /* c:\foo */
616 ASSERT(FileNameBuffer
[1] == L
':');
617 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer
[2]));
619 // FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
620 Prefix
= FileNameBuffer
;
621 PrefixLength
= 3 * sizeof(WCHAR
);
623 SourceLength
-= 3 * sizeof(WCHAR
);
629 case RtlPathTypeDriveRelative
: /* c:foo */
631 WCHAR CurDrive
, NewDrive
;
634 SourceLength
-= 2 * sizeof(WCHAR
);
636 CurDrive
= RtlpUpcaseUnicodeChar(CurDirName
->Buffer
[0]);
637 NewDrive
= RtlpUpcaseUnicodeChar(FileNameBuffer
[0]);
639 if ((NewDrive
!= CurDrive
) || CurDirName
->Buffer
[1] != L
':')
641 EnvVarNameBuffer
[0] = L
'=';
642 EnvVarNameBuffer
[1] = NewDrive
;
643 EnvVarNameBuffer
[2] = L
':';
644 EnvVarNameBuffer
[3] = UNICODE_NULL
;
646 EnvVarName
.Length
= 3 * sizeof(WCHAR
);
647 EnvVarName
.MaximumLength
= EnvVarName
.Length
+ sizeof(WCHAR
);
648 EnvVarName
.Buffer
= EnvVarNameBuffer
;
650 // FIXME: Is it possible to use the user-given buffer ?
651 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
652 EnvVarValue
.Length
= 0;
653 EnvVarValue
.MaximumLength
= (USHORT
)Size
;
654 EnvVarValue
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
655 if (EnvVarValue
.Buffer
== NULL
)
662 Status
= RtlQueryEnvironmentVariable_U(NULL
, &EnvVarName
, &EnvVarValue
);
668 * FIXME: Win2k seems to check that the environment
669 * variable actually points to an existing directory.
670 * If not, root of the drive is used (this seems also
671 * to be the only place in RtlGetFullPathName that the
672 * existence of a part of a path is checked).
674 EnvVarValue
.Buffer
[EnvVarValue
.Length
/ sizeof(WCHAR
)] = L
'\\';
675 Prefix
= EnvVarValue
.Buffer
;
676 PrefixLength
= EnvVarValue
.Length
+ sizeof(WCHAR
); /* Append trailing '\\' */
679 case STATUS_BUFFER_TOO_SMALL
:
680 reqsize
= EnvVarValue
.Length
+ SourceLength
+ sizeof(UNICODE_NULL
);
684 DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName
, Status
);
686 EnvVarNameBuffer
[0] = NewDrive
;
687 EnvVarNameBuffer
[1] = L
':';
688 EnvVarNameBuffer
[2] = L
'\\';
689 EnvVarNameBuffer
[3] = UNICODE_NULL
;
690 Prefix
= EnvVarNameBuffer
;
691 PrefixLength
= 3 * sizeof(WCHAR
);
693 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
694 EnvVarValue
.Buffer
= NULL
;
701 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
704 case RtlPathTypeRelative
: /* foo */
706 Prefix
= CurDirName
->Buffer
;
707 PrefixLength
= CurDirName
->Length
;
708 if (CurDirName
->Buffer
[1] != L
':')
710 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
719 case RtlPathTypeRooted
: /* \xxx */
721 if (CurDirName
->Buffer
[1] == L
':')
723 // The path starts with "C:\"
724 ASSERT(CurDirName
->Buffer
[1] == L
':');
725 ASSERT(IS_PATH_SEPARATOR(CurDirName
->Buffer
[2]));
727 Prefix
= CurDirName
->Buffer
;
728 PrefixLength
= 3 * sizeof(WCHAR
); // Skip "C:\"
730 PrefixCut
= 3; // Source index location incremented of + 3
734 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
735 PrefixLength
= PrefixCut
* sizeof(WCHAR
);
736 Prefix
= CurDirName
->Buffer
;
741 case RtlPathTypeRootLocalDevice
: /* \\. */
743 Prefix
= DeviceRootString
.Buffer
;
744 PrefixLength
= DeviceRootString
.Length
;
746 SourceLength
-= 3 * sizeof(WCHAR
);
752 case RtlPathTypeUnknown
:
756 /* Do we have enough space for storing the full path? */
757 reqsize
= PrefixLength
;
758 if (reqsize
+ SourceLength
+ sizeof(WCHAR
) > Size
)
760 /* Not enough space, return needed size (including terminating '\0') */
761 reqsize
+= SourceLength
+ sizeof(WCHAR
);
766 * Build the full path
768 /* Copy the path's prefix */
769 if (PrefixLength
) RtlMoveMemory(Buffer
, Prefix
, PrefixLength
);
770 /* Copy the remaining part of the path */
771 RtlMoveMemory(Buffer
+ PrefixLength
/ sizeof(WCHAR
), Source
, SourceLength
+ sizeof(WCHAR
));
775 if (EnvVarValue
.Buffer
)
777 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
778 EnvVarValue
.Buffer
= NULL
;
782 * Finally, put the path in canonical form (remove redundant . and ..,
783 * (back)slashes...) and retrieve the length of the full path name
784 * (without its terminating null character) (in chars).
786 reqsize
= RtlpCollapsePath(Buffer
, /* Size, reqsize, */ PrefixCut
, SkipTrailingPathSeparators
);
788 /* Find the file part, which is present after the last path separator */
791 ptr
= wcsrchr(Buffer
, L
'\\');
792 if (ptr
) ++ptr
; // Skip it
795 * For UNC paths, the file part is after the \\share\dir part of the path.
797 PrefixCut
= (*PathType
== RtlPathTypeUncAbsolute
? PrefixCut
: 3);
799 if (ptr
&& *ptr
&& (ptr
>= Buffer
+ PrefixCut
))
805 /* Zero-out the short name */
811 /* Release PEB lock */
819 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
820 OUT PUNICODE_STRING NtPath
,
821 OUT PCWSTR
*PartName
,
822 OUT PRTL_RELATIVE_NAME_U RelativeName
)
827 /* Validate the input */
828 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
830 /* Validate the DOS length */
831 DosLength
= DosPath
->Length
;
832 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
834 /* Make space for the new path */
835 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
837 DosLength
+ sizeof(UNICODE_NULL
));
838 if (!NewBuffer
) return STATUS_NO_MEMORY
;
840 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
841 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
842 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
843 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
844 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
845 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
847 /* Did the caller send a relative name? */
850 /* Zero initialize it */
851 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
852 RelativeName
->ContainingDirectory
= NULL
;
853 RelativeName
->CurDirRef
= 0;
856 /* Did the caller request a partial name? */
859 /* Loop from the back until we find a path separator */
860 p
= &NewBuffer
[DosLength
/ sizeof(WCHAR
)];
861 while (--p
> NewBuffer
)
863 /* We found a path separator, move past it */
864 if (*p
== OBJ_NAME_PATH_SEPARATOR
)
871 /* Check whether a separator was found and if something remains */
872 if ((p
> NewBuffer
) && *p
)
874 /* What follows the path separator is the partial name */
879 /* The path ends with a path separator, no partial name */
884 /* Build the final NT path string */
885 NtPath
->Buffer
= NewBuffer
;
886 NtPath
->Length
= (USHORT
)DosLength
;
887 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
888 return STATUS_SUCCESS
;
893 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
894 IN PCUNICODE_STRING DosName
,
895 OUT PUNICODE_STRING NtName
,
896 OUT PCWSTR
*PartName
,
897 OUT PRTL_RELATIVE_NAME_U RelativeName
)
899 WCHAR BigBuffer
[MAX_PATH
+ 1];
900 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
901 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
902 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
904 RTL_PATH_TYPE InputPathType
, BufferPathType
;
907 PCURDIR CurrentDirectory
;
909 /* Assume MAX_PATH for now */
910 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
911 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
912 MaxLength
= sizeof(BigBuffer
);
914 /* Validate the input */
915 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
917 /* Capture input string */
918 CapturedDosName
= *DosName
;
920 /* Check for the presence or absence of the NT prefix "\\?\" form */
921 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
922 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
923 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
924 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
925 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
926 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
928 /* NT prefix not present */
930 /* Quick path won't be used */
933 /* Use the static buffer */
935 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
937 /* Allocate a buffer to hold the path */
938 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
939 DPRINT("MaxLength: %lx\n", MaxLength
);
940 if (!NewBuffer
) return STATUS_NO_MEMORY
;
944 /* NT prefix present */
946 /* Use the optimized path after acquiring the lock */
951 /* Lock the PEB and check if the quick path can be used */
955 /* Some simple fixups will get us the correct path */
956 DPRINT("Quick path\n");
957 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
962 /* Release the lock, we're done here */
967 /* Call the main function to get the full path name and length */
968 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
969 MAX_PATH
* sizeof(WCHAR
),
974 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
976 /* Invalid name, fail */
977 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
978 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
980 return STATUS_OBJECT_NAME_INVALID
;
983 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
984 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
985 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
988 /* Check where it really is */
989 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
990 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
991 switch (BufferPathType
)
993 /* It's actually a UNC path in \??\UNC\ */
994 case RtlPathTypeUncAbsolute
:
995 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
996 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
1000 case RtlPathTypeLocalDevice
:
1001 /* We made a good guess, go with it but skip the \??\ */
1005 case RtlPathTypeDriveAbsolute
:
1006 case RtlPathTypeDriveRelative
:
1007 case RtlPathTypeRooted
:
1008 case RtlPathTypeRelative
:
1009 /* Our guess was good, roll with it */
1012 /* Nothing else is expected */
1017 /* Now copy the prefix and the buffer */
1018 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
1019 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
1021 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
1023 /* Compute the length */
1024 Length
= PathLength
+ PrefixLength
- PrefixCut
* sizeof(WCHAR
);
1025 LengthChars
= Length
/ sizeof(WCHAR
);
1027 /* Setup the actual NT path string and terminate it */
1028 NtName
->Buffer
= NewBuffer
;
1029 NtName
->Length
= (USHORT
)Length
;
1030 NtName
->MaximumLength
= (USHORT
)MaxLength
;
1031 NewBuffer
[LengthChars
] = UNICODE_NULL
;
1032 DPRINT("New buffer: %S\n", NewBuffer
);
1033 DPRINT("NT Name: %wZ\n", NtName
);
1035 /* Check if a partial name was requested */
1036 if ((PartName
) && (*PartName
))
1038 /* Convert to Unicode */
1039 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
1040 if (NT_SUCCESS(Status
))
1042 /* Set the partial name */
1043 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
1048 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1049 RtlReleasePebLock();
1054 /* Check if a relative name was asked for */
1057 /* Setup the structure */
1058 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
1059 RelativeName
->ContainingDirectory
= NULL
;
1060 RelativeName
->CurDirRef
= NULL
;
1062 /* Check if the input path itself was relative */
1063 if (InputPathType
== RtlPathTypeRelative
)
1065 /* Get current directory */
1066 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
1067 if (CurrentDirectory
->Handle
)
1069 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
1070 if (!NT_SUCCESS(Status
))
1072 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1073 RtlReleasePebLock();
1077 /* If current directory is bigger than full path, there's no way */
1078 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
1080 RtlReleasePebLock();
1084 /* File is in current directory */
1085 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
1087 /* Make relative name string */
1088 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ PrefixLength
+ FullPath
.Length
- PrefixCut
* sizeof(WCHAR
));
1089 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
1090 /* If relative name starts with \, skip it */
1091 if (RelativeName
->RelativeName
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1093 RelativeName
->RelativeName
.Buffer
++;
1094 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
1096 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
1097 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
1101 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1105 /* Give back current directory data & reference counter */
1106 RelativeName
->CurDirRef
= RtlpCurDirRef
;
1107 if (RelativeName
->CurDirRef
)
1109 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
1112 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1119 RtlReleasePebLock();
1120 return STATUS_SUCCESS
;
1125 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
1127 OUT PUNICODE_STRING NtName
,
1128 OUT PCWSTR
*PartName
,
1129 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1132 UNICODE_STRING NameString
;
1134 /* Create the unicode name */
1135 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
1136 if (NT_SUCCESS(Status
))
1138 /* Call the unicode function */
1139 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
1152 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
1153 OUT PUNICODE_STRING NtName
,
1154 OUT PCWSTR
*PartName
,
1155 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1157 /* Call the internal function */
1158 ASSERT(RelativeName
);
1159 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
1168 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1169 IN BOOLEAN SucceedIfBusy
)
1172 RTL_RELATIVE_NAME_U RelativeName
;
1173 UNICODE_STRING NtPathName
;
1175 OBJECT_ATTRIBUTES ObjectAttributes
;
1177 FILE_BASIC_INFORMATION BasicInformation
;
1179 /* Get the NT Path */
1180 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1184 if (!Result
) return FALSE
;
1186 /* Save the buffer */
1187 Buffer
= NtPathName
.Buffer
;
1189 /* Check if we have a relative name */
1190 if (RelativeName
.RelativeName
.Length
)
1193 NtPathName
= RelativeName
.RelativeName
;
1197 /* Otherwise ignore it */
1198 RelativeName
.ContainingDirectory
= NULL
;
1201 /* Initialize the object attributes */
1202 InitializeObjectAttributes(&ObjectAttributes
,
1204 OBJ_CASE_INSENSITIVE
,
1205 RelativeName
.ContainingDirectory
,
1208 /* Query the attributes and free the buffer now */
1209 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1210 RtlReleaseRelativeName(&RelativeName
);
1211 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1213 /* Check if we failed */
1214 if (!NT_SUCCESS(Status
))
1216 /* Check if we failed because the file is in use */
1217 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1218 (Status
== STATUS_ACCESS_DENIED
))
1220 /* Check if the caller wants this to be considered OK */
1221 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1225 /* A failure because the file didn't exist */
1231 /* The file exists */
1235 /* Return the result */
1241 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1243 /* Call the updated API */
1244 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1249 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1250 IN BOOLEAN SucceedIfBusy
)
1252 UNICODE_STRING NameString
;
1254 /* Create the unicode name*/
1255 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1257 /* Call the unicode function */
1258 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
1265 /* PUBLIC FUNCTIONS ***********************************************************/
1272 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
1274 /* Check if a directory reference was grabbed */
1275 if (RelativeName
->CurDirRef
)
1277 /* Decrease reference count */
1278 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
1280 /* If no one uses it any longer, close handle & free */
1281 NtClose(RelativeName
->CurDirRef
->Handle
);
1282 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
1284 RelativeName
->CurDirRef
= NULL
;
1293 RtlGetLongestNtPathLength(VOID
)
1296 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1297 * a mapped network drive), which is accessed through the DOS Global?? path.
1298 * This is, and has always been equal to, 269 characters, except in Wine
1299 * which claims this is 277. Go figure.
1301 return MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
/ sizeof(WCHAR
) + sizeof(ANSI_NULL
);
1306 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1307 * (with a 'e' instead of a 'a' in "Seperators").
1311 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags
,
1312 IN PCUNICODE_STRING PathString
,
1317 /* Parameters validation */
1318 if (Length
== NULL
) return STATUS_INVALID_PARAMETER
;
1322 if (PathString
== NULL
) return STATUS_INVALID_PARAMETER
;
1324 /* No flags are supported yet */
1325 if (Flags
!= 0) return STATUS_INVALID_PARAMETER
;
1327 NumChars
= PathString
->Length
/ sizeof(WCHAR
);
1330 * Notice that we skip the last character, therefore:
1331 * - if we have: "some/path/f" we test for: "some/path/"
1332 * - if we have: "some/path/" we test for: "some/path"
1333 * - if we have: "s" we test for: ""
1334 * - if we have: "" then NumChars was already zero and we aren't there
1337 while (NumChars
> 0 && IS_PATH_SEPARATOR(PathString
->Buffer
[NumChars
- 1]))
1343 return STATUS_SUCCESS
;
1351 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
1353 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
1355 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1356 if (IS_PATH_SEPARATOR(Path
[0]))
1358 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
1359 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
1360 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
1361 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
1362 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
1366 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
1367 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
1368 return RtlPathTypeDriveRelative
; /* x: */
1377 RtlIsDosDeviceName_U(IN PCWSTR Path
)
1379 UNICODE_STRING PathString
;
1382 /* Build the string */
1383 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
1384 if (!NT_SUCCESS(Status
)) return 0;
1387 * Returns 0 if name is not valid DOS device name, or DWORD with
1388 * offset in bytes to DOS device name from beginning of buffer in high word
1389 * and size in bytes of DOS device name in low word
1391 return RtlIsDosDeviceName_Ustr(&PathString
);
1399 RtlGetCurrentDirectory_U(
1400 _In_ ULONG MaximumLength
,
1401 _Out_bytecap_(MaximumLength
) PWSTR Buffer
)
1403 ULONG Length
, Bytes
;
1406 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
1408 /* Lock the PEB to get the current directory */
1409 RtlAcquirePebLock();
1410 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1412 /* Get the buffer and character length */
1413 CurDirName
= CurDir
->DosPath
.Buffer
;
1414 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
1415 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
1418 * DosPath.Buffer should always have a trailing slash. There is an assert
1419 * below which checks for this.
1421 * This function either returns x:\ for a root (keeping the original buffer)
1422 * or it returns x:\path\foo for a directory (replacing the trailing slash
1425 Bytes
= Length
* sizeof(WCHAR
);
1426 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
1428 /* Check if caller does not have enough space */
1429 if (MaximumLength
<= Bytes
)
1431 /* Call has no space for it, fail, add the trailing slash */
1432 RtlReleasePebLock();
1433 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1438 /* Check if caller does not have enough space */
1439 if (MaximumLength
< Bytes
)
1441 /* Call has no space for it, fail */
1442 RtlReleasePebLock();
1447 /* Copy the buffer since we seem to have space */
1448 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
1450 /* The buffer should end with a path separator */
1451 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
1453 /* Again check for our two cases (drive root vs path) */
1454 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
1456 /* Replace the trailing slash with a null */
1457 Buffer
[Length
- 1] = UNICODE_NULL
;
1462 /* Append the null char since there's no trailing slash */
1463 Buffer
[Length
] = UNICODE_NULL
;
1466 /* Release PEB lock */
1467 RtlReleasePebLock();
1468 DPRINT("CurrentDirectory %S\n", Buffer
);
1469 return Length
* sizeof(WCHAR
);
1477 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1481 RTL_PATH_TYPE PathType
;
1482 IO_STATUS_BLOCK IoStatusBlock
;
1483 UNICODE_STRING FullPath
, NtName
;
1484 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1485 OBJECT_ATTRIBUTES ObjectAttributes
;
1486 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1487 ULONG SavedLength
, CharLength
, FullPathLength
;
1488 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1490 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1492 /* Initialize for failure case */
1493 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1495 /* Can't set current directory on DOS device */
1496 if (RtlIsDosDeviceName_Ustr(Path
))
1498 return STATUS_NOT_A_DIRECTORY
;
1501 /* Get current directory */
1502 RtlAcquirePebLock();
1503 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1505 /* Check if we have to drop current handle */
1506 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1508 OldHandle
= CurDir
->Handle
;
1509 CurDir
->Handle
= NULL
;
1512 /* Allocate a buffer for full path (using max possible length */
1513 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1514 if (!FullPath
.Buffer
)
1516 Status
= STATUS_NO_MEMORY
;
1521 FullPath
.Length
= 0;
1522 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1524 /* Get new directory full path */
1525 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1526 if (!FullPathLength
)
1528 Status
= STATUS_OBJECT_NAME_INVALID
;
1532 SavedLength
= FullPath
.MaximumLength
;
1533 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1535 if (FullPathLength
> FullPath
.MaximumLength
)
1537 Status
= STATUS_NAME_TOO_LONG
;
1541 /* Translate it to NT name */
1542 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1544 Status
= STATUS_OBJECT_NAME_INVALID
;
1548 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1549 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1552 /* If previous current directory was removable, then check it for dropping */
1553 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1555 /* Get back normal handle */
1556 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1557 CurDir
->Handle
= NULL
;
1559 /* Get device information */
1560 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1563 sizeof(FileFsDeviceInfo
),
1564 FileFsDeviceInformation
);
1565 /* Retry without taking care of removable device */
1566 if (!NT_SUCCESS(Status
))
1568 Status
= RtlSetCurrentDirectory_U(Path
);
1574 /* Open directory */
1575 Status
= NtOpenFile(&CurDirHandle
,
1576 SYNCHRONIZE
| FILE_TRAVERSE
,
1579 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1580 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1581 if (!NT_SUCCESS(Status
)) goto Leave
;
1583 /* Get device information */
1584 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1587 sizeof(FileFsDeviceInfo
),
1588 FileFsDeviceInformation
);
1589 if (!NT_SUCCESS(Status
)) goto Leave
;
1592 /* If device is removable, mark handle */
1593 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1595 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1598 FullPath
.Length
= (USHORT
)FullPathLength
;
1600 /* If full path isn't \ terminated, do it */
1601 if (FullPath
.Buffer
[CharLength
- 1] != OBJ_NAME_PATH_SEPARATOR
)
1603 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1605 Status
= STATUS_NAME_TOO_LONG
;
1609 FullPath
.Buffer
[CharLength
] = OBJ_NAME_PATH_SEPARATOR
;
1610 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1611 FullPath
.Length
+= sizeof(WCHAR
);
1614 /* If we have previous current directory with only us as reference, save it */
1615 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1617 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1621 /* Allocate new current directory struct saving previous one */
1622 OldCurDir
= RtlpCurDirRef
;
1623 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1626 RtlpCurDirRef
= OldCurDir
;
1628 Status
= STATUS_NO_MEMORY
;
1632 /* Set reference to 1 (us) */
1633 RtlpCurDirRef
->RefCount
= 1;
1637 CurDir
->Handle
= CurDirHandle
;
1638 RtlpCurDirRef
->Handle
= CurDirHandle
;
1639 CurDirHandle
= NULL
;
1641 /* Copy full path */
1642 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1643 CurDir
->DosPath
.Length
= FullPath
.Length
;
1645 Status
= STATUS_SUCCESS
;
1648 RtlReleasePebLock();
1650 if (FullPath
.Buffer
)
1652 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1657 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1660 if (CurDirHandle
) NtClose(CurDirHandle
);
1662 if (OldHandle
) NtClose(OldHandle
);
1664 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1666 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1668 NtClose(OldCurDir
->Handle
);
1669 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1676 /******************************************************************
1677 * RtlGetFullPathName_U (NTDLL.@)
1679 * Returns the number of bytes written to buffer (not including the
1680 * terminating NULL) if the function succeeds, or the required number of bytes
1681 * (including the terminating NULL) if the buffer is too small.
1683 * file_part will point to the filename part inside buffer (except if we use
1684 * DOS device name, in which case file_in_buf is NULL)
1694 RtlGetFullPathName_U(
1695 _In_ PCWSTR FileName
,
1697 _Out_z_bytecap_(Size
) PWSTR Buffer
,
1698 _Out_opt_ PWSTR
*ShortName
)
1701 UNICODE_STRING FileNameString
;
1702 RTL_PATH_TYPE PathType
;
1704 /* Build the string */
1705 Status
= RtlInitUnicodeStringEx(&FileNameString
, FileName
);
1706 if (!NT_SUCCESS(Status
)) return 0;
1708 /* Call the extended function */
1709 return RtlGetFullPathName_Ustr(&FileNameString
,
1722 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1723 OUT PUNICODE_STRING NtName
,
1724 OUT PCWSTR
*PartName
,
1725 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1727 /* Call the internal function */
1728 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1740 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1741 OUT PUNICODE_STRING NtName
,
1742 OUT PCWSTR
*PartName
,
1743 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1745 /* Call the internal function */
1746 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1758 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1759 OUT PUNICODE_STRING NtName
,
1760 OUT PCWSTR
*PartName
,
1761 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1763 /* Call the internal function */
1764 ASSERT(RelativeName
);
1765 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1777 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1778 OUT PUNICODE_STRING NtName
,
1779 OUT PCWSTR
*PartName
,
1780 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1782 /* Call the internal function */
1783 ASSERT(RelativeName
);
1784 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1794 NTSTATUS NTAPI
RtlNtPathNameToDosPathName(IN ULONG Flags
,
1795 IN OUT PRTL_UNICODE_STRING_BUFFER Path
,
1796 OUT PULONG PathType
,
1799 PCUNICODE_STRING UsePrefix
= NULL
, AlternatePrefix
= NULL
;
1805 return STATUS_INVALID_PARAMETER
;
1807 /* The initial check is done on Path->String */
1808 if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix
, &Path
->String
, TRUE
))
1810 UsePrefix
= &RtlpDosDevicesUncPrefix
;
1811 AlternatePrefix
= &RtlpDoubleSlashPrefix
;
1813 *PathType
= RTL_CONVERTED_UNC_PATH
;
1815 else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix
, &Path
->String
, FALSE
))
1817 UsePrefix
= &RtlpDosDevicesPrefix
;
1819 *PathType
= RTL_CONVERTED_NT_PATH
;
1826 USHORT Len
= Path
->String
.Length
- UsePrefix
->Length
;
1827 if (AlternatePrefix
)
1828 Len
+= AlternatePrefix
->Length
;
1830 Status
= RtlEnsureBufferSize(0, &Path
->ByteBuffer
, Len
);
1831 if (!NT_SUCCESS(Status
))
1834 if (Len
+ sizeof(UNICODE_NULL
) <= Path
->ByteBuffer
.Size
)
1836 /* Then, the contents of Path->ByteBuffer are always used... */
1837 if (AlternatePrefix
)
1839 memcpy(Path
->ByteBuffer
.Buffer
, AlternatePrefix
->Buffer
, AlternatePrefix
->Length
);
1840 memmove(Path
->ByteBuffer
.Buffer
+ AlternatePrefix
->Length
, Path
->ByteBuffer
.Buffer
+ UsePrefix
->Length
,
1841 Len
- AlternatePrefix
->Length
);
1845 memmove(Path
->ByteBuffer
.Buffer
, Path
->ByteBuffer
.Buffer
+ UsePrefix
->Length
, Len
);
1847 Path
->String
.Buffer
= (PWSTR
)Path
->ByteBuffer
.Buffer
;
1848 Path
->String
.Length
= Len
;
1849 Path
->String
.MaximumLength
= Path
->ByteBuffer
.Size
;
1850 Path
->String
.Buffer
[Len
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1852 return STATUS_SUCCESS
;
1857 switch (RtlDetermineDosPathNameType_Ustr(&Path
->String
))
1859 case RtlPathTypeUncAbsolute
:
1860 case RtlPathTypeDriveAbsolute
:
1861 case RtlPathTypeLocalDevice
:
1862 case RtlPathTypeRootLocalDevice
:
1863 *PathType
= RTL_UNCHANGED_DOS_PATH
;
1865 case RtlPathTypeUnknown
:
1866 case RtlPathTypeDriveRelative
:
1867 case RtlPathTypeRooted
:
1868 case RtlPathTypeRelative
:
1869 *PathType
= RTL_UNCHANGED_UNK_PATH
;
1874 return STATUS_SUCCESS
;
1882 RtlDosSearchPath_U(IN PCWSTR Path
,
1884 IN PCWSTR Extension
,
1887 OUT PWSTR
*PartName
)
1890 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1891 UNICODE_STRING TempString
;
1892 PWCHAR NewBuffer
, BufferStart
;
1895 /* Check if this is an absolute path */
1896 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1898 /* Check if the file exists */
1899 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1901 /* Get the full name, which does the DOS lookup */
1902 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1905 /* Doesn't exist, so fail */
1909 /* Scan the filename */
1913 /* Looking for an extension */
1916 /* No extension string needed -- it's part of the filename */
1921 /* Next character */
1925 /* Do we have an extension? */
1928 /* Nope, don't worry about one */
1929 ExtensionLength
= 0;
1933 /* Build a temporary string to get the extension length */
1934 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1935 if (!NT_SUCCESS(Status
)) return 0;
1936 ExtensionLength
= TempString
.Length
;
1939 /* Build a temporary string to get the path length */
1940 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1941 if (!NT_SUCCESS(Status
)) return 0;
1942 PathLength
= TempString
.Length
;
1944 /* Build a temporary string to get the filename length */
1945 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1946 if (!NT_SUCCESS(Status
)) return 0;
1947 FileNameLength
= TempString
.Length
;
1949 /* Allocate the buffer for the new string name */
1950 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1959 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1964 /* Final loop to build the path */
1967 /* Check if we have a valid character */
1968 BufferStart
= NewBuffer
;
1971 /* Loop as long as there's no semicolon */
1972 while (*Path
!= L
';')
1974 /* Copy the next character */
1975 *BufferStart
++ = *Path
++;
1979 /* We found a semi-colon, to stop path processing on this loop */
1980 if (*Path
== L
';') ++Path
;
1983 /* Add a terminating slash if needed */
1984 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != OBJ_NAME_PATH_SEPARATOR
))
1986 *BufferStart
++ = OBJ_NAME_PATH_SEPARATOR
;
1989 /* Bail out if we reached the end */
1990 if (!*Path
) Path
= NULL
;
1992 /* Copy the file name and check if an extension is needed */
1993 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1994 if (ExtensionLength
)
1996 /* Copy the extension too */
1997 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1999 ExtensionLength
+ sizeof(WCHAR
));
2003 /* Just NULL-terminate */
2004 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
2007 /* Now, does this file exist? */
2008 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
2010 /* Call the full-path API to get the length */
2011 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
2015 /* If we got here, path doesn't exist, so fail the call */
2020 /* Free the allocation and return the length */
2021 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
2030 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
2031 IN PUNICODE_STRING StaticString
,
2032 IN PUNICODE_STRING DynamicString
,
2033 IN PUNICODE_STRING
*StringUsed
,
2034 IN PSIZE_T FilePartSize
,
2035 OUT PBOOLEAN NameInvalid
,
2036 OUT RTL_PATH_TYPE
* PathType
,
2037 OUT PSIZE_T LengthNeeded
)
2040 PWCHAR StaticBuffer
;
2043 USHORT StaticLength
;
2044 UNICODE_STRING TempDynamicString
;
2046 /* Initialize all our locals */
2048 StaticBuffer
= NULL
;
2049 TempDynamicString
.Buffer
= NULL
;
2051 /* Initialize the input parameters */
2052 if (StringUsed
) *StringUsed
= NULL
;
2053 if (LengthNeeded
) *LengthNeeded
= 0;
2054 if (FilePartSize
) *FilePartSize
= 0;
2056 /* Check for invalid parameters */
2057 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
2059 return STATUS_INVALID_PARAMETER
;
2062 /* Check if we did not get an input string */
2066 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
2067 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
2068 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
2072 /* Use the one we received */
2073 StaticBuffer
= StaticString
->Buffer
;
2074 StaticLength
= StaticString
->MaximumLength
;
2077 /* Call the lower-level function */
2078 Length
= RtlGetFullPathName_Ustr(FileName
,
2084 DPRINT("Length: %u StaticBuffer: %S\n", Length
, StaticBuffer
);
2087 /* Fail if it failed */
2088 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
2091 Status
= STATUS_OBJECT_NAME_INVALID
;
2095 /* Check if it fits inside our static string */
2096 if ((StaticString
) && (Length
< StaticLength
))
2098 /* Set the final length */
2099 StaticString
->Length
= (USHORT
)Length
;
2101 /* Set the file part size */
2102 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2104 /* Return the static string if requested */
2105 if (StringUsed
) *StringUsed
= StaticString
;
2107 /* We are done with success */
2108 Status
= STATUS_SUCCESS
;
2112 /* Did we not have an input dynamic string ?*/
2115 /* Return the length we need */
2116 if (LengthNeeded
) *LengthNeeded
= Length
;
2118 /* And fail such that the caller can try again */
2119 Status
= STATUS_BUFFER_TOO_SMALL
;
2123 /* Check if it fits in our static buffer */
2124 if ((StaticBuffer
) && (Length
< StaticLength
))
2126 /* NULL-terminate it */
2127 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2129 /* Set the settings for the dynamic string the caller sent */
2130 DynamicString
->MaximumLength
= StaticLength
;
2131 DynamicString
->Length
= (USHORT
)Length
;
2132 DynamicString
->Buffer
= StaticBuffer
;
2134 /* Set the part size */
2135 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
2137 /* Return the dynamic string if requested */
2138 if (StringUsed
) *StringUsed
= DynamicString
;
2140 /* Do not free the static buffer on exit, and return success */
2141 StaticBuffer
= NULL
;
2142 Status
= STATUS_SUCCESS
;
2146 /* Now try again under the PEB lock */
2147 RtlAcquirePebLock();
2148 Length
= RtlGetFullPathName_Ustr(FileName
,
2157 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2158 __FUNCTION__
, __LINE__
);
2159 Status
= STATUS_OBJECT_NAME_INVALID
;
2163 /* Check if it fits inside our static string now */
2164 if ((StaticString
) && (Length
< StaticLength
))
2166 /* Set the final length */
2167 StaticString
->Length
= (USHORT
)Length
;
2169 /* Set the file part size */
2170 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2172 /* Return the static string if requested */
2173 if (StringUsed
) *StringUsed
= StaticString
;
2175 /* We are done with success */
2176 Status
= STATUS_SUCCESS
;
2180 /* Check if the path won't even fit in a real string */
2181 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
2183 /* Name is way too long, fail */
2184 Status
= STATUS_NAME_TOO_LONG
;
2188 /* Allocate the string to hold the path name now */
2189 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
2191 if (!TempDynamicString
.Buffer
)
2193 /* Out of memory, fail */
2194 Status
= STATUS_NO_MEMORY
;
2198 /* Add space for a NULL terminator, and now check the full path */
2199 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
2200 Length
= RtlGetFullPathName_Ustr(FileName
,
2202 TempDynamicString
.Buffer
,
2208 /* Some path error, so fail out */
2209 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2210 __FUNCTION__
, __LINE__
);
2211 Status
= STATUS_OBJECT_NAME_INVALID
;
2215 /* It should fit in the string we just allocated */
2216 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
2217 if (Length
> TempDynamicString
.MaximumLength
)
2219 /* This is really weird and would mean some kind of race */
2220 Status
= STATUS_INTERNAL_ERROR
;
2224 /* Return the file part size */
2225 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
2227 /* Terminate the whole string now */
2228 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2230 /* Finalize the string and return it to the user */
2231 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
2232 DynamicString
->Length
= (USHORT
)Length
;
2233 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
2234 if (StringUsed
) *StringUsed
= DynamicString
;
2236 /* Return success and make sure we don't free the buffer on exit */
2237 TempDynamicString
.Buffer
= NULL
;
2238 Status
= STATUS_SUCCESS
;
2241 /* Release the PEB lock */
2242 RtlReleasePebLock();
2245 /* Free any buffers we should be freeing */
2246 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2247 if ((StaticString
) && (StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2249 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2251 if (TempDynamicString
.Buffer
)
2253 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2256 /* Print out any unusual errors */
2257 if ((NT_ERROR(Status
)) &&
2258 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2260 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2261 __FUNCTION__
, FileName
, Status
);
2264 /* Return, we're all done */
2273 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2274 IN PUNICODE_STRING PathString
,
2275 IN PUNICODE_STRING FileNameString
,
2276 IN PUNICODE_STRING ExtensionString
,
2277 IN PUNICODE_STRING CallerBuffer
,
2278 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2279 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2280 OUT PSIZE_T FilePartSize OPTIONAL
,
2281 OUT PSIZE_T LengthNeeded OPTIONAL
)
2283 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2284 UNICODE_STRING StaticCandidateString
;
2286 RTL_PATH_TYPE PathType
;
2287 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2288 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2289 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2290 PUNICODE_STRING FullIsolatedPath
;
2291 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2292 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2294 /* Initialize the input string */
2295 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2296 StaticCandidateBuffer
,
2297 sizeof(StaticCandidateBuffer
));
2299 /* Initialize optional arguments */
2300 if (FullNameOut
) *FullNameOut
= NULL
;
2301 if (FilePartSize
) *FilePartSize
= 0;
2302 if (LengthNeeded
) *LengthNeeded
= 0;
2305 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2306 DynamicString
->Buffer
= NULL
;
2309 /* Check for invalid parameters */
2312 !(FileNameString
) ||
2313 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2316 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2317 Status
= STATUS_INVALID_PARAMETER
;
2321 /* First check what kind of path this is */
2322 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2324 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2326 (PathType
== RtlPathTypeRelative
) &&
2327 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2328 (FileNameString
->Buffer
[0] == L
'.') &&
2329 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2330 ((FileNameString
->Buffer
[1] == L
'.') &&
2331 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2332 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2334 /* Yes, and this path is like that, so make it seem unknown */
2335 PathType
= RtlPathTypeUnknown
;
2338 /* Now check relative vs non-relative paths */
2339 if (PathType
== RtlPathTypeRelative
)
2341 /* Does the caller want SxS? */
2344 /* Apply the SxS magic */
2345 FullIsolatedPath
= NULL
;
2346 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2355 if (NT_SUCCESS(Status
))
2357 /* We found the SxS path, return it */
2358 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2361 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2363 /* Critical SxS error, fail */
2364 DbgPrint("%s: Failing because call to "
2365 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2374 /* No SxS key found, or not requested, check if there's an extension */
2375 if (ExtensionString
)
2377 /* Save the extension length, and check if there's a file name */
2378 ExtensionLength
= ExtensionString
->Length
;
2379 if (FileNameString
->Length
)
2381 /* Start parsing the file name */
2382 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2383 while (End
> FileNameString
->Buffer
)
2385 /* If we find a path separator, there's no extension */
2386 if (IS_PATH_SEPARATOR(*--End
)) break;
2388 /* Otherwise, did we find an extension dot? */
2391 /* Ignore what the caller sent it, use the filename's */
2392 ExtensionString
= NULL
;
2393 ExtensionLength
= 0;
2400 /* Check if we got a path */
2401 if (PathString
->Length
)
2403 /* Start parsing the path name, looking for path separators */
2404 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2406 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2408 /* This is the size of the path -- handle a trailing slash */
2409 PathSize
= End
- p
- 1;
2410 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2412 /* Check if we found a bigger path than before */
2413 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2415 /* Keep going with the path after this path separator */
2419 /* This is the trailing path, run the same code as above */
2421 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2422 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2424 /* Finally, convert the largest path size into WCHAR */
2425 MaxPathSize
*= sizeof(WCHAR
);
2428 /* Use the extension, the file name, and the largest path as the size */
2429 WorstCaseLength
= ExtensionLength
+
2430 FileNameString
->Length
+
2431 (USHORT
)MaxPathSize
+
2432 sizeof(UNICODE_NULL
);
2433 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2435 /* It has to fit in a registry string, if not, fail here */
2436 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2437 "worst case file name length is %Iu bytes\n",
2440 Status
= STATUS_NAME_TOO_LONG
;
2444 /* Scan the path now, to see if we can find the file */
2445 p
= PathString
->Buffer
;
2446 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2449 /* Find out where this path ends */
2450 for (SegmentEnd
= p
;
2451 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2454 /* Compute the size of this path */
2455 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2457 /* Handle trailing slash if there isn't one */
2458 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2460 /* Add space for one */
2461 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2464 /* Now check if our initial static buffer is too small */
2465 if (StaticCandidateString
.MaximumLength
<
2466 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2468 /* At this point we should've been using our static buffer */
2469 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2470 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2472 /* Something is really messed up if this was the dynamic string */
2473 DbgPrint("%s: internal error #1; "
2474 "CandidateString.Buffer = %p; "
2475 "StaticCandidateBuffer = %p\n",
2477 StaticCandidateString
.Buffer
,
2478 StaticCandidateBuffer
);
2479 Status
= STATUS_INTERNAL_ERROR
;
2483 /* We checked before that the maximum possible size shoudl fit! */
2484 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2485 UNICODE_STRING_MAX_BYTES
);
2486 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2487 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2489 /* For some reason it's not fitting anymore. Something messed up */
2490 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2491 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2494 FileNameString
->Length
,
2496 Status
= STATUS_INTERNAL_ERROR
;
2500 /* Now allocate the dynamic string */
2501 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2503 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2505 if (!StaticCandidateString
.Buffer
)
2507 /* Out of memory, fail */
2508 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2510 StaticCandidateString
.MaximumLength
);
2511 Status
= STATUS_NO_MEMORY
;
2516 /* Copy the path in the string */
2517 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2519 /* Get to the end of the string, and add the trailing slash if missing */
2520 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2521 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2523 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2526 /* Copy the filename now */
2527 RtlCopyMemory(CandidateEnd
,
2528 FileNameString
->Buffer
,
2529 FileNameString
->Length
);
2530 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2532 /* Check if there was an extension */
2533 if (ExtensionString
)
2535 /* Copy the extension too */
2536 RtlCopyMemory(CandidateEnd
,
2537 ExtensionString
->Buffer
,
2538 ExtensionString
->Length
);
2539 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2542 /* We are done, terminate it */
2543 *CandidateEnd
= UNICODE_NULL
;
2545 /* Now set the final length of the string so it becomes valid */
2546 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2547 StaticCandidateString
.Buffer
) *
2550 /* Check if this file exists */
2551 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2552 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2554 /* Awesome, it does, now get the full path */
2555 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2558 (PUNICODE_STRING
*)FullNameOut
,
2563 if (!(NT_SUCCESS(Status
)) &&
2564 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2565 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2567 DbgPrint("%s: Failing because we thought we found %wZ on "
2568 "the search path, but RtlGetfullPathNameUStrEx() "
2571 &StaticCandidateString
,
2574 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2579 /* Otherwise, move to the next path */
2580 if (SegmentEnd
!= End
)
2582 /* Handle the case of the path separator trailing */
2592 /* Loop finished and we didn't break out -- fail */
2593 Status
= STATUS_NO_SUCH_FILE
;
2597 /* We have a full path, so check if it does exist */
2598 DPRINT("%wZ\n", FileNameString
);
2599 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2601 /* It doesn't exist, did we have an extension? */
2602 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2604 /* No extension, so just fail */
2605 Status
= STATUS_NO_SUCH_FILE
;
2609 /* There was an extension, check if the filename already had one */
2610 if (!(Flags
& 4) && (FileNameString
->Length
))
2612 /* Parse the filename */
2613 p
= FileNameString
->Buffer
;
2614 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2617 /* If there's a path separator, there's no extension */
2618 if (IS_PATH_SEPARATOR(*--End
)) break;
2620 /* Othwerwise, did we find an extension dot? */
2623 /* File already had an extension, so fail */
2624 Status
= STATUS_NO_SUCH_FILE
;
2630 /* So there is an extension, we'll try again by adding it */
2631 NamePlusExtLength
= FileNameString
->Length
+
2632 ExtensionString
->Length
+
2633 sizeof(UNICODE_NULL
);
2634 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2636 /* It won't fit in any kind of valid string, so fail */
2637 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2640 Status
= STATUS_NAME_TOO_LONG
;
2644 /* Fill it fit in our temporary string? */
2645 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2647 /* It won't fit anymore, allocate a dynamic string for it */
2648 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2649 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2651 if (!StaticCandidateString
.Buffer
)
2653 /* Ran out of memory, so fail */
2654 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2656 Status
= STATUS_NO_MEMORY
;
2661 /* Copy the filename */
2662 RtlCopyUnicodeString(&StaticCandidateString
, FileNameString
);
2664 /* Copy the extension */
2665 RtlAppendUnicodeStringToString(&StaticCandidateString
,
2668 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2670 /* And check if this file now exists */
2671 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2673 /* Still no joy, fail out */
2674 Status
= STATUS_NO_SUCH_FILE
;
2678 /* File was found, get the final full path */
2679 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2682 (PUNICODE_STRING
*)FullNameOut
,
2687 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2689 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2690 "failed with status %08lx\n",
2692 &StaticCandidateString
,
2695 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2699 /* File was found on the first try, get the final full path */
2700 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2703 (PUNICODE_STRING
*)FullNameOut
,
2708 if (!(NT_SUCCESS(Status
)) &&
2709 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2710 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2712 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2713 "failed with status %08lx\n",
2718 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2723 /* Anything that was not an error, turn into STATUS_SUCCESS */
2724 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2726 /* Check if we had a dynamic string */
2727 if ((StaticCandidateString
.Buffer
) &&
2728 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2731 RtlFreeUnicodeString(&StaticCandidateString
);
2734 /* Return the status */
2743 RtlDoesFileExists_U(IN PCWSTR FileName
)
2745 /* Call the new function */
2746 return RtlDoesFileExists_UEx(FileName
, TRUE
);