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 unless it's empty */
214 ASSERT(!(EnvPath
.Length
& 1));
217 /* Reserve space for the variable and a semicolon */
218 PathLengthInBytes
+= (EnvPath
.Length
+ sizeof(L
';'));
223 /* Compute the size of the default search path */
224 case BaseSearchPathDefault
:
226 /* Just add it... it already has a ';' at the end */
227 ASSERT(!(BaseDefaultPath
.Length
& 1));
228 PathLengthInBytes
+= BaseDefaultPath
.Length
;
231 /* Compute the size of the current app directory */
232 case BaseSearchPathApp
:
233 /* Find out where the app name ends, to get only the directory */
234 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
236 /* Check if there was no application name passed in */
237 if (!(AppName
) || !(AppNameEnd
))
239 /* Do we have a per-thread CURDIR to use? */
240 if (NtCurrentTeb()->NtTib
.SubSystemTib
)
242 /* This means someone added RTL_PERTHREAD_CURDIR */
243 UNIMPLEMENTED_DBGBREAK();
246 /* We do not. Do we have the LDR_ENTRY for the executable? */
247 if (!BasepExeLdrEntry
)
249 /* We do not. Grab it */
250 LdrEnumerateLoadedModules(0,
251 BasepLocateExeLdrEntry
,
252 NtCurrentPeb()->ImageBaseAddress
);
255 /* Now do we have it? */
256 if (BasepExeLdrEntry
)
258 /* Yes, so read the name out of it */
259 AppName
= BasepExeLdrEntry
->FullDllName
.Buffer
;
262 /* Find out where the app name ends, to get only the directory */
263 if (AppName
) AppNameEnd
= BasepEndOfDirName(AppName
);
266 /* So, do we have an application name and its directory? */
267 if ((AppName
) && (AppNameEnd
))
269 /* Add the size of the app's directory, plus the separator */
270 PathLengthInBytes
+= ((AppNameEnd
- AppName
) * sizeof(WCHAR
)) + sizeof(L
';');
279 /* Bam, all done, we now have the final path size */
280 ASSERT(PathLengthInBytes
> 0);
281 ASSERT(!(PathLengthInBytes
& 1));
283 /* Allocate the buffer to hold it */
284 PathBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes
);
288 Status
= STATUS_NO_MEMORY
;
292 /* Now we loop again, this time to copy the data */
293 PathCurrent
= PathBuffer
;
294 for (Order
= PathOrder
; *Order
!= BaseSearchPathInvalid
; Order
++) {
297 /* Add the DLL path */
298 case BaseSearchPathDll
:
299 if (BaseDllDirectory
.Length
)
301 /* Copy it in the buffer, ASSERT there's enough space */
302 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + BaseDllDirectory
.Length
) <= PathLengthInBytes
);
303 RtlCopyMemory(PathCurrent
,
304 BaseDllDirectory
.Buffer
,
305 BaseDllDirectory
.Length
);
307 /* Update the current pointer, add a separator */
308 PathCurrent
+= (BaseDllDirectory
.Length
/ sizeof(WCHAR
));
309 *PathCurrent
++ = ';';
313 /* Add the current application path */
314 case BaseSearchPathApp
:
315 if ((AppName
) && (AppNameEnd
))
317 /* Copy it in the buffer, ASSERT there's enough space */
318 ASSERT(((PathCurrent
- PathBuffer
+ 1 + (AppNameEnd
- AppName
)) * sizeof(WCHAR
)) <= PathLengthInBytes
);
319 RtlCopyMemory(PathCurrent
,
321 (AppNameEnd
- AppName
) * sizeof(WCHAR
));
323 /* Update the current pointer, add a separator */
324 PathCurrent
+= AppNameEnd
- AppName
;
325 *PathCurrent
++ = ';';
329 /* Add the default search path */
330 case BaseSearchPathDefault
:
331 /* Copy it in the buffer, ASSERT there's enough space */
332 ASSERT((((PathCurrent
- PathBuffer
) * sizeof(WCHAR
)) + BaseDefaultPath
.Length
) <= PathLengthInBytes
);
333 RtlCopyMemory(PathCurrent
, BaseDefaultPath
.Buffer
, BaseDefaultPath
.Length
);
335 /* Update the current pointer. The default path already has a ";" */
336 PathCurrent
+= (BaseDefaultPath
.Length
/ sizeof(WCHAR
));
339 /* Add the path in the PATH environment variable */
340 case BaseSearchPathEnv
:
343 /* Copy it in the buffer, ASSERT there's enough space */
344 ASSERT((((PathCurrent
- PathBuffer
+ 1) * sizeof(WCHAR
)) + EnvPath
.Length
) <= PathLengthInBytes
);
345 RtlCopyMemory(PathCurrent
, EnvPath
.Buffer
, EnvPath
.Length
);
347 /* Update the current pointer, add a separator */
348 PathCurrent
+= (EnvPath
.Length
/ sizeof(WCHAR
));
349 *PathCurrent
++ = ';';
353 /* Add the current directory */
354 case BaseSearchPathCurrent
:
356 /* Copy it in the buffer, ASSERT there's enough space */
357 ASSERT(((PathCurrent
- PathBuffer
+ 2) * sizeof(WCHAR
)) <= PathLengthInBytes
);
358 *PathCurrent
++ = '.';
360 /* Add the path separator */
361 *PathCurrent
++ = ';';
369 /* Everything should've perfectly fit in there */
370 ASSERT((PathCurrent
- PathBuffer
) * sizeof(WCHAR
) == PathLengthInBytes
);
371 ASSERT(PathCurrent
> PathBuffer
);
373 /* Terminate the whole thing */
374 PathCurrent
[-1] = UNICODE_NULL
;
377 /* Exit path: free our buffers */
378 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
381 /* This only gets freed in the failure path, since caller wants it */
382 if (!NT_SUCCESS(Status
))
384 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer
);
389 /* Return the path! */
395 BaseComputeProcessSearchPath(VOID
)
397 DPRINT("Computing Process Search path\n");
399 /* Compute the path using default process order */
400 return BasepComputeProcessPath(BaseProcessOrder
, NULL
, NULL
);
405 BaseComputeProcessExePath(IN LPWSTR FullPath
)
407 PBASE_SEARCH_PATH_TYPE PathOrder
;
408 DPRINT("Computing EXE path: %S\n", FullPath
);
410 /* Check if we should use the current directory */
411 PathOrder
= NeedCurrentDirectoryForExePathW(FullPath
) ?
412 BaseProcessOrder
: BaseProcessOrderNoCurrent
;
414 /* And now compute the path */
415 return BasepComputeProcessPath(PathOrder
, NULL
, NULL
);
420 BaseComputeProcessDllPath(IN LPWSTR FullPath
,
421 IN PVOID Environment
)
423 LPWSTR DllPath
= NULL
;
424 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
425 UNICODE_STRING ValueName
= RTL_CONSTANT_STRING(L
"SafeDllSearchMode");
426 OBJECT_ATTRIBUTES ObjectAttributes
= RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName
, OBJ_CASE_INSENSITIVE
);
427 KEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
431 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement
, OldCurrentDirPlacement
;
433 /* Acquire DLL directory lock */
434 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
436 /* Check if we have a base dll directory */
437 if (BaseDllDirectory
.Buffer
)
439 /* Then compute the process path using DLL order (without curdir) */
440 DllPath
= BasepComputeProcessPath(BaseDllOrderNoCurrent
, FullPath
, Environment
);
442 /* Release DLL directory lock */
443 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
445 /* Return dll path */
449 /* Release DLL directory lock */
450 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
452 /* Read the current placement */
453 CurrentDirPlacement
= BasepDllCurrentDirPlacement
;
454 if (CurrentDirPlacement
== BaseCurrentDirPlacementInvalid
)
456 /* Open the configuration key */
457 Status
= NtOpenKey(&KeyHandle
, KEY_QUERY_VALUE
, &ObjectAttributes
);
458 if (NT_SUCCESS(Status
))
460 /* Query if safe search is enabled */
461 Status
= NtQueryValueKey(KeyHandle
,
463 KeyValuePartialInformation
,
467 if (NT_SUCCESS(Status
))
469 /* Read the value if the size is OK */
470 if (ResultLength
== sizeof(PartialInfo
))
472 CurrentDirPlacement
= *(PULONG
)PartialInfo
.Data
;
476 /* Close the handle */
479 /* Validate the registry value */
480 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
481 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
483 /* Default to safe search */
484 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
488 /* Update the placement and read the old one */
489 OldCurrentDirPlacement
= InterlockedCompareExchange((PLONG
)&BasepDllCurrentDirPlacement
,
491 BaseCurrentDirPlacementInvalid
);
492 if (OldCurrentDirPlacement
!= BaseCurrentDirPlacementInvalid
)
494 /* If there already was a placement, use it */
495 CurrentDirPlacement
= OldCurrentDirPlacement
;
499 /* Check if the placement is invalid or not set */
500 if ((CurrentDirPlacement
<= BaseCurrentDirPlacementInvalid
) ||
501 (CurrentDirPlacement
>= BaseCurrentDirPlacementMax
))
503 /* Default to safe search */
504 CurrentDirPlacement
= BaseCurrentDirPlacementSafe
;
507 /* Compute the process path using either normal or safe search */
508 DllPath
= BasepComputeProcessPath(BaseDllOrderCurrent
[CurrentDirPlacement
],
512 /* Return dll path */
518 CheckForSameCurdir(IN PUNICODE_STRING DirName
)
520 PUNICODE_STRING CurDir
;
523 UNICODE_STRING CurDirCopy
;
525 CurDir
= &NtCurrentPeb()->ProcessParameters
->CurrentDirectory
.DosPath
;
527 CurLength
= CurDir
->Length
;
528 if (CurDir
->Length
<= 6)
530 if (CurLength
!= DirName
->Length
) return FALSE
;
534 if ((CurLength
- 2) != DirName
->Length
) return FALSE
;
539 CurDirCopy
= *CurDir
;
540 if (CurDirCopy
.Length
> 6) CurDirCopy
.Length
-= 2;
544 if (RtlEqualUnicodeString(&CurDirCopy
, DirName
, TRUE
)) Result
= TRUE
;
552 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
553 * identical (other than the Rtl can optionally check for spaces), however the
554 * Rtl will always convert to OEM, while kernel32 has two possible file modes
555 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
556 * the correct compatible results
560 IsShortName_U(IN PWCHAR Name
,
563 BOOLEAN HasExtension
;
566 UNICODE_STRING UnicodeName
;
567 ANSI_STRING AnsiName
;
569 CHAR AnsiBuffer
[MAX_PATH
];
572 /* What do you think 8.3 means? */
573 if (Length
> 12) return FALSE
;
575 /* Sure, any empty name is a short name */
576 if (!Length
) return TRUE
;
578 /* This could be . or .. or something else */
581 /* Which one is it */
582 if ((Length
== 1) || ((Length
== 2) && *(Name
+ 1) == L
'.'))
584 /* . or .., this is good */
588 /* Some other bizare dot-based name, not good */
592 /* Initialize our two strings */
593 RtlInitEmptyAnsiString(&AnsiName
, AnsiBuffer
, MAX_PATH
);
594 RtlInitEmptyUnicodeString(&UnicodeName
, Name
, (USHORT
)Length
* sizeof(WCHAR
));
595 UnicodeName
.Length
= UnicodeName
.MaximumLength
;
597 /* Now do the conversion */
598 Status
= BasepUnicodeStringTo8BitString(&AnsiName
, &UnicodeName
, FALSE
);
599 if (!NT_SUCCESS(Status
)) return FALSE
;
601 /* Now we loop the name */
602 HasExtension
= FALSE
;
603 for (i
= 0, Dots
= Length
- 1; i
< AnsiName
.Length
; i
++, Dots
--)
605 /* Read the current byte */
606 c
= AnsiName
.Buffer
[i
];
609 if (IsDBCSLeadByte(c
))
611 /* If we're near the end of the string, we can't allow a DBCS */
612 if ((!(HasExtension
) && (i
>= 7)) || (i
== AnsiName
.Length
- 1))
617 /* Otherwise we skip over it */
621 /* Check for illegal characters */
622 if ((c
> 0x7F) || (IllegalMask
[c
/ 32] & (1 << (c
% 32))))
627 /* Check if this is perhaps an extension? */
630 /* Unless the extension is too large or there's more than one */
631 if ((HasExtension
) || (Dots
> 3)) return FALSE
;
633 /* This looks like an extension */
637 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
638 if ((i
>= 8) && !(HasExtension
)) return FALSE
;
641 /* You survived the loop, this is a good short name */
647 IsLongName_U(IN PWCHAR FileName
,
650 BOOLEAN HasExtension
;
653 /* More than 8.3, any combination of dots, and NULL names are all long */
654 if (!(Length
) || (Length
> 12) || (*FileName
== L
'.')) return TRUE
;
656 /* Otherwise, initialize our scanning loop */
657 HasExtension
= FALSE
;
658 for (i
= 0, Dots
= Length
- 1; i
< Length
; i
++, Dots
--)
660 /* Check if this could be an extension */
661 if (FileName
[i
] == L
'.')
663 /* Unlike the short case, we WANT more than one extension, or a long one */
664 if ((HasExtension
) || (Dots
> 3))
671 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
672 if ((i
>= 8) && (!HasExtension
)) return TRUE
;
675 /* The name *seems* to conform to 8.3 */
681 FindLFNorSFN_U(IN PWCHAR Path
,
691 /* Loop while there is something in the path */
694 /* Loop within the path skipping slashes */
695 while ((*Path
== L
'\\') || (*Path
== L
'/')) Path
++;
697 /* Make sure there's something after the slashes too! */
698 if (*Path
== UNICODE_NULL
) break;
700 /* Now skip past the file name until we get to the first slash */
702 while ((*p
) && ((*p
!= L
'\\') && (*p
!= L
'/'))) p
++;
704 /* Whatever is in between those two is now the file name length */
708 * Check if it is valid
709 * Note that !IsShortName != IsLongName, these two functions simply help
710 * us determine if a conversion is necessary or not.
711 * "Found" really means: "Is a conversion necessary?", hence the "!"
713 Found
= UseShort
? !IsShortName_U(Path
, Length
) : !IsLongName_U(Path
, Length
);
716 /* It is! did the caller request to know the markers? */
717 if ((First
) && (Last
))
726 /* Is there anything else following this sub-path/filename? */
727 if (*p
== UNICODE_NULL
) break;
729 /* Yes, keep going */
733 /* Return if anything was found and valid */
739 SkipPathTypeIndicator_U(IN LPWSTR Path
)
744 /* Check what kind of path this is and how many slashes to skip */
745 switch (RtlDetermineDosPathNameType_U(Path
))
747 case RtlPathTypeUncAbsolute
:
748 case RtlPathTypeLocalDevice
:
750 /* Keep going until we bypass the path indicators */
751 for (ReturnPath
= Path
+ 2, i
= 2; (i
> 0) && (*ReturnPath
); ReturnPath
++)
753 /* We look for 2 slashes, so keep at it until we find them */
754 if ((*ReturnPath
== L
'\\') || (*ReturnPath
== L
'/')) i
--;
760 case RtlPathTypeDriveAbsolute
:
763 case RtlPathTypeDriveRelative
:
766 case RtlPathTypeRooted
:
769 case RtlPathTypeRelative
:
772 case RtlPathTypeRootLocalDevice
:
780 BasepIsCurDirAllowedForPlainExeNames(VOID
)
783 UNICODE_STRING EmptyString
;
785 RtlInitEmptyUnicodeString(&EmptyString
, NULL
, 0);
786 Status
= RtlQueryEnvironmentVariable_U(NULL
,
787 &NoDefaultCurrentDirectoryInExePath
,
789 return !NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
;
792 /* PUBLIC FUNCTIONS ***********************************************************/
799 SetDllDirectoryW(IN LPCWSTR lpPathName
)
801 UNICODE_STRING OldDirectory
, DllDirectory
;
805 if (wcschr(lpPathName
, L
';'))
807 SetLastError(ERROR_INVALID_PARAMETER
);
810 if (!RtlCreateUnicodeString(&DllDirectory
, lpPathName
))
812 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
818 RtlInitUnicodeString(&DllDirectory
, NULL
);
821 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
823 OldDirectory
= BaseDllDirectory
;
824 BaseDllDirectory
= DllDirectory
;
826 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
828 RtlFreeUnicodeString(&OldDirectory
);
837 SetDllDirectoryA(IN LPCSTR lpPathName
)
839 ANSI_STRING AnsiDllDirectory
;
840 UNICODE_STRING OldDirectory
, DllDirectory
;
845 if (strchr(lpPathName
, ';'))
847 SetLastError(ERROR_INVALID_PARAMETER
);
851 Status
= RtlInitAnsiStringEx(&AnsiDllDirectory
, lpPathName
);
852 if (NT_SUCCESS(Status
))
854 Status
= Basep8BitStringToUnicodeString(&DllDirectory
,
859 if (!NT_SUCCESS(Status
))
861 BaseSetLastNTError(Status
);
867 RtlInitUnicodeString(&DllDirectory
, NULL
);
870 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
872 OldDirectory
= BaseDllDirectory
;
873 BaseDllDirectory
= DllDirectory
;
875 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
877 RtlFreeUnicodeString(&OldDirectory
);
886 GetDllDirectoryW(IN DWORD nBufferLength
,
891 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
893 if ((nBufferLength
* sizeof(WCHAR
)) > BaseDllDirectory
.Length
)
895 RtlCopyMemory(lpBuffer
, BaseDllDirectory
.Buffer
, BaseDllDirectory
.Length
);
896 Length
= BaseDllDirectory
.Length
/ sizeof(WCHAR
);
897 lpBuffer
[Length
] = UNICODE_NULL
;
901 Length
= (BaseDllDirectory
.Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
);
902 if (lpBuffer
) *lpBuffer
= UNICODE_NULL
;
905 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
914 GetDllDirectoryA(IN DWORD nBufferLength
,
918 ANSI_STRING AnsiDllDirectory
;
921 RtlInitEmptyAnsiString(&AnsiDllDirectory
, lpBuffer
, (USHORT
)nBufferLength
);
923 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
925 Length
= BasepUnicodeStringTo8BitSize(&BaseDllDirectory
);
926 if (Length
> nBufferLength
)
928 Status
= STATUS_SUCCESS
;
929 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
934 Status
= BasepUnicodeStringTo8BitString(&AnsiDllDirectory
,
939 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
941 if (!NT_SUCCESS(Status
))
943 BaseSetLastNTError(Status
);
945 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
956 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName
)
958 if (wcschr(ExeName
, L
'\\')) return TRUE
;
960 return BasepIsCurDirAllowedForPlainExeNames();
968 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName
)
970 if (strchr(ExeName
, '\\')) return TRUE
;
972 return BasepIsCurDirAllowedForPlainExeNames();
978 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
979 * beyond what you would usually expect. There are two main reasons:
981 * First, these APIs are subject to the ANSI/OEM File API selection status that
982 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
984 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
985 * length of the paths in "ANSI" by dividing their internal Wide character count
986 * by two... this is usually correct when dealing with pure-ASCII codepages but
987 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
988 * for CJK, for example.
992 GetFullPathNameA(IN LPCSTR lpFileName
,
993 IN DWORD nBufferLength
,
995 OUT LPSTR
*lpFilePart
)
998 PWCHAR Buffer
= NULL
;
999 ULONG PathSize
, FilePartSize
;
1000 ANSI_STRING AnsiString
;
1001 UNICODE_STRING FileNameString
, UniString
;
1002 PWCHAR LocalFilePart
;
1005 /* If the caller wants filepart, use a local wide buffer since this is A */
1006 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1008 /* Initialize for Quickie */
1009 FilePartSize
= PathSize
= 0;
1010 FileNameString
.Buffer
= NULL
;
1012 /* First get our string in Unicode */
1013 Status
= Basep8BitStringToDynamicUnicodeString(&FileNameString
, lpFileName
);
1014 if (!NT_SUCCESS(Status
)) goto Quickie
;
1016 /* Allocate a buffer to hold teh path name */
1017 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
1019 MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1022 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES
);
1026 /* Call into RTL to get the full Unicode path name */
1027 PathSize
= RtlGetFullPathName_U(FileNameString
.Buffer
,
1028 MAX_PATH
* sizeof(WCHAR
),
1031 if (PathSize
<= (MAX_PATH
* sizeof(WCHAR
)))
1033 /* The buffer will fit, get the real ANSI string size now */
1034 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
);
1035 if (NT_SUCCESS(Status
))
1037 /* Now check if the user wanted file part size as well */
1038 if ((PathSize
) && (lpFilePart
) && (LocalFilePart
))
1040 /* Yep, so in this case get the length of the file part too */
1041 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1043 (LocalFilePart
- Buffer
) *
1045 if (!NT_SUCCESS(Status
))
1047 /* We failed to do that, so fail the whole call */
1048 BaseSetLastNTError(Status
);
1056 /* Reset the path size since the buffer is not large enough */
1060 /* Either no path, or local buffer was too small, enter failure code */
1061 if (!PathSize
) goto Quickie
;
1063 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
1064 if (PathSize
>= nBufferLength
)
1070 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
1071 RtlInitUnicodeString(&UniString
, Buffer
);
1072 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, &UniString
, TRUE
);
1073 if (!NT_SUCCESS(Status
))
1075 /* Final conversion failed, fail the call */
1076 BaseSetLastNTError(Status
);
1081 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
1082 RtlCopyMemory(lpBuffer
, AnsiString
.Buffer
, PathSize
+ 1);
1083 RtlFreeAnsiString(&AnsiString
);
1085 /* And finally, did the caller request file part information? */
1088 /* Use the size we computed earlier and add it to the buffer */
1089 *lpFilePart
= LocalFilePart
? &lpBuffer
[FilePartSize
] : 0;
1094 /* Cleanup and return the path size */
1095 if (FileNameString
.Buffer
) RtlFreeUnicodeString(&FileNameString
);
1096 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1105 GetFullPathNameW(IN LPCWSTR lpFileName
,
1106 IN DWORD nBufferLength
,
1107 OUT LPWSTR lpBuffer
,
1108 OUT LPWSTR
*lpFilePart
)
1110 /* Call Rtl to do the work */
1111 return RtlGetFullPathName_U(lpFileName
,
1112 nBufferLength
* sizeof(WCHAR
),
1114 lpFilePart
) / sizeof(WCHAR
);
1122 SearchPathA(IN LPCSTR lpPath OPTIONAL
,
1123 IN LPCSTR lpFileName
,
1124 IN LPCSTR lpExtension OPTIONAL
,
1125 IN DWORD nBufferLength
,
1127 OUT LPSTR
*lpFilePart OPTIONAL
)
1129 PUNICODE_STRING FileNameString
;
1130 UNICODE_STRING PathString
, ExtensionString
;
1132 ULONG PathSize
, FilePartSize
, AnsiLength
;
1133 PWCHAR LocalFilePart
, Buffer
;
1136 /* If the caller wants filepart, use a local wide buffer since this is A */
1137 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
1139 /* Initialize stuff for Quickie */
1142 ExtensionString
.Buffer
= PathString
.Buffer
= NULL
;
1144 /* Get the UNICODE_STRING file name */
1145 FileNameString
= Basep8BitStringToStaticUnicodeString(lpFileName
);
1146 if (!FileNameString
) return 0;
1148 /* Did the caller specify an extension */
1151 /* Yup, convert it into UNICODE_STRING */
1152 Status
= Basep8BitStringToDynamicUnicodeString(&ExtensionString
,
1154 if (!NT_SUCCESS(Status
)) goto Quickie
;
1157 /* Did the caller specify a path */
1160 /* Yup, convert it into UNICODE_STRING */
1161 Status
= Basep8BitStringToDynamicUnicodeString(&PathString
, lpPath
);
1162 if (!NT_SUCCESS(Status
)) goto Quickie
;
1165 /* Allocate our output buffer */
1166 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength
* sizeof(WCHAR
));
1169 /* It failed, bail out */
1170 BaseSetLastNTError(STATUS_NO_MEMORY
);
1174 /* Now run the Wide search with the input buffer lengths */
1175 PathSize
= SearchPathW(PathString
.Buffer
,
1176 FileNameString
->Buffer
,
1177 ExtensionString
.Buffer
,
1181 if (PathSize
<= nBufferLength
)
1183 /* It fits, but is it empty? If so, bail out */
1184 if (!PathSize
) goto Quickie
;
1186 /* The length above is inexact, we need it in ANSI */
1187 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
, Buffer
, PathSize
* sizeof(WCHAR
));
1188 if (!NT_SUCCESS(Status
))
1190 /* Conversion failed, fail the call */
1192 BaseSetLastNTError(Status
);
1196 /* If the correct ANSI size is too big, return required length plus a NULL */
1197 if (AnsiLength
>= nBufferLength
)
1199 PathSize
= AnsiLength
+ 1;
1203 /* Now apply the final conversion to ANSI */
1204 Status
= RtlUnicodeToMultiByteN(lpBuffer
,
1208 PathSize
* sizeof(WCHAR
));
1209 if (!NT_SUCCESS(Status
))
1211 /* Conversion failed, fail the whole call */
1213 BaseSetLastNTError(STATUS_NO_MEMORY
);
1217 /* NULL-terminate and return the real ANSI length */
1218 lpBuffer
[AnsiLength
] = ANSI_NULL
;
1219 PathSize
= AnsiLength
;
1221 /* Now check if the user wanted file part size as well */
1224 /* If we didn't get a file part, clear the caller's */
1231 /* Yep, so in this case get the length of the file part too */
1232 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
1234 (LocalFilePart
- Buffer
) *
1236 if (!NT_SUCCESS(Status
))
1238 /* We failed to do that, so fail the whole call */
1239 BaseSetLastNTError(Status
);
1243 /* Return the file part buffer */
1244 *lpFilePart
= lpBuffer
+ FilePartSize
;
1250 /* Our initial buffer guess was too small, allocate a bigger one */
1251 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1252 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize
* sizeof(WCHAR
));
1255 /* Out of memory, fail everything */
1256 BaseSetLastNTError(STATUS_NO_MEMORY
);
1260 /* Do the search again -- it will fail, we just want the path size */
1261 PathSize
= SearchPathW(PathString
.Buffer
,
1262 FileNameString
->Buffer
,
1263 ExtensionString
.Buffer
,
1267 if (!PathSize
) goto Quickie
;
1269 /* Convert it to a correct size */
1270 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
* sizeof(WCHAR
));
1271 if (NT_SUCCESS(Status
))
1273 /* Make space for the NULL-char */
1278 /* Conversion failed for some reason, fail the call */
1279 BaseSetLastNTError(Status
);
1285 /* Cleanup/complete path */
1286 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1287 if (ExtensionString
.Buffer
) RtlFreeUnicodeString(&ExtensionString
);
1288 if (PathString
.Buffer
) RtlFreeUnicodeString(&PathString
);
1297 SearchPathW(IN LPCWSTR lpPath OPTIONAL
,
1298 IN LPCWSTR lpFileName
,
1299 IN LPCWSTR lpExtension OPTIONAL
,
1300 IN DWORD nBufferLength
,
1301 OUT LPWSTR lpBuffer
,
1302 OUT LPWSTR
*lpFilePart OPTIONAL
)
1304 UNICODE_STRING FileNameString
, ExtensionString
, PathString
, CallerBuffer
;
1305 ULONG Flags
, LengthNeeded
, FilePartSize
;
1309 /* Default flags for RtlDosSearchPath_Ustr */
1312 /* Clear file part in case we fail */
1313 if (lpFilePart
) *lpFilePart
= NULL
;
1315 /* Initialize path buffer for free later */
1316 PathString
.Buffer
= NULL
;
1318 /* Convert filename to a unicode string and eliminate trailing spaces */
1319 RtlInitUnicodeString(&FileNameString
, lpFileName
);
1320 while ((FileNameString
.Length
>= sizeof(WCHAR
)) &&
1321 (FileNameString
.Buffer
[(FileNameString
.Length
/ sizeof(WCHAR
)) - 1] == L
' '))
1323 FileNameString
.Length
-= sizeof(WCHAR
);
1326 /* Was it all just spaces? */
1327 if (!FileNameString
.Length
)
1330 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
1334 /* Convert extension to a unicode string */
1335 RtlInitUnicodeString(&ExtensionString
, lpExtension
);
1337 /* Check if the user sent a path */
1340 /* Convert it to a unicode string too */
1341 Status
= RtlInitUnicodeStringEx(&PathString
, lpPath
);
1342 if (NT_ERROR(Status
))
1344 /* Fail if it was too long */
1345 BaseSetLastNTError(Status
);
1351 /* A path wasn't sent, so compute it ourselves */
1352 PathString
.Buffer
= BaseComputeProcessSearchPath();
1353 if (!PathString
.Buffer
)
1355 /* Fail if we couldn't compute it */
1356 BaseSetLastNTError(STATUS_NO_MEMORY
);
1360 /* See how big the computed path is */
1361 LengthNeeded
= lstrlenW(PathString
.Buffer
);
1362 if (LengthNeeded
> UNICODE_STRING_MAX_CHARS
)
1364 /* Fail if it's too long */
1365 BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1369 /* Set the path size now that we have it */
1370 PathString
.MaximumLength
= PathString
.Length
= (USHORT
)LengthNeeded
* sizeof(WCHAR
);
1372 /* Request SxS isolation from RtlDosSearchPath_Ustr */
1376 /* Create the string that describes the output buffer from the caller */
1377 CallerBuffer
.Length
= 0;
1378 CallerBuffer
.Buffer
= lpBuffer
;
1380 /* How much space does the caller have? */
1381 if (nBufferLength
<= UNICODE_STRING_MAX_CHARS
)
1383 /* Add it into the string */
1384 CallerBuffer
.MaximumLength
= (USHORT
)nBufferLength
* sizeof(WCHAR
);
1388 /* Caller wants too much, limit it to the maximum length of a string */
1389 CallerBuffer
.MaximumLength
= UNICODE_STRING_MAX_BYTES
;
1392 /* Call Rtl to do the work */
1393 Status
= RtlDosSearchPath_Ustr(Flags
,
1402 if (NT_ERROR(Status
))
1404 /* Check for unusual status codes */
1405 if ((Status
!= STATUS_NO_SUCH_FILE
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
1407 /* Print them out since maybe an app needs fixing */
1408 DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
1412 DbgPrint(" Path = %wZ\n", &PathString
);
1415 /* Check if the failure was due to a small buffer */
1416 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1418 /* Check if the length was actually too big for Rtl to work with */
1419 Result
= LengthNeeded
/ sizeof(WCHAR
);
1420 if (Result
> 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG
);
1424 /* Some other error, set the error code */
1425 BaseSetLastNTError(Status
);
1430 /* It worked! Write the file part now */
1431 if (lpFilePart
) *lpFilePart
= &lpBuffer
[FilePartSize
];
1433 /* Convert the final result length */
1434 Result
= CallerBuffer
.Length
/ sizeof(WCHAR
);
1438 /* Check if there was a dynamic path string to free */
1439 if ((PathString
.Buffer
!= lpPath
) && (PathString
.Buffer
))
1442 RtlFreeHeap(RtlGetProcessHeap(), 0, PathString
.Buffer
);
1445 /* Return the final result length */
1454 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
1455 OUT LPWSTR lpszLongPath
,
1458 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1464 BOOLEAN Found
= FALSE
;
1465 WIN32_FIND_DATAW FindFileData
;
1467 /* Initialize so Quickie knows there's nothing to do */
1468 Buffer
= Original
= NULL
;
1471 /* First check if the input path was obviously NULL */
1474 /* Fail the request */
1475 SetLastError(ERROR_INVALID_PARAMETER
);
1479 /* We will be touching removed, removable drives -- don't warn the user */
1480 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1482 /* Do a simple check to see if the path exists */
1483 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
1485 /* It doesn't, so fail */
1490 /* Now get a pointer to the actual path, skipping indicators */
1491 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
1493 /* Is there any path or filename in there? */
1495 (*Path
== UNICODE_NULL
) ||
1496 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
1498 /* There isn't, so the long path is simply the short path */
1499 ReturnLength
= wcslen(lpszShortPath
);
1501 /* Is there space for it? */
1502 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1504 /* Make sure the pointers aren't already the same */
1505 if (lpszLongPath
!= lpszShortPath
)
1507 /* They're not -- copy the short path into the long path */
1508 RtlMoveMemory(lpszLongPath
,
1510 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1515 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1521 /* We are still in the game -- compute the current size */
1522 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
1523 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1524 if (!Original
) goto ErrorQuickie
;
1526 /* Make a copy of it */
1527 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
1529 /* Compute the new first and last markers */
1530 First
= &Original
[First
- lpszShortPath
];
1531 Last
= &Original
[Last
- lpszShortPath
];
1533 /* Set the current destination pointer for a copy */
1537 * Windows allows the paths to overlap -- we have to be careful with this and
1538 * see if it's same to do so, and if not, allocate our own internal buffer
1539 * that we'll return at the end.
1541 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1543 if ((cchBuffer
) && (lpszLongPath
) &&
1544 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
1545 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
1547 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1548 if (!Buffer
) goto ErrorQuickie
;
1550 /* New destination */
1554 /* Prepare for the loop */
1559 /* Current delta in the loop */
1560 Length
= First
- Src
;
1562 /* Update the return length by it */
1563 ReturnLength
+= Length
;
1565 /* Is there a delta? If so, is there space and buffer for it? */
1566 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1568 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1572 /* "Terminate" this portion of the path's substring so we can do a find */
1574 *Last
= UNICODE_NULL
;
1575 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1578 /* This portion wasn't found, so fail */
1579 if (FindHandle
== INVALID_HANDLE_VALUE
)
1585 /* Close the find handle */
1586 FindClose(FindHandle
);
1588 /* Now check the length of the long name */
1589 Length
= wcslen(FindFileData
.cFileName
);
1592 /* This is our new first marker */
1593 First
= FindFileData
.cFileName
;
1597 /* Otherwise, the name is the delta between our current markers */
1598 Length
= Last
- First
;
1601 /* Update the return length with the short name length, if any */
1602 ReturnLength
+= Length
;
1604 /* Once again check for appropriate space and buffer */
1605 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1607 /* And do the copy if there is */
1608 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1612 /* Now update the source pointer */
1614 if (*Src
== UNICODE_NULL
) break;
1616 /* Are there more names in there? */
1617 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1621 /* The loop is done, is there anything left? */
1624 /* Get the length of the straggling path */
1625 Length
= wcslen(Src
);
1626 ReturnLength
+= Length
;
1628 /* Once again check for appropriate space and buffer */
1629 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1631 /* And do the copy if there is -- accounting for NULL here */
1632 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1634 /* What about our buffer? */
1637 /* Copy it into the caller's long path */
1638 RtlMoveMemory(lpszLongPath
,
1640 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1645 /* Buffer is too small, let the caller know, making space for NULL */
1650 /* We're all done */
1654 /* This is the goto for memory failures */
1655 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1658 /* General function end: free memory, restore error mode, return length */
1659 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1660 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1661 SetErrorMode(ErrorMode
);
1662 return ReturnLength
;
1670 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1671 OUT LPSTR lpszLongPath
,
1674 ULONG Result
, PathLength
;
1677 UNICODE_STRING LongPathUni
, ShortPathUni
;
1678 ANSI_STRING LongPathAnsi
;
1679 WCHAR LongPathBuffer
[MAX_PATH
];
1682 LongPathAnsi
.Buffer
= NULL
;
1683 ShortPathUni
.Buffer
= NULL
;
1688 SetLastError(ERROR_INVALID_PARAMETER
);
1692 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1693 if (!NT_SUCCESS(Status
)) goto Quickie
;
1695 LongPath
= LongPathBuffer
;
1697 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1698 if (PathLength
>= MAX_PATH
)
1700 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1704 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1708 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1712 if (!PathLength
) goto Quickie
;
1714 ShortPathUni
.MaximumLength
= (USHORT
)PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1715 LongPathUni
.Buffer
= LongPath
;
1716 LongPathUni
.Length
= (USHORT
)PathLength
* sizeof(WCHAR
);
1718 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1719 if (!NT_SUCCESS(Status
))
1721 BaseSetLastNTError(Status
);
1725 Result
= LongPathAnsi
.Length
;
1726 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1728 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1729 lpszLongPath
[Result
] = ANSI_NULL
;
1733 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1737 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1738 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1739 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1741 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1751 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1752 OUT LPSTR lpszShortPath
,
1755 ULONG Result
, PathLength
;
1758 UNICODE_STRING LongPathUni
, ShortPathUni
;
1759 ANSI_STRING ShortPathAnsi
;
1760 WCHAR ShortPathBuffer
[MAX_PATH
];
1763 ShortPathAnsi
.Buffer
= NULL
;
1764 LongPathUni
.Buffer
= NULL
;
1769 SetLastError(ERROR_INVALID_PARAMETER
);
1773 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1774 if (!NT_SUCCESS(Status
)) goto Quickie
;
1776 ShortPath
= ShortPathBuffer
;
1778 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1779 if (PathLength
>= MAX_PATH
)
1781 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1785 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1789 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1793 if (!PathLength
) goto Quickie
;
1795 LongPathUni
.MaximumLength
= (USHORT
)PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1796 ShortPathUni
.Buffer
= ShortPath
;
1797 ShortPathUni
.Length
= (USHORT
)PathLength
* sizeof(WCHAR
);
1799 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1800 if (!NT_SUCCESS(Status
))
1802 BaseSetLastNTError(Status
);
1806 Result
= ShortPathAnsi
.Length
;
1807 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1809 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1810 lpszShortPath
[Result
] = ANSI_NULL
;
1814 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1818 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1819 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1820 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1822 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1832 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1833 OUT LPWSTR lpszShortPath
,
1836 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1842 BOOLEAN Found
= FALSE
;
1843 WIN32_FIND_DATAW FindFileData
;
1845 /* Initialize so Quickie knows there's nothing to do */
1846 Buffer
= Original
= NULL
;
1849 /* First check if the input path was obviously NULL */
1852 /* Fail the request */
1853 SetLastError(ERROR_INVALID_PARAMETER
);
1857 /* We will be touching removed, removable drives -- don't warn the user */
1858 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1860 /* Do a simple check to see if the path exists */
1861 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1863 /* Windows checks for an application compatibility flag to allow this */
1864 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1866 /* It doesn't, so fail */
1872 /* Now get a pointer to the actual path, skipping indicators */
1873 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1875 /* Is there any path or filename in there? */
1877 (*Path
== UNICODE_NULL
) ||
1878 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1880 /* There isn't, so the long path is simply the short path */
1881 ReturnLength
= wcslen(lpszLongPath
);
1883 /* Is there space for it? */
1884 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1886 /* Make sure the pointers aren't already the same */
1887 if (lpszLongPath
!= lpszShortPath
)
1889 /* They're not -- copy the short path into the long path */
1890 RtlMoveMemory(lpszShortPath
,
1892 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1897 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1903 /* We are still in the game -- compute the current size */
1904 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1905 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1906 if (!Original
) goto ErrorQuickie
;
1908 /* Make a copy of it */
1909 wcsncpy(Original
, lpszLongPath
, Length
);
1911 /* Compute the new first and last markers */
1912 First
= &Original
[First
- lpszLongPath
];
1913 Last
= &Original
[Last
- lpszLongPath
];
1915 /* Set the current destination pointer for a copy */
1916 Dst
= lpszShortPath
;
1919 * Windows allows the paths to overlap -- we have to be careful with this and
1920 * see if it's same to do so, and if not, allocate our own internal buffer
1921 * that we'll return at the end.
1923 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1925 if ((cchBuffer
) && (lpszShortPath
) &&
1926 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1927 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1929 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1930 if (!Buffer
) goto ErrorQuickie
;
1932 /* New destination */
1936 /* Prepare for the loop */
1941 /* Current delta in the loop */
1942 Length
= First
- Src
;
1944 /* Update the return length by it */
1945 ReturnLength
+= Length
;
1947 /* Is there a delta? If so, is there space and buffer for it? */
1948 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1950 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1954 /* "Terminate" this portion of the path's substring so we can do a find */
1956 *Last
= UNICODE_NULL
;
1957 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1960 /* This portion wasn't found, so fail */
1961 if (FindHandle
== INVALID_HANDLE_VALUE
)
1967 /* Close the find handle */
1968 FindClose(FindHandle
);
1970 /* Now check the length of the short name */
1971 Length
= wcslen(FindFileData
.cAlternateFileName
);
1974 /* This is our new first marker */
1975 First
= FindFileData
.cAlternateFileName
;
1979 /* Otherwise, the name is the delta between our current markers */
1980 Length
= Last
- First
;
1983 /* Update the return length with the short name length, if any */
1984 ReturnLength
+= Length
;
1986 /* Once again check for appropriate space and buffer */
1987 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1989 /* And do the copy if there is */
1990 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1994 /* Now update the source pointer */
1996 if (*Src
== UNICODE_NULL
) break;
1998 /* Are there more names in there? */
1999 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
2003 /* The loop is done, is there anything left? */
2006 /* Get the length of the straggling path */
2007 Length
= wcslen(Src
);
2008 ReturnLength
+= Length
;
2010 /* Once again check for appropriate space and buffer */
2011 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
2013 /* And do the copy if there is -- accounting for NULL here */
2014 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2016 /* What about our buffer? */
2019 /* Copy it into the caller's long path */
2020 RtlMoveMemory(lpszShortPath
,
2022 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
2027 /* Buffer is too small, let the caller know, making space for NULL */
2032 /* We're all done */
2036 /* This is the goto for memory failures */
2037 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2040 /* General function end: free memory, restore error mode, return length */
2041 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
2042 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
2043 SetErrorMode(ErrorMode
);
2044 return ReturnLength
;
2050 * NOTE: Windows returns a dos/short (8.3) path
2054 GetTempPathA(IN DWORD nBufferLength
,
2057 WCHAR BufferW
[MAX_PATH
];
2060 ret
= GetTempPathW(MAX_PATH
, BufferW
);
2066 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2070 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
2080 GetTempPathW(IN DWORD count
,
2083 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
2084 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
2085 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2086 WCHAR tmp_path
[MAX_PATH
];
2087 WCHAR full_tmp_path
[MAX_PATH
];
2090 DPRINT("%u,%p\n", count
, path
);
2092 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
2093 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
2094 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
2095 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
2102 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2106 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, full_tmp_path
, NULL
);
2109 if (ret
> MAX_PATH
- 2)
2111 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
2115 if (full_tmp_path
[ret
-1] != '\\')
2117 full_tmp_path
[ret
++] = '\\';
2118 full_tmp_path
[ret
] = '\0';
2121 ret
++; /* add space for terminating 0 */
2125 lstrcpynW(path
, full_tmp_path
, count
);
2126 /* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767
2127 * bytes after it, we will assume the > XP behavior for now */
2128 memset(path
+ ret
, 0, (min(count
, 32767) - ret
) * sizeof(WCHAR
));
2129 ret
--; /* return length without 0 */
2133 /* the buffer must be cleared if contents will not fit */
2134 memset(path
, 0, count
* sizeof(WCHAR
));
2137 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
2146 GetCurrentDirectoryA(IN DWORD nBufferLength
,
2149 ANSI_STRING AnsiString
;
2151 PUNICODE_STRING StaticString
;
2154 StaticString
= &NtCurrentTeb()->StaticUnicodeString
;
2156 MaxLength
= nBufferLength
;
2157 if (nBufferLength
>= UNICODE_STRING_MAX_BYTES
)
2159 MaxLength
= UNICODE_STRING_MAX_BYTES
- 1;
2162 StaticString
->Length
= (USHORT
)RtlGetCurrentDirectory_U(StaticString
->MaximumLength
,
2163 StaticString
->Buffer
);
2164 Status
= RtlUnicodeToMultiByteSize(&nBufferLength
,
2165 StaticString
->Buffer
,
2166 StaticString
->Length
);
2167 if (!NT_SUCCESS(Status
))
2169 BaseSetLastNTError(Status
);
2173 if (MaxLength
<= nBufferLength
)
2175 return nBufferLength
+ 1;
2178 AnsiString
.Buffer
= lpBuffer
;
2179 AnsiString
.MaximumLength
= (USHORT
)MaxLength
;
2180 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, StaticString
, FALSE
);
2181 if (!NT_SUCCESS(Status
))
2183 BaseSetLastNTError(Status
);
2187 return AnsiString
.Length
;
2195 GetCurrentDirectoryW(IN DWORD nBufferLength
,
2196 OUT LPWSTR lpBuffer
)
2198 return RtlGetCurrentDirectory_U(nBufferLength
* sizeof(WCHAR
), lpBuffer
) / sizeof(WCHAR
);
2206 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
2208 PUNICODE_STRING DirName
;
2213 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2217 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
);
2218 if (!DirName
) return FALSE
;
2220 if (CheckForSameCurdir(DirName
)) return TRUE
;
2222 Status
= RtlSetCurrentDirectory_U(DirName
);
2223 if (NT_SUCCESS(Status
)) return TRUE
;
2225 if ((*DirName
->Buffer
!= L
'"') || (DirName
->Length
<= 2))
2227 BaseSetLastNTError(Status
);
2231 DirName
= Basep8BitStringToStaticUnicodeString(lpPathName
+ 1);
2232 if (!DirName
) return FALSE
;
2234 Status
= RtlSetCurrentDirectory_U(DirName
);
2235 if (!NT_SUCCESS(Status
))
2237 BaseSetLastNTError(Status
);
2249 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
2252 UNICODE_STRING UnicodeString
;
2256 BaseSetLastNTError(STATUS_INVALID_PARAMETER
);
2260 Status
= RtlInitUnicodeStringEx(&UnicodeString
, lpPathName
);
2261 if (NT_SUCCESS(Status
))
2263 if (!CheckForSameCurdir(&UnicodeString
))
2265 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
2269 if (!NT_SUCCESS(Status
))
2271 BaseSetLastNTError(Status
);
2283 GetSystemDirectoryA(OUT LPSTR lpBuffer
,
2286 ANSI_STRING AnsiString
;
2290 /* Get the correct size of the Unicode Base directory */
2291 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2292 BaseWindowsSystemDirectory
.Buffer
,
2293 BaseWindowsSystemDirectory
.MaximumLength
);
2294 if (!NT_SUCCESS(Status
)) return 0;
2296 if (uSize
< AnsiLength
) return AnsiLength
;
2298 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2300 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2301 &BaseWindowsSystemDirectory
,
2303 if (!NT_SUCCESS(Status
)) return 0;
2305 return AnsiString
.Length
;
2313 GetSystemDirectoryW(OUT LPWSTR lpBuffer
,
2318 ReturnLength
= BaseWindowsSystemDirectory
.MaximumLength
;
2319 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2321 RtlCopyMemory(lpBuffer
,
2322 BaseWindowsSystemDirectory
.Buffer
,
2323 BaseWindowsSystemDirectory
.Length
);
2324 lpBuffer
[BaseWindowsSystemDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2326 ReturnLength
= BaseWindowsSystemDirectory
.Length
;
2329 return ReturnLength
/ sizeof(WCHAR
);
2337 GetWindowsDirectoryA(OUT LPSTR lpBuffer
,
2340 /* Is this a TS installation? */
2341 if (gpTermsrvGetWindowsDirectoryA
) UNIMPLEMENTED
;
2343 /* Otherwise, call the System API */
2344 return GetSystemWindowsDirectoryA(lpBuffer
, uSize
);
2352 GetWindowsDirectoryW(OUT LPWSTR lpBuffer
,
2355 /* Is this a TS installation? */
2356 if (gpTermsrvGetWindowsDirectoryW
) UNIMPLEMENTED
;
2358 /* Otherwise, call the System API */
2359 return GetSystemWindowsDirectoryW(lpBuffer
, uSize
);
2367 GetSystemWindowsDirectoryA(OUT LPSTR lpBuffer
,
2370 ANSI_STRING AnsiString
;
2374 /* Get the correct size of the Unicode Base directory */
2375 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
,
2376 BaseWindowsDirectory
.Buffer
,
2377 BaseWindowsDirectory
.MaximumLength
);
2378 if (!NT_SUCCESS(Status
)) return 0;
2380 if (uSize
< AnsiLength
) return AnsiLength
;
2382 RtlInitEmptyAnsiString(&AnsiString
, lpBuffer
, uSize
);
2384 Status
= BasepUnicodeStringTo8BitString(&AnsiString
,
2385 &BaseWindowsDirectory
,
2387 if (!NT_SUCCESS(Status
)) return 0;
2389 return AnsiString
.Length
;
2397 GetSystemWindowsDirectoryW(OUT LPWSTR lpBuffer
,
2402 ReturnLength
= BaseWindowsDirectory
.MaximumLength
;
2403 if ((uSize
* sizeof(WCHAR
)) >= ReturnLength
)
2405 RtlCopyMemory(lpBuffer
,
2406 BaseWindowsDirectory
.Buffer
,
2407 BaseWindowsDirectory
.Length
);
2408 lpBuffer
[BaseWindowsDirectory
.Length
/ sizeof(WCHAR
)] = ANSI_NULL
;
2410 ReturnLength
= BaseWindowsDirectory
.Length
;
2413 return ReturnLength
/ sizeof(WCHAR
);
2421 GetSystemWow64DirectoryW(OUT LPWSTR lpBuffer
,
2428 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2438 GetSystemWow64DirectoryA(OUT LPSTR lpBuffer
,
2445 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);