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 */
243 /* We do not. Do we have the LDR_ENTRY for the executable? */
244 if (!BasepExeLdrEntry
)
246 /* We do not. Grab it */
247 LdrEnumerateLoadedModules(0,
248 BasepLocateExeLdrEntry
,
249 NtCurrentPeb()->ImageBaseAddress
);
252 /* Now do we have it? */
253 if (BasepExeLdrEntry
)
255 /* Yes, so read the name out of it */
256 AppName
= BasepExeLdrEntry
->FullDllName
.Buffer
;
259 /* Find out where the app name ends, to get only the directory */
260 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
263 /* So, do we have an application name and its directory? */
264 if ((AppName
) && (AppNameEnd
))
266 /* Add the size of the app's directory, plus the separator */
267 PathLengthInBytes
+= ((AppNameEnd
- AppName
) * sizeof(WCHAR
)) + sizeof(L
';');
276 /* Bam, all done, we now have the final path size */
277 ASSERT(PathLengthInBytes
> 0);
278 ASSERT(!(PathLengthInBytes
& 1));
280 /* Allocate the buffer to hold it */
281 PathBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes
);
285 Status
= STATUS_NO_MEMORY
;
289 /* Now we loop again, this time to copy the data */
290 PathCurrent
= PathBuffer
;
291 for (Order
= PathOrder
; *Order
!= BaseSearchPathInvalid
; Order
++) {
294 /* Add the DLL path */
295 case BaseSearchPathDll
:
296 if (BaseDllDirectory
.Length
)
298 /* Copy it in the buffer, ASSERT there's enough space */
299 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + BaseDllDirectory
.Length
) <= PathLengthInBytes
);
300 RtlCopyMemory(PathCurrent
,
301 BaseDllDirectory
.Buffer
,
302 BaseDllDirectory
.Length
);
304 /* Update the current pointer, add a separator */
305 PathCurrent
+= (BaseDllDirectory
.Length
/ sizeof(WCHAR
));
306 *PathCurrent
++ = ';';
310 /* Add the current applicaiton path */
311 case BaseSearchPathApp
:
312 if ((AppName
) && (AppNameEnd
))
314 /* Copy it in the buffer, ASSERT there's enough space */
315 ASSERT(((PathCurrent
- PathBuffer
+ 1 + (AppNameEnd
- AppName
)) * sizeof(WCHAR
)) <= PathLengthInBytes
);
316 RtlCopyMemory(PathCurrent
,
318 (AppNameEnd
- AppName
) * sizeof(WCHAR
));
320 /* Update the current pointer, add a separator */
321 PathCurrent
+= AppNameEnd
- AppName
;
322 *PathCurrent
++ = ';';
326 /* Add the default search path */
327 case BaseSearchPathDefault
:
328 /* Copy it in the buffer, ASSERT there's enough space */
329 ASSERT((((PathCurrent
- PathBuffer
) * sizeof(WCHAR
)) + BaseDefaultPath
.Length
) <= PathLengthInBytes
);
330 RtlCopyMemory(PathCurrent
, BaseDefaultPath
.Buffer
, BaseDefaultPath
.Length
);
332 /* Update the current pointer. The default path already has a ";" */
333 PathCurrent
+= (BaseDefaultPath
.Length
/ sizeof(WCHAR
));
336 /* Add the path in the PATH environment variable */
337 case BaseSearchPathEnv
:
340 /* Copy it in the buffer, ASSERT there's enough space */
341 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + EnvPath
.Length
) <= PathLengthInBytes
);
342 RtlCopyMemory(PathCurrent
, EnvPath
.Buffer
, EnvPath
.Length
);
344 /* Update the current pointer, add a separator */
345 PathCurrent
+= (EnvPath
.Length
/ sizeof(WCHAR
));
346 *PathCurrent
++ = ';';
350 /* Add the current dierctory */
351 case BaseSearchPathCurrent
:
353 /* Copy it in the buffer, ASSERT there's enough space */
354 ASSERT(((PathCurrent
- PathBuffer
+ 2) * sizeof(WCHAR
)) <= PathLengthInBytes
);
355 *PathCurrent
++ = '.';
357 /* Add the path separator */
358 *PathCurrent
++ = ';';
366 /* Everything should've perfectly fit in there */
367 ASSERT((PathCurrent
- PathBuffer
) * sizeof(WCHAR
) == PathLengthInBytes
);
368 ASSERT(PathCurrent
> PathBuffer
);
370 /* Terminate the whole thing */
371 PathCurrent
[-1] = UNICODE_NULL
;
374 /* Exit path: free our buffers */
375 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
378 /* This only gets freed in the failure path, since caller wants it */
379 if (!NT_SUCCESS(Status
))
381 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer
);
386 /* Return the path! */
392 BaseComputeProcessSearchPath(VOID
)
394 DPRINT1("Computing Process Search path\n");
396 /* Compute the path using default process order */
397 return BasepComputeProcessPath(BaseProcessOrder
, NULL
, NULL
);
402 BaseComputeProcessExePath(IN LPWSTR FullPath
)
404 PBASE_SEARCH_PATH_TYPE PathOrder
;
405 DPRINT1("Computing EXE path: %wZ\n", FullPath
);
407 /* Check if we should use the current directory */
408 PathOrder
= NeedCurrentDirectoryForExePathW(FullPath
) ?
409 BaseProcessOrder
: BaseProcessOrderNoCurrent
;
411 /* And now compute the path */
412 return BasepComputeProcessPath(PathOrder
, NULL
, NULL
);
417 BaseComputeProcessDllPath(IN LPWSTR FullPath
,
418 IN PVOID Environment
)
420 LPWSTR DllPath
= NULL
;
421 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
422 UNICODE_STRING ValueName
= RTL_CONSTANT_STRING(L
"SafeDllSearchMode");
423 OBJECT_ATTRIBUTES ObjectAttributes
= RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
424 KEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
428 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement
, OldCurrentDirPlacement
;
430 /* Acquire DLL directory lock */
431 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
433 /* Check if we have a base dll directory */
434 if (BaseDllDirectory
.Buffer
)
436 /* Then compute the process path using DLL order (without curdir) */
437 DllPath
= BasepComputeProcessPath(BaseDllOrderNoCurrent
, FullPath
, Environment
);
439 /* Release DLL directory lock */
440 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
442 /* Return dll path */
446 /* Release DLL directory lock */
447 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
449 /* Read the current placement */
450 CurrentDirPlacement
= BasepDllCurrentDirPlacement
;
451 if (CurrentDirPlacement
== BaseCurrentDirPlacementInvalid
)
453 /* Open the configuration key */
454 Status
= NtOpenKey(&KeyHandle
, KEY_QUERY_VALUE
, &ObjectAttributes
);
455 if (NT_SUCCESS(Status
))
457 /* Query if safe search is enabled */
458 Status
= NtQueryValueKey(KeyHandle
,
460 KeyValuePartialInformation
,
464 if (NT_SUCCESS(Status
))
466 /* Read the value if the size is OK */
467 if (ResultLength
== sizeof(PartialInfo
))
469 CurrentDirPlacement
= *(PULONG
)PartialInfo
.Data
;
473 /* Close the handle */
476 /* Validate the registry value */
477 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
478 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
480 /* Default to safe search */
481 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
485 /* Update the placement and read the old one */
486 OldCurrentDirPlacement
= InterlockedCompareExchange((PLONG
)&BasepDllCurrentDirPlacement
,
488 BaseCurrentDirPlacementInvalid
);
489 if (OldCurrentDirPlacement
!= BaseCurrentDirPlacementInvalid
)
491 /* If there already was a placement, use it */
492 CurrentDirPlacement
= OldCurrentDirPlacement
;
496 /* Check if the placement is invalid or not set */
497 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
498 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
500 /* Default to safe search */
501 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
504 /* Compute the process path using either normal or safe search */
505 DllPath
= BasepComputeProcessPath(BaseDllOrderCurrent
[CurrentDirPlacement
],
509 /* Return dll path */
515 CheckForSameCurdir(IN PUNICODE_STRING DirName
)
517 PUNICODE_STRING CurDir
;
520 UNICODE_STRING CurDirCopy
;
522 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
524 CurLength
= CurDir
->Length
;
525 if (CurDir
->Length
<= 6)
527 if (CurLength
!= DirName
->Length
) return FALSE
;
531 if ((CurLength
- 2) != DirName
->Length
) return FALSE
;
536 CurDirCopy
= *CurDir
;
537 if (CurDirCopy
.Length
> 6) CurDirCopy
.Length
-= 2;
541 if (RtlEqualUnicodeString(&CurDirCopy
, DirName
, TRUE
)) Result
= TRUE
;
549 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
550 * identical (other than the Rtl can optionally check for spaces), however the
551 * Rtl will always convert to OEM, while kernel32 has two possible file modes
552 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
553 * the correct compatible results
557 IsShortName_U(IN PWCHAR Name
,
560 BOOLEAN HasExtension
;
563 UNICODE_STRING UnicodeName
;
564 ANSI_STRING AnsiName
;
566 CHAR AnsiBuffer
[MAX_PATH
];
569 /* What do you think 8.3 means? */
570 if (Length
> 12) return FALSE
;
572 /* Sure, any emtpy name is a short name */
573 if (!Length
) return TRUE
;
575 /* This could be . or .. or something else */
578 /* Which one is it */
579 if ((Length
== 1) || ((Length
== 2) && *(Name
+ 1) == L
'.'))
581 /* . or .., this is good */
585 /* Some other bizare dot-based name, not good */
589 /* Initialize our two strings */
590 RtlInitEmptyAnsiString(&AnsiName
, AnsiBuffer
, MAX_PATH
);
591 RtlInitEmptyUnicodeString(&UnicodeName
, Name
, Length
* sizeof(WCHAR
));
592 UnicodeName
.Length
= UnicodeName
.MaximumLength
;
594 /* Now do the conversion */
595 Status
= BasepUnicodeStringTo8BitString(&AnsiName
, &UnicodeName
, FALSE
);
596 if (!NT_SUCCESS(Status
)) return FALSE
;
598 /* Now we loop the name */
599 HasExtension
= FALSE
;
600 for (i
= 0, Dots
= Length
- 1; i
< AnsiName
.Length
; i
++, Dots
--)
602 /* Read the current byte */
603 c
= AnsiName
.Buffer
[i
];
606 if (IsDBCSLeadByte(c
))
608 /* If we're near the end of the string, we can't allow a DBCS */
609 if ((!(HasExtension
) && (i
>= 7)) || (i
== AnsiName
.Length
- 1))
614 /* Otherwise we skip over it */
618 /* Check for illegal characters */
619 if ((c
> 0x7F) || (IllegalMask
[c
/ 32] & (1 << (c
% 32))))
624 /* Check if this is perhaps an extension? */
627 /* Unless the extension is too large or there's more than one */
628 if ((HasExtension
) || (Dots
> 3)) return FALSE
;
630 /* This looks like an extension */
634 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
635 if ((i
>= 8) && !(HasExtension
)) return FALSE
;
638 /* You survived the loop, this is a good short name */
644 IsLongName_U(IN PWCHAR FileName
,
647 BOOLEAN HasExtension
;
650 /* More than 8.3, any combination of dots, and NULL names are all long */
651 if (!(Length
) || (Length
> 12) || (*FileName
== L
'.')) return TRUE
;
653 /* Otherwise, initialize our scanning loop */
654 HasExtension
= FALSE
;
655 for (i
= 0, Dots
= Length
- 1; i
< Length
; i
++, Dots
--)
657 /* Check if this could be an extension */
658 if (FileName
[i
] == L
'.')
660 /* Unlike the short case, we WANT more than one extension, or a long one */
661 if ((HasExtension
) || (Dots
> 3))
668 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
669 if ((i
>= 8) && (!HasExtension
)) return TRUE
;
672 /* The name *seems* to conform to 8.3 */
678 FindLFNorSFN_U(IN PWCHAR Path
,
688 /* Loop while there is something in the path */
691 /* Loop within the path skipping slashes */
692 while ((*Path
== L
'\\') || (*Path
== L
'/')) Path
++;
694 /* Make sure there's something after the slashes too! */
695 if (*Path
== UNICODE_NULL
) break;
697 /* Now skip past the file name until we get to the first slash */
699 while ((*p
) && ((*p
!= L
'\\') && (*p
!= L
'/'))) p
++;
701 /* Whatever is in between those two is now the file name length */
705 * Check if it is valid
706 * Note that !IsShortName != IsLongName, these two functions simply help
707 * us determine if a conversion is necessary or not.
708 * "Found" really means: "Is a conversion necessary?", hence the "!"
710 Found
= UseShort
? !IsShortName_U(Path
, Length
) : !IsLongName_U(Path
, Length
);
713 /* It is! did the caller request to know the markers? */
714 if ((First
) && (Last
))
723 /* Is there anything else following this sub-path/filename? */
724 if (*p
== UNICODE_NULL
) break;
726 /* Yes, keep going */
730 /* Return if anything was found and valid */
736 SkipPathTypeIndicator_U(IN LPWSTR Path
)
741 /* Check what kind of path this is and how many slashes to skip */
742 switch (RtlDetermineDosPathNameType_U(Path
))
744 case RtlPathTypeDriveAbsolute
:
747 case RtlPathTypeDriveRelative
:
750 case RtlPathTypeRooted
:
753 case RtlPathTypeRelative
:
756 case RtlPathTypeRootLocalDevice
:
760 case RtlPathTypeUncAbsolute
:
761 case RtlPathTypeLocalDevice
:
763 /* Keep going until we bypass the path indicators */
764 for (ReturnPath
= Path
+ 2, i
= 2; (i
> 0) && (*ReturnPath
); ReturnPath
++)
766 /* We look for 2 slashes, so keep at it until we find them */
767 if ((*ReturnPath
== L
'\\') || (*ReturnPath
== L
'/')) i
--;
776 BasepIsCurDirAllowedForPlainExeNames(VOID
)
779 UNICODE_STRING EmptyString
;
781 RtlInitEmptyUnicodeString(&EmptyString
, NULL
, 0);
782 Status
= RtlQueryEnvironmentVariable_U(NULL
,
783 &NoDefaultCurrentDirectoryInExePath
,
785 return !NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
;
788 /* PUBLIC FUNCTIONS ***********************************************************/
795 SetDllDirectoryW(IN LPCWSTR lpPathName
)
797 UNICODE_STRING OldDirectory
, DllDirectory
;
801 if (wcschr(lpPathName
, L
';'))
803 SetLastError(ERROR_INVALID_PARAMETER
);
806 if (!RtlCreateUnicodeString(&DllDirectory
, lpPathName
))
808 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
814 RtlInitUnicodeString(&DllDirectory
, NULL
);
817 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
819 OldDirectory
= BaseDllDirectory
;
820 BaseDllDirectory
= DllDirectory
;
822 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
824 RtlFreeUnicodeString(&OldDirectory
);
833 SetDllDirectoryA(IN LPCSTR lpPathName
)
835 ANSI_STRING AnsiDllDirectory
;
836 UNICODE_STRING OldDirectory
, DllDirectory
;
841 if (strchr(lpPathName
, ';'))
843 SetLastError(ERROR_INVALID_PARAMETER
);
847 Status
= RtlInitAnsiStringEx(&AnsiDllDirectory
, lpPathName
);
848 if (NT_SUCCESS(Status
))
850 Status
= Basep8BitStringToUnicodeString(&DllDirectory
,
855 if (!NT_SUCCESS(Status
))
857 BaseSetLastNTError(Status
);
863 RtlInitUnicodeString(&DllDirectory
, NULL
);
866 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
868 OldDirectory
= BaseDllDirectory
;
869 BaseDllDirectory
= DllDirectory
;
871 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
873 RtlFreeUnicodeString(&OldDirectory
);
882 GetDllDirectoryW(IN DWORD nBufferLength
,
887 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
889 if ((nBufferLength
* sizeof(WCHAR
)) > BaseDllDirectory
.Length
)
891 RtlCopyMemory(lpBuffer
, BaseDllDirectory
.Buffer
, BaseDllDirectory
.Length
);
892 Length
= BaseDllDirectory
.Length
/ sizeof(WCHAR
);
893 lpBuffer
[Length
] = UNICODE_NULL
;
897 Length
= (BaseDllDirectory
.Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
);
898 if (lpBuffer
) *lpBuffer
= UNICODE_NULL
;
901 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
910 GetDllDirectoryA(IN DWORD nBufferLength
,
914 ANSI_STRING AnsiDllDirectory
;
917 RtlInitEmptyAnsiString(&AnsiDllDirectory
, lpBuffer
, nBufferLength
);
919 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
921 Length
= BasepUnicodeStringTo8BitSize(&BaseDllDirectory
);
922 if (Length
> nBufferLength
)
924 Status
= STATUS_SUCCESS
;
925 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
930 Status
= BasepUnicodeStringTo8BitString(&AnsiDllDirectory
,
935 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
937 if (!NT_SUCCESS(Status
))
939 BaseSetLastNTError(Status
);
941 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
952 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName
)
954 if (wcschr(ExeName
, L
'\\')) return TRUE
;
956 return BasepIsCurDirAllowedForPlainExeNames();
964 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName
)
966 if (strchr(ExeName
, '\\')) return TRUE
;
968 return BasepIsCurDirAllowedForPlainExeNames();
974 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
975 * beyond what you would usually expect. There are two main reasons:
977 * First, these APIs are subject to the ANSI/OEM File API selection status that
978 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
980 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
981 * length of the paths in "ANSI" by dividing their internal Wide character count
982 * by two... this is usually correct when dealing with pure-ASCII codepages but
983 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
984 * for CJK, for example.
988 GetFullPathNameA(IN LPCSTR lpFileName
,
989 IN DWORD nBufferLength
,
991 IN LPSTR
*lpFilePart
)
995 ULONG PathSize
, FilePartSize
;
996 ANSI_STRING AnsiString
;
997 UNICODE_STRING FileNameString
, UniString
;
998 PWCHAR LocalFilePart
;
1001 /* If the caller wants filepart, use a local wide buffer since this is A */
1002 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1004 /* Initialize for Quickie */
1005 FilePartSize
= PathSize
= 0;
1006 FileNameString
.Buffer
= NULL
;
1008 /* First get our string in Unicode */
1009 Status
= Basep8BitStringToDynamicUnicodeString(&FileNameString
, lpFileName
);
1010 if (!NT_SUCCESS(Status
)) goto Quickie
;
1012 /* Allocate a buffer to hold teh path name */
1013 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1015 MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1018 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES
);
1022 /* Call into RTL to get the full Unicode path name */
1023 PathSize
= RtlGetFullPathName_U(FileNameString
.Buffer
,
1024 MAX_PATH
* sizeof(WCHAR
),
1027 if (PathSize
<= (MAX_PATH
* sizeof(WCHAR
)))
1029 /* The buffer will fit, get the real ANSI string size now */
1030 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
);
1031 if (NT_SUCCESS(Status
))
1033 /* Now check if the user wanted file part size as well */
1034 if ((PathSize
) && (lpFilePart
) && (LocalFilePart
))
1036 /* Yep, so in this case get the length of the file part too */
1037 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1039 (LocalFilePart
- Buffer
) *
1041 if (!NT_SUCCESS(Status
))
1043 /* We failed to do that, so fail the whole call */
1044 BaseSetLastNTError(Status
);
1052 /* Reset the path size since the buffer is not large enough */
1056 /* Either no path, or local buffer was too small, enter failure code */
1057 if (!PathSize
) goto Quickie
;
1059 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
1060 if (PathSize
>= nBufferLength
)
1066 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
1067 RtlInitUnicodeString(&UniString
, Buffer
);
1068 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, &UniString
, TRUE
);
1069 if (!NT_SUCCESS(Status
))
1071 /* Final conversion failed, fail the call */
1072 BaseSetLastNTError(Status
);
1077 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
1078 RtlCopyMemory(lpBuffer
, AnsiString
.Buffer
, PathSize
+ 1);
1079 RtlFreeAnsiString(&AnsiString
);
1081 /* And finally, did the caller request file part information? */
1084 /* Use the size we computed earlier and add it to the buffer */
1085 *lpFilePart
= LocalFilePart
? &lpBuffer
[FilePartSize
] : 0;
1090 /* Cleanup and return the path size */
1091 if (FileNameString
.Buffer
) RtlFreeUnicodeString(&FileNameString
);
1092 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1101 GetFullPathNameW(IN LPCWSTR lpFileName
,
1102 IN DWORD nBufferLength
,
1104 OUT LPWSTR
*lpFilePart
)
1106 /* Call Rtl to do the work */
1107 return RtlGetFullPathName_U((LPWSTR
)lpFileName
,
1108 nBufferLength
* sizeof(WCHAR
),
1110 lpFilePart
) / sizeof(WCHAR
);
1118 SearchPathA(IN LPCSTR lpPath
,
1119 IN LPCSTR lpFileName
,
1120 IN LPCSTR lpExtension
,
1121 IN DWORD nBufferLength
,
1123 OUT LPSTR
*lpFilePart
)
1125 PUNICODE_STRING FileNameString
;
1126 UNICODE_STRING PathString
, ExtensionString
;
1128 ULONG PathSize
, FilePartSize
, AnsiLength
;
1129 PWCHAR LocalFilePart
, Buffer
;
1132 /* If the caller wants filepart, use a local wide buffer since this is A */
1133 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1135 /* Initialize stuff for Quickie */
1138 ExtensionString
.Buffer
= PathString
.Buffer
= NULL
;
1140 /* Get the UNICODE_STRING file name */
1141 FileNameString
= Basep8BitStringToStaticUnicodeString(lpFileName
);
1142 if (!FileNameString
) return 0;
1144 /* Did the caller specify an extension */
1147 /* Yup, convert it into UNICODE_STRING */
1148 Status
= Basep8BitStringToDynamicUnicodeString(&ExtensionString
,
1150 if (!NT_SUCCESS(Status
)) goto Quickie
;
1153 /* Did the caller specify a path */
1156 /* Yup, convert it into UNICODE_STRING */
1157 Status
= Basep8BitStringToDynamicUnicodeString(&PathString
, lpPath
);
1158 if (!NT_SUCCESS(Status
)) goto Quickie
;
1161 /* Allocate our output buffer */
1162 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength
* sizeof(WCHAR
));
1165 /* It failed, bail out */
1166 BaseSetLastNTError(STATUS_NO_MEMORY
);
1170 /* Now run the Wide search with the input buffer lengths */
1171 PathSize
= SearchPathW(PathString
.Buffer
,
1172 FileNameString
->Buffer
,
1173 ExtensionString
.Buffer
,
1177 if (PathSize
<= nBufferLength
)
1179 /* It fits, but is it empty? If so, bail out */
1180 if (!PathSize
) goto Quickie
;
1182 /* The length above is inexact, we need it in ANSI */
1183 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
, Buffer
, PathSize
* sizeof(WCHAR
));
1184 if (!NT_SUCCESS(Status
))
1186 /* Conversion failed, fail the call */
1188 BaseSetLastNTError(Status
);
1192 /* If the correct ANSI size is too big, return requird length plus a NULL */
1193 if (AnsiLength
>= nBufferLength
)
1195 PathSize
= AnsiLength
+ 1;
1199 /* Now apply the final conversion to ANSI */
1200 Status
= RtlUnicodeToMultiByteN(lpBuffer
,
1204 PathSize
* sizeof(WCHAR
));
1205 if (!NT_SUCCESS(Status
))
1207 /* Conversion failed, fail the whole call */
1209 BaseSetLastNTError(STATUS_NO_MEMORY
);
1213 /* NULL-terminate and return the real ANSI length */
1214 lpBuffer
[AnsiLength
] = ANSI_NULL
;
1215 PathSize
= AnsiLength
;
1217 /* Now check if the user wanted file part size as well */
1220 /* If we didn't get a file part, clear the caller's */
1227 /* Yep, so in this case get the length of the file part too */
1228 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1230 (LocalFilePart
- Buffer
) *
1232 if (!NT_SUCCESS(Status
))
1234 /* We failed to do that, so fail the whole call */
1235 BaseSetLastNTError(Status
);
1239 /* Return the file part buffer */
1240 *lpFilePart
= lpBuffer
+ FilePartSize
;
1246 /* Our initial buffer guess was too small, allocate a bigger one */
1247 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1248 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize
* sizeof(WCHAR
));
1251 /* Out of memory, fail everything */
1252 BaseSetLastNTError(STATUS_NO_MEMORY
);
1256 /* Do the search again -- it will fail, we just want the path size */
1257 PathSize
= SearchPathW(PathString
.Buffer
,
1258 FileNameString
->Buffer
,
1259 ExtensionString
.Buffer
,
1263 if (!PathSize
) goto Quickie
;
1265 /* Convert it to a correct size */
1266 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
* sizeof(WCHAR
));
1267 if (NT_SUCCESS(Status
))
1269 /* Make space for the NULL-char */
1274 /* Conversion failed for some reason, fail the call */
1275 BaseSetLastNTError(Status
);
1281 /* Cleanup/complete path */
1282 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1283 if (ExtensionString
.Buffer
) RtlFreeUnicodeString(&ExtensionString
);
1284 if (PathString
.Buffer
) RtlFreeUnicodeString(&PathString
);
1288 /***********************************************************************
1289 * ContainsPath (Wine name: contains_pathW)
1291 * Check if the file name contains a path; helper for SearchPathW.
1292 * A relative path is not considered a path unless it starts with ./ or ../
1296 ContainsPath(LPCWSTR name
)
1298 if (RtlDetermineDosPathNameType_U(name
) != RtlPathTypeRelative
) return TRUE
;
1299 if (name
[0] != '.') return FALSE
;
1300 if (name
[1] == '/' || name
[1] == '\\' || name
[1] == '\0') return TRUE
;
1301 return (name
[1] == '.' && (name
[2] == '/' || name
[2] == '\\'));
1305 * @name GetDllLoadPath
1307 * Internal function to compute the load path to use for a given dll.
1309 * @remarks Returned pointer must be freed by caller.
1313 GetDllLoadPath(LPCWSTR lpModule
)
1315 ULONG Pos
= 0, Length
= 4, Tmp
;
1316 PWCHAR EnvironmentBufferW
= NULL
;
1317 LPCWSTR lpModuleEnd
= NULL
;
1318 UNICODE_STRING ModuleName
;
1319 DWORD LastError
= GetLastError(); /* GetEnvironmentVariable changes LastError */
1321 // FIXME: This function is used only by SearchPathW, and is deprecated and will be deleted ASAP.
1323 if (lpModule
!= NULL
&& wcslen(lpModule
) > 2 && lpModule
[1] == ':')
1325 lpModuleEnd
= lpModule
+ wcslen(lpModule
);
1329 ModuleName
= NtCurrentPeb()->ProcessParameters
->ImagePathName
;
1330 lpModule
= ModuleName
.Buffer
;
1331 lpModuleEnd
= lpModule
+ (ModuleName
.Length
/ sizeof(WCHAR
));
1334 if (lpModule
!= NULL
)
1336 while (lpModuleEnd
> lpModule
&& *lpModuleEnd
!= L
'/' &&
1337 *lpModuleEnd
!= L
'\\' && *lpModuleEnd
!= L
':')
1341 Length
= (lpModuleEnd
- lpModule
) + 1;
1344 Length
+= GetCurrentDirectoryW(0, NULL
);
1345 Length
+= GetDllDirectoryW(0, NULL
);
1346 Length
+= GetSystemDirectoryW(NULL
, 0);
1347 Length
+= GetWindowsDirectoryW(NULL
, 0);
1348 Length
+= GetEnvironmentVariableW(L
"PATH", NULL
, 0);
1350 EnvironmentBufferW
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
1351 (Length
+ 1) * sizeof(WCHAR
));
1352 if (EnvironmentBufferW
== NULL
)
1359 RtlCopyMemory(EnvironmentBufferW
, lpModule
,
1360 (lpModuleEnd
- lpModule
) * sizeof(WCHAR
));
1361 Pos
+= lpModuleEnd
- lpModule
;
1362 EnvironmentBufferW
[Pos
++] = L
';';
1365 Tmp
= GetCurrentDirectoryW(Length
, EnvironmentBufferW
+ Pos
);
1366 if(Tmp
> 0 && Tmp
< Length
- Pos
)
1369 if(Pos
< Length
) EnvironmentBufferW
[Pos
++] = L
';';
1372 Tmp
= GetDllDirectoryW(Length
- Pos
, EnvironmentBufferW
+ Pos
);
1373 if(Tmp
> 0 && Tmp
< Length
- Pos
)
1376 if(Pos
< Length
) EnvironmentBufferW
[Pos
++] = L
';';
1379 Tmp
= GetSystemDirectoryW(EnvironmentBufferW
+ Pos
, Length
- Pos
);
1380 if(Tmp
> 0 && Tmp
< Length
- Pos
)
1383 if(Pos
< Length
) EnvironmentBufferW
[Pos
++] = L
';';
1386 Tmp
= GetWindowsDirectoryW(EnvironmentBufferW
+ Pos
, Length
- Pos
);
1387 if(Tmp
> 0 && Tmp
< Length
- Pos
)
1390 if(Pos
< Length
) EnvironmentBufferW
[Pos
++] = L
';';
1393 Tmp
= GetEnvironmentVariableW(L
"PATH", EnvironmentBufferW
+ Pos
, Length
- Pos
);
1395 /* Make sure buffer is null terminated */
1396 EnvironmentBufferW
[Pos
++] = UNICODE_NULL
;
1399 SetLastError(LastError
);
1400 return EnvironmentBufferW
;
1408 SearchPathW(LPCWSTR lpPath
,
1410 LPCWSTR lpExtension
,
1411 DWORD nBufferLength
,
1417 if (!lpFileName
|| !lpFileName
[0])
1419 SetLastError(ERROR_INVALID_PARAMETER
);
1423 /* If the name contains an explicit path, ignore the path */
1424 if (ContainsPath(lpFileName
))
1426 /* try first without extension */
1427 if (RtlDoesFileExists_U(lpFileName
))
1428 return GetFullPathNameW(lpFileName
, nBufferLength
, lpBuffer
, lpFilePart
);
1432 LPCWSTR p
= wcsrchr(lpFileName
, '.');
1433 if (p
&& !strchr((const char *)p
, '/') && !wcschr( p
, '\\' ))
1434 lpExtension
= NULL
; /* Ignore the specified extension */
1437 /* Allocate a buffer for the file name and extension */
1441 DWORD len
= wcslen(lpFileName
) + wcslen(lpExtension
);
1443 if (!(tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
))))
1445 SetLastError(ERROR_OUTOFMEMORY
);
1448 wcscpy(tmp
, lpFileName
);
1449 wcscat(tmp
, lpExtension
);
1450 if (RtlDoesFileExists_U(tmp
))
1451 ret
= GetFullPathNameW(tmp
, nBufferLength
, lpBuffer
, lpFilePart
);
1452 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
1455 else if (lpPath
&& lpPath
[0]) /* search in the specified path */
1457 ret
= RtlDosSearchPath_U(lpPath
,
1460 nBufferLength
* sizeof(WCHAR
),
1462 lpFilePart
) / sizeof(WCHAR
);
1464 else /* search in the default path */
1466 WCHAR
*DllPath
= GetDllLoadPath(NULL
);
1470 ret
= RtlDosSearchPath_U(DllPath
,
1473 nBufferLength
* sizeof(WCHAR
),
1475 lpFilePart
) / sizeof(WCHAR
);
1476 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath
);
1480 SetLastError(ERROR_OUTOFMEMORY
);
1485 if (!ret
) SetLastError(ERROR_FILE_NOT_FOUND
);
1495 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
1496 IN LPWSTR lpszLongPath
,
1499 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1505 BOOLEAN Found
= FALSE
;
1506 WIN32_FIND_DATAW FindFileData
;
1508 /* Initialize so Quickie knows there's nothing to do */
1509 Buffer
= Original
= NULL
;
1512 /* First check if the input path was obviously NULL */
1515 /* Fail the request */
1516 SetLastError(ERROR_INVALID_PARAMETER
);
1520 /* We will be touching removed, removable drives -- don't warn the user */
1521 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1523 /* Do a simple check to see if the path exists */
1524 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
1526 /* It doesn't, so fail */
1531 /* Now get a pointer to the actual path, skipping indicators */
1532 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
1534 /* Is there any path or filename in there? */
1536 (*Path
== UNICODE_NULL
) ||
1537 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
1539 /* There isn't, so the long path is simply the short path */
1540 ReturnLength
= wcslen(lpszShortPath
);
1542 /* Is there space for it? */
1543 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1545 /* Make sure the pointers aren't already the same */
1546 if (lpszLongPath
!= lpszShortPath
)
1548 /* They're not -- copy the short path into the long path */
1549 RtlMoveMemory(lpszLongPath
,
1551 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1556 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1562 /* We are still in the game -- compute the current size */
1563 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
1564 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1565 if (!Original
) goto ErrorQuickie
;
1567 /* Make a copy of it */
1568 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
1570 /* Compute the new first and last markers */
1571 First
= &Original
[First
- lpszShortPath
];
1572 Last
= &Original
[Last
- lpszShortPath
];
1574 /* Set the current destination pointer for a copy */
1578 * Windows allows the paths to overlap -- we have to be careful with this and
1579 * see if it's same to do so, and if not, allocate our own internal buffer
1580 * that we'll return at the end.
1582 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1584 if ((cchBuffer
) && (lpszLongPath
) &&
1585 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
1586 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
1588 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1589 if (!Buffer
) goto ErrorQuickie
;
1591 /* New destination */
1595 /* Prepare for the loop */
1600 /* Current delta in the loop */
1601 Length
= First
- Src
;
1603 /* Update the return length by it */
1604 ReturnLength
+= Length
;
1606 /* Is there a delta? If so, is there space and buffer for it? */
1607 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1609 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1613 /* "Terminate" this portion of the path's substring so we can do a find */
1615 *Last
= UNICODE_NULL
;
1616 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1619 /* This portion wasn't found, so fail */
1620 if (FindHandle
== INVALID_HANDLE_VALUE
)
1626 /* Close the find handle */
1627 FindClose(FindHandle
);
1629 /* Now check the length of the long name */
1630 Length
= wcslen(FindFileData
.cFileName
);
1633 /* This is our new first marker */
1634 First
= FindFileData
.cFileName
;
1638 /* Otherwise, the name is the delta between our current markers */
1639 Length
= Last
- First
;
1642 /* Update the return length with the short name length, if any */
1643 ReturnLength
+= Length
;
1645 /* Once again check for appropriate space and buffer */
1646 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1648 /* And do the copy if there is */
1649 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1653 /* Now update the source pointer */
1655 if (*Src
== UNICODE_NULL
) break;
1657 /* Are there more names in there? */
1658 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1662 /* The loop is done, is there anything left? */
1665 /* Get the length of the straggling path */
1666 Length
= wcslen(Src
);
1667 ReturnLength
+= Length
;
1669 /* Once again check for appropriate space and buffer */
1670 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1672 /* And do the copy if there is -- accounting for NULL here */
1673 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1675 /* What about our buffer? */
1678 /* Copy it into the caller's long path */
1679 RtlMoveMemory(lpszLongPath
,
1681 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1686 /* Buffer is too small, let the caller know, making space for NULL */
1691 /* We're all done */
1695 /* This is the goto for memory failures */
1696 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1699 /* General function end: free memory, restore error mode, return length */
1700 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1701 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1702 SetErrorMode(ErrorMode
);
1703 return ReturnLength
;
1711 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1712 IN LPSTR lpszLongPath
,
1715 ULONG Result
, PathLength
;
1718 UNICODE_STRING LongPathUni
, ShortPathUni
;
1719 ANSI_STRING LongPathAnsi
;
1720 WCHAR LongPathBuffer
[MAX_PATH
];
1723 LongPathAnsi
.Buffer
= NULL
;
1724 ShortPathUni
.Buffer
= NULL
;
1729 SetLastError(ERROR_INVALID_PARAMETER
);
1733 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1734 if (!NT_SUCCESS(Status
)) goto Quickie
;
1736 LongPath
= LongPathBuffer
;
1738 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1739 if (PathLength
>= MAX_PATH
)
1741 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1745 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1749 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1753 if (!PathLength
) goto Quickie
;
1755 ShortPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1756 LongPathUni
.Buffer
= LongPath
;
1757 LongPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1759 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1760 if (!NT_SUCCESS(Status
))
1762 BaseSetLastNTError(Status
);
1766 Result
= LongPathAnsi
.Length
;
1767 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1769 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1770 lpszLongPath
[Result
] = ANSI_NULL
;
1774 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1778 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1779 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1780 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1782 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1792 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1793 IN LPSTR lpszShortPath
,
1796 ULONG Result
, PathLength
;
1799 UNICODE_STRING LongPathUni
, ShortPathUni
;
1800 ANSI_STRING ShortPathAnsi
;
1801 WCHAR ShortPathBuffer
[MAX_PATH
];
1804 ShortPathAnsi
.Buffer
= NULL
;
1805 LongPathUni
.Buffer
= NULL
;
1810 SetLastError(ERROR_INVALID_PARAMETER
);
1814 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1815 if (!NT_SUCCESS(Status
)) goto Quickie
;
1817 ShortPath
= ShortPathBuffer
;
1819 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1820 if (PathLength
>= MAX_PATH
)
1822 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1826 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1830 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1834 if (!PathLength
) goto Quickie
;
1836 LongPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1837 ShortPathUni
.Buffer
= ShortPath
;
1838 ShortPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1840 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1841 if (!NT_SUCCESS(Status
))
1843 BaseSetLastNTError(Status
);
1847 Result
= ShortPathAnsi
.Length
;
1848 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1850 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1851 lpszShortPath
[Result
] = ANSI_NULL
;
1855 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1859 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1860 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1861 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1863 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1873 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1874 IN LPWSTR lpszShortPath
,
1877 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1883 BOOLEAN Found
= FALSE
;
1884 WIN32_FIND_DATAW FindFileData
;
1886 /* Initialize so Quickie knows there's nothing to do */
1887 Buffer
= Original
= NULL
;
1890 /* First check if the input path was obviously NULL */
1893 /* Fail the request */
1894 SetLastError(ERROR_INVALID_PARAMETER
);
1898 /* We will be touching removed, removable drives -- don't warn the user */
1899 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1901 /* Do a simple check to see if the path exists */
1902 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1904 /* Windows checks for an application compatibility flag to allow this */
1905 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1907 /* It doesn't, so fail */
1913 /* Now get a pointer to the actual path, skipping indicators */
1914 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1916 /* Is there any path or filename in there? */
1918 (*Path
== UNICODE_NULL
) ||
1919 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1921 /* There isn't, so the long path is simply the short path */
1922 ReturnLength
= wcslen(lpszLongPath
);
1924 /* Is there space for it? */
1925 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1927 /* Make sure the pointers aren't already the same */
1928 if (lpszLongPath
!= lpszShortPath
)
1930 /* They're not -- copy the short path into the long path */
1931 RtlMoveMemory(lpszShortPath
,
1933 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1938 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1944 /* We are still in the game -- compute the current size */
1945 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1946 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1947 if (!Original
) goto ErrorQuickie
;
1949 /* Make a copy of it */
1950 wcsncpy(Original
, lpszLongPath
, Length
);
1952 /* Compute the new first and last markers */
1953 First
= &Original
[First
- lpszLongPath
];
1954 Last
= &Original
[Last
- lpszLongPath
];
1956 /* Set the current destination pointer for a copy */
1957 Dst
= lpszShortPath
;
1960 * Windows allows the paths to overlap -- we have to be careful with this and
1961 * see if it's same to do so, and if not, allocate our own internal buffer
1962 * that we'll return at the end.
1964 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1966 if ((cchBuffer
) && (lpszShortPath
) &&
1967 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1968 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1970 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1971 if (!Buffer
) goto ErrorQuickie
;
1973 /* New destination */
1977 /* Prepare for the loop */
1982 /* Current delta in the loop */
1983 Length
= First
- Src
;
1985 /* Update the return length by it */
1986 ReturnLength
+= Length
;
1988 /* Is there a delta? If so, is there space and buffer for it? */
1989 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1991 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1995 /* "Terminate" this portion of the path's substring so we can do a find */
1997 *Last
= UNICODE_NULL
;
1998 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
2001 /* This portion wasn't found, so fail */
2002 if (FindHandle
== INVALID_HANDLE_VALUE
)
2008 /* Close the find handle */
2009 FindClose(FindHandle
);
2011 /* Now check the length of the short name */
2012 Length
= wcslen(FindFileData
.cAlternateFileName
);
2015 /* This is our new first marker */
2016 First
= FindFileData
.cAlternateFileName
;
2020 /* Otherwise, the name is the delta between our current markers */
2021 Length
= Last
- First
;
2024 /* Update the return length with the short name length, if any */
2025 ReturnLength
+= Length
;
2027 /* Once again check for appropriate space and buffer */
2028 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2030 /* And do the copy if there is */
2031 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
2035 /* Now update the source pointer */
2037 if (*Src
== UNICODE_NULL
) break;
2039 /* Are there more names in there? */
2040 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
2044 /* The loop is done, is there anything left? */
2047 /* Get the length of the straggling path */
2048 Length
= wcslen(Src
);
2049 ReturnLength
+= Length
;
2051 /* Once again check for appropriate space and buffer */
2052 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2054 /* And do the copy if there is -- accounting for NULL here */
2055 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2057 /* What about our buffer? */
2060 /* Copy it into the caller's long path */
2061 RtlMoveMemory(lpszShortPath
,
2063 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2068 /* Buffer is too small, let the caller know, making space for NULL */
2073 /* We're all done */
2077 /* This is the goto for memory failures */
2078 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2081 /* General function end: free memory, restore error mode, return length */
2082 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
2083 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
2084 SetErrorMode(ErrorMode
);
2085 return ReturnLength
;
2091 * NOTE: Windows returns a dos/short (8.3) path
2095 GetTempPathA(IN DWORD nBufferLength
,
2098 WCHAR BufferW
[MAX_PATH
];
2101 ret
= GetTempPathW(MAX_PATH
, BufferW
);
2107 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2111 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
2121 GetTempPathW(IN DWORD count
,
2124 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
2125 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
2126 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2127 WCHAR tmp_path
[MAX_PATH
];
2130 DPRINT("%u,%p\n", count
, path
);
2132 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
2133 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
2134 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
2135 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
2140 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2144 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, tmp_path
, NULL
);
2147 if (ret
> MAX_PATH
- 2)
2149 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2153 if (tmp_path
[ret
-1] != '\\')
2155 tmp_path
[ret
++] = '\\';
2156 tmp_path
[ret
] = '\0';
2159 ret
++; /* add space for terminating 0 */
2163 lstrcpynW(path
, tmp_path
, count
);
2165 ret
--; /* return length without 0 */
2167 path
[0] = 0; /* avoid returning ambiguous "X:" */
2170 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
2179 GetCurrentDirectoryA(IN DWORD nBufferLength
,
2182 ANSI_STRING AnsiString
;
2184 PUNICODE_STRING StaticString
;
2187 StaticString
= &NtCurrentTeb()->StaticUnicodeString
;
2189 MaxLength
= nBufferLength
;
2190 if (nBufferLength
>= UNICODE_STRING_MAX_BYTES
)
2192 MaxLength
= UNICODE_STRING_MAX_BYTES
- 1;
2195 StaticString
->Length
= RtlGetCurrentDirectory_U(StaticString
->MaximumLength
,
2196 StaticString
->Buffer
);
2197 Status
= RtlUnicodeToMultiByteSize(&nBufferLength
,
2198 StaticString
->Buffer
,
2199 StaticString
->Length
);
2200 if (!NT_SUCCESS(Status
))
2202 BaseSetLastNTError(Status
);
2206 if (MaxLength
<= nBufferLength
)
2208 return nBufferLength
+ 1;
2211 AnsiString
.Buffer
= lpBuffer
;
2212 AnsiString
.MaximumLength
= MaxLength
;
2213 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, StaticString
, FALSE
);
2214 if (!NT_SUCCESS(Status
))
2216 BaseSetLastNTError(Status
);
2220 return AnsiString
.Length
;
2228 GetCurrentDirectoryW(IN DWORD nBufferLength
,
2231 return RtlGetCurrentDirectory_U(nBufferLength
* sizeof(WCHAR
), lpBuffer
) / sizeof(WCHAR
);
2239 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
2241 PUNICODE_STRING DirName
;
2246 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2250 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
);
2251 if (!DirName
) return FALSE
;
2253 if (CheckForSameCurdir(DirName
)) return FALSE
;
2255 Status
= RtlSetCurrentDirectory_U(DirName
);
2256 if (NT_SUCCESS(Status
)) return TRUE
;
2258 if ((*DirName
->Buffer
!= L
'"') || (DirName
->Length
<= 2))
2260 BaseSetLastNTError(Status
);
2264 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
+ 1);
2265 if (!DirName
) return FALSE
;
2267 Status
= RtlSetCurrentDirectory_U(DirName
);
2268 if (!NT_SUCCESS(Status
))
2270 BaseSetLastNTError(Status
);
2282 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
2285 UNICODE_STRING UnicodeString
;
2289 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2293 Status
= RtlInitUnicodeStringEx(&UnicodeString
, lpPathName
);
2294 if (NT_SUCCESS(Status
))
2296 if (!CheckForSameCurdir(&UnicodeString
))
2298 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
2302 if (!NT_SUCCESS(Status
))
2304 BaseSetLastNTError(Status
);
2316 GetSystemDirectoryA(IN LPSTR lpBuffer
,
2319 ANSI_STRING AnsiString
;
2323 /* Get the correct size of the Unicode Base directory */
2324 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2325 BaseWindowsSystemDirectory
.Buffer
,
2326 BaseWindowsSystemDirectory
.MaximumLength
);
2327 if (!NT_SUCCESS(Status
)) return 0;
2329 if (uSize
< AnsiLength
) return AnsiLength
;
2331 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2333 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2334 &BaseWindowsSystemDirectory
,
2336 if (!NT_SUCCESS(Status
)) return 0;
2338 return AnsiString
.Length
;
2346 GetSystemDirectoryW(IN LPWSTR lpBuffer
,
2351 ReturnLength
= BaseWindowsSystemDirectory
.MaximumLength
;
2352 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2354 RtlCopyMemory(lpBuffer
,
2355 BaseWindowsSystemDirectory
.Buffer
,
2356 BaseWindowsSystemDirectory
.Length
);
2357 lpBuffer
[BaseWindowsSystemDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2359 ReturnLength
= BaseWindowsSystemDirectory
.Length
;
2362 return ReturnLength
/ sizeof(WCHAR
);
2370 GetWindowsDirectoryA(IN LPSTR lpBuffer
,
2373 /* Is this a TS installation? */
2374 if (gpTermsrvGetWindowsDirectoryA
) UNIMPLEMENTED
;
2376 /* Otherwise, call the System API */
2377 return GetSystemWindowsDirectoryA(lpBuffer
, uSize
);
2385 GetWindowsDirectoryW(IN LPWSTR lpBuffer
,
2388 /* Is this a TS installation? */
2389 if (gpTermsrvGetWindowsDirectoryW
) UNIMPLEMENTED
;
2391 /* Otherwise, call the System API */
2392 return GetSystemWindowsDirectoryW(lpBuffer
, uSize
);
2400 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer
,
2403 ANSI_STRING AnsiString
;
2407 /* Get the correct size of the Unicode Base directory */
2408 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2409 BaseWindowsDirectory
.Buffer
,
2410 BaseWindowsDirectory
.MaximumLength
);
2411 if (!NT_SUCCESS(Status
)) return 0;
2413 if (uSize
< AnsiLength
) return AnsiLength
;
2415 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2417 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2418 &BaseWindowsDirectory
,
2420 if (!NT_SUCCESS(Status
)) return 0;
2422 return AnsiString
.Length
;
2430 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer
,
2435 ReturnLength
= BaseWindowsDirectory
.MaximumLength
;
2436 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2438 RtlCopyMemory(lpBuffer
,
2439 BaseWindowsDirectory
.Buffer
,
2440 BaseWindowsDirectory
.Length
);
2441 lpBuffer
[BaseWindowsDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2443 ReturnLength
= BaseWindowsDirectory
.Length
;
2446 return ReturnLength
/ sizeof(WCHAR
);
2454 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer
,
2461 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2471 GetSystemWow64DirectoryA(IN LPSTR lpBuffer
,
2478 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);