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 */
244 /* We do not. Do we have the LDR_ENTRY for the executable? */
245 if (!BasepExeLdrEntry
)
247 /* We do not. Grab it */
248 LdrEnumerateLoadedModules(0,
249 BasepLocateExeLdrEntry
,
250 NtCurrentPeb()->ImageBaseAddress
);
253 /* Now do we have it? */
254 if (BasepExeLdrEntry
)
256 /* Yes, so read the name out of it */
257 AppName
= BasepExeLdrEntry
->FullDllName
.Buffer
;
260 /* Find out where the app name ends, to get only the directory */
261 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
264 /* So, do we have an application name and its directory? */
265 if ((AppName
) && (AppNameEnd
))
267 /* Add the size of the app's directory, plus the separator */
268 PathLengthInBytes
+= ((AppNameEnd
- AppName
) * sizeof(WCHAR
)) + sizeof(L
';');
277 /* Bam, all done, we now have the final path size */
278 ASSERT(PathLengthInBytes
> 0);
279 ASSERT(!(PathLengthInBytes
& 1));
281 /* Allocate the buffer to hold it */
282 PathBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes
);
286 Status
= STATUS_NO_MEMORY
;
290 /* Now we loop again, this time to copy the data */
291 PathCurrent
= PathBuffer
;
292 for (Order
= PathOrder
; *Order
!= BaseSearchPathInvalid
; Order
++) {
295 /* Add the DLL path */
296 case BaseSearchPathDll
:
297 if (BaseDllDirectory
.Length
)
299 /* Copy it in the buffer, ASSERT there's enough space */
300 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + BaseDllDirectory
.Length
) <= PathLengthInBytes
);
301 RtlCopyMemory(PathCurrent
,
302 BaseDllDirectory
.Buffer
,
303 BaseDllDirectory
.Length
);
305 /* Update the current pointer, add a separator */
306 PathCurrent
+= (BaseDllDirectory
.Length
/ sizeof(WCHAR
));
307 *PathCurrent
++ = ';';
311 /* Add the current application path */
312 case BaseSearchPathApp
:
313 if ((AppName
) && (AppNameEnd
))
315 /* Copy it in the buffer, ASSERT there's enough space */
316 ASSERT(((PathCurrent
- PathBuffer
+ 1 + (AppNameEnd
- AppName
)) * sizeof(WCHAR
)) <= PathLengthInBytes
);
317 RtlCopyMemory(PathCurrent
,
319 (AppNameEnd
- AppName
) * sizeof(WCHAR
));
321 /* Update the current pointer, add a separator */
322 PathCurrent
+= AppNameEnd
- AppName
;
323 *PathCurrent
++ = ';';
327 /* Add the default search path */
328 case BaseSearchPathDefault
:
329 /* Copy it in the buffer, ASSERT there's enough space */
330 ASSERT((((PathCurrent
- PathBuffer
) * sizeof(WCHAR
)) + BaseDefaultPath
.Length
) <= PathLengthInBytes
);
331 RtlCopyMemory(PathCurrent
, BaseDefaultPath
.Buffer
, BaseDefaultPath
.Length
);
333 /* Update the current pointer. The default path already has a ";" */
334 PathCurrent
+= (BaseDefaultPath
.Length
/ sizeof(WCHAR
));
337 /* Add the path in the PATH environment variable */
338 case BaseSearchPathEnv
:
341 /* Copy it in the buffer, ASSERT there's enough space */
342 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + EnvPath
.Length
) <= PathLengthInBytes
);
343 RtlCopyMemory(PathCurrent
, EnvPath
.Buffer
, EnvPath
.Length
);
345 /* Update the current pointer, add a separator */
346 PathCurrent
+= (EnvPath
.Length
/ sizeof(WCHAR
));
347 *PathCurrent
++ = ';';
351 /* Add the current dierctory */
352 case BaseSearchPathCurrent
:
354 /* Copy it in the buffer, ASSERT there's enough space */
355 ASSERT(((PathCurrent
- PathBuffer
+ 2) * sizeof(WCHAR
)) <= PathLengthInBytes
);
356 *PathCurrent
++ = '.';
358 /* Add the path separator */
359 *PathCurrent
++ = ';';
367 /* Everything should've perfectly fit in there */
368 ASSERT((PathCurrent
- PathBuffer
) * sizeof(WCHAR
) == PathLengthInBytes
);
369 ASSERT(PathCurrent
> PathBuffer
);
371 /* Terminate the whole thing */
372 PathCurrent
[-1] = UNICODE_NULL
;
375 /* Exit path: free our buffers */
376 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
379 /* This only gets freed in the failure path, since caller wants it */
380 if (!NT_SUCCESS(Status
))
382 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer
);
387 /* Return the path! */
393 BaseComputeProcessSearchPath(VOID
)
395 DPRINT("Computing Process Search path\n");
397 /* Compute the path using default process order */
398 return BasepComputeProcessPath(BaseProcessOrder
, NULL
, NULL
);
403 BaseComputeProcessExePath(IN LPWSTR FullPath
)
405 PBASE_SEARCH_PATH_TYPE PathOrder
;
406 DPRINT1("Computing EXE path: %wZ\n", FullPath
);
408 /* Check if we should use the current directory */
409 PathOrder
= NeedCurrentDirectoryForExePathW(FullPath
) ?
410 BaseProcessOrder
: BaseProcessOrderNoCurrent
;
412 /* And now compute the path */
413 return BasepComputeProcessPath(PathOrder
, NULL
, NULL
);
418 BaseComputeProcessDllPath(IN LPWSTR FullPath
,
419 IN PVOID Environment
)
421 LPWSTR DllPath
= NULL
;
422 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
423 UNICODE_STRING ValueName
= RTL_CONSTANT_STRING(L
"SafeDllSearchMode");
424 OBJECT_ATTRIBUTES ObjectAttributes
= RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
425 KEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
429 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement
, OldCurrentDirPlacement
;
431 /* Acquire DLL directory lock */
432 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
434 /* Check if we have a base dll directory */
435 if (BaseDllDirectory
.Buffer
)
437 /* Then compute the process path using DLL order (without curdir) */
438 DllPath
= BasepComputeProcessPath(BaseDllOrderNoCurrent
, FullPath
, Environment
);
440 /* Release DLL directory lock */
441 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
443 /* Return dll path */
447 /* Release DLL directory lock */
448 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
450 /* Read the current placement */
451 CurrentDirPlacement
= BasepDllCurrentDirPlacement
;
452 if (CurrentDirPlacement
== BaseCurrentDirPlacementInvalid
)
454 /* Open the configuration key */
455 Status
= NtOpenKey(&KeyHandle
, KEY_QUERY_VALUE
, &ObjectAttributes
);
456 if (NT_SUCCESS(Status
))
458 /* Query if safe search is enabled */
459 Status
= NtQueryValueKey(KeyHandle
,
461 KeyValuePartialInformation
,
465 if (NT_SUCCESS(Status
))
467 /* Read the value if the size is OK */
468 if (ResultLength
== sizeof(PartialInfo
))
470 CurrentDirPlacement
= *(PULONG
)PartialInfo
.Data
;
474 /* Close the handle */
477 /* Validate the registry value */
478 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
479 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
481 /* Default to safe search */
482 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
486 /* Update the placement and read the old one */
487 OldCurrentDirPlacement
= InterlockedCompareExchange((PLONG
)&BasepDllCurrentDirPlacement
,
489 BaseCurrentDirPlacementInvalid
);
490 if (OldCurrentDirPlacement
!= BaseCurrentDirPlacementInvalid
)
492 /* If there already was a placement, use it */
493 CurrentDirPlacement
= OldCurrentDirPlacement
;
497 /* Check if the placement is invalid or not set */
498 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
499 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
501 /* Default to safe search */
502 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
505 /* Compute the process path using either normal or safe search */
506 DllPath
= BasepComputeProcessPath(BaseDllOrderCurrent
[CurrentDirPlacement
],
510 /* Return dll path */
516 CheckForSameCurdir(IN PUNICODE_STRING DirName
)
518 PUNICODE_STRING CurDir
;
521 UNICODE_STRING CurDirCopy
;
523 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
525 CurLength
= CurDir
->Length
;
526 if (CurDir
->Length
<= 6)
528 if (CurLength
!= DirName
->Length
) return FALSE
;
532 if ((CurLength
- 2) != DirName
->Length
) return FALSE
;
537 CurDirCopy
= *CurDir
;
538 if (CurDirCopy
.Length
> 6) CurDirCopy
.Length
-= 2;
542 if (RtlEqualUnicodeString(&CurDirCopy
, DirName
, TRUE
)) Result
= TRUE
;
550 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
551 * identical (other than the Rtl can optionally check for spaces), however the
552 * Rtl will always convert to OEM, while kernel32 has two possible file modes
553 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
554 * the correct compatible results
558 IsShortName_U(IN PWCHAR Name
,
561 BOOLEAN HasExtension
;
564 UNICODE_STRING UnicodeName
;
565 ANSI_STRING AnsiName
;
567 CHAR AnsiBuffer
[MAX_PATH
];
570 /* What do you think 8.3 means? */
571 if (Length
> 12) return FALSE
;
573 /* Sure, any emtpy name is a short name */
574 if (!Length
) return TRUE
;
576 /* This could be . or .. or something else */
579 /* Which one is it */
580 if ((Length
== 1) || ((Length
== 2) && *(Name
+ 1) == L
'.'))
582 /* . or .., this is good */
586 /* Some other bizare dot-based name, not good */
590 /* Initialize our two strings */
591 RtlInitEmptyAnsiString(&AnsiName
, AnsiBuffer
, MAX_PATH
);
592 RtlInitEmptyUnicodeString(&UnicodeName
, Name
, Length
* sizeof(WCHAR
));
593 UnicodeName
.Length
= UnicodeName
.MaximumLength
;
595 /* Now do the conversion */
596 Status
= BasepUnicodeStringTo8BitString(&AnsiName
, &UnicodeName
, FALSE
);
597 if (!NT_SUCCESS(Status
)) return FALSE
;
599 /* Now we loop the name */
600 HasExtension
= FALSE
;
601 for (i
= 0, Dots
= Length
- 1; i
< AnsiName
.Length
; i
++, Dots
--)
603 /* Read the current byte */
604 c
= AnsiName
.Buffer
[i
];
607 if (IsDBCSLeadByte(c
))
609 /* If we're near the end of the string, we can't allow a DBCS */
610 if ((!(HasExtension
) && (i
>= 7)) || (i
== AnsiName
.Length
- 1))
615 /* Otherwise we skip over it */
619 /* Check for illegal characters */
620 if ((c
> 0x7F) || (IllegalMask
[c
/ 32] & (1 << (c
% 32))))
625 /* Check if this is perhaps an extension? */
628 /* Unless the extension is too large or there's more than one */
629 if ((HasExtension
) || (Dots
> 3)) return FALSE
;
631 /* This looks like an extension */
635 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
636 if ((i
>= 8) && !(HasExtension
)) return FALSE
;
639 /* You survived the loop, this is a good short name */
645 IsLongName_U(IN PWCHAR FileName
,
648 BOOLEAN HasExtension
;
651 /* More than 8.3, any combination of dots, and NULL names are all long */
652 if (!(Length
) || (Length
> 12) || (*FileName
== L
'.')) return TRUE
;
654 /* Otherwise, initialize our scanning loop */
655 HasExtension
= FALSE
;
656 for (i
= 0, Dots
= Length
- 1; i
< Length
; i
++, Dots
--)
658 /* Check if this could be an extension */
659 if (FileName
[i
] == L
'.')
661 /* Unlike the short case, we WANT more than one extension, or a long one */
662 if ((HasExtension
) || (Dots
> 3))
669 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
670 if ((i
>= 8) && (!HasExtension
)) return TRUE
;
673 /* The name *seems* to conform to 8.3 */
679 FindLFNorSFN_U(IN PWCHAR Path
,
689 /* Loop while there is something in the path */
692 /* Loop within the path skipping slashes */
693 while ((*Path
== L
'\\') || (*Path
== L
'/')) Path
++;
695 /* Make sure there's something after the slashes too! */
696 if (*Path
== UNICODE_NULL
) break;
698 /* Now skip past the file name until we get to the first slash */
700 while ((*p
) && ((*p
!= L
'\\') && (*p
!= L
'/'))) p
++;
702 /* Whatever is in between those two is now the file name length */
706 * Check if it is valid
707 * Note that !IsShortName != IsLongName, these two functions simply help
708 * us determine if a conversion is necessary or not.
709 * "Found" really means: "Is a conversion necessary?", hence the "!"
711 Found
= UseShort
? !IsShortName_U(Path
, Length
) : !IsLongName_U(Path
, Length
);
714 /* It is! did the caller request to know the markers? */
715 if ((First
) && (Last
))
724 /* Is there anything else following this sub-path/filename? */
725 if (*p
== UNICODE_NULL
) break;
727 /* Yes, keep going */
731 /* Return if anything was found and valid */
737 SkipPathTypeIndicator_U(IN LPWSTR Path
)
742 /* Check what kind of path this is and how many slashes to skip */
743 switch (RtlDetermineDosPathNameType_U(Path
))
745 case RtlPathTypeDriveAbsolute
:
748 case RtlPathTypeDriveRelative
:
751 case RtlPathTypeRooted
:
754 case RtlPathTypeRelative
:
757 case RtlPathTypeRootLocalDevice
:
761 case RtlPathTypeUncAbsolute
:
762 case RtlPathTypeLocalDevice
:
764 /* Keep going until we bypass the path indicators */
765 for (ReturnPath
= Path
+ 2, i
= 2; (i
> 0) && (*ReturnPath
); ReturnPath
++)
767 /* We look for 2 slashes, so keep at it until we find them */
768 if ((*ReturnPath
== L
'\\') || (*ReturnPath
== L
'/')) i
--;
777 BasepIsCurDirAllowedForPlainExeNames(VOID
)
780 UNICODE_STRING EmptyString
;
782 RtlInitEmptyUnicodeString(&EmptyString
, NULL
, 0);
783 Status
= RtlQueryEnvironmentVariable_U(NULL
,
784 &NoDefaultCurrentDirectoryInExePath
,
786 return !NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
;
789 /* PUBLIC FUNCTIONS ***********************************************************/
796 SetDllDirectoryW(IN LPCWSTR lpPathName
)
798 UNICODE_STRING OldDirectory
, DllDirectory
;
802 if (wcschr(lpPathName
, L
';'))
804 SetLastError(ERROR_INVALID_PARAMETER
);
807 if (!RtlCreateUnicodeString(&DllDirectory
, lpPathName
))
809 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
815 RtlInitUnicodeString(&DllDirectory
, NULL
);
818 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
820 OldDirectory
= BaseDllDirectory
;
821 BaseDllDirectory
= DllDirectory
;
823 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
825 RtlFreeUnicodeString(&OldDirectory
);
834 SetDllDirectoryA(IN LPCSTR lpPathName
)
836 ANSI_STRING AnsiDllDirectory
;
837 UNICODE_STRING OldDirectory
, DllDirectory
;
842 if (strchr(lpPathName
, ';'))
844 SetLastError(ERROR_INVALID_PARAMETER
);
848 Status
= RtlInitAnsiStringEx(&AnsiDllDirectory
, lpPathName
);
849 if (NT_SUCCESS(Status
))
851 Status
= Basep8BitStringToUnicodeString(&DllDirectory
,
856 if (!NT_SUCCESS(Status
))
858 BaseSetLastNTError(Status
);
864 RtlInitUnicodeString(&DllDirectory
, NULL
);
867 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
869 OldDirectory
= BaseDllDirectory
;
870 BaseDllDirectory
= DllDirectory
;
872 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
874 RtlFreeUnicodeString(&OldDirectory
);
883 GetDllDirectoryW(IN DWORD nBufferLength
,
888 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
890 if ((nBufferLength
* sizeof(WCHAR
)) > BaseDllDirectory
.Length
)
892 RtlCopyMemory(lpBuffer
, BaseDllDirectory
.Buffer
, BaseDllDirectory
.Length
);
893 Length
= BaseDllDirectory
.Length
/ sizeof(WCHAR
);
894 lpBuffer
[Length
] = UNICODE_NULL
;
898 Length
= (BaseDllDirectory
.Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
);
899 if (lpBuffer
) *lpBuffer
= UNICODE_NULL
;
902 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
911 GetDllDirectoryA(IN DWORD nBufferLength
,
915 ANSI_STRING AnsiDllDirectory
;
918 RtlInitEmptyAnsiString(&AnsiDllDirectory
, lpBuffer
, nBufferLength
);
920 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
922 Length
= BasepUnicodeStringTo8BitSize(&BaseDllDirectory
);
923 if (Length
> nBufferLength
)
925 Status
= STATUS_SUCCESS
;
926 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
931 Status
= BasepUnicodeStringTo8BitString(&AnsiDllDirectory
,
936 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
938 if (!NT_SUCCESS(Status
))
940 BaseSetLastNTError(Status
);
942 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
953 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName
)
955 if (wcschr(ExeName
, L
'\\')) return TRUE
;
957 return BasepIsCurDirAllowedForPlainExeNames();
965 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName
)
967 if (strchr(ExeName
, '\\')) return TRUE
;
969 return BasepIsCurDirAllowedForPlainExeNames();
975 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
976 * beyond what you would usually expect. There are two main reasons:
978 * First, these APIs are subject to the ANSI/OEM File API selection status that
979 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
981 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
982 * length of the paths in "ANSI" by dividing their internal Wide character count
983 * by two... this is usually correct when dealing with pure-ASCII codepages but
984 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
985 * for CJK, for example.
989 GetFullPathNameA(IN LPCSTR lpFileName
,
990 IN DWORD nBufferLength
,
992 IN LPSTR
*lpFilePart
)
996 ULONG PathSize
, FilePartSize
;
997 ANSI_STRING AnsiString
;
998 UNICODE_STRING FileNameString
, UniString
;
999 PWCHAR LocalFilePart
;
1002 /* If the caller wants filepart, use a local wide buffer since this is A */
1003 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1005 /* Initialize for Quickie */
1006 FilePartSize
= PathSize
= 0;
1007 FileNameString
.Buffer
= NULL
;
1009 /* First get our string in Unicode */
1010 Status
= Basep8BitStringToDynamicUnicodeString(&FileNameString
, lpFileName
);
1011 if (!NT_SUCCESS(Status
)) goto Quickie
;
1013 /* Allocate a buffer to hold teh path name */
1014 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1016 MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1019 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES
);
1023 /* Call into RTL to get the full Unicode path name */
1024 PathSize
= RtlGetFullPathName_U(FileNameString
.Buffer
,
1025 MAX_PATH
* sizeof(WCHAR
),
1028 if (PathSize
<= (MAX_PATH
* sizeof(WCHAR
)))
1030 /* The buffer will fit, get the real ANSI string size now */
1031 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
);
1032 if (NT_SUCCESS(Status
))
1034 /* Now check if the user wanted file part size as well */
1035 if ((PathSize
) && (lpFilePart
) && (LocalFilePart
))
1037 /* Yep, so in this case get the length of the file part too */
1038 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1040 (LocalFilePart
- Buffer
) *
1042 if (!NT_SUCCESS(Status
))
1044 /* We failed to do that, so fail the whole call */
1045 BaseSetLastNTError(Status
);
1053 /* Reset the path size since the buffer is not large enough */
1057 /* Either no path, or local buffer was too small, enter failure code */
1058 if (!PathSize
) goto Quickie
;
1060 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
1061 if (PathSize
>= nBufferLength
)
1067 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
1068 RtlInitUnicodeString(&UniString
, Buffer
);
1069 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, &UniString
, TRUE
);
1070 if (!NT_SUCCESS(Status
))
1072 /* Final conversion failed, fail the call */
1073 BaseSetLastNTError(Status
);
1078 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
1079 RtlCopyMemory(lpBuffer
, AnsiString
.Buffer
, PathSize
+ 1);
1080 RtlFreeAnsiString(&AnsiString
);
1082 /* And finally, did the caller request file part information? */
1085 /* Use the size we computed earlier and add it to the buffer */
1086 *lpFilePart
= LocalFilePart
? &lpBuffer
[FilePartSize
] : 0;
1091 /* Cleanup and return the path size */
1092 if (FileNameString
.Buffer
) RtlFreeUnicodeString(&FileNameString
);
1093 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1102 GetFullPathNameW(IN LPCWSTR lpFileName
,
1103 IN DWORD nBufferLength
,
1105 OUT LPWSTR
*lpFilePart
)
1107 /* Call Rtl to do the work */
1108 return RtlGetFullPathName_U((LPWSTR
)lpFileName
,
1109 nBufferLength
* sizeof(WCHAR
),
1111 lpFilePart
) / sizeof(WCHAR
);
1119 SearchPathA(IN LPCSTR lpPath
,
1120 IN LPCSTR lpFileName
,
1121 IN LPCSTR lpExtension
,
1122 IN DWORD nBufferLength
,
1124 OUT LPSTR
*lpFilePart
)
1126 PUNICODE_STRING FileNameString
;
1127 UNICODE_STRING PathString
, ExtensionString
;
1129 ULONG PathSize
, FilePartSize
, AnsiLength
;
1130 PWCHAR LocalFilePart
, Buffer
;
1133 /* If the caller wants filepart, use a local wide buffer since this is A */
1134 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1136 /* Initialize stuff for Quickie */
1139 ExtensionString
.Buffer
= PathString
.Buffer
= NULL
;
1141 /* Get the UNICODE_STRING file name */
1142 FileNameString
= Basep8BitStringToStaticUnicodeString(lpFileName
);
1143 if (!FileNameString
) return 0;
1145 /* Did the caller specify an extension */
1148 /* Yup, convert it into UNICODE_STRING */
1149 Status
= Basep8BitStringToDynamicUnicodeString(&ExtensionString
,
1151 if (!NT_SUCCESS(Status
)) goto Quickie
;
1154 /* Did the caller specify a path */
1157 /* Yup, convert it into UNICODE_STRING */
1158 Status
= Basep8BitStringToDynamicUnicodeString(&PathString
, lpPath
);
1159 if (!NT_SUCCESS(Status
)) goto Quickie
;
1162 /* Allocate our output buffer */
1163 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength
* sizeof(WCHAR
));
1166 /* It failed, bail out */
1167 BaseSetLastNTError(STATUS_NO_MEMORY
);
1171 /* Now run the Wide search with the input buffer lengths */
1172 PathSize
= SearchPathW(PathString
.Buffer
,
1173 FileNameString
->Buffer
,
1174 ExtensionString
.Buffer
,
1178 if (PathSize
<= nBufferLength
)
1180 /* It fits, but is it empty? If so, bail out */
1181 if (!PathSize
) goto Quickie
;
1183 /* The length above is inexact, we need it in ANSI */
1184 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
, Buffer
, PathSize
* sizeof(WCHAR
));
1185 if (!NT_SUCCESS(Status
))
1187 /* Conversion failed, fail the call */
1189 BaseSetLastNTError(Status
);
1193 /* If the correct ANSI size is too big, return requird length plus a NULL */
1194 if (AnsiLength
>= nBufferLength
)
1196 PathSize
= AnsiLength
+ 1;
1200 /* Now apply the final conversion to ANSI */
1201 Status
= RtlUnicodeToMultiByteN(lpBuffer
,
1205 PathSize
* sizeof(WCHAR
));
1206 if (!NT_SUCCESS(Status
))
1208 /* Conversion failed, fail the whole call */
1210 BaseSetLastNTError(STATUS_NO_MEMORY
);
1214 /* NULL-terminate and return the real ANSI length */
1215 lpBuffer
[AnsiLength
] = ANSI_NULL
;
1216 PathSize
= AnsiLength
;
1218 /* Now check if the user wanted file part size as well */
1221 /* If we didn't get a file part, clear the caller's */
1228 /* Yep, so in this case get the length of the file part too */
1229 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1231 (LocalFilePart
- Buffer
) *
1233 if (!NT_SUCCESS(Status
))
1235 /* We failed to do that, so fail the whole call */
1236 BaseSetLastNTError(Status
);
1240 /* Return the file part buffer */
1241 *lpFilePart
= lpBuffer
+ FilePartSize
;
1247 /* Our initial buffer guess was too small, allocate a bigger one */
1248 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1249 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize
* sizeof(WCHAR
));
1252 /* Out of memory, fail everything */
1253 BaseSetLastNTError(STATUS_NO_MEMORY
);
1257 /* Do the search again -- it will fail, we just want the path size */
1258 PathSize
= SearchPathW(PathString
.Buffer
,
1259 FileNameString
->Buffer
,
1260 ExtensionString
.Buffer
,
1264 if (!PathSize
) goto Quickie
;
1266 /* Convert it to a correct size */
1267 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
* sizeof(WCHAR
));
1268 if (NT_SUCCESS(Status
))
1270 /* Make space for the NULL-char */
1275 /* Conversion failed for some reason, fail the call */
1276 BaseSetLastNTError(Status
);
1282 /* Cleanup/complete path */
1283 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1284 if (ExtensionString
.Buffer
) RtlFreeUnicodeString(&ExtensionString
);
1285 if (PathString
.Buffer
) RtlFreeUnicodeString(&PathString
);
1294 SearchPathW(IN LPCWSTR lpPath
,
1295 IN LPCWSTR lpFileName
,
1296 IN LPCWSTR lpExtension
,
1297 IN DWORD nBufferLength
,
1299 OUT LPWSTR
*lpFilePart
)
1301 UNICODE_STRING FileNameString
, ExtensionString
, PathString
, CallerBuffer
;
1302 ULONG Flags
, LengthNeeded
, FilePartSize
;
1306 /* Default flags for RtlDosSearchPath_Ustr */
1309 /* Clear file part in case we fail */
1310 if (lpFilePart
) *lpFilePart
= NULL
;
1312 /* Initialize path buffer for free later */
1313 PathString
.Buffer
= NULL
;
1315 /* Convert filename to a unicode string and eliminate trailing spaces */
1316 RtlInitUnicodeString(&FileNameString
, lpFileName
);
1317 while ((FileNameString
.Length
>= sizeof(WCHAR
)) &&
1318 (FileNameString
.Buffer
[(FileNameString
.Length
/ sizeof(WCHAR
)) - 1] == L
' '))
1320 FileNameString
.Length
-= sizeof(WCHAR
);
1323 /* Was it all just spaces? */
1324 if (!FileNameString
.Length
)
1327 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
1331 /* Convert extension to a unicode string */
1332 RtlInitUnicodeString(&ExtensionString
, lpExtension
);
1334 /* Check if the user sent a path */
1337 /* Convert it to a unicode string too */
1338 Status
= RtlInitUnicodeStringEx(&PathString
, lpPath
);
1339 if (NT_ERROR(Status
))
1341 /* Fail if it was too long */
1342 BaseSetLastNTError(Status
);
1348 /* A path wasn't sent, so compute it ourselves */
1349 PathString
.Buffer
= BaseComputeProcessSearchPath();
1350 if (!PathString
.Buffer
)
1352 /* Fail if we couldn't compute it */
1353 BaseSetLastNTError(STATUS_NO_MEMORY
);
1357 /* See how big the computed path is */
1358 LengthNeeded
= lstrlenW(PathString
.Buffer
);
1359 if (LengthNeeded
> UNICODE_STRING_MAX_CHARS
)
1361 /* Fail if it's too long */
1362 BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1366 /* Set the path size now that we have it */
1367 PathString
.MaximumLength
= PathString
.Length
= LengthNeeded
* sizeof(WCHAR
);
1369 /* Request SxS isolation from RtlDosSearchPath_Ustr */
1373 /* Create the string that describes the output buffer from the caller */
1374 CallerBuffer
.Length
= 0;
1375 CallerBuffer
.Buffer
= lpBuffer
;
1377 /* How much space does the caller have? */
1378 if (nBufferLength
<= UNICODE_STRING_MAX_CHARS
)
1380 /* Add it into the string */
1381 CallerBuffer
.MaximumLength
= nBufferLength
* sizeof(WCHAR
);
1385 /* Caller wants too much, limit it to the maximum length of a string */
1386 CallerBuffer
.MaximumLength
= UNICODE_STRING_MAX_BYTES
;
1389 /* Call Rtl to do the work */
1390 Status
= RtlDosSearchPath_Ustr(Flags
,
1399 if (NT_ERROR(Status
))
1401 /* Check for unusual status codes */
1402 if ((Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
1404 /* Print them out since maybe an app needs fixing */
1405 DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
1409 DbgPrint(" Path = %wZ\n", &PathString
);
1412 /* Check if the failure was due to a small buffer */
1413 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1415 /* Check if the length was actually too big for Rtl to work with */
1416 Result
= LengthNeeded
/ sizeof(WCHAR
);
1417 if (Result
> 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1421 /* Some other error, set the error code */
1422 BaseSetLastNTError(Status
);
1427 /* It worked! Write the file part now */
1428 if (lpFilePart
) *lpFilePart
= &lpBuffer
[FilePartSize
];
1430 /* Convert the final result length */
1431 Result
= CallerBuffer
.Length
/ sizeof(WCHAR
);
1435 /* Check if there was a dynamic path stirng to free */
1436 if ((PathString
.Buffer
!= lpPath
) && (PathString
.Buffer
))
1439 RtlFreeHeap(RtlGetProcessHeap(), 0, PathString
.Buffer
);
1442 /* Return the final result lenght */
1451 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
1452 IN LPWSTR lpszLongPath
,
1455 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1461 BOOLEAN Found
= FALSE
;
1462 WIN32_FIND_DATAW FindFileData
;
1464 /* Initialize so Quickie knows there's nothing to do */
1465 Buffer
= Original
= NULL
;
1468 /* First check if the input path was obviously NULL */
1471 /* Fail the request */
1472 SetLastError(ERROR_INVALID_PARAMETER
);
1476 /* We will be touching removed, removable drives -- don't warn the user */
1477 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1479 /* Do a simple check to see if the path exists */
1480 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
1482 /* It doesn't, so fail */
1487 /* Now get a pointer to the actual path, skipping indicators */
1488 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
1490 /* Is there any path or filename in there? */
1492 (*Path
== UNICODE_NULL
) ||
1493 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
1495 /* There isn't, so the long path is simply the short path */
1496 ReturnLength
= wcslen(lpszShortPath
);
1498 /* Is there space for it? */
1499 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1501 /* Make sure the pointers aren't already the same */
1502 if (lpszLongPath
!= lpszShortPath
)
1504 /* They're not -- copy the short path into the long path */
1505 RtlMoveMemory(lpszLongPath
,
1507 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1512 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1518 /* We are still in the game -- compute the current size */
1519 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
1520 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1521 if (!Original
) goto ErrorQuickie
;
1523 /* Make a copy of it */
1524 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
1526 /* Compute the new first and last markers */
1527 First
= &Original
[First
- lpszShortPath
];
1528 Last
= &Original
[Last
- lpszShortPath
];
1530 /* Set the current destination pointer for a copy */
1534 * Windows allows the paths to overlap -- we have to be careful with this and
1535 * see if it's same to do so, and if not, allocate our own internal buffer
1536 * that we'll return at the end.
1538 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1540 if ((cchBuffer
) && (lpszLongPath
) &&
1541 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
1542 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
1544 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1545 if (!Buffer
) goto ErrorQuickie
;
1547 /* New destination */
1551 /* Prepare for the loop */
1556 /* Current delta in the loop */
1557 Length
= First
- Src
;
1559 /* Update the return length by it */
1560 ReturnLength
+= Length
;
1562 /* Is there a delta? If so, is there space and buffer for it? */
1563 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1565 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1569 /* "Terminate" this portion of the path's substring so we can do a find */
1571 *Last
= UNICODE_NULL
;
1572 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1575 /* This portion wasn't found, so fail */
1576 if (FindHandle
== INVALID_HANDLE_VALUE
)
1582 /* Close the find handle */
1583 FindClose(FindHandle
);
1585 /* Now check the length of the long name */
1586 Length
= wcslen(FindFileData
.cFileName
);
1589 /* This is our new first marker */
1590 First
= FindFileData
.cFileName
;
1594 /* Otherwise, the name is the delta between our current markers */
1595 Length
= Last
- First
;
1598 /* Update the return length with the short name length, if any */
1599 ReturnLength
+= Length
;
1601 /* Once again check for appropriate space and buffer */
1602 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1604 /* And do the copy if there is */
1605 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1609 /* Now update the source pointer */
1611 if (*Src
== UNICODE_NULL
) break;
1613 /* Are there more names in there? */
1614 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1618 /* The loop is done, is there anything left? */
1621 /* Get the length of the straggling path */
1622 Length
= wcslen(Src
);
1623 ReturnLength
+= Length
;
1625 /* Once again check for appropriate space and buffer */
1626 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1628 /* And do the copy if there is -- accounting for NULL here */
1629 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1631 /* What about our buffer? */
1634 /* Copy it into the caller's long path */
1635 RtlMoveMemory(lpszLongPath
,
1637 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1642 /* Buffer is too small, let the caller know, making space for NULL */
1647 /* We're all done */
1651 /* This is the goto for memory failures */
1652 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1655 /* General function end: free memory, restore error mode, return length */
1656 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1657 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1658 SetErrorMode(ErrorMode
);
1659 return ReturnLength
;
1667 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1668 IN LPSTR lpszLongPath
,
1671 ULONG Result
, PathLength
;
1674 UNICODE_STRING LongPathUni
, ShortPathUni
;
1675 ANSI_STRING LongPathAnsi
;
1676 WCHAR LongPathBuffer
[MAX_PATH
];
1679 LongPathAnsi
.Buffer
= NULL
;
1680 ShortPathUni
.Buffer
= NULL
;
1685 SetLastError(ERROR_INVALID_PARAMETER
);
1689 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1690 if (!NT_SUCCESS(Status
)) goto Quickie
;
1692 LongPath
= LongPathBuffer
;
1694 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1695 if (PathLength
>= MAX_PATH
)
1697 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1701 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1705 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1709 if (!PathLength
) goto Quickie
;
1711 ShortPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1712 LongPathUni
.Buffer
= LongPath
;
1713 LongPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1715 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1716 if (!NT_SUCCESS(Status
))
1718 BaseSetLastNTError(Status
);
1722 Result
= LongPathAnsi
.Length
;
1723 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1725 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1726 lpszLongPath
[Result
] = ANSI_NULL
;
1730 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1734 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1735 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1736 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1738 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1748 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1749 IN LPSTR lpszShortPath
,
1752 ULONG Result
, PathLength
;
1755 UNICODE_STRING LongPathUni
, ShortPathUni
;
1756 ANSI_STRING ShortPathAnsi
;
1757 WCHAR ShortPathBuffer
[MAX_PATH
];
1760 ShortPathAnsi
.Buffer
= NULL
;
1761 LongPathUni
.Buffer
= NULL
;
1766 SetLastError(ERROR_INVALID_PARAMETER
);
1770 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1771 if (!NT_SUCCESS(Status
)) goto Quickie
;
1773 ShortPath
= ShortPathBuffer
;
1775 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1776 if (PathLength
>= MAX_PATH
)
1778 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1782 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1786 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1790 if (!PathLength
) goto Quickie
;
1792 LongPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1793 ShortPathUni
.Buffer
= ShortPath
;
1794 ShortPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1796 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1797 if (!NT_SUCCESS(Status
))
1799 BaseSetLastNTError(Status
);
1803 Result
= ShortPathAnsi
.Length
;
1804 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1806 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1807 lpszShortPath
[Result
] = ANSI_NULL
;
1811 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1815 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1816 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1817 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1819 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1829 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1830 IN LPWSTR lpszShortPath
,
1833 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1839 BOOLEAN Found
= FALSE
;
1840 WIN32_FIND_DATAW FindFileData
;
1842 /* Initialize so Quickie knows there's nothing to do */
1843 Buffer
= Original
= NULL
;
1846 /* First check if the input path was obviously NULL */
1849 /* Fail the request */
1850 SetLastError(ERROR_INVALID_PARAMETER
);
1854 /* We will be touching removed, removable drives -- don't warn the user */
1855 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1857 /* Do a simple check to see if the path exists */
1858 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1860 /* Windows checks for an application compatibility flag to allow this */
1861 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1863 /* It doesn't, so fail */
1869 /* Now get a pointer to the actual path, skipping indicators */
1870 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1872 /* Is there any path or filename in there? */
1874 (*Path
== UNICODE_NULL
) ||
1875 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1877 /* There isn't, so the long path is simply the short path */
1878 ReturnLength
= wcslen(lpszLongPath
);
1880 /* Is there space for it? */
1881 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1883 /* Make sure the pointers aren't already the same */
1884 if (lpszLongPath
!= lpszShortPath
)
1886 /* They're not -- copy the short path into the long path */
1887 RtlMoveMemory(lpszShortPath
,
1889 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1894 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1900 /* We are still in the game -- compute the current size */
1901 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1902 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1903 if (!Original
) goto ErrorQuickie
;
1905 /* Make a copy of it */
1906 wcsncpy(Original
, lpszLongPath
, Length
);
1908 /* Compute the new first and last markers */
1909 First
= &Original
[First
- lpszLongPath
];
1910 Last
= &Original
[Last
- lpszLongPath
];
1912 /* Set the current destination pointer for a copy */
1913 Dst
= lpszShortPath
;
1916 * Windows allows the paths to overlap -- we have to be careful with this and
1917 * see if it's same to do so, and if not, allocate our own internal buffer
1918 * that we'll return at the end.
1920 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1922 if ((cchBuffer
) && (lpszShortPath
) &&
1923 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1924 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1926 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1927 if (!Buffer
) goto ErrorQuickie
;
1929 /* New destination */
1933 /* Prepare for the loop */
1938 /* Current delta in the loop */
1939 Length
= First
- Src
;
1941 /* Update the return length by it */
1942 ReturnLength
+= Length
;
1944 /* Is there a delta? If so, is there space and buffer for it? */
1945 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1947 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1951 /* "Terminate" this portion of the path's substring so we can do a find */
1953 *Last
= UNICODE_NULL
;
1954 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1957 /* This portion wasn't found, so fail */
1958 if (FindHandle
== INVALID_HANDLE_VALUE
)
1964 /* Close the find handle */
1965 FindClose(FindHandle
);
1967 /* Now check the length of the short name */
1968 Length
= wcslen(FindFileData
.cAlternateFileName
);
1971 /* This is our new first marker */
1972 First
= FindFileData
.cAlternateFileName
;
1976 /* Otherwise, the name is the delta between our current markers */
1977 Length
= Last
- First
;
1980 /* Update the return length with the short name length, if any */
1981 ReturnLength
+= Length
;
1983 /* Once again check for appropriate space and buffer */
1984 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1986 /* And do the copy if there is */
1987 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1991 /* Now update the source pointer */
1993 if (*Src
== UNICODE_NULL
) break;
1995 /* Are there more names in there? */
1996 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
2000 /* The loop is done, is there anything left? */
2003 /* Get the length of the straggling path */
2004 Length
= wcslen(Src
);
2005 ReturnLength
+= Length
;
2007 /* Once again check for appropriate space and buffer */
2008 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2010 /* And do the copy if there is -- accounting for NULL here */
2011 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2013 /* What about our buffer? */
2016 /* Copy it into the caller's long path */
2017 RtlMoveMemory(lpszShortPath
,
2019 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2024 /* Buffer is too small, let the caller know, making space for NULL */
2029 /* We're all done */
2033 /* This is the goto for memory failures */
2034 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2037 /* General function end: free memory, restore error mode, return length */
2038 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
2039 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
2040 SetErrorMode(ErrorMode
);
2041 return ReturnLength
;
2047 * NOTE: Windows returns a dos/short (8.3) path
2051 GetTempPathA(IN DWORD nBufferLength
,
2054 WCHAR BufferW
[MAX_PATH
];
2057 ret
= GetTempPathW(MAX_PATH
, BufferW
);
2063 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2067 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
2077 GetTempPathW(IN DWORD count
,
2080 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
2081 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
2082 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2083 WCHAR tmp_path
[MAX_PATH
];
2086 DPRINT("%u,%p\n", count
, path
);
2088 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
2089 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
2090 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
2091 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
2096 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2100 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, tmp_path
, NULL
);
2103 if (ret
> MAX_PATH
- 2)
2105 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2109 if (tmp_path
[ret
-1] != '\\')
2111 tmp_path
[ret
++] = '\\';
2112 tmp_path
[ret
] = '\0';
2115 ret
++; /* add space for terminating 0 */
2119 lstrcpynW(path
, tmp_path
, count
);
2121 ret
--; /* return length without 0 */
2123 path
[0] = 0; /* avoid returning ambiguous "X:" */
2126 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
2135 GetCurrentDirectoryA(IN DWORD nBufferLength
,
2138 ANSI_STRING AnsiString
;
2140 PUNICODE_STRING StaticString
;
2143 StaticString
= &NtCurrentTeb()->StaticUnicodeString
;
2145 MaxLength
= nBufferLength
;
2146 if (nBufferLength
>= UNICODE_STRING_MAX_BYTES
)
2148 MaxLength
= UNICODE_STRING_MAX_BYTES
- 1;
2151 StaticString
->Length
= RtlGetCurrentDirectory_U(StaticString
->MaximumLength
,
2152 StaticString
->Buffer
);
2153 Status
= RtlUnicodeToMultiByteSize(&nBufferLength
,
2154 StaticString
->Buffer
,
2155 StaticString
->Length
);
2156 if (!NT_SUCCESS(Status
))
2158 BaseSetLastNTError(Status
);
2162 if (MaxLength
<= nBufferLength
)
2164 return nBufferLength
+ 1;
2167 AnsiString
.Buffer
= lpBuffer
;
2168 AnsiString
.MaximumLength
= MaxLength
;
2169 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, StaticString
, FALSE
);
2170 if (!NT_SUCCESS(Status
))
2172 BaseSetLastNTError(Status
);
2176 return AnsiString
.Length
;
2184 GetCurrentDirectoryW(IN DWORD nBufferLength
,
2187 return RtlGetCurrentDirectory_U(nBufferLength
* sizeof(WCHAR
), lpBuffer
) / sizeof(WCHAR
);
2195 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
2197 PUNICODE_STRING DirName
;
2202 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2206 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
);
2207 if (!DirName
) return FALSE
;
2209 if (CheckForSameCurdir(DirName
)) return TRUE
;
2211 Status
= RtlSetCurrentDirectory_U(DirName
);
2212 if (NT_SUCCESS(Status
)) return TRUE
;
2214 if ((*DirName
->Buffer
!= L
'"') || (DirName
->Length
<= 2))
2216 BaseSetLastNTError(Status
);
2220 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
+ 1);
2221 if (!DirName
) return FALSE
;
2223 Status
= RtlSetCurrentDirectory_U(DirName
);
2224 if (!NT_SUCCESS(Status
))
2226 BaseSetLastNTError(Status
);
2238 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
2241 UNICODE_STRING UnicodeString
;
2245 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2249 Status
= RtlInitUnicodeStringEx(&UnicodeString
, lpPathName
);
2250 if (NT_SUCCESS(Status
))
2252 if (!CheckForSameCurdir(&UnicodeString
))
2254 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
2258 if (!NT_SUCCESS(Status
))
2260 BaseSetLastNTError(Status
);
2272 GetSystemDirectoryA(IN LPSTR lpBuffer
,
2275 ANSI_STRING AnsiString
;
2279 /* Get the correct size of the Unicode Base directory */
2280 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2281 BaseWindowsSystemDirectory
.Buffer
,
2282 BaseWindowsSystemDirectory
.MaximumLength
);
2283 if (!NT_SUCCESS(Status
)) return 0;
2285 if (uSize
< AnsiLength
) return AnsiLength
;
2287 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2289 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2290 &BaseWindowsSystemDirectory
,
2292 if (!NT_SUCCESS(Status
)) return 0;
2294 return AnsiString
.Length
;
2302 GetSystemDirectoryW(IN LPWSTR lpBuffer
,
2307 ReturnLength
= BaseWindowsSystemDirectory
.MaximumLength
;
2308 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2310 RtlCopyMemory(lpBuffer
,
2311 BaseWindowsSystemDirectory
.Buffer
,
2312 BaseWindowsSystemDirectory
.Length
);
2313 lpBuffer
[BaseWindowsSystemDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2315 ReturnLength
= BaseWindowsSystemDirectory
.Length
;
2318 return ReturnLength
/ sizeof(WCHAR
);
2326 GetWindowsDirectoryA(IN LPSTR lpBuffer
,
2329 /* Is this a TS installation? */
2330 if (gpTermsrvGetWindowsDirectoryA
) UNIMPLEMENTED
;
2332 /* Otherwise, call the System API */
2333 return GetSystemWindowsDirectoryA(lpBuffer
, uSize
);
2341 GetWindowsDirectoryW(IN LPWSTR lpBuffer
,
2344 /* Is this a TS installation? */
2345 if (gpTermsrvGetWindowsDirectoryW
) UNIMPLEMENTED
;
2347 /* Otherwise, call the System API */
2348 return GetSystemWindowsDirectoryW(lpBuffer
, uSize
);
2356 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer
,
2359 ANSI_STRING AnsiString
;
2363 /* Get the correct size of the Unicode Base directory */
2364 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2365 BaseWindowsDirectory
.Buffer
,
2366 BaseWindowsDirectory
.MaximumLength
);
2367 if (!NT_SUCCESS(Status
)) return 0;
2369 if (uSize
< AnsiLength
) return AnsiLength
;
2371 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2373 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2374 &BaseWindowsDirectory
,
2376 if (!NT_SUCCESS(Status
)) return 0;
2378 return AnsiString
.Length
;
2386 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer
,
2391 ReturnLength
= BaseWindowsDirectory
.MaximumLength
;
2392 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2394 RtlCopyMemory(lpBuffer
,
2395 BaseWindowsDirectory
.Buffer
,
2396 BaseWindowsDirectory
.Length
);
2397 lpBuffer
[BaseWindowsDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2399 ReturnLength
= BaseWindowsDirectory
.Length
;
2402 return ReturnLength
/ sizeof(WCHAR
);
2410 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer
,
2417 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2427 GetSystemWow64DirectoryA(IN LPSTR lpBuffer
,
2434 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);