2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: Path and current directory functions
6 * PROGRAMMERS: Wine team
9 * Alex Ionescu (alex.ionescu@reactos.org)
10 * Pierre Schweitzer (pierre@reactos.org)
13 /* INCLUDES *****************************************************************/
20 /* DEFINITONS and MACROS ******************************************************/
22 #define MAX_PFX_SIZE 16
24 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
26 #define RTL_CURDIR_IS_REMOVABLE 0x1
27 #define RTL_CURDIR_DROP_OLD_HANDLE 0x2
28 #define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3
29 C_ASSERT(RTL_CURDIR_ALL_FLAGS
== OBJ_HANDLE_TAGBITS
);
32 /* GLOBALS ********************************************************************/
34 static const WCHAR DeviceRootW
[] = L
"\\\\.\\";
35 const UNICODE_STRING DeviceRootString
= RTL_CONSTANT_STRING(L
"\\\\.\\");
37 const UNICODE_STRING RtlpDosDevicesUncPrefix
= RTL_CONSTANT_STRING(L
"\\??\\UNC\\");
38 const UNICODE_STRING RtlpWin32NtRootSlash
= RTL_CONSTANT_STRING(L
"\\\\?\\");
39 const UNICODE_STRING RtlpDosSlashCONDevice
= RTL_CONSTANT_STRING(L
"\\\\.\\CON");
40 const UNICODE_STRING RtlpDosDevicesPrefix
= RTL_CONSTANT_STRING(L
"\\??\\");
42 const UNICODE_STRING RtlpDosLPTDevice
= RTL_CONSTANT_STRING(L
"LPT");
43 const UNICODE_STRING RtlpDosCOMDevice
= RTL_CONSTANT_STRING(L
"COM");
44 const UNICODE_STRING RtlpDosPRNDevice
= RTL_CONSTANT_STRING(L
"PRN");
45 const UNICODE_STRING RtlpDosAUXDevice
= RTL_CONSTANT_STRING(L
"AUX");
46 const UNICODE_STRING RtlpDosCONDevice
= RTL_CONSTANT_STRING(L
"CON");
47 const UNICODE_STRING RtlpDosNULDevice
= RTL_CONSTANT_STRING(L
"NUL");
49 PRTLP_CURDIR_REF RtlpCurDirRef
;
51 /* PRIVATE FUNCTIONS **********************************************************/
55 RtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString
)
60 Path
= PathString
->Buffer
;
61 Chars
= PathString
->Length
/ sizeof(WCHAR
);
63 /* Return if there are no characters */
64 if (!Chars
) return RtlPathTypeRelative
;
67 * The algorithm is similar to RtlDetermineDosPathNameType_U but here we
68 * actually check for the path length before touching the characters
70 if (IS_PATH_SEPARATOR(Path
[0]))
72 if ((Chars
< 2) || !(IS_PATH_SEPARATOR(Path
[1]))) return RtlPathTypeRooted
; /* \x */
73 if ((Chars
< 3) || ((Path
[2] != L
'.') && (Path
[2] != L
'?'))) return RtlPathTypeUncAbsolute
;/* \\x */
74 if ((Chars
>= 4) && (IS_PATH_SEPARATOR(Path
[3]))) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
75 if (Chars
!= 3) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
76 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
80 if ((Chars
< 2) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
81 if ((Chars
< 3) || !(IS_PATH_SEPARATOR(Path
[2]))) return RtlPathTypeDriveRelative
; /* x: */
82 return RtlPathTypeDriveAbsolute
; /* x:\ */
88 RtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString
)
90 UNICODE_STRING PathCopy
;
92 USHORT PathChars
, ColonCount
= 0;
93 USHORT ReturnOffset
= 0, ReturnLength
, OriginalLength
;
96 /* Validate the input */
97 if (!PathString
) return 0;
99 /* Check what type of path this is */
100 switch (RtlDetermineDosPathNameType_Ustr(PathString
))
102 /* Fail for UNC or unknown paths */
103 case RtlPathTypeUnknown
:
104 case RtlPathTypeUncAbsolute
:
107 /* Make special check for the CON device */
108 case RtlPathTypeLocalDevice
:
109 if (RtlEqualUnicodeString(PathString
, &RtlpDosSlashCONDevice
, TRUE
))
111 /* This should return 0x80006 */
112 return MAKELONG(RtlpDosCONDevice
.Length
, DeviceRootString
.Length
);
120 /* Make a copy of the string */
121 PathCopy
= *PathString
;
122 OriginalLength
= PathString
->Length
;
124 /* Return if there's no characters */
125 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
126 if (!PathChars
) return 0;
128 /* Check for drive path and truncate */
129 if (PathCopy
.Buffer
[PathChars
- 1] == L
':')
131 /* Fixup the lengths */
132 PathCopy
.Length
-= sizeof(WCHAR
);
133 if (!--PathChars
) return 0;
135 /* Remember this for later */
139 /* Check for extension or space, and truncate */
142 /* Stop if we hit something else than a space or period */
143 c
= PathCopy
.Buffer
[PathChars
- 1];
144 if ((c
!= L
'.') && (c
!= L
' ')) break;
146 /* Fixup the lengths */
147 PathCopy
.Length
-= sizeof(WCHAR
);
149 /* Remember this for later */
151 } while (--PathChars
);
153 /* Anything still left? */
156 /* Loop from the end */
157 for (End
= &PathCopy
.Buffer
[PathChars
- 1];
158 End
>= PathCopy
.Buffer
;
161 /* Check if the character is a path or drive separator */
163 if (IS_PATH_SEPARATOR(c
) || ((c
== L
':') && (End
== PathCopy
.Buffer
+ 1)))
165 /* Get the next lower case character */
167 c
= RtlDowncaseUnicodeChar(*End
);
169 /* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */
170 if ((End
< &PathCopy
.Buffer
[OriginalLength
/ sizeof(WCHAR
)]) &&
171 ((c
== L
'l') || (c
== L
'c') || (c
== L
'p') || (c
== L
'a') || (c
== L
'n')))
173 /* Calculate the offset */
174 ReturnOffset
= (USHORT
)((PCHAR
)End
- (PCHAR
)PathCopy
.Buffer
);
176 /* Build the final string */
177 PathCopy
.Length
= OriginalLength
- ReturnOffset
- (ColonCount
* sizeof(WCHAR
));
178 PathCopy
.Buffer
= End
;
180 /* Save new amount of chars in the path */
181 PathChars
= PathCopy
.Length
/ sizeof(WCHAR
);
192 /* Get the next lower case character and check if it's a DOS device */
193 c
= RtlDowncaseUnicodeChar(*PathCopy
.Buffer
);
194 if ((c
!= L
'l') && (c
!= L
'c') && (c
!= L
'p') && (c
!= L
'a') && (c
!= L
'n'))
196 /* Not LPT, COM, PRN, AUX, or NUL */
201 /* Now skip past any extra extension or drive letter characters */
202 Start
= PathCopy
.Buffer
;
203 End
= &Start
[PathChars
];
207 if ((c
== L
'.') || (c
== L
':')) break;
211 /* And then go backwards to get rid of spaces */
212 while ((Start
> PathCopy
.Buffer
) && (Start
[-1] == L
' ')) --Start
;
214 /* Finally see how many characters are left, and that's our size */
215 PathChars
= (USHORT
)(Start
- PathCopy
.Buffer
);
216 PathCopy
.Length
= PathChars
* sizeof(WCHAR
);
218 /* Check if this is a COM or LPT port, which has a digit after it */
219 if ((PathChars
== 4) &&
220 (iswdigit(PathCopy
.Buffer
[3]) && (PathCopy
.Buffer
[3] != L
'0')))
222 /* Don't compare the number part, just check for LPT or COM */
223 PathCopy
.Length
-= sizeof(WCHAR
);
224 if ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosLPTDevice
, TRUE
)) ||
225 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCOMDevice
, TRUE
)))
228 ReturnLength
= sizeof(L
"COM1") - sizeof(WCHAR
);
229 return MAKELONG(ReturnLength
, ReturnOffset
);
232 else if ((PathChars
== 3) &&
233 ((RtlEqualUnicodeString(&PathCopy
, &RtlpDosPRNDevice
, TRUE
)) ||
234 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosAUXDevice
, TRUE
)) ||
235 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosNULDevice
, TRUE
)) ||
236 (RtlEqualUnicodeString(&PathCopy
, &RtlpDosCONDevice
, TRUE
))))
238 /* Otherwise this was something like AUX, NUL, PRN, or CON */
239 ReturnLength
= sizeof(L
"AUX") - sizeof(WCHAR
);
240 return MAKELONG(ReturnLength
, ReturnOffset
);
243 /* Otherwise, this is not a valid DOS device */
249 RtlpCheckDeviceName(IN PUNICODE_STRING FileName
,
251 OUT PBOOLEAN NameInvalid
)
256 /* Allocate a large enough buffer */
257 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName
->Length
);
263 /* Copy the filename */
264 RtlCopyMemory(Buffer
, FileName
->Buffer
, FileName
->Length
);
266 /* And add a dot at the end */
267 Buffer
[Length
/ sizeof(WCHAR
)] = L
'.';
268 Buffer
[(Length
/ sizeof(WCHAR
)) + 1] = UNICODE_NULL
;
270 /* Check if the file exists or not */
271 *NameInvalid
= RtlDoesFileExists_U(Buffer
) ? FALSE
: TRUE
;
273 /* Get rid of the buffer now */
274 Status
= RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
278 /* Assume the name is ok, but fail the call */
279 *NameInvalid
= FALSE
;
280 Status
= STATUS_NO_MEMORY
;
283 /* Return the status */
289 /******************************************************************
290 * RtlpCollapsePath (from WINE)
292 * Helper for RtlGetFullPathName_U.
293 * 1) Convert slashes into backslashes
294 * 2) Get rid of duplicate backslashes
295 * 3) Get rid of . and .. components in the path.
298 RtlpCollapsePath(PWSTR path
, ULONG mark
, BOOLEAN SkipTrailingPathSeparators
)
302 /* convert every / into a \ */
303 for (p
= path
; *p
; p
++)
305 if (*p
== L
'/') *p
= L
'\\';
308 /* collapse duplicate backslashes */
309 next
= path
+ max( 1, mark
);
310 for (p
= next
; *p
; p
++)
312 if (*p
!= L
'\\' || next
[-1] != L
'\\') *next
++ = *p
;
314 *next
= UNICODE_NULL
;
323 case UNICODE_NULL
: /* final . */
324 if (p
> path
+ mark
) p
--;
327 case L
'\\': /* .\ component */
329 RtlMoveMemory( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
332 if (p
[2] == L
'\\') /* ..\ component */
338 while (p
> path
+ mark
&& p
[-1] != L
'\\') p
--;
340 RtlMoveMemory( p
, next
, (wcslen(next
) + 1) * sizeof(WCHAR
) );
343 else if (!p
[2]) /* final .. */
348 while (p
> path
+ mark
&& p
[-1] != L
'\\') p
--;
349 if (p
> path
+ mark
) p
--;
357 /* skip to the next component */
358 while (*p
&& *p
!= L
'\\') p
++;
361 /* remove last dot in previous dir name */
362 if (p
> path
+ mark
&& p
[-1] == L
'.')
363 RtlMoveMemory( p
-1, p
, (wcslen(p
) + 1) * sizeof(WCHAR
) );
369 /* Remove trailing backslashes if needed (after the UNC part if it exists) */
370 if (SkipTrailingPathSeparators
)
372 while (p
> path
+ mark
&& IS_PATH_SEPARATOR(p
[-1])) *p
-- = UNICODE_NULL
;
375 /* Remove trailing spaces and dots (for all the path) */
376 while (p
> path
&& (p
[-1] == L
' ' || p
[-1] == L
'.')) *p
-- = UNICODE_NULL
;
378 /* Null-terminate the string */
382 /******************************************************************
383 * RtlpSkipUNCPrefix (from WINE)
385 * Helper for RtlGetFullPathName_U
386 * Skip the \\share\dir part of a file name and return the new position
387 * (which can point on the last backslash of "dir\")
390 RtlpSkipUNCPrefix(PCWSTR FileNameBuffer
)
392 PCWSTR UncPath
= FileNameBuffer
+ 2;
393 DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer
);
395 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* share name */
396 while (IS_PATH_SEPARATOR(*UncPath
)) UncPath
++;
397 while (*UncPath
&& !IS_PATH_SEPARATOR(*UncPath
)) UncPath
++; /* dir name */
398 /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
400 return (UncPath
- FileNameBuffer
);
405 RtlpApplyLengthFunction(IN ULONG Flags
,
407 IN PVOID UnicodeStringOrUnicodeStringBuffer
,
408 IN PVOID LengthFunction
)
411 return STATUS_NOT_IMPLEMENTED
;
416 RtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags
,
418 OUT PULONG LengthOut
)
421 return STATUS_NOT_IMPLEMENTED
;
426 RtlComputePrivatizedDllName_U(IN PUNICODE_STRING DllName
,
427 IN PUNICODE_STRING a2
,
428 IN PUNICODE_STRING a3
)
431 return STATUS_NOT_IMPLEMENTED
;
436 RtlGetFullPathName_Ustr(
437 _In_ PUNICODE_STRING FileName
,
439 _Out_z_bytecap_(Size
) PWSTR Buffer
,
440 _Out_opt_ PCWSTR
*ShortName
,
441 _Out_opt_ PBOOLEAN InvalidName
,
442 _Out_ RTL_PATH_TYPE
*PathType
)
445 PWCHAR FileNameBuffer
;
446 ULONG FileNameLength
, FileNameChars
, DosLength
, DosLengthOffset
, FullLength
;
447 BOOLEAN SkipTrailingPathSeparators
;
454 PCUNICODE_STRING CurDirName
;
455 UNICODE_STRING EnvVarName
, EnvVarValue
;
456 WCHAR EnvVarNameBuffer
[4];
458 ULONG PrefixCut
= 0; // Where the path really starts (after the skipped prefix)
459 PWCHAR Prefix
= NULL
; // pointer to the string to be inserted as the new path prefix
460 ULONG PrefixLength
= 0;
465 /* For now, assume the name is valid */
466 DPRINT("Filename: %wZ\n", FileName
);
467 DPRINT("Size and buffer: %lx %p\n", Size
, Buffer
);
468 if (InvalidName
) *InvalidName
= FALSE
;
470 /* Handle initial path type and failure case */
471 *PathType
= RtlPathTypeUnknown
;
472 if ((FileName
->Length
== 0) || (FileName
->Buffer
[0] == UNICODE_NULL
)) return 0;
474 /* Break filename into component parts */
475 FileNameBuffer
= FileName
->Buffer
;
476 FileNameLength
= FileName
->Length
;
477 FileNameChars
= FileNameLength
/ sizeof(WCHAR
);
479 /* Kill trailing spaces */
480 c
= FileNameBuffer
[FileNameChars
- 1];
481 while ((FileNameLength
!= 0) && (c
== L
' '))
483 /* Keep going, ignoring the spaces */
484 FileNameLength
-= sizeof(WCHAR
);
485 if (FileNameLength
!= 0) c
= FileNameBuffer
[FileNameLength
/ sizeof(WCHAR
) - 1];
488 /* Check if anything is left */
489 if (FileNameLength
== 0) return 0;
492 * Check whether we'll need to skip trailing path separators in the
493 * computed full path name. If the original file name already contained
494 * trailing separators, then we keep them in the full path name. On the
495 * other hand, if the original name didn't contain any trailing separators
496 * then we'll skip it in the full path name.
498 SkipTrailingPathSeparators
= !IS_PATH_SEPARATOR(FileNameBuffer
[FileNameChars
- 1]);
500 /* Check if this is a DOS name */
501 DosLength
= RtlIsDosDeviceName_Ustr(FileName
);
502 DPRINT("DOS length for filename: %lx %wZ\n", DosLength
, FileName
);
505 /* Zero out the short name */
506 if (ShortName
) *ShortName
= NULL
;
508 /* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */
509 DosLengthOffset
= HIWORD(DosLength
);
510 DosLength
= LOWORD(DosLength
);
512 /* Do we have a DOS length, and does the caller want validity? */
513 if (InvalidName
&& (DosLengthOffset
!= 0))
516 Status
= RtlpCheckDeviceName(FileName
, DosLengthOffset
, InvalidName
);
518 /* If the check failed, or the name is invalid, fail here */
519 if (!NT_SUCCESS(Status
)) return 0;
520 if (*InvalidName
) return 0;
523 /* Add the size of the device root and check if it fits in the size */
524 FullLength
= DosLength
+ DeviceRootString
.Length
;
525 if (FullLength
< Size
)
527 /* Add the device string */
528 RtlMoveMemory(Buffer
, DeviceRootString
.Buffer
, DeviceRootString
.Length
);
530 /* Now add the DOS device name */
531 RtlMoveMemory((PCHAR
)Buffer
+ DeviceRootString
.Length
,
532 (PCHAR
)FileNameBuffer
+ DosLengthOffset
,
536 *(PWCHAR
)((ULONG_PTR
)Buffer
+ FullLength
) = UNICODE_NULL
;
540 /* Otherwise, there's no space, so return the buffer size needed */
541 if ((FullLength
+ sizeof(UNICODE_NULL
)) > UNICODE_STRING_MAX_BYTES
) return 0;
542 return FullLength
+ sizeof(UNICODE_NULL
);
545 /* Zero out the destination buffer. FileName must be different from Buffer */
546 RtlZeroMemory(Buffer
, Size
);
548 /* Get the path type */
549 *PathType
= RtlDetermineDosPathNameType_U(FileNameBuffer
);
553 /**********************************************
554 ** CODE REWRITTEN IS HAPPENING THERE **
555 **********************************************/
556 Source
= FileNameBuffer
;
557 SourceLength
= FileNameLength
;
558 EnvVarValue
.Buffer
= NULL
;
560 /* Lock the PEB to get the current directory */
562 CurDirName
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
566 case RtlPathTypeUncAbsolute
: /* \\foo */
567 PrefixCut
= RtlpSkipUNCPrefix(FileNameBuffer
);
570 case RtlPathTypeLocalDevice
: /* \\.\foo */
574 case RtlPathTypeDriveAbsolute
: /* c:\foo */
575 ASSERT(FileNameBuffer
[1] == L
':');
576 ASSERT(IS_PATH_SEPARATOR(FileNameBuffer
[2]));
578 Prefix
= FileNameBuffer
;
579 PrefixLength
= 3 * sizeof(WCHAR
);
581 SourceLength
-= 3 * sizeof(WCHAR
);
586 case RtlPathTypeDriveRelative
: /* c:foo */
588 SourceLength
-= 2 * sizeof(WCHAR
);
589 if (RtlUpcaseUnicodeChar(FileNameBuffer
[0]) != RtlUpcaseUnicodeChar(CurDirName
->Buffer
[0]) ||
590 CurDirName
->Buffer
[1] != L
':')
592 EnvVarNameBuffer
[0] = L
'=';
593 EnvVarNameBuffer
[1] = FileNameBuffer
[0];
594 EnvVarNameBuffer
[2] = L
':';
595 EnvVarNameBuffer
[3] = UNICODE_NULL
;
597 EnvVarName
.Length
= 3 * sizeof(WCHAR
);
598 EnvVarName
.MaximumLength
= EnvVarName
.Length
+ sizeof(WCHAR
);
599 EnvVarName
.Buffer
= EnvVarNameBuffer
;
601 // FIXME: Is it possible to use the user-given buffer ?
602 // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size);
603 EnvVarValue
.Length
= 0;
604 EnvVarValue
.MaximumLength
= (USHORT
)Size
;
605 EnvVarValue
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
606 if (EnvVarValue
.Buffer
== NULL
)
613 Status
= RtlQueryEnvironmentVariable_U(NULL
, &EnvVarName
, &EnvVarValue
);
619 * FIXME: Win2k seems to check that the environment
620 * variable actually points to an existing directory.
621 * If not, root of the drive is used (this seems also
622 * to be the only spot in RtlGetFullPathName that the
623 * existence of a part of a path is checked).
625 EnvVarValue
.Buffer
[EnvVarValue
.Length
/ sizeof(WCHAR
)] = L
'\\';
626 Prefix
= EnvVarValue
.Buffer
;
627 PrefixLength
= EnvVarValue
.Length
+ sizeof(WCHAR
); /* Append trailing '\\' */
630 case STATUS_BUFFER_TOO_SMALL
:
631 reqsize
= EnvVarValue
.Length
+ SourceLength
+ sizeof(UNICODE_NULL
);
635 DPRINT1("RtlQueryEnvironmentVariable_U returned 0x%08lx\n", Status
);
637 EnvVarNameBuffer
[0] = FileNameBuffer
[0];
638 EnvVarNameBuffer
[1] = L
':';
639 EnvVarNameBuffer
[2] = L
'\\';
640 EnvVarNameBuffer
[3] = UNICODE_NULL
;
641 Prefix
= EnvVarNameBuffer
;
642 PrefixLength
= 3 * sizeof(WCHAR
);
644 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
645 EnvVarValue
.Buffer
= NULL
;
652 DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n");
654 case RtlPathTypeRelative
: /* foo */
655 Prefix
= CurDirName
->Buffer
;
656 PrefixLength
= CurDirName
->Length
;
657 if (CurDirName
->Buffer
[1] != L
':')
659 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
667 case RtlPathTypeRooted
: /* \xxx */
668 if (CurDirName
->Buffer
[1] == L
':')
670 // The path starts with "C:\"
671 ASSERT(CurDirName
->Buffer
[1] == L
':');
672 ASSERT(IS_PATH_SEPARATOR(CurDirName
->Buffer
[2]));
674 Prefix
= CurDirName
->Buffer
;
675 PrefixLength
= 3 * sizeof(WCHAR
); // Skip "C:\"
677 PrefixCut
= 3; // Source index location incremented of + 3
681 PrefixCut
= RtlpSkipUNCPrefix(CurDirName
->Buffer
);
682 PrefixLength
= PrefixCut
* sizeof(WCHAR
);
683 Prefix
= CurDirName
->Buffer
;
687 case RtlPathTypeRootLocalDevice
: /* \\. */
688 Prefix
= DeviceRootString
.Buffer
;
689 PrefixLength
= DeviceRootString
.Length
;
691 SourceLength
-= 3 * sizeof(WCHAR
);
696 case RtlPathTypeUnknown
:
700 /* Do we have enough space for storing the full path? */
701 reqsize
= PrefixLength
;
702 if (reqsize
+ SourceLength
+ sizeof(WCHAR
) > Size
)
704 /* Not enough space, return needed size (including terminating '\0') */
705 reqsize
+= SourceLength
+ sizeof(WCHAR
);
710 * Build the full path
712 // if (ShortName) DPRINT1("buffer(1) = %S\n", Buffer);
713 /* Copy the path's prefix */
714 if (PrefixLength
) RtlMoveMemory(Buffer
, Prefix
, PrefixLength
);
715 // if (ShortName) DPRINT1("buffer(2) = %S\n", Buffer);
716 /* Copy the remaining part of the path */
717 RtlMoveMemory(Buffer
+ PrefixLength
/ sizeof(WCHAR
), Source
, SourceLength
+ sizeof(WCHAR
));
718 // if (ShortName) DPRINT1("buffer(3) = %S\n", Buffer);
722 if (EnvVarValue
.Buffer
)
724 RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue
.Buffer
);
725 EnvVarValue
.Buffer
= NULL
;
729 * Finally, put the path in canonical form,
730 * i.e. simplify redundant . and .., etc...
732 // if (*PathType == RtlPathTypeUncAbsolute) DPRINT1("RtlpCollapsePath('%S', %lu)\n", Buffer, PrefixCut);
733 RtlpCollapsePath(Buffer
, PrefixCut
, SkipTrailingPathSeparators
);
734 // if (ShortName) DPRINT1("buffer(4) = %S\n", Buffer);
736 /* Get the length of the full path name, without its terminating null character */
737 reqsize
= wcslen(Buffer
) * sizeof(WCHAR
);
739 /* Find the file part, which is present after the last path separator */
742 ptr
= wcsrchr(Buffer
, L
'\\');
743 if (ptr
) ++ptr
; // Skip it
746 * For UNC paths, the file part is after the \\share\dir part of the path.
748 PrefixCut
= (*PathType
== RtlPathTypeUncAbsolute
? PrefixCut
: 3);
750 if (ptr
&& *ptr
&& (ptr
>= Buffer
+ PrefixCut
))
756 /* Zero-out the short name */
762 /* Release PEB lock */
764 return (ULONG
)reqsize
;
769 RtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath
,
770 OUT PUNICODE_STRING NtPath
,
771 OUT PCWSTR
*PartName
,
772 OUT PRTL_RELATIVE_NAME_U RelativeName
)
777 /* Validate the input */
778 if (!DosPath
) return STATUS_OBJECT_NAME_INVALID
;
780 /* Validate the DOS length */
781 DosLength
= DosPath
->Length
;
782 if (DosLength
>= UNICODE_STRING_MAX_BYTES
) return STATUS_NAME_TOO_LONG
;
784 /* Make space for the new path */
785 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
787 DosLength
+ sizeof(UNICODE_NULL
));
788 if (!NewBuffer
) return STATUS_NO_MEMORY
;
790 /* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */
791 RtlCopyMemory(NewBuffer
, RtlpDosDevicesPrefix
.Buffer
, RtlpDosDevicesPrefix
.Length
);
792 RtlCopyMemory((PCHAR
)NewBuffer
+ RtlpDosDevicesPrefix
.Length
,
793 DosPath
->Buffer
+ RtlpDosDevicesPrefix
.Length
/ sizeof(WCHAR
),
794 DosPath
->Length
- RtlpDosDevicesPrefix
.Length
);
795 NewBuffer
[DosLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
797 /* Did the caller send a relative name? */
800 /* Zero initialize it */
801 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
802 RelativeName
->ContainingDirectory
= NULL
;
803 RelativeName
->CurDirRef
= 0;
806 /* Did the caller request a partial name? */
809 /* Loop from the back until we find a path separator */
810 p
= &NewBuffer
[DosLength
/ sizeof(WCHAR
)];
811 while (--p
> NewBuffer
)
813 /* We found a path separator, move past it */
814 if (*p
== OBJ_NAME_PATH_SEPARATOR
)
821 /* Check whether a separator was found and if something remains */
822 if ((p
> NewBuffer
) && *p
)
824 /* What follows the path separator is the partial name */
829 /* The path ends with a path separator, no partial name */
834 /* Build the final NT path string */
835 NtPath
->Buffer
= NewBuffer
;
836 NtPath
->Length
= (USHORT
)DosLength
;
837 NtPath
->MaximumLength
= (USHORT
)DosLength
+ sizeof(UNICODE_NULL
);
838 return STATUS_SUCCESS
;
843 RtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative
,
844 IN PCUNICODE_STRING DosName
,
845 OUT PUNICODE_STRING NtName
,
846 OUT PCWSTR
*PartName
,
847 OUT PRTL_RELATIVE_NAME_U RelativeName
)
849 WCHAR BigBuffer
[MAX_PATH
+ 1];
850 PWCHAR PrefixBuffer
, NewBuffer
, Buffer
;
851 ULONG MaxLength
, PathLength
, PrefixLength
, PrefixCut
, LengthChars
, Length
;
852 UNICODE_STRING CapturedDosName
, PartNameString
, FullPath
;
854 RTL_PATH_TYPE InputPathType
, BufferPathType
;
857 PCURDIR CurrentDirectory
;
859 /* Assume MAX_PATH for now */
860 DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n",
861 HaveRelative
, DosName
, NtName
, PartName
, RelativeName
);
862 MaxLength
= sizeof(BigBuffer
);
864 /* Validate the input */
865 if (!DosName
) return STATUS_OBJECT_NAME_INVALID
;
867 /* Capture input string */
868 CapturedDosName
= *DosName
;
870 /* Check for the presence or absence of the NT prefix "\\?\" form */
871 // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE))
872 if ((CapturedDosName
.Length
<= RtlpWin32NtRootSlash
.Length
) ||
873 (CapturedDosName
.Buffer
[0] != RtlpWin32NtRootSlash
.Buffer
[0]) ||
874 (CapturedDosName
.Buffer
[1] != RtlpWin32NtRootSlash
.Buffer
[1]) ||
875 (CapturedDosName
.Buffer
[2] != RtlpWin32NtRootSlash
.Buffer
[2]) ||
876 (CapturedDosName
.Buffer
[3] != RtlpWin32NtRootSlash
.Buffer
[3]))
878 /* NT prefix not present */
880 /* Quick path won't be used */
883 /* Use the static buffer */
885 MaxLength
+= RtlpDosDevicesUncPrefix
.Length
;
887 /* Allocate a buffer to hold the path */
888 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength
);
889 DPRINT("MaxLength: %lx\n", MaxLength
);
890 if (!NewBuffer
) return STATUS_NO_MEMORY
;
894 /* NT prefix present */
896 /* Use the optimized path after acquiring the lock */
901 /* Lock the PEB and check if the quick path can be used */
905 /* Some simple fixups will get us the correct path */
906 DPRINT("Quick path\n");
907 Status
= RtlpWin32NTNameToNtPathName_U(&CapturedDosName
,
912 /* Release the lock, we're done here */
917 /* Call the main function to get the full path name and length */
918 PathLength
= RtlGetFullPathName_Ustr(&CapturedDosName
,
919 MAX_PATH
* sizeof(WCHAR
),
924 if ((NameInvalid
) || !(PathLength
) || (PathLength
> (MAX_PATH
* sizeof(WCHAR
))))
926 /* Invalid name, fail */
927 DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid
, PathLength
);
928 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
930 return STATUS_OBJECT_NAME_INVALID
;
933 /* Start by assuming the path starts with \??\ (DOS Devices Path) */
934 PrefixLength
= RtlpDosDevicesPrefix
.Length
;
935 PrefixBuffer
= RtlpDosDevicesPrefix
.Buffer
;
938 /* Check where it really is */
939 BufferPathType
= RtlDetermineDosPathNameType_U(Buffer
);
940 DPRINT("Buffer: %S Type: %lx\n", Buffer
, BufferPathType
);
941 switch (BufferPathType
)
943 /* It's actually a UNC path in \??\UNC\ */
944 case RtlPathTypeUncAbsolute
:
945 PrefixLength
= RtlpDosDevicesUncPrefix
.Length
;
946 PrefixBuffer
= RtlpDosDevicesUncPrefix
.Buffer
;
950 case RtlPathTypeLocalDevice
:
951 /* We made a good guess, go with it but skip the \??\ */
955 case RtlPathTypeDriveAbsolute
:
956 case RtlPathTypeDriveRelative
:
957 case RtlPathTypeRooted
:
958 case RtlPathTypeRelative
:
959 /* Our guess was good, roll with it */
962 /* Nothing else is expected */
967 /* Now copy the prefix and the buffer */
968 RtlCopyMemory(NewBuffer
, PrefixBuffer
, PrefixLength
);
969 RtlCopyMemory((PCHAR
)NewBuffer
+ PrefixLength
,
971 PathLength
- (PrefixCut
* sizeof(WCHAR
)));
973 /* Compute the length */
974 Length
= PathLength
+ PrefixLength
- PrefixCut
* sizeof(WCHAR
);
975 LengthChars
= Length
/ sizeof(WCHAR
);
977 /* Setup the actual NT path string and terminate it */
978 NtName
->Buffer
= NewBuffer
;
979 NtName
->Length
= (USHORT
)Length
;
980 NtName
->MaximumLength
= (USHORT
)MaxLength
;
981 NewBuffer
[LengthChars
] = UNICODE_NULL
;
982 DPRINT("New buffer: %S\n", NewBuffer
);
983 DPRINT("NT Name: %wZ\n", NtName
);
985 /* Check if a partial name was requested */
986 if ((PartName
) && (*PartName
))
988 /* Convert to Unicode */
989 Status
= RtlInitUnicodeStringEx(&PartNameString
, *PartName
);
990 if (NT_SUCCESS(Status
))
992 /* Set the partial name */
993 *PartName
= &NewBuffer
[LengthChars
- (PartNameString
.Length
/ sizeof(WCHAR
))];
998 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1004 /* Check if a relative name was asked for */
1007 /* Setup the structure */
1008 RtlInitEmptyUnicodeString(&RelativeName
->RelativeName
, NULL
, 0);
1009 RelativeName
->ContainingDirectory
= NULL
;
1010 RelativeName
->CurDirRef
= NULL
;
1012 /* Check if the input path itself was relative */
1013 if (InputPathType
== RtlPathTypeRelative
)
1015 /* Get current directory */
1016 CurrentDirectory
= &(NtCurrentPeb()->ProcessParameters
->CurrentDirectory
);
1017 if (CurrentDirectory
->Handle
)
1019 Status
= RtlInitUnicodeStringEx(&FullPath
, Buffer
);
1020 if (!NT_SUCCESS(Status
))
1022 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1023 RtlReleasePebLock();
1027 /* If current directory is bigger than full path, there's no way */
1028 if (CurrentDirectory
->DosPath
.Length
> FullPath
.Length
)
1030 RtlReleasePebLock();
1034 /* File is in current directory */
1035 if (RtlEqualUnicodeString(&FullPath
, &CurrentDirectory
->DosPath
, TRUE
))
1037 /* Make relative name string */
1038 RelativeName
->RelativeName
.Buffer
= (PWSTR
)((ULONG_PTR
)NewBuffer
+ PrefixLength
+ FullPath
.Length
- PrefixCut
* sizeof(WCHAR
));
1039 RelativeName
->RelativeName
.Length
= (USHORT
)(PathLength
- FullPath
.Length
);
1040 /* If relative name starts with \, skip it */
1041 if (RelativeName
->RelativeName
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
1043 RelativeName
->RelativeName
.Buffer
++;
1044 RelativeName
->RelativeName
.Length
-= sizeof(WCHAR
);
1046 RelativeName
->RelativeName
.MaximumLength
= RelativeName
->RelativeName
.Length
;
1047 DPRINT("RelativeName: %wZ\n", &(RelativeName
->RelativeName
));
1051 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1055 /* Give back current directory data & reference counter */
1056 RelativeName
->CurDirRef
= RtlpCurDirRef
;
1057 if (RelativeName
->CurDirRef
)
1059 InterlockedIncrement(&RtlpCurDirRef
->RefCount
);
1062 RelativeName
->ContainingDirectory
= CurrentDirectory
->Handle
;
1069 RtlReleasePebLock();
1070 return STATUS_SUCCESS
;
1075 RtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative
,
1077 OUT PUNICODE_STRING NtName
,
1078 OUT PCWSTR
*PartName
,
1079 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1082 UNICODE_STRING NameString
;
1084 /* Create the unicode name */
1085 Status
= RtlInitUnicodeStringEx(&NameString
, DosName
);
1086 if (NT_SUCCESS(Status
))
1088 /* Call the unicode function */
1089 Status
= RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative
,
1102 RtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName
,
1103 OUT PUNICODE_STRING NtName
,
1104 OUT PCWSTR
*PartName
,
1105 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1107 /* Call the internal function */
1108 ASSERT(RelativeName
);
1109 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE
,
1118 RtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName
,
1119 IN BOOLEAN SucceedIfBusy
)
1122 RTL_RELATIVE_NAME_U RelativeName
;
1123 UNICODE_STRING NtPathName
;
1125 OBJECT_ATTRIBUTES ObjectAttributes
;
1127 FILE_BASIC_INFORMATION BasicInformation
;
1129 /* Get the NT Path */
1130 Result
= RtlDosPathNameToRelativeNtPathName_Ustr(FileName
,
1134 if (!Result
) return FALSE
;
1136 /* Save the buffer */
1137 Buffer
= NtPathName
.Buffer
;
1139 /* Check if we have a relative name */
1140 if (RelativeName
.RelativeName
.Length
)
1143 NtPathName
= RelativeName
.RelativeName
;
1147 /* Otherwise ignore it */
1148 RelativeName
.ContainingDirectory
= NULL
;
1151 /* Initialize the object attributes */
1152 InitializeObjectAttributes(&ObjectAttributes
,
1154 OBJ_CASE_INSENSITIVE
,
1155 RelativeName
.ContainingDirectory
,
1158 /* Query the attributes and free the buffer now */
1159 Status
= ZwQueryAttributesFile(&ObjectAttributes
, &BasicInformation
);
1160 RtlReleaseRelativeName(&RelativeName
);
1161 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1163 /* Check if we failed */
1164 if (!NT_SUCCESS(Status
))
1166 /* Check if we failed because the file is in use */
1167 if ((Status
== STATUS_SHARING_VIOLATION
) ||
1168 (Status
== STATUS_ACCESS_DENIED
))
1170 /* Check if the caller wants this to be considered OK */
1171 Result
= SucceedIfBusy
? TRUE
: FALSE
;
1175 /* A failure because the file didn't exist */
1181 /* The file exists */
1185 /* Return the result */
1191 RtlDoesFileExists_UStr(IN PUNICODE_STRING FileName
)
1193 /* Call the updated API */
1194 return RtlDoesFileExists_UstrEx(FileName
, TRUE
);
1199 RtlDoesFileExists_UEx(IN PCWSTR FileName
,
1200 IN BOOLEAN SucceedIfBusy
)
1202 UNICODE_STRING NameString
;
1204 /* Create the unicode name*/
1205 if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString
, FileName
)))
1207 /* Call the unicode function */
1208 return RtlDoesFileExists_UstrEx(&NameString
, SucceedIfBusy
);
1215 /* PUBLIC FUNCTIONS ***********************************************************/
1222 RtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName
)
1224 /* Check if a directory reference was grabbed */
1225 if (RelativeName
->CurDirRef
)
1227 /* Decrease reference count */
1228 if (!InterlockedDecrement(&RelativeName
->CurDirRef
->RefCount
))
1230 /* If no one uses it any longer, close handle & free */
1231 NtClose(RelativeName
->CurDirRef
->Handle
);
1232 RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName
->CurDirRef
);
1234 RelativeName
->CurDirRef
= NULL
;
1243 RtlGetLongestNtPathLength(VOID
)
1246 * The longest NT path is a DOS path that actually sits on a UNC path (ie:
1247 * a mapped network drive), which is accessed through the DOS Global?? path.
1248 * This is, and has always been equal to, 269 characters, except in Wine
1249 * which claims this is 277. Go figure.
1251 return MAX_PATH
+ RtlpDosDevicesUncPrefix
.Length
/ sizeof(WCHAR
) + sizeof(ANSI_NULL
);
1256 * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators
1257 * (with a 'e' instead of a 'a' in "Seperators").
1261 RtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags
,
1262 IN PCUNICODE_STRING PathString
,
1267 /* Parameters validation */
1268 if (Length
== NULL
) return STATUS_INVALID_PARAMETER
;
1272 if (PathString
== NULL
) return STATUS_INVALID_PARAMETER
;
1274 /* No flags are supported yet */
1275 if (Flags
!= 0) return STATUS_INVALID_PARAMETER
;
1277 NumChars
= PathString
->Length
/ sizeof(WCHAR
);
1280 * Notice that we skip the last character, therefore:
1281 * - if we have: "some/path/f" we test for: "some/path/"
1282 * - if we have: "some/path/" we test for: "some/path"
1283 * - if we have: "s" we test for: ""
1284 * - if we have: "" then NumChars was already zero and we aren't there
1287 while (NumChars
> 0 && IS_PATH_SEPARATOR(PathString
->Buffer
[NumChars
- 1]))
1293 return STATUS_SUCCESS
;
1301 RtlDetermineDosPathNameType_U(IN PCWSTR Path
)
1303 DPRINT("RtlDetermineDosPathNameType_U %S\n", Path
);
1305 /* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */
1306 if (IS_PATH_SEPARATOR(Path
[0]))
1308 if (!IS_PATH_SEPARATOR(Path
[1])) return RtlPathTypeRooted
; /* \x */
1309 if ((Path
[2] != L
'.') && (Path
[2] != L
'?')) return RtlPathTypeUncAbsolute
;/* \\x */
1310 if (IS_PATH_SEPARATOR(Path
[3])) return RtlPathTypeLocalDevice
; /* \\.\x or \\?\x */
1311 if (Path
[3]) return RtlPathTypeUncAbsolute
; /* \\.x or \\?x */
1312 return RtlPathTypeRootLocalDevice
; /* \\. or \\? */
1316 if (!(Path
[0]) || (Path
[1] != L
':')) return RtlPathTypeRelative
; /* x */
1317 if (IS_PATH_SEPARATOR(Path
[2])) return RtlPathTypeDriveAbsolute
; /* x:\ */
1318 return RtlPathTypeDriveRelative
; /* x: */
1327 RtlIsDosDeviceName_U(IN PCWSTR Path
)
1329 UNICODE_STRING PathString
;
1332 /* Build the string */
1333 Status
= RtlInitUnicodeStringEx(&PathString
, Path
);
1334 if (!NT_SUCCESS(Status
)) return 0;
1337 * Returns 0 if name is not valid DOS device name, or DWORD with
1338 * offset in bytes to DOS device name from beginning of buffer in high word
1339 * and size in bytes of DOS device name in low word
1341 return RtlIsDosDeviceName_Ustr(&PathString
);
1349 RtlGetCurrentDirectory_U(
1350 _In_ ULONG MaximumLength
,
1351 _Out_bytecap_(MaximumLength
) PWSTR Buffer
)
1353 ULONG Length
, Bytes
;
1356 DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength
, Buffer
);
1358 /* Lock the PEB to get the current directory */
1359 RtlAcquirePebLock();
1360 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1362 /* Get the buffer and character length */
1363 CurDirName
= CurDir
->DosPath
.Buffer
;
1364 Length
= CurDir
->DosPath
.Length
/ sizeof(WCHAR
);
1365 ASSERT((CurDirName
!= NULL
) && (Length
> 0));
1368 * DosPath.Buffer should always have a trailing slash. There is an assert
1369 * below which checks for this.
1371 * This function either returns x:\ for a root (keeping the original buffer)
1372 * or it returns x:\path\foo for a directory (replacing the trailing slash
1375 Bytes
= Length
* sizeof(WCHAR
);
1376 if ((Length
<= 1) || (CurDirName
[Length
- 2] == L
':'))
1378 /* Check if caller does not have enough space */
1379 if (MaximumLength
<= Bytes
)
1381 /* Call has no space for it, fail, add the trailing slash */
1382 RtlReleasePebLock();
1383 return Bytes
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1388 /* Check if caller does not have enough space */
1389 if (MaximumLength
< Bytes
)
1391 /* Call has no space for it, fail */
1392 RtlReleasePebLock();
1397 /* Copy the buffer since we seem to have space */
1398 RtlCopyMemory(Buffer
, CurDirName
, Bytes
);
1400 /* The buffer should end with a path separator */
1401 ASSERT(Buffer
[Length
- 1] == OBJ_NAME_PATH_SEPARATOR
);
1403 /* Again check for our two cases (drive root vs path) */
1404 if ((Length
<= 1) || (Buffer
[Length
- 2] != L
':'))
1406 /* Replace the trailing slash with a null */
1407 Buffer
[Length
- 1] = UNICODE_NULL
;
1412 /* Append the null char since there's no trailing slash */
1413 Buffer
[Length
] = UNICODE_NULL
;
1416 /* Release PEB lock */
1417 RtlReleasePebLock();
1418 DPRINT("CurrentDirectory %S\n", Buffer
);
1419 return Length
* sizeof(WCHAR
);
1427 RtlSetCurrentDirectory_U(IN PUNICODE_STRING Path
)
1431 RTL_PATH_TYPE PathType
;
1432 IO_STATUS_BLOCK IoStatusBlock
;
1433 UNICODE_STRING FullPath
, NtName
;
1434 PRTLP_CURDIR_REF OldCurDir
= NULL
;
1435 OBJECT_ATTRIBUTES ObjectAttributes
;
1436 FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo
;
1437 ULONG SavedLength
, CharLength
, FullPathLength
;
1438 HANDLE OldHandle
= NULL
, CurDirHandle
= NULL
, OldCurDirHandle
= NULL
;
1440 DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path
);
1442 /* Initialize for failure case */
1443 RtlInitEmptyUnicodeString(&NtName
, NULL
, 0);
1445 /* Can't set current directory on DOS device */
1446 if (RtlIsDosDeviceName_Ustr(Path
))
1448 return STATUS_NOT_A_DIRECTORY
;
1451 /* Get current directory */
1452 RtlAcquirePebLock();
1453 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
;
1455 /* Check if we have to drop current handle */
1456 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_DROP_OLD_HANDLE
)
1458 OldHandle
= CurDir
->Handle
;
1459 CurDir
->Handle
= NULL
;
1462 /* Allocate a buffer for full path (using max possible length */
1463 FullPath
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir
->DosPath
.MaximumLength
);
1464 if (!FullPath
.Buffer
)
1466 Status
= STATUS_NO_MEMORY
;
1471 FullPath
.Length
= 0;
1472 FullPath
.MaximumLength
= CurDir
->DosPath
.MaximumLength
;
1474 /* Get new directory full path */
1475 FullPathLength
= RtlGetFullPathName_Ustr(Path
, FullPath
.MaximumLength
, FullPath
.Buffer
, NULL
, NULL
, &PathType
);
1476 if (!FullPathLength
)
1478 Status
= STATUS_OBJECT_NAME_INVALID
;
1482 SavedLength
= FullPath
.MaximumLength
;
1483 CharLength
= FullPathLength
/ sizeof(WCHAR
);
1485 if (FullPathLength
> FullPath
.MaximumLength
)
1487 Status
= STATUS_NAME_TOO_LONG
;
1491 /* Translate it to NT name */
1492 if (!RtlDosPathNameToNtPathName_U(FullPath
.Buffer
, &NtName
, NULL
, NULL
))
1494 Status
= STATUS_OBJECT_NAME_INVALID
;
1498 InitializeObjectAttributes(&ObjectAttributes
, &NtName
,
1499 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
1502 /* If previous current directory was removable, then check it for dropping */
1503 if (((ULONG_PTR
)(CurDir
->Handle
) & RTL_CURDIR_ALL_FLAGS
) == RTL_CURDIR_ALL_FLAGS
)
1505 /* Get back normal handle */
1506 CurDirHandle
= (HANDLE
)((ULONG_PTR
)(CurDir
->Handle
) & ~RTL_CURDIR_ALL_FLAGS
);
1507 CurDir
->Handle
= NULL
;
1509 /* Get device information */
1510 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1513 sizeof(FileFsDeviceInfo
),
1514 FileFsDeviceInformation
);
1515 /* Retry without taking care of removable device */
1516 if (!NT_SUCCESS(Status
))
1518 Status
= RtlSetCurrentDirectory_U(Path
);
1524 /* Open directory */
1525 Status
= NtOpenFile(&CurDirHandle
,
1526 SYNCHRONIZE
| FILE_TRAVERSE
,
1529 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1530 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
1531 if (!NT_SUCCESS(Status
)) goto Leave
;
1533 /* Get device information */
1534 Status
= NtQueryVolumeInformationFile(CurDirHandle
,
1537 sizeof(FileFsDeviceInfo
),
1538 FileFsDeviceInformation
);
1539 if (!NT_SUCCESS(Status
)) goto Leave
;
1542 /* If device is removable, mark handle */
1543 if (FileFsDeviceInfo
.Characteristics
& FILE_REMOVABLE_MEDIA
)
1545 CurDirHandle
= (HANDLE
)((ULONG_PTR
)CurDirHandle
| RTL_CURDIR_IS_REMOVABLE
);
1548 FullPath
.Length
= (USHORT
)FullPathLength
;
1550 /* If full path isn't \ terminated, do it */
1551 if (FullPath
.Buffer
[CharLength
- 1] != OBJ_NAME_PATH_SEPARATOR
)
1553 if ((CharLength
+ 1) * sizeof(WCHAR
) > SavedLength
)
1555 Status
= STATUS_NAME_TOO_LONG
;
1559 FullPath
.Buffer
[CharLength
] = OBJ_NAME_PATH_SEPARATOR
;
1560 FullPath
.Buffer
[CharLength
+ 1] = UNICODE_NULL
;
1561 FullPath
.Length
+= sizeof(WCHAR
);
1564 /* If we have previous current directory with only us as reference, save it */
1565 if (RtlpCurDirRef
!= NULL
&& RtlpCurDirRef
->RefCount
== 1)
1567 OldCurDirHandle
= RtlpCurDirRef
->Handle
;
1571 /* Allocate new current directory struct saving previous one */
1572 OldCurDir
= RtlpCurDirRef
;
1573 RtlpCurDirRef
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF
));
1576 RtlpCurDirRef
= OldCurDir
;
1578 Status
= STATUS_NO_MEMORY
;
1582 /* Set reference to 1 (us) */
1583 RtlpCurDirRef
->RefCount
= 1;
1587 CurDir
->Handle
= CurDirHandle
;
1588 RtlpCurDirRef
->Handle
= CurDirHandle
;
1589 CurDirHandle
= NULL
;
1591 /* Copy full path */
1592 RtlCopyMemory(CurDir
->DosPath
.Buffer
, FullPath
.Buffer
, FullPath
.Length
+ sizeof(WCHAR
));
1593 CurDir
->DosPath
.Length
= FullPath
.Length
;
1595 Status
= STATUS_SUCCESS
;
1598 RtlReleasePebLock();
1600 if (FullPath
.Buffer
)
1602 RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath
.Buffer
);
1607 RtlFreeHeap(RtlGetProcessHeap(), 0, NtName
.Buffer
);
1610 if (CurDirHandle
) NtClose(CurDirHandle
);
1612 if (OldHandle
) NtClose(OldHandle
);
1614 if (OldCurDirHandle
) NtClose(OldCurDirHandle
);
1616 if (OldCurDir
&& InterlockedDecrement(&OldCurDir
->RefCount
) == 0)
1618 NtClose(OldCurDir
->Handle
);
1619 RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir
);
1626 /******************************************************************
1627 * RtlGetFullPathName_U (NTDLL.@)
1629 * Returns the number of bytes written to buffer (not including the
1630 * terminating NULL) if the function succeeds, or the required number of bytes
1631 * (including the terminating NULL) if the buffer is too small.
1633 * file_part will point to the filename part inside buffer (except if we use
1634 * DOS device name, in which case file_in_buf is NULL)
1644 RtlGetFullPathName_U(
1645 _In_ PCWSTR FileName
,
1647 _Out_z_bytecap_(Size
) PWSTR Buffer
,
1648 _Out_opt_ PWSTR
*ShortName
)
1651 UNICODE_STRING FileNameString
;
1652 RTL_PATH_TYPE PathType
;
1654 /* Build the string */
1655 Status
= RtlInitUnicodeStringEx(&FileNameString
, FileName
);
1656 if (!NT_SUCCESS(Status
)) return 0;
1658 /* Call the extended function */
1659 return RtlGetFullPathName_Ustr(&FileNameString
,
1672 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName
,
1673 OUT PUNICODE_STRING NtName
,
1674 OUT PCWSTR
*PartName
,
1675 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1677 /* Call the internal function */
1678 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1690 RtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName
,
1691 OUT PUNICODE_STRING NtName
,
1692 OUT PCWSTR
*PartName
,
1693 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1695 /* Call the internal function */
1696 return RtlpDosPathNameToRelativeNtPathName_U(FALSE
,
1708 RtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName
,
1709 OUT PUNICODE_STRING NtName
,
1710 OUT PCWSTR
*PartName
,
1711 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1713 /* Call the internal function */
1714 ASSERT(RelativeName
);
1715 return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1727 RtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName
,
1728 OUT PUNICODE_STRING NtName
,
1729 OUT PCWSTR
*PartName
,
1730 OUT PRTL_RELATIVE_NAME_U RelativeName
)
1732 /* Call the internal function */
1733 ASSERT(RelativeName
);
1734 return RtlpDosPathNameToRelativeNtPathName_U(TRUE
,
1745 RtlNtPathNameToDosPathName(ULONG Unknown1
, ULONG Unknown2
, ULONG Unknown3
, ULONG Unknown4
)
1747 DPRINT1("RtlNtPathNameToDosPathName: stub\n");
1748 return STATUS_NOT_IMPLEMENTED
;
1756 RtlDosSearchPath_U(IN PCWSTR Path
,
1758 IN PCWSTR Extension
,
1761 OUT PWSTR
*PartName
)
1764 ULONG ExtensionLength
, Length
, FileNameLength
, PathLength
;
1765 UNICODE_STRING TempString
;
1766 PWCHAR NewBuffer
, BufferStart
;
1769 /* Check if this is an absolute path */
1770 if (RtlDetermineDosPathNameType_U(FileName
) != RtlPathTypeRelative
)
1772 /* Check if the file exists */
1773 if (RtlDoesFileExists_UEx(FileName
, TRUE
))
1775 /* Get the full name, which does the DOS lookup */
1776 return RtlGetFullPathName_U(FileName
, Size
, Buffer
, PartName
);
1779 /* Doesn't exist, so fail */
1783 /* Scan the filename */
1787 /* Looking for an extension */
1790 /* No extension string needed -- it's part of the filename */
1795 /* Next character */
1799 /* Do we have an extension? */
1802 /* Nope, don't worry about one */
1803 ExtensionLength
= 0;
1807 /* Build a temporary string to get the extension length */
1808 Status
= RtlInitUnicodeStringEx(&TempString
, Extension
);
1809 if (!NT_SUCCESS(Status
)) return 0;
1810 ExtensionLength
= TempString
.Length
;
1813 /* Build a temporary string to get the path length */
1814 Status
= RtlInitUnicodeStringEx(&TempString
, Path
);
1815 if (!NT_SUCCESS(Status
)) return 0;
1816 PathLength
= TempString
.Length
;
1818 /* Build a temporary string to get the filename length */
1819 Status
= RtlInitUnicodeStringEx(&TempString
, FileName
);
1820 if (!NT_SUCCESS(Status
)) return 0;
1821 FileNameLength
= TempString
.Length
;
1823 /* Allocate the buffer for the new string name */
1824 NewBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1833 DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n",
1838 /* Final loop to build the path */
1841 /* Check if we have a valid character */
1842 BufferStart
= NewBuffer
;
1845 /* Loop as long as there's no semicolon */
1846 while (*Path
!= L
';')
1848 /* Copy the next character */
1849 *BufferStart
++ = *Path
++;
1853 /* We found a semi-colon, to stop path processing on this loop */
1854 if (*Path
== L
';') ++Path
;
1857 /* Add a terminating slash if needed */
1858 if ((BufferStart
!= NewBuffer
) && (BufferStart
[-1] != OBJ_NAME_PATH_SEPARATOR
))
1860 *BufferStart
++ = OBJ_NAME_PATH_SEPARATOR
;
1863 /* Bail out if we reached the end */
1864 if (!*Path
) Path
= NULL
;
1866 /* Copy the file name and check if an extension is needed */
1867 RtlCopyMemory(BufferStart
, FileName
, FileNameLength
);
1868 if (ExtensionLength
)
1870 /* Copy the extension too */
1871 RtlCopyMemory((PCHAR
)BufferStart
+ FileNameLength
,
1873 ExtensionLength
+ sizeof(WCHAR
));
1877 /* Just NULL-terminate */
1878 *(PWCHAR
)((PCHAR
)BufferStart
+ FileNameLength
) = UNICODE_NULL
;
1881 /* Now, does this file exist? */
1882 if (RtlDoesFileExists_UEx(NewBuffer
, FALSE
))
1884 /* Call the full-path API to get the length */
1885 Length
= RtlGetFullPathName_U(NewBuffer
, Size
, Buffer
, PartName
);
1889 /* If we got here, path doesn't exist, so fail the call */
1894 /* Free the allocation and return the length */
1895 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer
);
1904 RtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName
,
1905 IN PUNICODE_STRING StaticString
,
1906 IN PUNICODE_STRING DynamicString
,
1907 IN PUNICODE_STRING
*StringUsed
,
1908 IN PSIZE_T FilePartSize
,
1909 OUT PBOOLEAN NameInvalid
,
1910 OUT RTL_PATH_TYPE
* PathType
,
1911 OUT PSIZE_T LengthNeeded
)
1914 PWCHAR StaticBuffer
;
1917 USHORT StaticLength
;
1918 UNICODE_STRING TempDynamicString
;
1920 /* Initialize all our locals */
1922 StaticBuffer
= NULL
;
1923 TempDynamicString
.Buffer
= NULL
;
1925 /* Initialize the input parameters */
1926 if (StringUsed
) *StringUsed
= NULL
;
1927 if (LengthNeeded
) *LengthNeeded
= 0;
1928 if (FilePartSize
) *FilePartSize
= 0;
1930 /* Check for invalid parameters */
1931 if ((DynamicString
) && !(StringUsed
) && (StaticString
))
1933 return STATUS_INVALID_PARAMETER
;
1936 /* Check if we did not get an input string */
1940 StaticLength
= MAX_PATH
* sizeof(WCHAR
);
1941 StaticBuffer
= RtlpAllocateStringMemory(MAX_PATH
* sizeof(WCHAR
), TAG_USTR
);
1942 if (!StaticBuffer
) return STATUS_NO_MEMORY
;
1946 /* Use the one we received */
1947 StaticBuffer
= StaticString
->Buffer
;
1948 StaticLength
= StaticString
->MaximumLength
;
1951 /* Call the lower-level function */
1952 Length
= RtlGetFullPathName_Ustr(FileName
,
1958 DPRINT("Length: %u StaticBuffer: %S\n", Length
, StaticBuffer
);
1961 /* Fail if it failed */
1962 DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n",
1965 Status
= STATUS_OBJECT_NAME_INVALID
;
1969 /* Check if it fits inside our static string */
1970 if ((StaticString
) && (Length
< StaticLength
))
1972 /* Set the final length */
1973 StaticString
->Length
= (USHORT
)Length
;
1975 /* Set the file part size */
1976 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
1978 /* Return the static string if requested */
1979 if (StringUsed
) *StringUsed
= StaticString
;
1981 /* We are done with success */
1982 Status
= STATUS_SUCCESS
;
1986 /* Did we not have an input dynamic string ?*/
1989 /* Return the length we need */
1990 if (LengthNeeded
) *LengthNeeded
= Length
;
1992 /* And fail such that the caller can try again */
1993 Status
= STATUS_BUFFER_TOO_SMALL
;
1997 /* Check if it fits in our static buffer */
1998 if ((StaticBuffer
) && (Length
< StaticLength
))
2000 /* NULL-terminate it */
2001 StaticBuffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2003 /* Set the settings for the dynamic string the caller sent */
2004 DynamicString
->MaximumLength
= StaticLength
;
2005 DynamicString
->Length
= (USHORT
)Length
;
2006 DynamicString
->Buffer
= StaticBuffer
;
2008 /* Set the part size */
2009 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticBuffer
) : 0;
2011 /* Return the dynamic string if requested */
2012 if (StringUsed
) *StringUsed
= DynamicString
;
2014 /* Do not free the static buffer on exit, and return success */
2015 StaticBuffer
= NULL
;
2016 Status
= STATUS_SUCCESS
;
2020 /* Now try again under the PEB lock */
2021 RtlAcquirePebLock();
2022 Length
= RtlGetFullPathName_Ustr(FileName
,
2031 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2032 __FUNCTION__
, __LINE__
);
2033 Status
= STATUS_OBJECT_NAME_INVALID
;
2037 /* Check if it fits inside our static string now */
2038 if ((StaticString
) && (Length
< StaticLength
))
2040 /* Set the final length */
2041 StaticString
->Length
= (USHORT
)Length
;
2043 /* Set the file part size */
2044 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- StaticString
->Buffer
) : 0;
2046 /* Return the static string if requested */
2047 if (StringUsed
) *StringUsed
= StaticString
;
2049 /* We are done with success */
2050 Status
= STATUS_SUCCESS
;
2054 /* Check if the path won't even fit in a real string */
2055 if ((Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
2057 /* Name is way too long, fail */
2058 Status
= STATUS_NAME_TOO_LONG
;
2062 /* Allocate the string to hold the path name now */
2063 TempDynamicString
.Buffer
= RtlpAllocateStringMemory(Length
+ sizeof(WCHAR
),
2065 if (!TempDynamicString
.Buffer
)
2067 /* Out of memory, fail */
2068 Status
= STATUS_NO_MEMORY
;
2072 /* Add space for a NULL terminator, and now check the full path */
2073 TempDynamicString
.MaximumLength
= (USHORT
)Length
+ sizeof(UNICODE_NULL
);
2074 Length
= RtlGetFullPathName_Ustr(FileName
,
2076 TempDynamicString
.Buffer
,
2082 /* Some path error, so fail out */
2083 DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n",
2084 __FUNCTION__
, __LINE__
);
2085 Status
= STATUS_OBJECT_NAME_INVALID
;
2089 /* It should fit in the string we just allocated */
2090 ASSERT(Length
< (TempDynamicString
.MaximumLength
- sizeof(WCHAR
)));
2091 if (Length
> TempDynamicString
.MaximumLength
)
2093 /* This is really weird and would mean some kind of race */
2094 Status
= STATUS_INTERNAL_ERROR
;
2098 /* Return the file part size */
2099 if (FilePartSize
) *FilePartSize
= ShortName
? (ShortName
- TempDynamicString
.Buffer
) : 0;
2101 /* Terminate the whole string now */
2102 TempDynamicString
.Buffer
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
2104 /* Finalize the string and return it to the user */
2105 DynamicString
->Buffer
= TempDynamicString
.Buffer
;
2106 DynamicString
->Length
= (USHORT
)Length
;
2107 DynamicString
->MaximumLength
= TempDynamicString
.MaximumLength
;
2108 if (StringUsed
) *StringUsed
= DynamicString
;
2110 /* Return success and make sure we don't free the buffer on exit */
2111 TempDynamicString
.Buffer
= NULL
;
2112 Status
= STATUS_SUCCESS
;
2115 /* Release the PEB lock */
2116 RtlReleasePebLock();
2119 /* Free any buffers we should be freeing */
2120 DPRINT("Status: %lx %S %S\n", Status
, StaticBuffer
, TempDynamicString
.Buffer
);
2121 if ((StaticString
) && (StaticBuffer
) && (StaticBuffer
!= StaticString
->Buffer
))
2123 RtlpFreeMemory(StaticBuffer
, TAG_USTR
);
2125 if (TempDynamicString
.Buffer
)
2127 RtlpFreeMemory(TempDynamicString
.Buffer
, TAG_USTR
);
2130 /* Print out any unusual errors */
2131 if ((NT_ERROR(Status
)) &&
2132 (Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2134 DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n",
2135 __FUNCTION__
, FileName
, Status
);
2138 /* Return, we're all done */
2147 RtlDosSearchPath_Ustr(IN ULONG Flags
,
2148 IN PUNICODE_STRING PathString
,
2149 IN PUNICODE_STRING FileNameString
,
2150 IN PUNICODE_STRING ExtensionString
,
2151 IN PUNICODE_STRING CallerBuffer
,
2152 IN OUT PUNICODE_STRING DynamicString OPTIONAL
,
2153 OUT PUNICODE_STRING
* FullNameOut OPTIONAL
,
2154 OUT PSIZE_T FilePartSize OPTIONAL
,
2155 OUT PSIZE_T LengthNeeded OPTIONAL
)
2157 WCHAR StaticCandidateBuffer
[MAX_PATH
];
2158 UNICODE_STRING StaticCandidateString
;
2160 RTL_PATH_TYPE PathType
;
2161 PWCHAR p
, End
, CandidateEnd
, SegmentEnd
;
2162 SIZE_T SegmentSize
, ByteCount
, PathSize
, MaxPathSize
= 0;
2163 USHORT NamePlusExtLength
, WorstCaseLength
, ExtensionLength
= 0;
2164 PUNICODE_STRING FullIsolatedPath
;
2165 DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n",
2166 Flags
, PathString
, FileNameString
, ExtensionString
, CallerBuffer
, DynamicString
);
2168 /* Initialize the input string */
2169 RtlInitEmptyUnicodeString(&StaticCandidateString
,
2170 StaticCandidateBuffer
,
2171 sizeof(StaticCandidateBuffer
));
2173 /* Initialize optional arguments */
2174 if (FullNameOut
) *FullNameOut
= NULL
;
2175 if (FilePartSize
) *FilePartSize
= 0;
2176 if (LengthNeeded
) *LengthNeeded
= 0;
2179 DynamicString
->Length
= DynamicString
->MaximumLength
= 0;
2180 DynamicString
->Buffer
= NULL
;
2183 /* Check for invalid parameters */
2186 !(FileNameString
) ||
2187 ((CallerBuffer
) && (DynamicString
) && !(FullNameOut
)))
2190 DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__
);
2191 Status
= STATUS_INVALID_PARAMETER
;
2195 /* First check what kind of path this is */
2196 PathType
= RtlDetermineDosPathNameType_Ustr(FileNameString
);
2198 /* Check if the caller wants to prevent relative .\ and ..\ paths */
2200 (PathType
== RtlPathTypeRelative
) &&
2201 (FileNameString
->Length
>= (2 * sizeof(WCHAR
))) &&
2202 (FileNameString
->Buffer
[0] == L
'.') &&
2203 ((IS_PATH_SEPARATOR(FileNameString
->Buffer
[1])) ||
2204 ((FileNameString
->Buffer
[1] == L
'.') &&
2205 ((FileNameString
->Length
>= (3 * sizeof(WCHAR
))) &&
2206 (IS_PATH_SEPARATOR(FileNameString
->Buffer
[2]))))))
2208 /* Yes, and this path is like that, so make it seem unknown */
2209 PathType
= RtlPathTypeUnknown
;
2212 /* Now check relative vs non-relative paths */
2213 if (PathType
== RtlPathTypeRelative
)
2215 /* Does the caller want SxS? */
2218 /* Apply the SxS magic */
2219 FullIsolatedPath
= NULL
;
2220 Status
= RtlDosApplyFileIsolationRedirection_Ustr(TRUE
,
2229 if (NT_SUCCESS(Status
))
2231 /* We found the SxS path, return it */
2232 if (FullNameOut
) *FullNameOut
= FullIsolatedPath
;
2235 else if (Status
!= STATUS_SXS_KEY_NOT_FOUND
)
2237 /* Critical SxS error, fail */
2238 DbgPrint("%s: Failing because call to "
2239 "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with "
2248 /* No SxS key found, or not requested, check if there's an extension */
2249 if (ExtensionString
)
2251 /* Save the extension length, and check if there's a file name */
2252 ExtensionLength
= ExtensionString
->Length
;
2253 if (FileNameString
->Length
)
2255 /* Start parsing the file name */
2256 End
= &FileNameString
->Buffer
[FileNameString
->Length
/ sizeof(WCHAR
)];
2257 while (End
> FileNameString
->Buffer
)
2259 /* If we find a path separator, there's no extension */
2260 if (IS_PATH_SEPARATOR(*--End
)) break;
2262 /* Otherwise, did we find an extension dot? */
2265 /* Ignore what the caller sent it, use the filename's */
2266 ExtensionString
= NULL
;
2267 ExtensionLength
= 0;
2274 /* Check if we got a path */
2275 if (PathString
->Length
)
2277 /* Start parsing the path name, looking for path separators */
2278 End
= &PathString
->Buffer
[PathString
->Length
/ sizeof(WCHAR
)];
2280 while ((p
> PathString
->Buffer
) && (*--p
== L
';'))
2282 /* This is the size of the path -- handle a trailing slash */
2283 PathSize
= End
- p
- 1;
2284 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2286 /* Check if we found a bigger path than before */
2287 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2289 /* Keep going with the path after this path separator */
2293 /* This is the trailing path, run the same code as above */
2295 if ((PathSize
) && !(IS_PATH_SEPARATOR(*(End
- 1)))) PathSize
++;
2296 if (PathSize
> MaxPathSize
) MaxPathSize
= PathSize
;
2298 /* Finally, convert the largest path size into WCHAR */
2299 MaxPathSize
*= sizeof(WCHAR
);
2302 /* Use the extension, the file name, and the largest path as the size */
2303 WorstCaseLength
= ExtensionLength
+
2304 FileNameString
->Length
+
2305 (USHORT
)MaxPathSize
+
2306 sizeof(UNICODE_NULL
);
2307 if (WorstCaseLength
> UNICODE_STRING_MAX_BYTES
)
2309 /* It has to fit in a registry string, if not, fail here */
2310 DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed "
2311 "worst case file name length is %Iu bytes\n",
2314 Status
= STATUS_NAME_TOO_LONG
;
2318 /* Scan the path now, to see if we can find the file */
2319 p
= PathString
->Buffer
;
2320 End
= &p
[PathString
->Length
/ sizeof(WCHAR
)];
2323 /* Find out where this path ends */
2324 for (SegmentEnd
= p
;
2325 ((SegmentEnd
!= End
) && (*SegmentEnd
!= L
';'));
2328 /* Compute the size of this path */
2329 ByteCount
= SegmentSize
= (SegmentEnd
- p
) * sizeof(WCHAR
);
2331 /* Handle trailing slash if there isn't one */
2332 if ((SegmentSize
) && !(IS_PATH_SEPARATOR(*(SegmentEnd
- 1))))
2334 /* Add space for one */
2335 SegmentSize
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
2338 /* Now check if our initial static buffer is too small */
2339 if (StaticCandidateString
.MaximumLength
<
2340 (SegmentSize
+ ExtensionLength
+ FileNameString
->Length
))
2342 /* At this point we should've been using our static buffer */
2343 ASSERT(StaticCandidateString
.Buffer
== StaticCandidateBuffer
);
2344 if (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
)
2346 /* Something is really messed up if this was the dynamic string */
2347 DbgPrint("%s: internal error #1; "
2348 "CandidateString.Buffer = %p; "
2349 "StaticCandidateBuffer = %p\n",
2351 StaticCandidateString
.Buffer
,
2352 StaticCandidateBuffer
);
2353 Status
= STATUS_INTERNAL_ERROR
;
2357 /* We checked before that the maximum possible size shoudl fit! */
2358 ASSERT((SegmentSize
+ FileNameString
->Length
+ ExtensionLength
) <
2359 UNICODE_STRING_MAX_BYTES
);
2360 if ((SegmentSize
+ ExtensionLength
+ FileNameString
->Length
) >
2361 (UNICODE_STRING_MAX_BYTES
- sizeof(WCHAR
)))
2363 /* For some reason it's not fitting anymore. Something messed up */
2364 DbgPrint("%s: internal error #2; SegmentSize = %u, "
2365 "FileName->Length = %u, DefaultExtensionLength = %u\n",
2368 FileNameString
->Length
,
2370 Status
= STATUS_INTERNAL_ERROR
;
2374 /* Now allocate the dynamic string */
2375 StaticCandidateString
.MaximumLength
= FileNameString
->Length
+
2377 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(WorstCaseLength
,
2379 if (!StaticCandidateString
.Buffer
)
2381 /* Out of memory, fail */
2382 DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n",
2384 StaticCandidateString
.MaximumLength
);
2385 Status
= STATUS_NO_MEMORY
;
2390 /* Copy the path in the string */
2391 RtlCopyMemory(StaticCandidateString
.Buffer
, p
, ByteCount
);
2393 /* Get to the end of the string, and add the trailing slash if missing */
2394 CandidateEnd
= &StaticCandidateString
.Buffer
[ByteCount
/ sizeof(WCHAR
)];
2395 if ((SegmentSize
) && (SegmentSize
!= ByteCount
))
2397 *CandidateEnd
++ = OBJ_NAME_PATH_SEPARATOR
;
2400 /* Copy the filename now */
2401 RtlCopyMemory(CandidateEnd
,
2402 FileNameString
->Buffer
,
2403 FileNameString
->Length
);
2404 CandidateEnd
+= (FileNameString
->Length
/ sizeof(WCHAR
));
2406 /* Check if there was an extension */
2407 if (ExtensionString
)
2409 /* Copy the extension too */
2410 RtlCopyMemory(CandidateEnd
,
2411 ExtensionString
->Buffer
,
2412 ExtensionString
->Length
);
2413 CandidateEnd
+= (ExtensionString
->Length
/ sizeof(WCHAR
));
2416 /* We are done, terminate it */
2417 *CandidateEnd
= UNICODE_NULL
;
2419 /* Now set the final length of the string so it becomes valid */
2420 StaticCandidateString
.Length
= (USHORT
)(CandidateEnd
-
2421 StaticCandidateString
.Buffer
) *
2424 /* Check if this file exists */
2425 DPRINT("BUFFER: %S\n", StaticCandidateString
.Buffer
);
2426 if (RtlDoesFileExists_UEx(StaticCandidateString
.Buffer
, FALSE
))
2428 /* Awesome, it does, now get the full path */
2429 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2432 (PUNICODE_STRING
*)FullNameOut
,
2437 if (!(NT_SUCCESS(Status
)) &&
2438 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2439 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2441 DbgPrint("%s: Failing because we thought we found %wZ on "
2442 "the search path, but RtlGetfullPathNameUStrEx() "
2445 &StaticCandidateString
,
2448 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2453 /* Otherwise, move to the next path */
2454 if (SegmentEnd
!= End
)
2456 /* Handle the case of the path separator trailing */
2466 /* Loop finished and we didn't break out -- fail */
2467 Status
= STATUS_NO_SUCH_FILE
;
2471 /* We have a full path, so check if it does exist */
2472 DPRINT("%wZ\n", FileNameString
);
2473 if (!RtlDoesFileExists_UstrEx(FileNameString
, TRUE
))
2475 /* It doesn't exist, did we have an extension? */
2476 if (!(ExtensionString
) || !(ExtensionString
->Length
))
2478 /* No extension, so just fail */
2479 Status
= STATUS_NO_SUCH_FILE
;
2483 /* There was an extension, check if the filename already had one */
2484 if (!(Flags
& 4) && (FileNameString
->Length
))
2486 /* Parse the filename */
2487 p
= FileNameString
->Buffer
;
2488 End
= &p
[FileNameString
->Length
/ sizeof(WCHAR
)];
2491 /* If there's a path separator, there's no extension */
2492 if (IS_PATH_SEPARATOR(*--End
)) break;
2494 /* Othwerwise, did we find an extension dot? */
2497 /* File already had an extension, so fail */
2498 Status
= STATUS_NO_SUCH_FILE
;
2504 /* So there is an extension, we'll try again by adding it */
2505 NamePlusExtLength
= FileNameString
->Length
+
2506 ExtensionString
->Length
+
2507 sizeof(UNICODE_NULL
);
2508 if (NamePlusExtLength
> UNICODE_STRING_MAX_BYTES
)
2510 /* It won't fit in any kind of valid string, so fail */
2511 DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n",
2514 Status
= STATUS_NAME_TOO_LONG
;
2518 /* Fill it fit in our temporary string? */
2519 if (NamePlusExtLength
> StaticCandidateString
.MaximumLength
)
2521 /* It won't fit anymore, allocate a dynamic string for it */
2522 StaticCandidateString
.MaximumLength
= NamePlusExtLength
;
2523 StaticCandidateString
.Buffer
= RtlpAllocateStringMemory(NamePlusExtLength
,
2525 if (!StaticCandidateString
.Buffer
)
2527 /* Ran out of memory, so fail */
2528 DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n",
2530 Status
= STATUS_NO_MEMORY
;
2535 /* Copy the filename */
2536 RtlCopyUnicodeString(&StaticCandidateString
, FileNameString
);
2538 /* Copy the extension */
2539 RtlAppendUnicodeStringToString(&StaticCandidateString
,
2542 DPRINT("SB: %wZ\n", &StaticCandidateString
);
2544 /* And check if this file now exists */
2545 if (!RtlDoesFileExists_UstrEx(&StaticCandidateString
, TRUE
))
2547 /* Still no joy, fail out */
2548 Status
= STATUS_NO_SUCH_FILE
;
2552 /* File was found, get the final full path */
2553 Status
= RtlGetFullPathName_UstrEx(&StaticCandidateString
,
2556 (PUNICODE_STRING
*)FullNameOut
,
2561 if (!(NT_SUCCESS(Status
)) && (Status
!= STATUS_NO_SUCH_FILE
))
2563 DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() "
2564 "failed with status %08lx\n",
2566 &StaticCandidateString
,
2569 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2573 /* File was found on the first try, get the final full path */
2574 Status
= RtlGetFullPathName_UstrEx(FileNameString
,
2577 (PUNICODE_STRING
*)FullNameOut
,
2582 if (!(NT_SUCCESS(Status
)) &&
2583 ((Status
!= STATUS_NO_SUCH_FILE
) &&
2584 (Status
!= STATUS_BUFFER_TOO_SMALL
)))
2586 DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ "
2587 "failed with status %08lx\n",
2592 DPRINT("Status: %lx BUFFER: %S\n", Status
, CallerBuffer
->Buffer
);
2597 /* Anything that was not an error, turn into STATUS_SUCCESS */
2598 if (NT_SUCCESS(Status
)) Status
= STATUS_SUCCESS
;
2600 /* Check if we had a dynamic string */
2601 if ((StaticCandidateString
.Buffer
) &&
2602 (StaticCandidateString
.Buffer
!= StaticCandidateBuffer
))
2605 RtlFreeUnicodeString(&StaticCandidateString
);
2608 /* Return the status */
2617 RtlDoesFileExists_U(IN PCWSTR FileName
)
2619 /* Call the new function */
2620 return RtlDoesFileExists_UEx(FileName
, TRUE
);