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 DPRINT("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
);
1293 SearchPathW(IN LPCWSTR lpPath
,
1294 IN LPCWSTR lpFileName
,
1295 IN LPCWSTR lpExtension
,
1296 IN DWORD nBufferLength
,
1298 OUT LPWSTR
*lpFilePart
)
1300 UNICODE_STRING FileNameString
, ExtensionString
, PathString
, CallerBuffer
;
1301 ULONG Flags
, LengthNeeded
, FilePartSize
;
1305 /* Default flags for RtlDosSearchPath_Ustr */
1308 /* Clear file part in case we fail */
1309 if (lpFilePart
) *lpFilePart
= NULL
;
1311 /* Initialize path buffer for free later */
1312 PathString
.Buffer
= NULL
;
1314 /* Convert filename to a unicode string and eliminate trailing spaces */
1315 RtlInitUnicodeString(&FileNameString
, lpFileName
);
1316 while ((FileNameString
.Length
>= sizeof(WCHAR
)) &&
1317 (FileNameString
.Buffer
[(FileNameString
.Length
/ sizeof(WCHAR
)) - 1] == L
' '))
1319 FileNameString
.Length
-= sizeof(WCHAR
);
1322 /* Was it all just spaces? */
1323 if (!FileNameString
.Length
)
1326 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
1330 /* Convert extension to a unicode string */
1331 RtlInitUnicodeString(&ExtensionString
, lpExtension
);
1333 /* Check if the user sent a path */
1336 /* Convert it to a unicode string too */
1337 Status
= RtlInitUnicodeStringEx(&PathString
, lpPath
);
1338 if (NT_ERROR(Status
))
1340 /* Fail if it was too long */
1341 BaseSetLastNTError(Status
);
1347 /* A path wasn't sent, so compute it ourselves */
1348 PathString
.Buffer
= BaseComputeProcessSearchPath();
1349 if (!PathString
.Buffer
)
1351 /* Fail if we couldn't compute it */
1352 BaseSetLastNTError(STATUS_NO_MEMORY
);
1356 /* See how big the computed path is */
1357 LengthNeeded
= lstrlenW(PathString
.Buffer
);
1358 if (LengthNeeded
> UNICODE_STRING_MAX_CHARS
)
1360 /* Fail if it's too long */
1361 BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1365 /* Set the path size now that we have it */
1366 PathString
.MaximumLength
= PathString
.Length
= LengthNeeded
* sizeof(WCHAR
);
1368 /* Request SxS isolation from RtlDosSearchPath_Ustr */
1372 /* Create the string that describes the output buffer from the caller */
1373 CallerBuffer
.Length
= 0;
1374 CallerBuffer
.Buffer
= lpBuffer
;
1376 /* How much space does the caller have? */
1377 if (nBufferLength
<= UNICODE_STRING_MAX_CHARS
)
1379 /* Add it into the string */
1380 CallerBuffer
.MaximumLength
= nBufferLength
* sizeof(WCHAR
);
1384 /* Caller wants too much, limit it to the maximum length of a string */
1385 CallerBuffer
.MaximumLength
= UNICODE_STRING_MAX_BYTES
;
1388 /* Call Rtl to do the work */
1389 Status
= RtlDosSearchPath_Ustr(Flags
,
1398 if (NT_ERROR(Status
))
1400 /* Check for unusual status codes */
1401 if ((Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
1403 /* Print them out since maybe an app needs fixing */
1404 DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
1408 DbgPrint(" Path = %wZ\n", &PathString
);
1411 /* Check if the failure was due to a small buffer */
1412 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1414 /* Check if the length was actually too big for Rtl to work with */
1415 Result
= LengthNeeded
/ sizeof(WCHAR
);
1416 if (Result
> 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1420 /* Some other error, set the error code */
1421 BaseSetLastNTError(Status
);
1426 /* It worked! Write the file part now */
1427 if (lpFilePart
) *lpFilePart
= &lpBuffer
[FilePartSize
];
1429 /* Convert the final result length */
1430 Result
= CallerBuffer
.Length
/ sizeof(WCHAR
);
1434 /* Check if there was a dynamic path stirng to free */
1435 if ((PathString
.Buffer
!= lpPath
) && (PathString
.Buffer
))
1438 RtlFreeHeap(RtlGetProcessHeap(), 0, PathString
.Buffer
);
1441 /* Return the final result lenght */
1450 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
1451 IN LPWSTR lpszLongPath
,
1454 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1460 BOOLEAN Found
= FALSE
;
1461 WIN32_FIND_DATAW FindFileData
;
1463 /* Initialize so Quickie knows there's nothing to do */
1464 Buffer
= Original
= NULL
;
1467 /* First check if the input path was obviously NULL */
1470 /* Fail the request */
1471 SetLastError(ERROR_INVALID_PARAMETER
);
1475 /* We will be touching removed, removable drives -- don't warn the user */
1476 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1478 /* Do a simple check to see if the path exists */
1479 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
1481 /* It doesn't, so fail */
1486 /* Now get a pointer to the actual path, skipping indicators */
1487 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
1489 /* Is there any path or filename in there? */
1491 (*Path
== UNICODE_NULL
) ||
1492 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
1494 /* There isn't, so the long path is simply the short path */
1495 ReturnLength
= wcslen(lpszShortPath
);
1497 /* Is there space for it? */
1498 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1500 /* Make sure the pointers aren't already the same */
1501 if (lpszLongPath
!= lpszShortPath
)
1503 /* They're not -- copy the short path into the long path */
1504 RtlMoveMemory(lpszLongPath
,
1506 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1511 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1517 /* We are still in the game -- compute the current size */
1518 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
1519 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1520 if (!Original
) goto ErrorQuickie
;
1522 /* Make a copy of it */
1523 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
1525 /* Compute the new first and last markers */
1526 First
= &Original
[First
- lpszShortPath
];
1527 Last
= &Original
[Last
- lpszShortPath
];
1529 /* Set the current destination pointer for a copy */
1533 * Windows allows the paths to overlap -- we have to be careful with this and
1534 * see if it's same to do so, and if not, allocate our own internal buffer
1535 * that we'll return at the end.
1537 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1539 if ((cchBuffer
) && (lpszLongPath
) &&
1540 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
1541 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
1543 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1544 if (!Buffer
) goto ErrorQuickie
;
1546 /* New destination */
1550 /* Prepare for the loop */
1555 /* Current delta in the loop */
1556 Length
= First
- Src
;
1558 /* Update the return length by it */
1559 ReturnLength
+= Length
;
1561 /* Is there a delta? If so, is there space and buffer for it? */
1562 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1564 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1568 /* "Terminate" this portion of the path's substring so we can do a find */
1570 *Last
= UNICODE_NULL
;
1571 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1574 /* This portion wasn't found, so fail */
1575 if (FindHandle
== INVALID_HANDLE_VALUE
)
1581 /* Close the find handle */
1582 FindClose(FindHandle
);
1584 /* Now check the length of the long name */
1585 Length
= wcslen(FindFileData
.cFileName
);
1588 /* This is our new first marker */
1589 First
= FindFileData
.cFileName
;
1593 /* Otherwise, the name is the delta between our current markers */
1594 Length
= Last
- First
;
1597 /* Update the return length with the short name length, if any */
1598 ReturnLength
+= Length
;
1600 /* Once again check for appropriate space and buffer */
1601 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1603 /* And do the copy if there is */
1604 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1608 /* Now update the source pointer */
1610 if (*Src
== UNICODE_NULL
) break;
1612 /* Are there more names in there? */
1613 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1617 /* The loop is done, is there anything left? */
1620 /* Get the length of the straggling path */
1621 Length
= wcslen(Src
);
1622 ReturnLength
+= Length
;
1624 /* Once again check for appropriate space and buffer */
1625 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1627 /* And do the copy if there is -- accounting for NULL here */
1628 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1630 /* What about our buffer? */
1633 /* Copy it into the caller's long path */
1634 RtlMoveMemory(lpszLongPath
,
1636 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1641 /* Buffer is too small, let the caller know, making space for NULL */
1646 /* We're all done */
1650 /* This is the goto for memory failures */
1651 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1654 /* General function end: free memory, restore error mode, return length */
1655 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1656 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1657 SetErrorMode(ErrorMode
);
1658 return ReturnLength
;
1666 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1667 IN LPSTR lpszLongPath
,
1670 ULONG Result
, PathLength
;
1673 UNICODE_STRING LongPathUni
, ShortPathUni
;
1674 ANSI_STRING LongPathAnsi
;
1675 WCHAR LongPathBuffer
[MAX_PATH
];
1678 LongPathAnsi
.Buffer
= NULL
;
1679 ShortPathUni
.Buffer
= NULL
;
1684 SetLastError(ERROR_INVALID_PARAMETER
);
1688 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1689 if (!NT_SUCCESS(Status
)) goto Quickie
;
1691 LongPath
= LongPathBuffer
;
1693 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1694 if (PathLength
>= MAX_PATH
)
1696 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1700 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1704 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1708 if (!PathLength
) goto Quickie
;
1710 ShortPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1711 LongPathUni
.Buffer
= LongPath
;
1712 LongPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1714 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1715 if (!NT_SUCCESS(Status
))
1717 BaseSetLastNTError(Status
);
1721 Result
= LongPathAnsi
.Length
;
1722 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1724 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1725 lpszLongPath
[Result
] = ANSI_NULL
;
1729 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1733 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1734 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1735 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1737 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1747 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1748 IN LPSTR lpszShortPath
,
1751 ULONG Result
, PathLength
;
1754 UNICODE_STRING LongPathUni
, ShortPathUni
;
1755 ANSI_STRING ShortPathAnsi
;
1756 WCHAR ShortPathBuffer
[MAX_PATH
];
1759 ShortPathAnsi
.Buffer
= NULL
;
1760 LongPathUni
.Buffer
= NULL
;
1765 SetLastError(ERROR_INVALID_PARAMETER
);
1769 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1770 if (!NT_SUCCESS(Status
)) goto Quickie
;
1772 ShortPath
= ShortPathBuffer
;
1774 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1775 if (PathLength
>= MAX_PATH
)
1777 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1781 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1785 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1789 if (!PathLength
) goto Quickie
;
1791 LongPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1792 ShortPathUni
.Buffer
= ShortPath
;
1793 ShortPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1795 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1796 if (!NT_SUCCESS(Status
))
1798 BaseSetLastNTError(Status
);
1802 Result
= ShortPathAnsi
.Length
;
1803 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1805 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1806 lpszShortPath
[Result
] = ANSI_NULL
;
1810 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1814 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1815 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1816 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1818 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1828 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1829 IN LPWSTR lpszShortPath
,
1832 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1838 BOOLEAN Found
= FALSE
;
1839 WIN32_FIND_DATAW FindFileData
;
1841 /* Initialize so Quickie knows there's nothing to do */
1842 Buffer
= Original
= NULL
;
1845 /* First check if the input path was obviously NULL */
1848 /* Fail the request */
1849 SetLastError(ERROR_INVALID_PARAMETER
);
1853 /* We will be touching removed, removable drives -- don't warn the user */
1854 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1856 /* Do a simple check to see if the path exists */
1857 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1859 /* Windows checks for an application compatibility flag to allow this */
1860 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1862 /* It doesn't, so fail */
1868 /* Now get a pointer to the actual path, skipping indicators */
1869 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1871 /* Is there any path or filename in there? */
1873 (*Path
== UNICODE_NULL
) ||
1874 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1876 /* There isn't, so the long path is simply the short path */
1877 ReturnLength
= wcslen(lpszLongPath
);
1879 /* Is there space for it? */
1880 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1882 /* Make sure the pointers aren't already the same */
1883 if (lpszLongPath
!= lpszShortPath
)
1885 /* They're not -- copy the short path into the long path */
1886 RtlMoveMemory(lpszShortPath
,
1888 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1893 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1899 /* We are still in the game -- compute the current size */
1900 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1901 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1902 if (!Original
) goto ErrorQuickie
;
1904 /* Make a copy of it */
1905 wcsncpy(Original
, lpszLongPath
, Length
);
1907 /* Compute the new first and last markers */
1908 First
= &Original
[First
- lpszLongPath
];
1909 Last
= &Original
[Last
- lpszLongPath
];
1911 /* Set the current destination pointer for a copy */
1912 Dst
= lpszShortPath
;
1915 * Windows allows the paths to overlap -- we have to be careful with this and
1916 * see if it's same to do so, and if not, allocate our own internal buffer
1917 * that we'll return at the end.
1919 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1921 if ((cchBuffer
) && (lpszShortPath
) &&
1922 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1923 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1925 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1926 if (!Buffer
) goto ErrorQuickie
;
1928 /* New destination */
1932 /* Prepare for the loop */
1937 /* Current delta in the loop */
1938 Length
= First
- Src
;
1940 /* Update the return length by it */
1941 ReturnLength
+= Length
;
1943 /* Is there a delta? If so, is there space and buffer for it? */
1944 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1946 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1950 /* "Terminate" this portion of the path's substring so we can do a find */
1952 *Last
= UNICODE_NULL
;
1953 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1956 /* This portion wasn't found, so fail */
1957 if (FindHandle
== INVALID_HANDLE_VALUE
)
1963 /* Close the find handle */
1964 FindClose(FindHandle
);
1966 /* Now check the length of the short name */
1967 Length
= wcslen(FindFileData
.cAlternateFileName
);
1970 /* This is our new first marker */
1971 First
= FindFileData
.cAlternateFileName
;
1975 /* Otherwise, the name is the delta between our current markers */
1976 Length
= Last
- First
;
1979 /* Update the return length with the short name length, if any */
1980 ReturnLength
+= Length
;
1982 /* Once again check for appropriate space and buffer */
1983 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1985 /* And do the copy if there is */
1986 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1990 /* Now update the source pointer */
1992 if (*Src
== UNICODE_NULL
) break;
1994 /* Are there more names in there? */
1995 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
1999 /* The loop is done, is there anything left? */
2002 /* Get the length of the straggling path */
2003 Length
= wcslen(Src
);
2004 ReturnLength
+= Length
;
2006 /* Once again check for appropriate space and buffer */
2007 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2009 /* And do the copy if there is -- accounting for NULL here */
2010 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2012 /* What about our buffer? */
2015 /* Copy it into the caller's long path */
2016 RtlMoveMemory(lpszShortPath
,
2018 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2023 /* Buffer is too small, let the caller know, making space for NULL */
2028 /* We're all done */
2032 /* This is the goto for memory failures */
2033 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2036 /* General function end: free memory, restore error mode, return length */
2037 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
2038 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
2039 SetErrorMode(ErrorMode
);
2040 return ReturnLength
;
2046 * NOTE: Windows returns a dos/short (8.3) path
2050 GetTempPathA(IN DWORD nBufferLength
,
2053 WCHAR BufferW
[MAX_PATH
];
2056 ret
= GetTempPathW(MAX_PATH
, BufferW
);
2062 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2066 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
2076 GetTempPathW(IN DWORD count
,
2079 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
2080 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
2081 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2082 WCHAR tmp_path
[MAX_PATH
];
2085 DPRINT("%u,%p\n", count
, path
);
2087 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
2088 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
2089 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
2090 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
2095 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2099 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, tmp_path
, NULL
);
2102 if (ret
> MAX_PATH
- 2)
2104 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2108 if (tmp_path
[ret
-1] != '\\')
2110 tmp_path
[ret
++] = '\\';
2111 tmp_path
[ret
] = '\0';
2114 ret
++; /* add space for terminating 0 */
2118 lstrcpynW(path
, tmp_path
, count
);
2120 ret
--; /* return length without 0 */
2122 path
[0] = 0; /* avoid returning ambiguous "X:" */
2125 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
2134 GetCurrentDirectoryA(IN DWORD nBufferLength
,
2137 ANSI_STRING AnsiString
;
2139 PUNICODE_STRING StaticString
;
2142 StaticString
= &NtCurrentTeb()->StaticUnicodeString
;
2144 MaxLength
= nBufferLength
;
2145 if (nBufferLength
>= UNICODE_STRING_MAX_BYTES
)
2147 MaxLength
= UNICODE_STRING_MAX_BYTES
- 1;
2150 StaticString
->Length
= RtlGetCurrentDirectory_U(StaticString
->MaximumLength
,
2151 StaticString
->Buffer
);
2152 Status
= RtlUnicodeToMultiByteSize(&nBufferLength
,
2153 StaticString
->Buffer
,
2154 StaticString
->Length
);
2155 if (!NT_SUCCESS(Status
))
2157 BaseSetLastNTError(Status
);
2161 if (MaxLength
<= nBufferLength
)
2163 return nBufferLength
+ 1;
2166 AnsiString
.Buffer
= lpBuffer
;
2167 AnsiString
.MaximumLength
= MaxLength
;
2168 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, StaticString
, FALSE
);
2169 if (!NT_SUCCESS(Status
))
2171 BaseSetLastNTError(Status
);
2175 return AnsiString
.Length
;
2183 GetCurrentDirectoryW(IN DWORD nBufferLength
,
2186 return RtlGetCurrentDirectory_U(nBufferLength
* sizeof(WCHAR
), lpBuffer
) / sizeof(WCHAR
);
2194 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
2196 PUNICODE_STRING DirName
;
2201 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2205 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
);
2206 if (!DirName
) return FALSE
;
2208 if (CheckForSameCurdir(DirName
)) return FALSE
;
2210 Status
= RtlSetCurrentDirectory_U(DirName
);
2211 if (NT_SUCCESS(Status
)) return TRUE
;
2213 if ((*DirName
->Buffer
!= L
'"') || (DirName
->Length
<= 2))
2215 BaseSetLastNTError(Status
);
2219 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
+ 1);
2220 if (!DirName
) return FALSE
;
2222 Status
= RtlSetCurrentDirectory_U(DirName
);
2223 if (!NT_SUCCESS(Status
))
2225 BaseSetLastNTError(Status
);
2237 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
2240 UNICODE_STRING UnicodeString
;
2244 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2248 Status
= RtlInitUnicodeStringEx(&UnicodeString
, lpPathName
);
2249 if (NT_SUCCESS(Status
))
2251 if (!CheckForSameCurdir(&UnicodeString
))
2253 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
2257 if (!NT_SUCCESS(Status
))
2259 BaseSetLastNTError(Status
);
2271 GetSystemDirectoryA(IN LPSTR lpBuffer
,
2274 ANSI_STRING AnsiString
;
2278 /* Get the correct size of the Unicode Base directory */
2279 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2280 BaseWindowsSystemDirectory
.Buffer
,
2281 BaseWindowsSystemDirectory
.MaximumLength
);
2282 if (!NT_SUCCESS(Status
)) return 0;
2284 if (uSize
< AnsiLength
) return AnsiLength
;
2286 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2288 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2289 &BaseWindowsSystemDirectory
,
2291 if (!NT_SUCCESS(Status
)) return 0;
2293 return AnsiString
.Length
;
2301 GetSystemDirectoryW(IN LPWSTR lpBuffer
,
2306 ReturnLength
= BaseWindowsSystemDirectory
.MaximumLength
;
2307 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2309 RtlCopyMemory(lpBuffer
,
2310 BaseWindowsSystemDirectory
.Buffer
,
2311 BaseWindowsSystemDirectory
.Length
);
2312 lpBuffer
[BaseWindowsSystemDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2314 ReturnLength
= BaseWindowsSystemDirectory
.Length
;
2317 return ReturnLength
/ sizeof(WCHAR
);
2325 GetWindowsDirectoryA(IN LPSTR lpBuffer
,
2328 /* Is this a TS installation? */
2329 if (gpTermsrvGetWindowsDirectoryA
) UNIMPLEMENTED
;
2331 /* Otherwise, call the System API */
2332 return GetSystemWindowsDirectoryA(lpBuffer
, uSize
);
2340 GetWindowsDirectoryW(IN LPWSTR lpBuffer
,
2343 /* Is this a TS installation? */
2344 if (gpTermsrvGetWindowsDirectoryW
) UNIMPLEMENTED
;
2346 /* Otherwise, call the System API */
2347 return GetSystemWindowsDirectoryW(lpBuffer
, uSize
);
2355 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer
,
2358 ANSI_STRING AnsiString
;
2362 /* Get the correct size of the Unicode Base directory */
2363 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2364 BaseWindowsDirectory
.Buffer
,
2365 BaseWindowsDirectory
.MaximumLength
);
2366 if (!NT_SUCCESS(Status
)) return 0;
2368 if (uSize
< AnsiLength
) return AnsiLength
;
2370 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2372 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2373 &BaseWindowsDirectory
,
2375 if (!NT_SUCCESS(Status
)) return 0;
2377 return AnsiString
.Length
;
2385 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer
,
2390 ReturnLength
= BaseWindowsDirectory
.MaximumLength
;
2391 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2393 RtlCopyMemory(lpBuffer
,
2394 BaseWindowsDirectory
.Buffer
,
2395 BaseWindowsDirectory
.Length
);
2396 lpBuffer
[BaseWindowsDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2398 ReturnLength
= BaseWindowsDirectory
.Length
;
2401 return ReturnLength
/ sizeof(WCHAR
);
2409 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer
,
2416 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2426 GetSystemWow64DirectoryA(IN LPSTR lpBuffer
,
2433 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);