2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/client/path.c
5 * PURPOSE: Handles path APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *******************************************************************/
16 /* GLOBALS ********************************************************************/
18 UNICODE_STRING NoDefaultCurrentDirectoryInExePath
= RTL_CONSTANT_STRING(L
"NoDefaultCurrentDirectoryInExePath");
20 UNICODE_STRING BaseWindowsSystemDirectory
, BaseWindowsDirectory
;
21 UNICODE_STRING BaseDefaultPathAppend
, BaseDefaultPath
, BaseDllDirectory
;
23 PVOID gpTermsrvGetWindowsDirectoryA
;
24 PVOID gpTermsrvGetWindowsDirectoryW
;
26 /* This is bitmask for each illegal filename character */
27 /* If someone has time, please feel free to use 0b notation */
28 DWORD IllegalMask
[4] =
30 0xFFFFFFFF, // None allowed (00 to 1F)
31 0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed
32 0x38000000, // 5B, 5C, 5D not allowed
33 0x10000000 // 7C not allowed
36 BASE_SEARCH_PATH_TYPE BaseDllOrderCurrent
[BaseCurrentDirPlacementMax
][BaseSearchPathMax
] =
40 BaseSearchPathCurrent
,
41 BaseSearchPathDefault
,
47 BaseSearchPathDefault
,
48 BaseSearchPathCurrent
,
54 BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent
[BaseSearchPathMax
] =
57 BaseSearchPathDefault
,
59 BaseSearchPathInvalid
,
63 BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent
[BaseSearchPathMax
] =
67 BaseSearchPathDefault
,
72 BASE_SEARCH_PATH_TYPE BaseProcessOrder
[BaseSearchPathMax
] =
75 BaseSearchPathCurrent
,
76 BaseSearchPathDefault
,
81 BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement
= BaseCurrentDirPlacementInvalid
;
83 extern UNICODE_STRING BasePathVariableName
;
85 /* PRIVATE FUNCTIONS **********************************************************/
89 BasepEndOfDirName(IN PWCHAR FileName
)
91 PWCHAR FileNameEnd
, FileNameSeparator
;
93 /* Find the first slash */
94 FileNameSeparator
= wcschr(FileName
, OBJ_NAME_PATH_SEPARATOR
);
95 if (FileNameSeparator
)
97 /* Find the last one */
98 FileNameEnd
= wcsrchr(FileNameSeparator
, OBJ_NAME_PATH_SEPARATOR
);
101 /* Handle the case where they are one and the same */
102 if (FileNameEnd
== FileNameSeparator
) FileNameEnd
++;
106 /* No directory was specified */
110 /* Return where the directory ends and the filename starts */
116 BasepComputeProcessPath(IN PBASE_SEARCH_PATH_TYPE PathOrder
,
118 IN LPVOID Environment
)
120 PWCHAR PathBuffer
, Buffer
, AppNameEnd
, PathCurrent
;
121 ULONG PathLengthInBytes
;
123 UNICODE_STRING EnvPath
;
124 PBASE_SEARCH_PATH_TYPE Order
;
126 /* Initialize state */
127 AppNameEnd
= Buffer
= PathBuffer
= NULL
;
128 Status
= STATUS_SUCCESS
;
129 PathLengthInBytes
= 0;
131 /* Loop the ordering array */
132 for (Order
= PathOrder
; *Order
!= BaseSearchPathInvalid
; Order
++) {
135 /* Compute the size of the DLL path */
136 case BaseSearchPathDll
:
138 /* This path only gets called if SetDllDirectory was called */
139 ASSERT(BaseDllDirectory
.Buffer
!= NULL
);
141 /* Make sure there's a DLL directory size */
142 if (BaseDllDirectory
.Length
)
144 /* Add it, plus the separator */
145 PathLengthInBytes
+= BaseDllDirectory
.Length
+ sizeof(L
';');
149 /* Compute the size of the current path */
150 case BaseSearchPathCurrent
:
153 PathLengthInBytes
+= (2 * sizeof(WCHAR
));
156 /* Compute the size of the "PATH" environment variable */
157 case BaseSearchPathEnv
:
159 /* Grab PEB lock if one wasn't passed in */
160 if (!Environment
) RtlAcquirePebLock();
162 /* Query the size first */
163 EnvPath
.MaximumLength
= 0;
164 Status
= RtlQueryEnvironmentVariable_U(Environment
,
165 &BasePathVariableName
,
167 if (Status
== STATUS_BUFFER_TOO_SMALL
)
169 /* Compute the size we'll need for the environment */
170 EnvPath
.MaximumLength
= EnvPath
.Length
+ sizeof(WCHAR
);
171 if ((EnvPath
.Length
+ sizeof(WCHAR
)) > UNICODE_STRING_MAX_BYTES
)
173 /* Don't let it overflow */
174 EnvPath
.MaximumLength
= EnvPath
.Length
;
177 /* Allocate the environment buffer */
178 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
180 EnvPath
.MaximumLength
);
183 /* Now query the PATH environment variable */
184 EnvPath
.Buffer
= Buffer
;
185 Status
= RtlQueryEnvironmentVariable_U(Environment
,
186 &BasePathVariableName
,
192 Status
= STATUS_NO_MEMORY
;
196 /* Release the PEB lock from above */
197 if (!Environment
) RtlReleasePebLock();
199 /* There might not be a PATH */
200 if (Status
== STATUS_VARIABLE_NOT_FOUND
)
202 /* In this case, skip this PathOrder */
203 EnvPath
.Length
= EnvPath
.MaximumLength
= 0;
204 Status
= STATUS_SUCCESS
;
206 else if (!NT_SUCCESS(Status
))
208 /* An early failure, go to exit code */
213 /* Add the length of the PATH variable */
214 ASSERT(!(EnvPath
.Length
& 1));
215 PathLengthInBytes
+= (EnvPath
.Length
+ sizeof(L
';'));
219 /* Compute the size of the default search path */
220 case BaseSearchPathDefault
:
222 /* Just add it... it already has a ';' at the end */
223 ASSERT(!(BaseDefaultPath
.Length
& 1));
224 PathLengthInBytes
+= BaseDefaultPath
.Length
;
227 /* Compute the size of the current app directory */
228 case BaseSearchPathApp
:
229 /* Find out where the app name ends, to get only the directory */
230 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
232 /* Check if there was no application name passed in */
233 if (!(AppName
) || !(AppNameEnd
))
235 /* Do we have a per-thread CURDIR to use? */
236 if (NtCurrentTeb()->NtTib
.SubSystemTib
)
238 /* This means someone added RTL_PERTHREAD_CURDIR */
239 UNIMPLEMENTED_DBGBREAK();
242 /* We do not. Do we have the LDR_ENTRY for the executable? */
243 if (!BasepExeLdrEntry
)
245 /* We do not. Grab it */
246 LdrEnumerateLoadedModules(0,
247 BasepLocateExeLdrEntry
,
248 NtCurrentPeb()->ImageBaseAddress
);
251 /* Now do we have it? */
252 if (BasepExeLdrEntry
)
254 /* Yes, so read the name out of it */
255 AppName
= BasepExeLdrEntry
->FullDllName
.Buffer
;
258 /* Find out where the app name ends, to get only the directory */
259 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
262 /* So, do we have an application name and its directory? */
263 if ((AppName
) && (AppNameEnd
))
265 /* Add the size of the app's directory, plus the separator */
266 PathLengthInBytes
+= ((AppNameEnd
- AppName
) * sizeof(WCHAR
)) + sizeof(L
';');
275 /* Bam, all done, we now have the final path size */
276 ASSERT(PathLengthInBytes
> 0);
277 ASSERT(!(PathLengthInBytes
& 1));
279 /* Allocate the buffer to hold it */
280 PathBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes
);
284 Status
= STATUS_NO_MEMORY
;
288 /* Now we loop again, this time to copy the data */
289 PathCurrent
= PathBuffer
;
290 for (Order
= PathOrder
; *Order
!= BaseSearchPathInvalid
; Order
++) {
293 /* Add the DLL path */
294 case BaseSearchPathDll
:
295 if (BaseDllDirectory
.Length
)
297 /* Copy it in the buffer, ASSERT there's enough space */
298 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + BaseDllDirectory
.Length
) <= PathLengthInBytes
);
299 RtlCopyMemory(PathCurrent
,
300 BaseDllDirectory
.Buffer
,
301 BaseDllDirectory
.Length
);
303 /* Update the current pointer, add a separator */
304 PathCurrent
+= (BaseDllDirectory
.Length
/ sizeof(WCHAR
));
305 *PathCurrent
++ = ';';
309 /* Add the current application path */
310 case BaseSearchPathApp
:
311 if ((AppName
) && (AppNameEnd
))
313 /* Copy it in the buffer, ASSERT there's enough space */
314 ASSERT(((PathCurrent
- PathBuffer
+ 1 + (AppNameEnd
- AppName
)) * sizeof(WCHAR
)) <= PathLengthInBytes
);
315 RtlCopyMemory(PathCurrent
,
317 (AppNameEnd
- AppName
) * sizeof(WCHAR
));
319 /* Update the current pointer, add a separator */
320 PathCurrent
+= AppNameEnd
- AppName
;
321 *PathCurrent
++ = ';';
325 /* Add the default search path */
326 case BaseSearchPathDefault
:
327 /* Copy it in the buffer, ASSERT there's enough space */
328 ASSERT((((PathCurrent
- PathBuffer
) * sizeof(WCHAR
)) + BaseDefaultPath
.Length
) <= PathLengthInBytes
);
329 RtlCopyMemory(PathCurrent
, BaseDefaultPath
.Buffer
, BaseDefaultPath
.Length
);
331 /* Update the current pointer. The default path already has a ";" */
332 PathCurrent
+= (BaseDefaultPath
.Length
/ sizeof(WCHAR
));
335 /* Add the path in the PATH environment variable */
336 case BaseSearchPathEnv
:
339 /* Copy it in the buffer, ASSERT there's enough space */
340 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + EnvPath
.Length
) <= PathLengthInBytes
);
341 RtlCopyMemory(PathCurrent
, EnvPath
.Buffer
, EnvPath
.Length
);
343 /* Update the current pointer, add a separator */
344 PathCurrent
+= (EnvPath
.Length
/ sizeof(WCHAR
));
345 *PathCurrent
++ = ';';
349 /* Add the current dierctory */
350 case BaseSearchPathCurrent
:
352 /* Copy it in the buffer, ASSERT there's enough space */
353 ASSERT(((PathCurrent
- PathBuffer
+ 2) * sizeof(WCHAR
)) <= PathLengthInBytes
);
354 *PathCurrent
++ = '.';
356 /* Add the path separator */
357 *PathCurrent
++ = ';';
365 /* Everything should've perfectly fit in there */
366 ASSERT((PathCurrent
- PathBuffer
) * sizeof(WCHAR
) == PathLengthInBytes
);
367 ASSERT(PathCurrent
> PathBuffer
);
369 /* Terminate the whole thing */
370 PathCurrent
[-1] = UNICODE_NULL
;
373 /* Exit path: free our buffers */
374 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
377 /* This only gets freed in the failure path, since caller wants it */
378 if (!NT_SUCCESS(Status
))
380 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer
);
385 /* Return the path! */
391 BaseComputeProcessSearchPath(VOID
)
393 DPRINT("Computing Process Search path\n");
395 /* Compute the path using default process order */
396 return BasepComputeProcessPath(BaseProcessOrder
, NULL
, NULL
);
401 BaseComputeProcessExePath(IN LPWSTR FullPath
)
403 PBASE_SEARCH_PATH_TYPE PathOrder
;
404 DPRINT1("Computing EXE path: %wZ\n", FullPath
);
406 /* Check if we should use the current directory */
407 PathOrder
= NeedCurrentDirectoryForExePathW(FullPath
) ?
408 BaseProcessOrder
: BaseProcessOrderNoCurrent
;
410 /* And now compute the path */
411 return BasepComputeProcessPath(PathOrder
, NULL
, NULL
);
416 BaseComputeProcessDllPath(IN LPWSTR FullPath
,
417 IN PVOID Environment
)
419 LPWSTR DllPath
= NULL
;
420 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
421 UNICODE_STRING ValueName
= RTL_CONSTANT_STRING(L
"SafeDllSearchMode");
422 OBJECT_ATTRIBUTES ObjectAttributes
= RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
423 KEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
427 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement
, OldCurrentDirPlacement
;
429 /* Acquire DLL directory lock */
430 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
432 /* Check if we have a base dll directory */
433 if (BaseDllDirectory
.Buffer
)
435 /* Then compute the process path using DLL order (without curdir) */
436 DllPath
= BasepComputeProcessPath(BaseDllOrderNoCurrent
, FullPath
, Environment
);
438 /* Release DLL directory lock */
439 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
441 /* Return dll path */
445 /* Release DLL directory lock */
446 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
448 /* Read the current placement */
449 CurrentDirPlacement
= BasepDllCurrentDirPlacement
;
450 if (CurrentDirPlacement
== BaseCurrentDirPlacementInvalid
)
452 /* Open the configuration key */
453 Status
= NtOpenKey(&KeyHandle
, KEY_QUERY_VALUE
, &ObjectAttributes
);
454 if (NT_SUCCESS(Status
))
456 /* Query if safe search is enabled */
457 Status
= NtQueryValueKey(KeyHandle
,
459 KeyValuePartialInformation
,
463 if (NT_SUCCESS(Status
))
465 /* Read the value if the size is OK */
466 if (ResultLength
== sizeof(PartialInfo
))
468 CurrentDirPlacement
= *(PULONG
)PartialInfo
.Data
;
472 /* Close the handle */
475 /* Validate the registry value */
476 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
477 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
479 /* Default to safe search */
480 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
484 /* Update the placement and read the old one */
485 OldCurrentDirPlacement
= InterlockedCompareExchange((PLONG
)&BasepDllCurrentDirPlacement
,
487 BaseCurrentDirPlacementInvalid
);
488 if (OldCurrentDirPlacement
!= BaseCurrentDirPlacementInvalid
)
490 /* If there already was a placement, use it */
491 CurrentDirPlacement
= OldCurrentDirPlacement
;
495 /* Check if the placement is invalid or not set */
496 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
497 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
499 /* Default to safe search */
500 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
503 /* Compute the process path using either normal or safe search */
504 DllPath
= BasepComputeProcessPath(BaseDllOrderCurrent
[CurrentDirPlacement
],
508 /* Return dll path */
514 CheckForSameCurdir(IN PUNICODE_STRING DirName
)
516 PUNICODE_STRING CurDir
;
519 UNICODE_STRING CurDirCopy
;
521 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
523 CurLength
= CurDir
->Length
;
524 if (CurDir
->Length
<= 6)
526 if (CurLength
!= DirName
->Length
) return FALSE
;
530 if ((CurLength
- 2) != DirName
->Length
) return FALSE
;
535 CurDirCopy
= *CurDir
;
536 if (CurDirCopy
.Length
> 6) CurDirCopy
.Length
-= 2;
540 if (RtlEqualUnicodeString(&CurDirCopy
, DirName
, TRUE
)) Result
= TRUE
;
548 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
549 * identical (other than the Rtl can optionally check for spaces), however the
550 * Rtl will always convert to OEM, while kernel32 has two possible file modes
551 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
552 * the correct compatible results
556 IsShortName_U(IN PWCHAR Name
,
559 BOOLEAN HasExtension
;
562 UNICODE_STRING UnicodeName
;
563 ANSI_STRING AnsiName
;
565 CHAR AnsiBuffer
[MAX_PATH
];
568 /* What do you think 8.3 means? */
569 if (Length
> 12) return FALSE
;
571 /* Sure, any emtpy name is a short name */
572 if (!Length
) return TRUE
;
574 /* This could be . or .. or something else */
577 /* Which one is it */
578 if ((Length
== 1) || ((Length
== 2) && *(Name
+ 1) == L
'.'))
580 /* . or .., this is good */
584 /* Some other bizare dot-based name, not good */
588 /* Initialize our two strings */
589 RtlInitEmptyAnsiString(&AnsiName
, AnsiBuffer
, MAX_PATH
);
590 RtlInitEmptyUnicodeString(&UnicodeName
, Name
, Length
* sizeof(WCHAR
));
591 UnicodeName
.Length
= UnicodeName
.MaximumLength
;
593 /* Now do the conversion */
594 Status
= BasepUnicodeStringTo8BitString(&AnsiName
, &UnicodeName
, FALSE
);
595 if (!NT_SUCCESS(Status
)) return FALSE
;
597 /* Now we loop the name */
598 HasExtension
= FALSE
;
599 for (i
= 0, Dots
= Length
- 1; i
< AnsiName
.Length
; i
++, Dots
--)
601 /* Read the current byte */
602 c
= AnsiName
.Buffer
[i
];
605 if (IsDBCSLeadByte(c
))
607 /* If we're near the end of the string, we can't allow a DBCS */
608 if ((!(HasExtension
) && (i
>= 7)) || (i
== AnsiName
.Length
- 1))
613 /* Otherwise we skip over it */
617 /* Check for illegal characters */
618 if ((c
> 0x7F) || (IllegalMask
[c
/ 32] & (1 << (c
% 32))))
623 /* Check if this is perhaps an extension? */
626 /* Unless the extension is too large or there's more than one */
627 if ((HasExtension
) || (Dots
> 3)) return FALSE
;
629 /* This looks like an extension */
633 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
634 if ((i
>= 8) && !(HasExtension
)) return FALSE
;
637 /* You survived the loop, this is a good short name */
643 IsLongName_U(IN PWCHAR FileName
,
646 BOOLEAN HasExtension
;
649 /* More than 8.3, any combination of dots, and NULL names are all long */
650 if (!(Length
) || (Length
> 12) || (*FileName
== L
'.')) return TRUE
;
652 /* Otherwise, initialize our scanning loop */
653 HasExtension
= FALSE
;
654 for (i
= 0, Dots
= Length
- 1; i
< Length
; i
++, Dots
--)
656 /* Check if this could be an extension */
657 if (FileName
[i
] == L
'.')
659 /* Unlike the short case, we WANT more than one extension, or a long one */
660 if ((HasExtension
) || (Dots
> 3))
667 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
668 if ((i
>= 8) && (!HasExtension
)) return TRUE
;
671 /* The name *seems* to conform to 8.3 */
677 FindLFNorSFN_U(IN PWCHAR Path
,
687 /* Loop while there is something in the path */
690 /* Loop within the path skipping slashes */
691 while ((*Path
== L
'\\') || (*Path
== L
'/')) Path
++;
693 /* Make sure there's something after the slashes too! */
694 if (*Path
== UNICODE_NULL
) break;
696 /* Now skip past the file name until we get to the first slash */
698 while ((*p
) && ((*p
!= L
'\\') && (*p
!= L
'/'))) p
++;
700 /* Whatever is in between those two is now the file name length */
704 * Check if it is valid
705 * Note that !IsShortName != IsLongName, these two functions simply help
706 * us determine if a conversion is necessary or not.
707 * "Found" really means: "Is a conversion necessary?", hence the "!"
709 Found
= UseShort
? !IsShortName_U(Path
, Length
) : !IsLongName_U(Path
, Length
);
712 /* It is! did the caller request to know the markers? */
713 if ((First
) && (Last
))
722 /* Is there anything else following this sub-path/filename? */
723 if (*p
== UNICODE_NULL
) break;
725 /* Yes, keep going */
729 /* Return if anything was found and valid */
735 SkipPathTypeIndicator_U(IN LPWSTR Path
)
740 /* Check what kind of path this is and how many slashes to skip */
741 switch (RtlDetermineDosPathNameType_U(Path
))
743 case RtlPathTypeDriveAbsolute
:
746 case RtlPathTypeDriveRelative
:
749 case RtlPathTypeRooted
:
752 case RtlPathTypeRelative
:
755 case RtlPathTypeRootLocalDevice
:
759 case RtlPathTypeUncAbsolute
:
760 case RtlPathTypeLocalDevice
:
762 /* Keep going until we bypass the path indicators */
763 for (ReturnPath
= Path
+ 2, i
= 2; (i
> 0) && (*ReturnPath
); ReturnPath
++)
765 /* We look for 2 slashes, so keep at it until we find them */
766 if ((*ReturnPath
== L
'\\') || (*ReturnPath
== L
'/')) i
--;
775 BasepIsCurDirAllowedForPlainExeNames(VOID
)
778 UNICODE_STRING EmptyString
;
780 RtlInitEmptyUnicodeString(&EmptyString
, NULL
, 0);
781 Status
= RtlQueryEnvironmentVariable_U(NULL
,
782 &NoDefaultCurrentDirectoryInExePath
,
784 return !NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
;
787 /* PUBLIC FUNCTIONS ***********************************************************/
794 SetDllDirectoryW(IN LPCWSTR lpPathName
)
796 UNICODE_STRING OldDirectory
, DllDirectory
;
800 if (wcschr(lpPathName
, L
';'))
802 SetLastError(ERROR_INVALID_PARAMETER
);
805 if (!RtlCreateUnicodeString(&DllDirectory
, lpPathName
))
807 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
813 RtlInitUnicodeString(&DllDirectory
, NULL
);
816 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
818 OldDirectory
= BaseDllDirectory
;
819 BaseDllDirectory
= DllDirectory
;
821 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
823 RtlFreeUnicodeString(&OldDirectory
);
832 SetDllDirectoryA(IN LPCSTR lpPathName
)
834 ANSI_STRING AnsiDllDirectory
;
835 UNICODE_STRING OldDirectory
, DllDirectory
;
840 if (strchr(lpPathName
, ';'))
842 SetLastError(ERROR_INVALID_PARAMETER
);
846 Status
= RtlInitAnsiStringEx(&AnsiDllDirectory
, lpPathName
);
847 if (NT_SUCCESS(Status
))
849 Status
= Basep8BitStringToUnicodeString(&DllDirectory
,
854 if (!NT_SUCCESS(Status
))
856 BaseSetLastNTError(Status
);
862 RtlInitUnicodeString(&DllDirectory
, NULL
);
865 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
867 OldDirectory
= BaseDllDirectory
;
868 BaseDllDirectory
= DllDirectory
;
870 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
872 RtlFreeUnicodeString(&OldDirectory
);
881 GetDllDirectoryW(IN DWORD nBufferLength
,
886 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
888 if ((nBufferLength
* sizeof(WCHAR
)) > BaseDllDirectory
.Length
)
890 RtlCopyMemory(lpBuffer
, BaseDllDirectory
.Buffer
, BaseDllDirectory
.Length
);
891 Length
= BaseDllDirectory
.Length
/ sizeof(WCHAR
);
892 lpBuffer
[Length
] = UNICODE_NULL
;
896 Length
= (BaseDllDirectory
.Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
);
897 if (lpBuffer
) *lpBuffer
= UNICODE_NULL
;
900 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
909 GetDllDirectoryA(IN DWORD nBufferLength
,
913 ANSI_STRING AnsiDllDirectory
;
916 RtlInitEmptyAnsiString(&AnsiDllDirectory
, lpBuffer
, nBufferLength
);
918 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
920 Length
= BasepUnicodeStringTo8BitSize(&BaseDllDirectory
);
921 if (Length
> nBufferLength
)
923 Status
= STATUS_SUCCESS
;
924 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
929 Status
= BasepUnicodeStringTo8BitString(&AnsiDllDirectory
,
934 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
936 if (!NT_SUCCESS(Status
))
938 BaseSetLastNTError(Status
);
940 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
951 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName
)
953 if (wcschr(ExeName
, L
'\\')) return TRUE
;
955 return BasepIsCurDirAllowedForPlainExeNames();
963 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName
)
965 if (strchr(ExeName
, '\\')) return TRUE
;
967 return BasepIsCurDirAllowedForPlainExeNames();
973 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
974 * beyond what you would usually expect. There are two main reasons:
976 * First, these APIs are subject to the ANSI/OEM File API selection status that
977 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
979 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
980 * length of the paths in "ANSI" by dividing their internal Wide character count
981 * by two... this is usually correct when dealing with pure-ASCII codepages but
982 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
983 * for CJK, for example.
987 GetFullPathNameA(IN LPCSTR lpFileName
,
988 IN DWORD nBufferLength
,
990 IN LPSTR
*lpFilePart
)
994 ULONG PathSize
, FilePartSize
;
995 ANSI_STRING AnsiString
;
996 UNICODE_STRING FileNameString
, UniString
;
997 PWCHAR LocalFilePart
;
1000 /* If the caller wants filepart, use a local wide buffer since this is A */
1001 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1003 /* Initialize for Quickie */
1004 FilePartSize
= PathSize
= 0;
1005 FileNameString
.Buffer
= NULL
;
1007 /* First get our string in Unicode */
1008 Status
= Basep8BitStringToDynamicUnicodeString(&FileNameString
, lpFileName
);
1009 if (!NT_SUCCESS(Status
)) goto Quickie
;
1011 /* Allocate a buffer to hold teh path name */
1012 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1014 MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1017 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES
);
1021 /* Call into RTL to get the full Unicode path name */
1022 PathSize
= RtlGetFullPathName_U(FileNameString
.Buffer
,
1023 MAX_PATH
* sizeof(WCHAR
),
1026 if (PathSize
<= (MAX_PATH
* sizeof(WCHAR
)))
1028 /* The buffer will fit, get the real ANSI string size now */
1029 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
);
1030 if (NT_SUCCESS(Status
))
1032 /* Now check if the user wanted file part size as well */
1033 if ((PathSize
) && (lpFilePart
) && (LocalFilePart
))
1035 /* Yep, so in this case get the length of the file part too */
1036 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1038 (LocalFilePart
- Buffer
) *
1040 if (!NT_SUCCESS(Status
))
1042 /* We failed to do that, so fail the whole call */
1043 BaseSetLastNTError(Status
);
1051 /* Reset the path size since the buffer is not large enough */
1055 /* Either no path, or local buffer was too small, enter failure code */
1056 if (!PathSize
) goto Quickie
;
1058 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
1059 if (PathSize
>= nBufferLength
)
1065 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
1066 RtlInitUnicodeString(&UniString
, Buffer
);
1067 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, &UniString
, TRUE
);
1068 if (!NT_SUCCESS(Status
))
1070 /* Final conversion failed, fail the call */
1071 BaseSetLastNTError(Status
);
1076 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
1077 RtlCopyMemory(lpBuffer
, AnsiString
.Buffer
, PathSize
+ 1);
1078 RtlFreeAnsiString(&AnsiString
);
1080 /* And finally, did the caller request file part information? */
1083 /* Use the size we computed earlier and add it to the buffer */
1084 *lpFilePart
= LocalFilePart
? &lpBuffer
[FilePartSize
] : 0;
1089 /* Cleanup and return the path size */
1090 if (FileNameString
.Buffer
) RtlFreeUnicodeString(&FileNameString
);
1091 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1100 GetFullPathNameW(IN LPCWSTR lpFileName
,
1101 IN DWORD nBufferLength
,
1103 OUT LPWSTR
*lpFilePart
)
1105 /* Call Rtl to do the work */
1106 return RtlGetFullPathName_U((LPWSTR
)lpFileName
,
1107 nBufferLength
* sizeof(WCHAR
),
1109 lpFilePart
) / sizeof(WCHAR
);
1117 SearchPathA(IN LPCSTR lpPath
,
1118 IN LPCSTR lpFileName
,
1119 IN LPCSTR lpExtension
,
1120 IN DWORD nBufferLength
,
1122 OUT LPSTR
*lpFilePart
)
1124 PUNICODE_STRING FileNameString
;
1125 UNICODE_STRING PathString
, ExtensionString
;
1127 ULONG PathSize
, FilePartSize
, AnsiLength
;
1128 PWCHAR LocalFilePart
, Buffer
;
1131 /* If the caller wants filepart, use a local wide buffer since this is A */
1132 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1134 /* Initialize stuff for Quickie */
1137 ExtensionString
.Buffer
= PathString
.Buffer
= NULL
;
1139 /* Get the UNICODE_STRING file name */
1140 FileNameString
= Basep8BitStringToStaticUnicodeString(lpFileName
);
1141 if (!FileNameString
) return 0;
1143 /* Did the caller specify an extension */
1146 /* Yup, convert it into UNICODE_STRING */
1147 Status
= Basep8BitStringToDynamicUnicodeString(&ExtensionString
,
1149 if (!NT_SUCCESS(Status
)) goto Quickie
;
1152 /* Did the caller specify a path */
1155 /* Yup, convert it into UNICODE_STRING */
1156 Status
= Basep8BitStringToDynamicUnicodeString(&PathString
, lpPath
);
1157 if (!NT_SUCCESS(Status
)) goto Quickie
;
1160 /* Allocate our output buffer */
1161 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength
* sizeof(WCHAR
));
1164 /* It failed, bail out */
1165 BaseSetLastNTError(STATUS_NO_MEMORY
);
1169 /* Now run the Wide search with the input buffer lengths */
1170 PathSize
= SearchPathW(PathString
.Buffer
,
1171 FileNameString
->Buffer
,
1172 ExtensionString
.Buffer
,
1176 if (PathSize
<= nBufferLength
)
1178 /* It fits, but is it empty? If so, bail out */
1179 if (!PathSize
) goto Quickie
;
1181 /* The length above is inexact, we need it in ANSI */
1182 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
, Buffer
, PathSize
* sizeof(WCHAR
));
1183 if (!NT_SUCCESS(Status
))
1185 /* Conversion failed, fail the call */
1187 BaseSetLastNTError(Status
);
1191 /* If the correct ANSI size is too big, return requird length plus a NULL */
1192 if (AnsiLength
>= nBufferLength
)
1194 PathSize
= AnsiLength
+ 1;
1198 /* Now apply the final conversion to ANSI */
1199 Status
= RtlUnicodeToMultiByteN(lpBuffer
,
1203 PathSize
* sizeof(WCHAR
));
1204 if (!NT_SUCCESS(Status
))
1206 /* Conversion failed, fail the whole call */
1208 BaseSetLastNTError(STATUS_NO_MEMORY
);
1212 /* NULL-terminate and return the real ANSI length */
1213 lpBuffer
[AnsiLength
] = ANSI_NULL
;
1214 PathSize
= AnsiLength
;
1216 /* Now check if the user wanted file part size as well */
1219 /* If we didn't get a file part, clear the caller's */
1226 /* Yep, so in this case get the length of the file part too */
1227 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1229 (LocalFilePart
- Buffer
) *
1231 if (!NT_SUCCESS(Status
))
1233 /* We failed to do that, so fail the whole call */
1234 BaseSetLastNTError(Status
);
1238 /* Return the file part buffer */
1239 *lpFilePart
= lpBuffer
+ FilePartSize
;
1245 /* Our initial buffer guess was too small, allocate a bigger one */
1246 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1247 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize
* sizeof(WCHAR
));
1250 /* Out of memory, fail everything */
1251 BaseSetLastNTError(STATUS_NO_MEMORY
);
1255 /* Do the search again -- it will fail, we just want the path size */
1256 PathSize
= SearchPathW(PathString
.Buffer
,
1257 FileNameString
->Buffer
,
1258 ExtensionString
.Buffer
,
1262 if (!PathSize
) goto Quickie
;
1264 /* Convert it to a correct size */
1265 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
* sizeof(WCHAR
));
1266 if (NT_SUCCESS(Status
))
1268 /* Make space for the NULL-char */
1273 /* Conversion failed for some reason, fail the call */
1274 BaseSetLastNTError(Status
);
1280 /* Cleanup/complete path */
1281 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1282 if (ExtensionString
.Buffer
) RtlFreeUnicodeString(&ExtensionString
);
1283 if (PathString
.Buffer
) RtlFreeUnicodeString(&PathString
);
1292 SearchPathW(IN LPCWSTR lpPath
,
1293 IN LPCWSTR lpFileName
,
1294 IN LPCWSTR lpExtension
,
1295 IN DWORD nBufferLength
,
1297 OUT LPWSTR
*lpFilePart
)
1299 UNICODE_STRING FileNameString
, ExtensionString
, PathString
, CallerBuffer
;
1300 ULONG Flags
, LengthNeeded
, FilePartSize
;
1304 /* Default flags for RtlDosSearchPath_Ustr */
1307 /* Clear file part in case we fail */
1308 if (lpFilePart
) *lpFilePart
= NULL
;
1310 /* Initialize path buffer for free later */
1311 PathString
.Buffer
= NULL
;
1313 /* Convert filename to a unicode string and eliminate trailing spaces */
1314 RtlInitUnicodeString(&FileNameString
, lpFileName
);
1315 while ((FileNameString
.Length
>= sizeof(WCHAR
)) &&
1316 (FileNameString
.Buffer
[(FileNameString
.Length
/ sizeof(WCHAR
)) - 1] == L
' '))
1318 FileNameString
.Length
-= sizeof(WCHAR
);
1321 /* Was it all just spaces? */
1322 if (!FileNameString
.Length
)
1325 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
1329 /* Convert extension to a unicode string */
1330 RtlInitUnicodeString(&ExtensionString
, lpExtension
);
1332 /* Check if the user sent a path */
1335 /* Convert it to a unicode string too */
1336 Status
= RtlInitUnicodeStringEx(&PathString
, lpPath
);
1337 if (NT_ERROR(Status
))
1339 /* Fail if it was too long */
1340 BaseSetLastNTError(Status
);
1346 /* A path wasn't sent, so compute it ourselves */
1347 PathString
.Buffer
= BaseComputeProcessSearchPath();
1348 if (!PathString
.Buffer
)
1350 /* Fail if we couldn't compute it */
1351 BaseSetLastNTError(STATUS_NO_MEMORY
);
1355 /* See how big the computed path is */
1356 LengthNeeded
= lstrlenW(PathString
.Buffer
);
1357 if (LengthNeeded
> UNICODE_STRING_MAX_CHARS
)
1359 /* Fail if it's too long */
1360 BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1364 /* Set the path size now that we have it */
1365 PathString
.MaximumLength
= PathString
.Length
= LengthNeeded
* sizeof(WCHAR
);
1367 /* Request SxS isolation from RtlDosSearchPath_Ustr */
1371 /* Create the string that describes the output buffer from the caller */
1372 CallerBuffer
.Length
= 0;
1373 CallerBuffer
.Buffer
= lpBuffer
;
1375 /* How much space does the caller have? */
1376 if (nBufferLength
<= UNICODE_STRING_MAX_CHARS
)
1378 /* Add it into the string */
1379 CallerBuffer
.MaximumLength
= nBufferLength
* sizeof(WCHAR
);
1383 /* Caller wants too much, limit it to the maximum length of a string */
1384 CallerBuffer
.MaximumLength
= UNICODE_STRING_MAX_BYTES
;
1387 /* Call Rtl to do the work */
1388 Status
= RtlDosSearchPath_Ustr(Flags
,
1397 if (NT_ERROR(Status
))
1399 /* Check for unusual status codes */
1400 if ((Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
1402 /* Print them out since maybe an app needs fixing */
1403 DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
1407 DbgPrint(" Path = %wZ\n", &PathString
);
1410 /* Check if the failure was due to a small buffer */
1411 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1413 /* Check if the length was actually too big for Rtl to work with */
1414 Result
= LengthNeeded
/ sizeof(WCHAR
);
1415 if (Result
> 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1419 /* Some other error, set the error code */
1420 BaseSetLastNTError(Status
);
1425 /* It worked! Write the file part now */
1426 if (lpFilePart
) *lpFilePart
= &lpBuffer
[FilePartSize
];
1428 /* Convert the final result length */
1429 Result
= CallerBuffer
.Length
/ sizeof(WCHAR
);
1433 /* Check if there was a dynamic path string to free */
1434 if ((PathString
.Buffer
!= lpPath
) && (PathString
.Buffer
))
1437 RtlFreeHeap(RtlGetProcessHeap(), 0, PathString
.Buffer
);
1440 /* Return the final result length */
1449 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
1450 IN LPWSTR lpszLongPath
,
1453 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1459 BOOLEAN Found
= FALSE
;
1460 WIN32_FIND_DATAW FindFileData
;
1462 /* Initialize so Quickie knows there's nothing to do */
1463 Buffer
= Original
= NULL
;
1466 /* First check if the input path was obviously NULL */
1469 /* Fail the request */
1470 SetLastError(ERROR_INVALID_PARAMETER
);
1474 /* We will be touching removed, removable drives -- don't warn the user */
1475 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1477 /* Do a simple check to see if the path exists */
1478 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
1480 /* It doesn't, so fail */
1485 /* Now get a pointer to the actual path, skipping indicators */
1486 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
1488 /* Is there any path or filename in there? */
1490 (*Path
== UNICODE_NULL
) ||
1491 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
1493 /* There isn't, so the long path is simply the short path */
1494 ReturnLength
= wcslen(lpszShortPath
);
1496 /* Is there space for it? */
1497 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1499 /* Make sure the pointers aren't already the same */
1500 if (lpszLongPath
!= lpszShortPath
)
1502 /* They're not -- copy the short path into the long path */
1503 RtlMoveMemory(lpszLongPath
,
1505 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1510 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1516 /* We are still in the game -- compute the current size */
1517 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
1518 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1519 if (!Original
) goto ErrorQuickie
;
1521 /* Make a copy of it */
1522 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
1524 /* Compute the new first and last markers */
1525 First
= &Original
[First
- lpszShortPath
];
1526 Last
= &Original
[Last
- lpszShortPath
];
1528 /* Set the current destination pointer for a copy */
1532 * Windows allows the paths to overlap -- we have to be careful with this and
1533 * see if it's same to do so, and if not, allocate our own internal buffer
1534 * that we'll return at the end.
1536 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1538 if ((cchBuffer
) && (lpszLongPath
) &&
1539 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
1540 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
1542 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1543 if (!Buffer
) goto ErrorQuickie
;
1545 /* New destination */
1549 /* Prepare for the loop */
1554 /* Current delta in the loop */
1555 Length
= First
- Src
;
1557 /* Update the return length by it */
1558 ReturnLength
+= Length
;
1560 /* Is there a delta? If so, is there space and buffer for it? */
1561 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1563 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1567 /* "Terminate" this portion of the path's substring so we can do a find */
1569 *Last
= UNICODE_NULL
;
1570 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1573 /* This portion wasn't found, so fail */
1574 if (FindHandle
== INVALID_HANDLE_VALUE
)
1580 /* Close the find handle */
1581 FindClose(FindHandle
);
1583 /* Now check the length of the long name */
1584 Length
= wcslen(FindFileData
.cFileName
);
1587 /* This is our new first marker */
1588 First
= FindFileData
.cFileName
;
1592 /* Otherwise, the name is the delta between our current markers */
1593 Length
= Last
- First
;
1596 /* Update the return length with the short name length, if any */
1597 ReturnLength
+= Length
;
1599 /* Once again check for appropriate space and buffer */
1600 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1602 /* And do the copy if there is */
1603 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1607 /* Now update the source pointer */
1609 if (*Src
== UNICODE_NULL
) break;
1611 /* Are there more names in there? */
1612 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1616 /* The loop is done, is there anything left? */
1619 /* Get the length of the straggling path */
1620 Length
= wcslen(Src
);
1621 ReturnLength
+= Length
;
1623 /* Once again check for appropriate space and buffer */
1624 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1626 /* And do the copy if there is -- accounting for NULL here */
1627 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1629 /* What about our buffer? */
1632 /* Copy it into the caller's long path */
1633 RtlMoveMemory(lpszLongPath
,
1635 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1640 /* Buffer is too small, let the caller know, making space for NULL */
1645 /* We're all done */
1649 /* This is the goto for memory failures */
1650 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1653 /* General function end: free memory, restore error mode, return length */
1654 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1655 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1656 SetErrorMode(ErrorMode
);
1657 return ReturnLength
;
1665 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1666 IN LPSTR lpszLongPath
,
1669 ULONG Result
, PathLength
;
1672 UNICODE_STRING LongPathUni
, ShortPathUni
;
1673 ANSI_STRING LongPathAnsi
;
1674 WCHAR LongPathBuffer
[MAX_PATH
];
1677 LongPathAnsi
.Buffer
= NULL
;
1678 ShortPathUni
.Buffer
= NULL
;
1683 SetLastError(ERROR_INVALID_PARAMETER
);
1687 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1688 if (!NT_SUCCESS(Status
)) goto Quickie
;
1690 LongPath
= LongPathBuffer
;
1692 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1693 if (PathLength
>= MAX_PATH
)
1695 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1699 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1703 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1707 if (!PathLength
) goto Quickie
;
1709 ShortPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1710 LongPathUni
.Buffer
= LongPath
;
1711 LongPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1713 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1714 if (!NT_SUCCESS(Status
))
1716 BaseSetLastNTError(Status
);
1720 Result
= LongPathAnsi
.Length
;
1721 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1723 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1724 lpszLongPath
[Result
] = ANSI_NULL
;
1728 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1732 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1733 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1734 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1736 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1746 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1747 IN LPSTR lpszShortPath
,
1750 ULONG Result
, PathLength
;
1753 UNICODE_STRING LongPathUni
, ShortPathUni
;
1754 ANSI_STRING ShortPathAnsi
;
1755 WCHAR ShortPathBuffer
[MAX_PATH
];
1758 ShortPathAnsi
.Buffer
= NULL
;
1759 LongPathUni
.Buffer
= NULL
;
1764 SetLastError(ERROR_INVALID_PARAMETER
);
1768 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1769 if (!NT_SUCCESS(Status
)) goto Quickie
;
1771 ShortPath
= ShortPathBuffer
;
1773 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1774 if (PathLength
>= MAX_PATH
)
1776 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1780 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1784 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1788 if (!PathLength
) goto Quickie
;
1790 LongPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1791 ShortPathUni
.Buffer
= ShortPath
;
1792 ShortPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1794 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1795 if (!NT_SUCCESS(Status
))
1797 BaseSetLastNTError(Status
);
1801 Result
= ShortPathAnsi
.Length
;
1802 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1804 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1805 lpszShortPath
[Result
] = ANSI_NULL
;
1809 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1813 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1814 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1815 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1817 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1827 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1828 IN LPWSTR lpszShortPath
,
1831 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1837 BOOLEAN Found
= FALSE
;
1838 WIN32_FIND_DATAW FindFileData
;
1840 /* Initialize so Quickie knows there's nothing to do */
1841 Buffer
= Original
= NULL
;
1844 /* First check if the input path was obviously NULL */
1847 /* Fail the request */
1848 SetLastError(ERROR_INVALID_PARAMETER
);
1852 /* We will be touching removed, removable drives -- don't warn the user */
1853 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1855 /* Do a simple check to see if the path exists */
1856 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1858 /* Windows checks for an application compatibility flag to allow this */
1859 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1861 /* It doesn't, so fail */
1867 /* Now get a pointer to the actual path, skipping indicators */
1868 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1870 /* Is there any path or filename in there? */
1872 (*Path
== UNICODE_NULL
) ||
1873 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1875 /* There isn't, so the long path is simply the short path */
1876 ReturnLength
= wcslen(lpszLongPath
);
1878 /* Is there space for it? */
1879 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1881 /* Make sure the pointers aren't already the same */
1882 if (lpszLongPath
!= lpszShortPath
)
1884 /* They're not -- copy the short path into the long path */
1885 RtlMoveMemory(lpszShortPath
,
1887 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1892 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1898 /* We are still in the game -- compute the current size */
1899 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1900 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1901 if (!Original
) goto ErrorQuickie
;
1903 /* Make a copy of it */
1904 wcsncpy(Original
, lpszLongPath
, Length
);
1906 /* Compute the new first and last markers */
1907 First
= &Original
[First
- lpszLongPath
];
1908 Last
= &Original
[Last
- lpszLongPath
];
1910 /* Set the current destination pointer for a copy */
1911 Dst
= lpszShortPath
;
1914 * Windows allows the paths to overlap -- we have to be careful with this and
1915 * see if it's same to do so, and if not, allocate our own internal buffer
1916 * that we'll return at the end.
1918 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1920 if ((cchBuffer
) && (lpszShortPath
) &&
1921 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1922 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1924 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1925 if (!Buffer
) goto ErrorQuickie
;
1927 /* New destination */
1931 /* Prepare for the loop */
1936 /* Current delta in the loop */
1937 Length
= First
- Src
;
1939 /* Update the return length by it */
1940 ReturnLength
+= Length
;
1942 /* Is there a delta? If so, is there space and buffer for it? */
1943 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1945 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1949 /* "Terminate" this portion of the path's substring so we can do a find */
1951 *Last
= UNICODE_NULL
;
1952 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1955 /* This portion wasn't found, so fail */
1956 if (FindHandle
== INVALID_HANDLE_VALUE
)
1962 /* Close the find handle */
1963 FindClose(FindHandle
);
1965 /* Now check the length of the short name */
1966 Length
= wcslen(FindFileData
.cAlternateFileName
);
1969 /* This is our new first marker */
1970 First
= FindFileData
.cAlternateFileName
;
1974 /* Otherwise, the name is the delta between our current markers */
1975 Length
= Last
- First
;
1978 /* Update the return length with the short name length, if any */
1979 ReturnLength
+= Length
;
1981 /* Once again check for appropriate space and buffer */
1982 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1984 /* And do the copy if there is */
1985 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1989 /* Now update the source pointer */
1991 if (*Src
== UNICODE_NULL
) break;
1993 /* Are there more names in there? */
1994 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
1998 /* The loop is done, is there anything left? */
2001 /* Get the length of the straggling path */
2002 Length
= wcslen(Src
);
2003 ReturnLength
+= Length
;
2005 /* Once again check for appropriate space and buffer */
2006 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2008 /* And do the copy if there is -- accounting for NULL here */
2009 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2011 /* What about our buffer? */
2014 /* Copy it into the caller's long path */
2015 RtlMoveMemory(lpszShortPath
,
2017 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2022 /* Buffer is too small, let the caller know, making space for NULL */
2027 /* We're all done */
2031 /* This is the goto for memory failures */
2032 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2035 /* General function end: free memory, restore error mode, return length */
2036 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
2037 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
2038 SetErrorMode(ErrorMode
);
2039 return ReturnLength
;
2045 * NOTE: Windows returns a dos/short (8.3) path
2049 GetTempPathA(IN DWORD nBufferLength
,
2052 WCHAR BufferW
[MAX_PATH
];
2055 ret
= GetTempPathW(MAX_PATH
, BufferW
);
2061 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2065 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
2075 GetTempPathW(IN DWORD count
,
2078 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
2079 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
2080 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2081 WCHAR tmp_path
[MAX_PATH
];
2084 DPRINT("%u,%p\n", count
, path
);
2086 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
2087 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
2088 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
2089 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
2094 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2098 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, tmp_path
, NULL
);
2101 if (ret
> MAX_PATH
- 2)
2103 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2107 if (tmp_path
[ret
-1] != '\\')
2109 tmp_path
[ret
++] = '\\';
2110 tmp_path
[ret
] = '\0';
2113 ret
++; /* add space for terminating 0 */
2117 lstrcpynW(path
, tmp_path
, count
);
2119 ret
--; /* return length without 0 */
2121 path
[0] = 0; /* avoid returning ambiguous "X:" */
2124 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
2133 GetCurrentDirectoryA(IN DWORD nBufferLength
,
2136 ANSI_STRING AnsiString
;
2138 PUNICODE_STRING StaticString
;
2141 StaticString
= &NtCurrentTeb()->StaticUnicodeString
;
2143 MaxLength
= nBufferLength
;
2144 if (nBufferLength
>= UNICODE_STRING_MAX_BYTES
)
2146 MaxLength
= UNICODE_STRING_MAX_BYTES
- 1;
2149 StaticString
->Length
= RtlGetCurrentDirectory_U(StaticString
->MaximumLength
,
2150 StaticString
->Buffer
);
2151 Status
= RtlUnicodeToMultiByteSize(&nBufferLength
,
2152 StaticString
->Buffer
,
2153 StaticString
->Length
);
2154 if (!NT_SUCCESS(Status
))
2156 BaseSetLastNTError(Status
);
2160 if (MaxLength
<= nBufferLength
)
2162 return nBufferLength
+ 1;
2165 AnsiString
.Buffer
= lpBuffer
;
2166 AnsiString
.MaximumLength
= MaxLength
;
2167 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, StaticString
, FALSE
);
2168 if (!NT_SUCCESS(Status
))
2170 BaseSetLastNTError(Status
);
2174 return AnsiString
.Length
;
2182 GetCurrentDirectoryW(IN DWORD nBufferLength
,
2185 return RtlGetCurrentDirectory_U(nBufferLength
* sizeof(WCHAR
), lpBuffer
) / sizeof(WCHAR
);
2193 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
2195 PUNICODE_STRING DirName
;
2200 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2204 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
);
2205 if (!DirName
) return FALSE
;
2207 if (CheckForSameCurdir(DirName
)) return TRUE
;
2209 Status
= RtlSetCurrentDirectory_U(DirName
);
2210 if (NT_SUCCESS(Status
)) return TRUE
;
2212 if ((*DirName
->Buffer
!= L
'"') || (DirName
->Length
<= 2))
2214 BaseSetLastNTError(Status
);
2218 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
+ 1);
2219 if (!DirName
) return FALSE
;
2221 Status
= RtlSetCurrentDirectory_U(DirName
);
2222 if (!NT_SUCCESS(Status
))
2224 BaseSetLastNTError(Status
);
2236 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
2239 UNICODE_STRING UnicodeString
;
2243 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2247 Status
= RtlInitUnicodeStringEx(&UnicodeString
, lpPathName
);
2248 if (NT_SUCCESS(Status
))
2250 if (!CheckForSameCurdir(&UnicodeString
))
2252 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
2256 if (!NT_SUCCESS(Status
))
2258 BaseSetLastNTError(Status
);
2270 GetSystemDirectoryA(IN LPSTR lpBuffer
,
2273 ANSI_STRING AnsiString
;
2277 /* Get the correct size of the Unicode Base directory */
2278 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2279 BaseWindowsSystemDirectory
.Buffer
,
2280 BaseWindowsSystemDirectory
.MaximumLength
);
2281 if (!NT_SUCCESS(Status
)) return 0;
2283 if (uSize
< AnsiLength
) return AnsiLength
;
2285 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2287 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2288 &BaseWindowsSystemDirectory
,
2290 if (!NT_SUCCESS(Status
)) return 0;
2292 return AnsiString
.Length
;
2300 GetSystemDirectoryW(IN LPWSTR lpBuffer
,
2305 ReturnLength
= BaseWindowsSystemDirectory
.MaximumLength
;
2306 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2308 RtlCopyMemory(lpBuffer
,
2309 BaseWindowsSystemDirectory
.Buffer
,
2310 BaseWindowsSystemDirectory
.Length
);
2311 lpBuffer
[BaseWindowsSystemDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2313 ReturnLength
= BaseWindowsSystemDirectory
.Length
;
2316 return ReturnLength
/ sizeof(WCHAR
);
2324 GetWindowsDirectoryA(IN LPSTR lpBuffer
,
2327 /* Is this a TS installation? */
2328 if (gpTermsrvGetWindowsDirectoryA
) UNIMPLEMENTED
;
2330 /* Otherwise, call the System API */
2331 return GetSystemWindowsDirectoryA(lpBuffer
, uSize
);
2339 GetWindowsDirectoryW(IN LPWSTR lpBuffer
,
2342 /* Is this a TS installation? */
2343 if (gpTermsrvGetWindowsDirectoryW
) UNIMPLEMENTED
;
2345 /* Otherwise, call the System API */
2346 return GetSystemWindowsDirectoryW(lpBuffer
, uSize
);
2354 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer
,
2357 ANSI_STRING AnsiString
;
2361 /* Get the correct size of the Unicode Base directory */
2362 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2363 BaseWindowsDirectory
.Buffer
,
2364 BaseWindowsDirectory
.MaximumLength
);
2365 if (!NT_SUCCESS(Status
)) return 0;
2367 if (uSize
< AnsiLength
) return AnsiLength
;
2369 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2371 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2372 &BaseWindowsDirectory
,
2374 if (!NT_SUCCESS(Status
)) return 0;
2376 return AnsiString
.Length
;
2384 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer
,
2389 ReturnLength
= BaseWindowsDirectory
.MaximumLength
;
2390 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2392 RtlCopyMemory(lpBuffer
,
2393 BaseWindowsDirectory
.Buffer
,
2394 BaseWindowsDirectory
.Length
);
2395 lpBuffer
[BaseWindowsDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2397 ReturnLength
= BaseWindowsDirectory
.Length
;
2400 return ReturnLength
/ sizeof(WCHAR
);
2408 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer
,
2415 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2425 GetSystemWow64DirectoryA(IN LPSTR lpBuffer
,
2432 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);