reactos/dll/win32/kernel32/client/path.c

2450 lines
70 KiB
C

/*
* PROJECT: ReactOS Win32 Base API
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/win32/kernel32/client/path.c
* PURPOSE: Handles path APIs
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES *******************************************************************/
#include <k32.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
UNICODE_STRING NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
UNICODE_STRING BaseWindowsSystemDirectory, BaseWindowsDirectory;
UNICODE_STRING BaseDefaultPathAppend, BaseDefaultPath, BaseDllDirectory;
PVOID gpTermsrvGetWindowsDirectoryA;
PVOID gpTermsrvGetWindowsDirectoryW;
/* This is bitmask for each illegal filename character */
/* If someone has time, please feel free to use 0b notation */
DWORD IllegalMask[4] =
{
0xFFFFFFFF, // None allowed (00 to 1F)
0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed
0x38000000, // 5B, 5C, 5D not allowed
0x10000000 // 7C not allowed
};
BASE_SEARCH_PATH_TYPE BaseDllOrderCurrent[BaseCurrentDirPlacementMax][BaseSearchPathMax] =
{
{
BaseSearchPathApp,
BaseSearchPathCurrent,
BaseSearchPathDefault,
BaseSearchPathEnv,
BaseSearchPathInvalid
},
{
BaseSearchPathApp,
BaseSearchPathDefault,
BaseSearchPathCurrent,
BaseSearchPathEnv,
BaseSearchPathInvalid
}
};
BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent[BaseSearchPathMax] =
{
BaseSearchPathApp,
BaseSearchPathDefault,
BaseSearchPathEnv,
BaseSearchPathInvalid,
BaseSearchPathInvalid
};
BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent[BaseSearchPathMax] =
{
BaseSearchPathApp,
BaseSearchPathDll,
BaseSearchPathDefault,
BaseSearchPathEnv,
BaseSearchPathInvalid
};
BASE_SEARCH_PATH_TYPE BaseProcessOrder[BaseSearchPathMax] =
{
BaseSearchPathApp,
BaseSearchPathCurrent,
BaseSearchPathDefault,
BaseSearchPathEnv,
BaseSearchPathInvalid
};
BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement = BaseCurrentDirPlacementInvalid;
extern UNICODE_STRING BasePathVariableName;
/* PRIVATE FUNCTIONS **********************************************************/
PWCHAR
WINAPI
BasepEndOfDirName(IN PWCHAR FileName)
{
PWCHAR FileNameEnd, FileNameSeparator;
/* Find the first slash */
FileNameSeparator = wcschr(FileName, OBJ_NAME_PATH_SEPARATOR);
if (FileNameSeparator)
{
/* Find the last one */
FileNameEnd = wcsrchr(FileNameSeparator, OBJ_NAME_PATH_SEPARATOR);
ASSERT(FileNameEnd);
/* Handle the case where they are one and the same */
if (FileNameEnd == FileNameSeparator) FileNameEnd++;
}
else
{
/* No directory was specified */
FileNameEnd = NULL;
}
/* Return where the directory ends and the filename starts */
return FileNameEnd;
}
LPWSTR
WINAPI
BasepComputeProcessPath(IN PBASE_SEARCH_PATH_TYPE PathOrder,
IN LPWSTR AppName,
IN LPVOID Environment)
{
PWCHAR PathBuffer, Buffer, AppNameEnd, PathCurrent;
ULONG PathLengthInBytes;
NTSTATUS Status;
UNICODE_STRING EnvPath;
PBASE_SEARCH_PATH_TYPE Order;
/* Initialize state */
AppNameEnd = Buffer = PathBuffer = NULL;
Status = STATUS_SUCCESS;
PathLengthInBytes = 0;
/* Loop the ordering array */
for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
switch (*Order)
{
/* Compute the size of the DLL path */
case BaseSearchPathDll:
/* This path only gets called if SetDllDirectory was called */
ASSERT(BaseDllDirectory.Buffer != NULL);
/* Make sure there's a DLL directory size */
if (BaseDllDirectory.Length)
{
/* Add it, plus the separator */
PathLengthInBytes += BaseDllDirectory.Length + sizeof(L';');
}
break;
/* Compute the size of the current path */
case BaseSearchPathCurrent:
/* Add ".;" */
PathLengthInBytes += (2 * sizeof(WCHAR));
break;
/* Compute the size of the "PATH" environment variable */
case BaseSearchPathEnv:
/* Grab PEB lock if one wasn't passed in */
if (!Environment) RtlAcquirePebLock();
/* Query the size first */
EnvPath.MaximumLength = 0;
Status = RtlQueryEnvironmentVariable_U(Environment,
&BasePathVariableName,
&EnvPath);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Compute the size we'll need for the environment */
EnvPath.MaximumLength = EnvPath.Length + sizeof(WCHAR);
if ((EnvPath.Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
{
/* Don't let it overflow */
EnvPath.MaximumLength = EnvPath.Length;
}
/* Allocate the environment buffer */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
0,
EnvPath.MaximumLength);
if (Buffer)
{
/* Now query the PATH environment variable */
EnvPath.Buffer = Buffer;
Status = RtlQueryEnvironmentVariable_U(Environment,
&BasePathVariableName,
&EnvPath);
}
else
{
/* Failure case */
Status = STATUS_NO_MEMORY;
}
}
/* Release the PEB lock from above */
if (!Environment) RtlReleasePebLock();
/* There might not be a PATH */
if (Status == STATUS_VARIABLE_NOT_FOUND)
{
/* In this case, skip this PathOrder */
EnvPath.Length = EnvPath.MaximumLength = 0;
Status = STATUS_SUCCESS;
}
else if (!NT_SUCCESS(Status))
{
/* An early failure, go to exit code */
goto Quickie;
}
else
{
/* Add the length of the PATH variable unless it's empty */
ASSERT(!(EnvPath.Length & 1));
if (EnvPath.Length)
{
/* Reserve space for the variable and a semicolon */
PathLengthInBytes += (EnvPath.Length + sizeof(L';'));
}
}
break;
/* Compute the size of the default search path */
case BaseSearchPathDefault:
/* Just add it... it already has a ';' at the end */
ASSERT(!(BaseDefaultPath.Length & 1));
PathLengthInBytes += BaseDefaultPath.Length;
break;
/* Compute the size of the current app directory */
case BaseSearchPathApp:
/* Find out where the app name ends, to get only the directory */
if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
/* Check if there was no application name passed in */
if (!(AppName) || !(AppNameEnd))
{
/* Do we have a per-thread CURDIR to use? */
if (NtCurrentTeb()->NtTib.SubSystemTib)
{
/* This means someone added RTL_PERTHREAD_CURDIR */
UNIMPLEMENTED_DBGBREAK();
}
/* We do not. Do we have the LDR_ENTRY for the executable? */
if (!BasepExeLdrEntry)
{
/* We do not. Grab it */
LdrEnumerateLoadedModules(0,
BasepLocateExeLdrEntry,
NtCurrentPeb()->ImageBaseAddress);
}
/* Now do we have it? */
if (BasepExeLdrEntry)
{
/* Yes, so read the name out of it */
AppName = BasepExeLdrEntry->FullDllName.Buffer;
}
/* Find out where the app name ends, to get only the directory */
if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
}
/* So, do we have an application name and its directory? */
if ((AppName) && (AppNameEnd))
{
/* Add the size of the app's directory, plus the separator */
PathLengthInBytes += ((AppNameEnd - AppName) * sizeof(WCHAR)) + sizeof(L';');
}
break;
default:
break;
}
}
/* Bam, all done, we now have the final path size */
ASSERT(PathLengthInBytes > 0);
ASSERT(!(PathLengthInBytes & 1));
/* Allocate the buffer to hold it */
PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes);
if (!PathBuffer)
{
/* Failure path */
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Now we loop again, this time to copy the data */
PathCurrent = PathBuffer;
for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
switch (*Order)
{
/* Add the DLL path */
case BaseSearchPathDll:
if (BaseDllDirectory.Length)
{
/* Copy it in the buffer, ASSERT there's enough space */
ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + BaseDllDirectory.Length) <= PathLengthInBytes);
RtlCopyMemory(PathCurrent,
BaseDllDirectory.Buffer,
BaseDllDirectory.Length);
/* Update the current pointer, add a separator */
PathCurrent += (BaseDllDirectory.Length / sizeof(WCHAR));
*PathCurrent++ = ';';
}
break;
/* Add the current application path */
case BaseSearchPathApp:
if ((AppName) && (AppNameEnd))
{
/* Copy it in the buffer, ASSERT there's enough space */
ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(WCHAR)) <= PathLengthInBytes);
RtlCopyMemory(PathCurrent,
AppName,
(AppNameEnd - AppName) * sizeof(WCHAR));
/* Update the current pointer, add a separator */
PathCurrent += AppNameEnd - AppName;
*PathCurrent++ = ';';
}
break;
/* Add the default search path */
case BaseSearchPathDefault:
/* Copy it in the buffer, ASSERT there's enough space */
ASSERT((((PathCurrent - PathBuffer) * sizeof(WCHAR)) + BaseDefaultPath.Length) <= PathLengthInBytes);
RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length);
/* Update the current pointer. The default path already has a ";" */
PathCurrent += (BaseDefaultPath.Length / sizeof(WCHAR));
break;
/* Add the path in the PATH environment variable */
case BaseSearchPathEnv:
if (EnvPath.Length)
{
/* Copy it in the buffer, ASSERT there's enough space */
ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + EnvPath.Length) <= PathLengthInBytes);
RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length);
/* Update the current pointer, add a separator */
PathCurrent += (EnvPath.Length / sizeof(WCHAR));
*PathCurrent++ = ';';
}
break;
/* Add the current directory */
case BaseSearchPathCurrent:
/* Copy it in the buffer, ASSERT there's enough space */
ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(WCHAR)) <= PathLengthInBytes);
*PathCurrent++ = '.';
/* Add the path separator */
*PathCurrent++ = ';';
break;
default:
break;
}
}
/* Everything should've perfectly fit in there */
ASSERT((PathCurrent - PathBuffer) * sizeof(WCHAR) == PathLengthInBytes);
ASSERT(PathCurrent > PathBuffer);
/* Terminate the whole thing */
PathCurrent[-1] = UNICODE_NULL;
Quickie:
/* Exit path: free our buffers */
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
if (PathBuffer)
{
/* This only gets freed in the failure path, since caller wants it */
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
PathBuffer = NULL;
}
}
/* Return the path! */
return PathBuffer;
}
LPWSTR
WINAPI
BaseComputeProcessSearchPath(VOID)
{
DPRINT("Computing Process Search path\n");
/* Compute the path using default process order */
return BasepComputeProcessPath(BaseProcessOrder, NULL, NULL);
}
LPWSTR
WINAPI
BaseComputeProcessExePath(IN LPWSTR FullPath)
{
PBASE_SEARCH_PATH_TYPE PathOrder;
DPRINT("Computing EXE path: %S\n", FullPath);
/* Check if we should use the current directory */
PathOrder = NeedCurrentDirectoryForExePathW(FullPath) ?
BaseProcessOrder : BaseProcessOrderNoCurrent;
/* And now compute the path */
return BasepComputeProcessPath(PathOrder, NULL, NULL);
}
LPWSTR
WINAPI
BaseComputeProcessDllPath(IN LPWSTR FullPath,
IN PVOID Environment)
{
LPWSTR DllPath = NULL;
UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode");
OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
HANDLE KeyHandle;
NTSTATUS Status;
ULONG ResultLength;
BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement, OldCurrentDirPlacement;
/* Acquire DLL directory lock */
RtlEnterCriticalSection(&BaseDllDirectoryLock);
/* Check if we have a base dll directory */
if (BaseDllDirectory.Buffer)
{
/* Then compute the process path using DLL order (without curdir) */
DllPath = BasepComputeProcessPath(BaseDllOrderNoCurrent, FullPath, Environment);
/* Release DLL directory lock */
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
/* Return dll path */
return DllPath;
}
/* Release DLL directory lock */
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
/* Read the current placement */
CurrentDirPlacement = BasepDllCurrentDirPlacement;
if (CurrentDirPlacement == BaseCurrentDirPlacementInvalid)
{
/* Open the configuration key */
Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
/* Query if safe search is enabled */
Status = NtQueryValueKey(KeyHandle,
&ValueName,
KeyValuePartialInformation,
&PartialInfo,
sizeof(PartialInfo),
&ResultLength);
if (NT_SUCCESS(Status))
{
/* Read the value if the size is OK */
if (ResultLength == sizeof(PartialInfo))
{
CurrentDirPlacement = *(PULONG)PartialInfo.Data;
}
}
/* Close the handle */
NtClose(KeyHandle);
/* Validate the registry value */
if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
(CurrentDirPlacement >= BaseCurrentDirPlacementMax))
{
/* Default to safe search */
CurrentDirPlacement = BaseCurrentDirPlacementSafe;
}
}
/* Update the placement and read the old one */
OldCurrentDirPlacement = InterlockedCompareExchange((PLONG)&BasepDllCurrentDirPlacement,
CurrentDirPlacement,
BaseCurrentDirPlacementInvalid);
if (OldCurrentDirPlacement != BaseCurrentDirPlacementInvalid)
{
/* If there already was a placement, use it */
CurrentDirPlacement = OldCurrentDirPlacement;
}
}
/* Check if the placement is invalid or not set */
if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
(CurrentDirPlacement >= BaseCurrentDirPlacementMax))
{
/* Default to safe search */
CurrentDirPlacement = BaseCurrentDirPlacementSafe;
}
/* Compute the process path using either normal or safe search */
DllPath = BasepComputeProcessPath(BaseDllOrderCurrent[CurrentDirPlacement],
FullPath,
Environment);
/* Return dll path */
return DllPath;
}
BOOLEAN
WINAPI
CheckForSameCurdir(IN PUNICODE_STRING DirName)
{
PUNICODE_STRING CurDir;
USHORT CurLength;
BOOLEAN Result;
UNICODE_STRING CurDirCopy;
CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
CurLength = CurDir->Length;
if (CurDir->Length <= 6)
{
if (CurLength != DirName->Length) return FALSE;
}
else
{
if ((CurLength - 2) != DirName->Length) return FALSE;
}
RtlAcquirePebLock();
CurDirCopy = *CurDir;
if (CurDirCopy.Length > 6) CurDirCopy.Length -= 2;
Result = 0;
if (RtlEqualUnicodeString(&CurDirCopy, DirName, TRUE)) Result = TRUE;
RtlReleasePebLock();
return Result;
}
/*
* Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
* identical (other than the Rtl can optionally check for spaces), however the
* Rtl will always convert to OEM, while kernel32 has two possible file modes
* (ANSI or OEM). Therefore we must duplicate the algorithm body to get
* the correct compatible results
*/
BOOL
WINAPI
IsShortName_U(IN PWCHAR Name,
IN ULONG Length)
{
BOOLEAN HasExtension;
UCHAR c;
NTSTATUS Status;
UNICODE_STRING UnicodeName;
ANSI_STRING AnsiName;
ULONG i, Dots;
CHAR AnsiBuffer[MAX_PATH];
ASSERT(Name);
/* What do you think 8.3 means? */
if (Length > 12) return FALSE;
/* Sure, any empty name is a short name */
if (!Length) return TRUE;
/* This could be . or .. or something else */
if (*Name == L'.')
{
/* Which one is it */
if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.'))
{
/* . or .., this is good */
return TRUE;
}
/* Some other bizare dot-based name, not good */
return FALSE;
}
/* Initialize our two strings */
RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH);
RtlInitEmptyUnicodeString(&UnicodeName, Name, (USHORT)Length * sizeof(WCHAR));
UnicodeName.Length = UnicodeName.MaximumLength;
/* Now do the conversion */
Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE);
if (!NT_SUCCESS(Status)) return FALSE;
/* Now we loop the name */
HasExtension = FALSE;
for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--)
{
/* Read the current byte */
c = AnsiName.Buffer[i];
/* Is it DBCS? */
if (IsDBCSLeadByte(c))
{
/* If we're near the end of the string, we can't allow a DBCS */
if ((!(HasExtension) && (i >= 7)) || (i == AnsiName.Length - 1))
{
return FALSE;
}
/* Otherwise we skip over it */
continue;
}
/* Check for illegal characters */
if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
{
return FALSE;
}
/* Check if this is perhaps an extension? */
if (c == '.')
{
/* Unless the extension is too large or there's more than one */
if ((HasExtension) || (Dots > 3)) return FALSE;
/* This looks like an extension */
HasExtension = TRUE;
}
/* 8.3 length was validated, but now we must guard against 9.2 or similar */
if ((i >= 8) && !(HasExtension)) return FALSE;
}
/* You survived the loop, this is a good short name */
return TRUE;
}
BOOL
WINAPI
IsLongName_U(IN PWCHAR FileName,
IN ULONG Length)
{
BOOLEAN HasExtension;
ULONG i, Dots;
/* More than 8.3, any combination of dots, and NULL names are all long */
if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE;
/* Otherwise, initialize our scanning loop */
HasExtension = FALSE;
for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
{
/* Check if this could be an extension */
if (FileName[i] == L'.')
{
/* Unlike the short case, we WANT more than one extension, or a long one */
if ((HasExtension) || (Dots > 3))
{
return TRUE;
}
HasExtension = TRUE;
}
/* Check if this would violate the "8" in 8.3, ie. 9.2 */
if ((i >= 8) && (!HasExtension)) return TRUE;
}
/* The name *seems* to conform to 8.3 */
return FALSE;
}
BOOL
WINAPI
FindLFNorSFN_U(IN PWCHAR Path,
OUT PWCHAR *First,
OUT PWCHAR *Last,
IN BOOL UseShort)
{
PWCHAR p;
ULONG Length;
BOOL Found = 0;
ASSERT(Path);
/* Loop while there is something in the path */
while (TRUE)
{
/* Loop within the path skipping slashes */
while ((*Path == L'\\') || (*Path == L'/')) Path++;
/* Make sure there's something after the slashes too! */
if (*Path == UNICODE_NULL) break;
/* Now skip past the file name until we get to the first slash */
p = Path + 1;
while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
/* Whatever is in between those two is now the file name length */
Length = p - Path;
/*
* Check if it is valid
* Note that !IsShortName != IsLongName, these two functions simply help
* us determine if a conversion is necessary or not.
* "Found" really means: "Is a conversion necessary?", hence the "!"
*/
Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
if (Found)
{
/* It is! did the caller request to know the markers? */
if ((First) && (Last))
{
/* Return them */
*First = Path;
*Last = p;
}
break;
}
/* Is there anything else following this sub-path/filename? */
if (*p == UNICODE_NULL) break;
/* Yes, keep going */
Path = p + 1;
}
/* Return if anything was found and valid */
return Found;
}
PWCHAR
WINAPI
SkipPathTypeIndicator_U(IN LPWSTR Path)
{
PWCHAR ReturnPath;
ULONG i;
/* Check what kind of path this is and how many slashes to skip */
switch (RtlDetermineDosPathNameType_U(Path))
{
case RtlPathTypeUncAbsolute:
case RtlPathTypeLocalDevice:
{
/* Keep going until we bypass the path indicators */
for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++)
{
/* We look for 2 slashes, so keep at it until we find them */
if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--;
}
return ReturnPath;
}
case RtlPathTypeDriveAbsolute:
return Path + 3;
case RtlPathTypeDriveRelative:
return Path + 2;
case RtlPathTypeRooted:
return Path + 1;
case RtlPathTypeRelative:
return Path;
case RtlPathTypeRootLocalDevice:
default:
return NULL;
}
}
BOOL
WINAPI
BasepIsCurDirAllowedForPlainExeNames(VOID)
{
NTSTATUS Status;
UNICODE_STRING EmptyString;
RtlInitEmptyUnicodeString(&EmptyString, NULL, 0);
Status = RtlQueryEnvironmentVariable_U(NULL,
&NoDefaultCurrentDirectoryInExePath,
&EmptyString);
return !NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
BOOL
WINAPI
SetDllDirectoryW(IN LPCWSTR lpPathName)
{
UNICODE_STRING OldDirectory, DllDirectory;
if (lpPathName)
{
if (wcschr(lpPathName, L';'))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!RtlCreateUnicodeString(&DllDirectory, lpPathName))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
}
else
{
RtlInitUnicodeString(&DllDirectory, NULL);
}
RtlEnterCriticalSection(&BaseDllDirectoryLock);
OldDirectory = BaseDllDirectory;
BaseDllDirectory = DllDirectory;
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
RtlFreeUnicodeString(&OldDirectory);
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
SetDllDirectoryA(IN LPCSTR lpPathName)
{
ANSI_STRING AnsiDllDirectory;
UNICODE_STRING OldDirectory, DllDirectory;
NTSTATUS Status;
if (lpPathName)
{
if (strchr(lpPathName, ';'))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
Status = RtlInitAnsiStringEx(&AnsiDllDirectory, lpPathName);
if (NT_SUCCESS(Status))
{
Status = Basep8BitStringToUnicodeString(&DllDirectory,
&AnsiDllDirectory,
TRUE);
}
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
}
else
{
RtlInitUnicodeString(&DllDirectory, NULL);
}
RtlEnterCriticalSection(&BaseDllDirectoryLock);
OldDirectory = BaseDllDirectory;
BaseDllDirectory = DllDirectory;
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
RtlFreeUnicodeString(&OldDirectory);
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
GetDllDirectoryW(IN DWORD nBufferLength,
OUT LPWSTR lpBuffer)
{
ULONG Length;
RtlEnterCriticalSection(&BaseDllDirectoryLock);
if ((nBufferLength * sizeof(WCHAR)) > BaseDllDirectory.Length)
{
RtlCopyMemory(lpBuffer, BaseDllDirectory.Buffer, BaseDllDirectory.Length);
Length = BaseDllDirectory.Length / sizeof(WCHAR);
lpBuffer[Length] = UNICODE_NULL;
}
else
{
Length = (BaseDllDirectory.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR);
if (lpBuffer) *lpBuffer = UNICODE_NULL;
}
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
return Length;
}
/*
* @implemented
*/
DWORD
WINAPI
GetDllDirectoryA(IN DWORD nBufferLength,
OUT LPSTR lpBuffer)
{
NTSTATUS Status;
ANSI_STRING AnsiDllDirectory;
ULONG Length;
RtlInitEmptyAnsiString(&AnsiDllDirectory, lpBuffer, (USHORT)nBufferLength);
RtlEnterCriticalSection(&BaseDllDirectoryLock);
Length = BasepUnicodeStringTo8BitSize(&BaseDllDirectory);
if (Length > nBufferLength)
{
Status = STATUS_SUCCESS;
if (lpBuffer) *lpBuffer = ANSI_NULL;
}
else
{
--Length;
Status = BasepUnicodeStringTo8BitString(&AnsiDllDirectory,
&BaseDllDirectory,
FALSE);
}
RtlLeaveCriticalSection(&BaseDllDirectoryLock);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Length = 0;
if (lpBuffer) *lpBuffer = ANSI_NULL;
}
return Length;
}
/*
* @implemented
*/
BOOL
WINAPI
NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName)
{
if (wcschr(ExeName, L'\\')) return TRUE;
return BasepIsCurDirAllowedForPlainExeNames();
}
/*
* @implemented
*/
BOOL
WINAPI
NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName)
{
if (strchr(ExeName, '\\')) return TRUE;
return BasepIsCurDirAllowedForPlainExeNames();
}
/*
* @implemented
*
* NOTE: Many of these A functions may seem to do rather complex A<->W mapping
* beyond what you would usually expect. There are two main reasons:
*
* First, these APIs are subject to the ANSI/OEM File API selection status that
* the caller has chosen, so we must use the "8BitString" internal Base APIs.
*
* Secondly, the Wide APIs (coming from the 9x world) are coded to return the
* length of the paths in "ANSI" by dividing their internal Wide character count
* by two... this is usually correct when dealing with pure-ASCII codepages but
* not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
* for CJK, for example.
*/
DWORD
WINAPI
GetFullPathNameA(IN LPCSTR lpFileName,
IN DWORD nBufferLength,
OUT LPSTR lpBuffer,
OUT LPSTR *lpFilePart)
{
NTSTATUS Status;
PWCHAR Buffer = NULL;
ULONG PathSize, FilePartSize;
ANSI_STRING AnsiString;
UNICODE_STRING FileNameString, UniString;
PWCHAR LocalFilePart;
PWCHAR* FilePart;
/* If the caller wants filepart, use a local wide buffer since this is A */
FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
/* Initialize for Quickie */
FilePartSize = PathSize = 0;
FileNameString.Buffer = NULL;
/* First get our string in Unicode */
Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName);
if (!NT_SUCCESS(Status)) goto Quickie;
/* Allocate a buffer to hold teh path name */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
0,
MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL));
if (!Buffer)
{
BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES);
goto Quickie;
}
/* Call into RTL to get the full Unicode path name */
PathSize = RtlGetFullPathName_U(FileNameString.Buffer,
MAX_PATH * sizeof(WCHAR),
Buffer,
FilePart);
if (PathSize <= (MAX_PATH * sizeof(WCHAR)))
{
/* The buffer will fit, get the real ANSI string size now */
Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize);
if (NT_SUCCESS(Status))
{
/* Now check if the user wanted file part size as well */
if ((PathSize) && (lpFilePart) && (LocalFilePart))
{
/* Yep, so in this case get the length of the file part too */
Status = RtlUnicodeToMultiByteSize(&FilePartSize,
Buffer,
(LocalFilePart - Buffer) *
sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
/* We failed to do that, so fail the whole call */
BaseSetLastNTError(Status);
PathSize = 0;
}
}
}
}
else
{
/* Reset the path size since the buffer is not large enough */
PathSize = 0;
}
/* Either no path, or local buffer was too small, enter failure code */
if (!PathSize) goto Quickie;
/* If the *caller's* buffer was too small, fail, but add in space for NULL */
if (PathSize >= nBufferLength)
{
PathSize++;
goto Quickie;
}
/* So far so good, initialize a unicode string to convert back to ANSI/OEM */
RtlInitUnicodeString(&UniString, Buffer);
Status = BasepUnicodeStringTo8BitString(&AnsiString, &UniString, TRUE);
if (!NT_SUCCESS(Status))
{
/* Final conversion failed, fail the call */
BaseSetLastNTError(Status);
PathSize = 0;
}
else
{
/* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1);
RtlFreeAnsiString(&AnsiString);
/* And finally, did the caller request file part information? */
if (lpFilePart)
{
/* Use the size we computed earlier and add it to the buffer */
*lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0;
}
}
Quickie:
/* Cleanup and return the path size */
if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString);
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
return PathSize;
}
/*
* @implemented
*/
DWORD
WINAPI
GetFullPathNameW(IN LPCWSTR lpFileName,
IN DWORD nBufferLength,
OUT LPWSTR lpBuffer,
OUT LPWSTR *lpFilePart)
{
/* Call Rtl to do the work */
return RtlGetFullPathName_U(lpFileName,
nBufferLength * sizeof(WCHAR),
lpBuffer,
lpFilePart) / sizeof(WCHAR);
}
/*
* @implemented
*/
DWORD
WINAPI
SearchPathA(IN LPCSTR lpPath OPTIONAL,
IN LPCSTR lpFileName,
IN LPCSTR lpExtension OPTIONAL,
IN DWORD nBufferLength,
OUT LPSTR lpBuffer,
OUT LPSTR *lpFilePart OPTIONAL)
{
PUNICODE_STRING FileNameString;
UNICODE_STRING PathString, ExtensionString;
NTSTATUS Status;
ULONG PathSize, FilePartSize, AnsiLength;
PWCHAR LocalFilePart, Buffer;
PWCHAR* FilePart;
/* If the caller wants filepart, use a local wide buffer since this is A */
FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
/* Initialize stuff for Quickie */
PathSize = 0;
Buffer = NULL;
ExtensionString.Buffer = PathString.Buffer = NULL;
/* Get the UNICODE_STRING file name */
FileNameString = Basep8BitStringToStaticUnicodeString(lpFileName);
if (!FileNameString) return 0;
/* Did the caller specify an extension */
if (lpExtension)
{
/* Yup, convert it into UNICODE_STRING */
Status = Basep8BitStringToDynamicUnicodeString(&ExtensionString,
lpExtension);
if (!NT_SUCCESS(Status)) goto Quickie;
}
/* Did the caller specify a path */
if (lpPath)
{
/* Yup, convert it into UNICODE_STRING */
Status = Basep8BitStringToDynamicUnicodeString(&PathString, lpPath);
if (!NT_SUCCESS(Status)) goto Quickie;
}
/* Allocate our output buffer */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength * sizeof(WCHAR));
if (!Buffer)
{
/* It failed, bail out */
BaseSetLastNTError(STATUS_NO_MEMORY);
goto Quickie;
}
/* Now run the Wide search with the input buffer lengths */
PathSize = SearchPathW(PathString.Buffer,
FileNameString->Buffer,
ExtensionString.Buffer,
nBufferLength,
Buffer,
FilePart);
if (PathSize <= nBufferLength)
{
/* It fits, but is it empty? If so, bail out */
if (!PathSize) goto Quickie;
/* The length above is inexact, we need it in ANSI */
Status = RtlUnicodeToMultiByteSize(&AnsiLength, Buffer, PathSize * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
/* Conversion failed, fail the call */
PathSize = 0;
BaseSetLastNTError(Status);
goto Quickie;
}
/* If the correct ANSI size is too big, return required length plus a NULL */
if (AnsiLength >= nBufferLength)
{
PathSize = AnsiLength + 1;
goto Quickie;
}
/* Now apply the final conversion to ANSI */
Status = RtlUnicodeToMultiByteN(lpBuffer,
nBufferLength - 1,
&AnsiLength,
Buffer,
PathSize * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
/* Conversion failed, fail the whole call */
PathSize = 0;
BaseSetLastNTError(STATUS_NO_MEMORY);
goto Quickie;
}
/* NULL-terminate and return the real ANSI length */
lpBuffer[AnsiLength] = ANSI_NULL;
PathSize = AnsiLength;
/* Now check if the user wanted file part size as well */
if (lpFilePart)
{
/* If we didn't get a file part, clear the caller's */
if (!LocalFilePart)
{
*lpFilePart = NULL;
}
else
{
/* Yep, so in this case get the length of the file part too */
Status = RtlUnicodeToMultiByteSize(&FilePartSize,
Buffer,
(LocalFilePart - Buffer) *
sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
/* We failed to do that, so fail the whole call */
BaseSetLastNTError(Status);
PathSize = 0;
}
/* Return the file part buffer */
*lpFilePart = lpBuffer + FilePartSize;
}
}
}
else
{
/* Our initial buffer guess was too small, allocate a bigger one */
RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize * sizeof(WCHAR));
if (!Buffer)
{
/* Out of memory, fail everything */
BaseSetLastNTError(STATUS_NO_MEMORY);
goto Quickie;
}
/* Do the search again -- it will fail, we just want the path size */
PathSize = SearchPathW(PathString.Buffer,
FileNameString->Buffer,
ExtensionString.Buffer,
PathSize,
Buffer,
FilePart);
if (!PathSize) goto Quickie;
/* Convert it to a correct size */
Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize * sizeof(WCHAR));
if (NT_SUCCESS(Status))
{
/* Make space for the NULL-char */
PathSize++;
}
else
{
/* Conversion failed for some reason, fail the call */
BaseSetLastNTError(Status);
PathSize = 0;
}
}
Quickie:
/* Cleanup/complete path */
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
if (ExtensionString.Buffer) RtlFreeUnicodeString(&ExtensionString);
if (PathString.Buffer) RtlFreeUnicodeString(&PathString);
return PathSize;
}
/*
* @implemented
*/
DWORD
WINAPI
SearchPathW(IN LPCWSTR lpPath OPTIONAL,
IN LPCWSTR lpFileName,
IN LPCWSTR lpExtension OPTIONAL,
IN DWORD nBufferLength,
OUT LPWSTR lpBuffer,
OUT LPWSTR *lpFilePart OPTIONAL)
{
UNICODE_STRING FileNameString, ExtensionString, PathString, CallerBuffer;
ULONG Flags, LengthNeeded, FilePartSize;
NTSTATUS Status;
DWORD Result = 0;
/* Default flags for RtlDosSearchPath_Ustr */
Flags = 6;
/* Clear file part in case we fail */
if (lpFilePart) *lpFilePart = NULL;
/* Initialize path buffer for free later */
PathString.Buffer = NULL;
/* Convert filename to a unicode string and eliminate trailing spaces */
RtlInitUnicodeString(&FileNameString, lpFileName);
while ((FileNameString.Length >= sizeof(WCHAR)) &&
(FileNameString.Buffer[(FileNameString.Length / sizeof(WCHAR)) - 1] == L' '))
{
FileNameString.Length -= sizeof(WCHAR);
}
/* Was it all just spaces? */
if (!FileNameString.Length)
{
/* Fail out */
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
goto Quickie;
}
/* Convert extension to a unicode string */
RtlInitUnicodeString(&ExtensionString, lpExtension);
/* Check if the user sent a path */
if (lpPath)
{
/* Convert it to a unicode string too */
Status = RtlInitUnicodeStringEx(&PathString, lpPath);
if (NT_ERROR(Status))
{
/* Fail if it was too long */
BaseSetLastNTError(Status);
goto Quickie;
}
}
else
{
/* A path wasn't sent, so compute it ourselves */
PathString.Buffer = BaseComputeProcessSearchPath();
if (!PathString.Buffer)
{
/* Fail if we couldn't compute it */
BaseSetLastNTError(STATUS_NO_MEMORY);
goto Quickie;
}
/* See how big the computed path is */
LengthNeeded = lstrlenW(PathString.Buffer);
if (LengthNeeded > UNICODE_STRING_MAX_CHARS)
{
/* Fail if it's too long */
BaseSetLastNTError(STATUS_NAME_TOO_LONG);
goto Quickie;
}
/* Set the path size now that we have it */
PathString.MaximumLength = PathString.Length = (USHORT)LengthNeeded * sizeof(WCHAR);
/* Request SxS isolation from RtlDosSearchPath_Ustr */
Flags |= 1;
}
/* Create the string that describes the output buffer from the caller */
CallerBuffer.Length = 0;
CallerBuffer.Buffer = lpBuffer;
/* How much space does the caller have? */
if (nBufferLength <= UNICODE_STRING_MAX_CHARS)
{
/* Add it into the string */
CallerBuffer.MaximumLength = (USHORT)nBufferLength * sizeof(WCHAR);
}
else
{
/* Caller wants too much, limit it to the maximum length of a string */
CallerBuffer.MaximumLength = UNICODE_STRING_MAX_BYTES;
}
/* Call Rtl to do the work */
Status = RtlDosSearchPath_Ustr(Flags,
&PathString,
&FileNameString,
&ExtensionString,
&CallerBuffer,
NULL,
NULL,
&FilePartSize,
&LengthNeeded);
if (NT_ERROR(Status))
{
/* Check for unusual status codes */
if ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))
{
/* Print them out since maybe an app needs fixing */
DbgPrint("%s on file %wZ failed; NTSTATUS = %08lx\n",
__FUNCTION__,
&FileNameString,
Status);
DbgPrint(" Path = %wZ\n", &PathString);
}
/* Check if the failure was due to a small buffer */
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Check if the length was actually too big for Rtl to work with */
Result = LengthNeeded / sizeof(WCHAR);
if (Result > 0xFFFFFFFF) BaseSetLastNTError(STATUS_NAME_TOO_LONG);
}
else
{
/* Some other error, set the error code */
BaseSetLastNTError(Status);
}
}
else
{
/* It worked! Write the file part now */
if (lpFilePart) *lpFilePart = &lpBuffer[FilePartSize];
/* Convert the final result length */
Result = CallerBuffer.Length / sizeof(WCHAR);
}
Quickie:
/* Check if there was a dynamic path string to free */
if ((PathString.Buffer != lpPath) && (PathString.Buffer))
{
/* And free it */
RtlFreeHeap(RtlGetProcessHeap(), 0, PathString.Buffer);
}
/* Return the final result length */
return Result;
}
/*
* @implemented
*/
DWORD
WINAPI
GetLongPathNameW(IN LPCWSTR lpszShortPath,
OUT LPWSTR lpszLongPath,
IN DWORD cchBuffer)
{
PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
ULONG Length;
WCHAR LastChar;
HANDLE FindHandle;
DWORD ReturnLength;
ULONG ErrorMode;
BOOLEAN Found = FALSE;
WIN32_FIND_DATAW FindFileData;
/* Initialize so Quickie knows there's nothing to do */
Buffer = Original = NULL;
ReturnLength = 0;
/* First check if the input path was obviously NULL */
if (!lpszShortPath)
{
/* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/* We will be touching removed, removable drives -- don't warn the user */
ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
/* Do a simple check to see if the path exists */
if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
{
/* It doesn't, so fail */
ReturnLength = 0;
goto Quickie;
}
/* Now get a pointer to the actual path, skipping indicators */
Path = SkipPathTypeIndicator_U((LPWSTR)lpszShortPath);
/* Is there any path or filename in there? */
if (!(Path) ||
(*Path == UNICODE_NULL) ||
!(FindLFNorSFN_U(Path, &First, &Last, FALSE)))
{
/* There isn't, so the long path is simply the short path */
ReturnLength = wcslen(lpszShortPath);
/* Is there space for it? */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{
/* Make sure the pointers aren't already the same */
if (lpszLongPath != lpszShortPath)
{
/* They're not -- copy the short path into the long path */
RtlMoveMemory(lpszLongPath,
lpszShortPath,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Otherwise, let caller know we need a bigger buffer, include NULL */
ReturnLength++;
}
goto Quickie;
}
/* We are still in the game -- compute the current size */
Length = wcslen(lpszShortPath) + sizeof(ANSI_NULL);
Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
if (!Original) goto ErrorQuickie;
/* Make a copy of it */
RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR));
/* Compute the new first and last markers */
First = &Original[First - lpszShortPath];
Last = &Original[Last - lpszShortPath];
/* Set the current destination pointer for a copy */
Dst = lpszLongPath;
/*
* Windows allows the paths to overlap -- we have to be careful with this and
* see if it's same to do so, and if not, allocate our own internal buffer
* that we'll return at the end.
*
* This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
*/
if ((cchBuffer) && (lpszLongPath) &&
(((lpszLongPath >= lpszShortPath) && (lpszLongPath < &lpszShortPath[Length])) ||
((lpszLongPath < lpszShortPath) && (&lpszLongPath[cchBuffer] >= lpszShortPath))))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
if (!Buffer) goto ErrorQuickie;
/* New destination */
Dst = Buffer;
}
/* Prepare for the loop */
Src = Original;
ReturnLength = 0;
while (TRUE)
{
/* Current delta in the loop */
Length = First - Src;
/* Update the return length by it */
ReturnLength += Length;
/* Is there a delta? If so, is there space and buffer for it? */
if ((Length) && (cchBuffer > ReturnLength) && (lpszLongPath))
{
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
Dst += Length;
}
/* "Terminate" this portion of the path's substring so we can do a find */
LastChar = *Last;
*Last = UNICODE_NULL;
FindHandle = FindFirstFileW(Original, &FindFileData);
*Last = LastChar;
/* This portion wasn't found, so fail */
if (FindHandle == INVALID_HANDLE_VALUE)
{
ReturnLength = 0;
break;
}
/* Close the find handle */
FindClose(FindHandle);
/* Now check the length of the long name */
Length = wcslen(FindFileData.cFileName);
if (Length)
{
/* This is our new first marker */
First = FindFileData.cFileName;
}
else
{
/* Otherwise, the name is the delta between our current markers */
Length = Last - First;
}
/* Update the return length with the short name length, if any */
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{
/* And do the copy if there is */
RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
Dst += Length;
}
/* Now update the source pointer */
Src = Last;
if (*Src == UNICODE_NULL) break;
/* Are there more names in there? */
Found = FindLFNorSFN_U(Src, &First, &Last, FALSE);
if (!Found) break;
}
/* The loop is done, is there anything left? */
if (ReturnLength)
{
/* Get the length of the straggling path */
Length = wcslen(Src);
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszLongPath))
{
/* And do the copy if there is -- accounting for NULL here */
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
/* What about our buffer? */
if (Buffer)
{
/* Copy it into the caller's long path */
RtlMoveMemory(lpszLongPath,
Buffer,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Buffer is too small, let the caller know, making space for NULL */
ReturnLength++;
}
}
/* We're all done */
goto Quickie;
ErrorQuickie:
/* This is the goto for memory failures */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Quickie:
/* General function end: free memory, restore error mode, return length */
if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
SetErrorMode(ErrorMode);
return ReturnLength;
}
/*
* @implemented
*/
DWORD
WINAPI
GetLongPathNameA(IN LPCSTR lpszShortPath,
OUT LPSTR lpszLongPath,
IN DWORD cchBuffer)
{
ULONG Result, PathLength;
PWCHAR LongPath;
NTSTATUS Status;
UNICODE_STRING LongPathUni, ShortPathUni;
ANSI_STRING LongPathAnsi;
WCHAR LongPathBuffer[MAX_PATH];
LongPath = NULL;
LongPathAnsi.Buffer = NULL;
ShortPathUni.Buffer = NULL;
Result = 0;
if (!lpszShortPath)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath);
if (!NT_SUCCESS(Status)) goto Quickie;
LongPath = LongPathBuffer;
PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPathBuffer, MAX_PATH);
if (PathLength >= MAX_PATH)
{
LongPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
if (!LongPath)
{
PathLength = 0;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength);
}
}
if (!PathLength) goto Quickie;
ShortPathUni.MaximumLength = (USHORT)PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
LongPathUni.Buffer = LongPath;
LongPathUni.Length = (USHORT)PathLength * sizeof(WCHAR);
Status = BasepUnicodeStringTo8BitString(&LongPathAnsi, &LongPathUni, TRUE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Result = 0;
}
Result = LongPathAnsi.Length;
if ((lpszLongPath) && (cchBuffer > LongPathAnsi.Length))
{
RtlMoveMemory(lpszLongPath, LongPathAnsi.Buffer, LongPathAnsi.Length);
lpszLongPath[Result] = ANSI_NULL;
}
else
{
Result = LongPathAnsi.Length + sizeof(ANSI_NULL);
}
Quickie:
if (ShortPathUni.Buffer) RtlFreeUnicodeString(&ShortPathUni);
if (LongPathAnsi.Buffer) RtlFreeAnsiString(&LongPathAnsi);
if ((LongPath) && (LongPath != LongPathBuffer))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath);
}
return Result;
}
/*
* @implemented
*/
DWORD
WINAPI
GetShortPathNameA(IN LPCSTR lpszLongPath,
OUT LPSTR lpszShortPath,
IN DWORD cchBuffer)
{
ULONG Result, PathLength;
PWCHAR ShortPath;
NTSTATUS Status;
UNICODE_STRING LongPathUni, ShortPathUni;
ANSI_STRING ShortPathAnsi;
WCHAR ShortPathBuffer[MAX_PATH];
ShortPath = NULL;
ShortPathAnsi.Buffer = NULL;
LongPathUni.Buffer = NULL;
Result = 0;
if (!lpszLongPath)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath);
if (!NT_SUCCESS(Status)) goto Quickie;
ShortPath = ShortPathBuffer;
PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPathBuffer, MAX_PATH);
if (PathLength >= MAX_PATH)
{
ShortPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
if (!ShortPath)
{
PathLength = 0;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
}
}
if (!PathLength) goto Quickie;
LongPathUni.MaximumLength = (USHORT)PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
ShortPathUni.Buffer = ShortPath;
ShortPathUni.Length = (USHORT)PathLength * sizeof(WCHAR);
Status = BasepUnicodeStringTo8BitString(&ShortPathAnsi, &ShortPathUni, TRUE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
Result = 0;
}
Result = ShortPathAnsi.Length;
if ((lpszShortPath) && (cchBuffer > ShortPathAnsi.Length))
{
RtlMoveMemory(lpszShortPath, ShortPathAnsi.Buffer, ShortPathAnsi.Length);
lpszShortPath[Result] = ANSI_NULL;
}
else
{
Result = ShortPathAnsi.Length + sizeof(ANSI_NULL);
}
Quickie:
if (LongPathUni.Buffer) RtlFreeUnicodeString(&LongPathUni);
if (ShortPathAnsi.Buffer) RtlFreeAnsiString(&ShortPathAnsi);
if ((ShortPath) && (ShortPath != ShortPathBuffer))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath);
}
return Result;
}
/*
* @implemented
*/
DWORD
WINAPI
GetShortPathNameW(IN LPCWSTR lpszLongPath,
OUT LPWSTR lpszShortPath,
IN DWORD cchBuffer)
{
PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
ULONG Length;
WCHAR LastChar;
HANDLE FindHandle;
DWORD ReturnLength;
ULONG ErrorMode;
BOOLEAN Found = FALSE;
WIN32_FIND_DATAW FindFileData;
/* Initialize so Quickie knows there's nothing to do */
Buffer = Original = NULL;
ReturnLength = 0;
/* First check if the input path was obviously NULL */
if (!lpszLongPath)
{
/* Fail the request */
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/* We will be touching removed, removable drives -- don't warn the user */
ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
/* Do a simple check to see if the path exists */
if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
{
/* Windows checks for an application compatibility flag to allow this */
if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & GetShortPathNameNT4))
{
/* It doesn't, so fail */
ReturnLength = 0;
goto Quickie;
}
}
/* Now get a pointer to the actual path, skipping indicators */
Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath);
/* Is there any path or filename in there? */
if (!(Path) ||
(*Path == UNICODE_NULL) ||
!(FindLFNorSFN_U(Path, &First, &Last, TRUE)))
{
/* There isn't, so the long path is simply the short path */
ReturnLength = wcslen(lpszLongPath);
/* Is there space for it? */
if ((cchBuffer > ReturnLength) && (lpszShortPath))
{
/* Make sure the pointers aren't already the same */
if (lpszLongPath != lpszShortPath)
{
/* They're not -- copy the short path into the long path */
RtlMoveMemory(lpszShortPath,
lpszLongPath,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Otherwise, let caller know we need a bigger buffer, include NULL */
ReturnLength++;
}
goto Quickie;
}
/* We are still in the game -- compute the current size */
Length = wcslen(lpszLongPath) + sizeof(ANSI_NULL);
Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
if (!Original) goto ErrorQuickie;
/* Make a copy of it */
wcsncpy(Original, lpszLongPath, Length);
/* Compute the new first and last markers */
First = &Original[First - lpszLongPath];
Last = &Original[Last - lpszLongPath];
/* Set the current destination pointer for a copy */
Dst = lpszShortPath;
/*
* Windows allows the paths to overlap -- we have to be careful with this and
* see if it's same to do so, and if not, allocate our own internal buffer
* that we'll return at the end.
*
* This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
*/
if ((cchBuffer) && (lpszShortPath) &&
(((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) ||
((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath))))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
if (!Buffer) goto ErrorQuickie;
/* New destination */
Dst = Buffer;
}
/* Prepare for the loop */
Src = Original;
ReturnLength = 0;
while (TRUE)
{
/* Current delta in the loop */
Length = First - Src;
/* Update the return length by it */
ReturnLength += Length;
/* Is there a delta? If so, is there space and buffer for it? */
if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath))
{
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
Dst += Length;
}
/* "Terminate" this portion of the path's substring so we can do a find */
LastChar = *Last;
*Last = UNICODE_NULL;
FindHandle = FindFirstFileW(Original, &FindFileData);
*Last = LastChar;
/* This portion wasn't found, so fail */
if (FindHandle == INVALID_HANDLE_VALUE)
{
ReturnLength = 0;
break;
}
/* Close the find handle */
FindClose(FindHandle);
/* Now check the length of the short name */
Length = wcslen(FindFileData.cAlternateFileName);
if (Length)
{
/* This is our new first marker */
First = FindFileData.cAlternateFileName;
}
else
{
/* Otherwise, the name is the delta between our current markers */
Length = Last - First;
}
/* Update the return length with the short name length, if any */
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszShortPath))
{
/* And do the copy if there is */
RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
Dst += Length;
}
/* Now update the source pointer */
Src = Last;
if (*Src == UNICODE_NULL) break;
/* Are there more names in there? */
Found = FindLFNorSFN_U(Src, &First, &Last, TRUE);
if (!Found) break;
}
/* The loop is done, is there anything left? */
if (ReturnLength)
{
/* Get the length of the straggling path */
Length = wcslen(Src);
ReturnLength += Length;
/* Once again check for appropriate space and buffer */
if ((cchBuffer > ReturnLength) && (lpszShortPath))
{
/* And do the copy if there is -- accounting for NULL here */
RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
/* What about our buffer? */
if (Buffer)
{
/* Copy it into the caller's long path */
RtlMoveMemory(lpszShortPath,
Buffer,
ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
}
}
else
{
/* Buffer is too small, let the caller know, making space for NULL */
ReturnLength++;
}
}
/* We're all done */
goto Quickie;
ErrorQuickie:
/* This is the goto for memory failures */
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Quickie:
/* General function end: free memory, restore error mode, return length */
if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
SetErrorMode(ErrorMode);
return ReturnLength;
}
/*
* @implemented
*
* NOTE: Windows returns a dos/short (8.3) path
*/
DWORD
WINAPI
GetTempPathA(IN DWORD nBufferLength,
OUT LPSTR lpBuffer)
{
WCHAR BufferW[MAX_PATH];
DWORD ret;
ret = GetTempPathW(MAX_PATH, BufferW);
if (!ret) return 0;
if (ret > MAX_PATH)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return 0;
}
return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
}
/*
* @implemented
*
* ripped from wine
*/
DWORD
WINAPI
GetTempPathW(IN DWORD count,
OUT LPWSTR path)
{
static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
WCHAR tmp_path[MAX_PATH];
WCHAR full_tmp_path[MAX_PATH];
UINT ret;
DPRINT("%u,%p\n", count, path);
if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
!(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
!(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
!(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
{
return 0;
}
if (ret > MAX_PATH)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return 0;
}
ret = GetFullPathNameW(tmp_path, MAX_PATH, full_tmp_path, NULL);
if (!ret) return 0;
if (ret > MAX_PATH - 2)
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return 0;
}
if (full_tmp_path[ret-1] != '\\')
{
full_tmp_path[ret++] = '\\';
full_tmp_path[ret] = '\0';
}
ret++; /* add space for terminating 0 */
if (count >= ret)
{
lstrcpynW(path, full_tmp_path, count);
/* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767
* bytes after it, we will assume the > XP behavior for now */
memset(path + ret, 0, (min(count, 32767) - ret) * sizeof(WCHAR));
ret--; /* return length without 0 */
}
else if (count)
{
/* the buffer must be cleared if contents will not fit */
memset(path, 0, count * sizeof(WCHAR));
}
DPRINT("GetTempPathW returning %u, %S\n", ret, path);
return ret;
}
/*
* @implemented
*/
DWORD
WINAPI
GetCurrentDirectoryA(IN DWORD nBufferLength,
OUT LPSTR lpBuffer)
{
ANSI_STRING AnsiString;
NTSTATUS Status;
PUNICODE_STRING StaticString;
ULONG MaxLength;
StaticString = &NtCurrentTeb()->StaticUnicodeString;
MaxLength = nBufferLength;
if (nBufferLength >= UNICODE_STRING_MAX_BYTES)
{
MaxLength = UNICODE_STRING_MAX_BYTES - 1;
}
StaticString->Length = (USHORT)RtlGetCurrentDirectory_U(StaticString->MaximumLength,
StaticString->Buffer);
Status = RtlUnicodeToMultiByteSize(&nBufferLength,
StaticString->Buffer,
StaticString->Length);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return 0;
}
if (MaxLength <= nBufferLength)
{
return nBufferLength + 1;
}
AnsiString.Buffer = lpBuffer;
AnsiString.MaximumLength = (USHORT)MaxLength;
Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return 0;
}
return AnsiString.Length;
}
/*
* @implemented
*/
DWORD
WINAPI
GetCurrentDirectoryW(IN DWORD nBufferLength,
OUT LPWSTR lpBuffer)
{
return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR);
}
/*
* @implemented
*/
BOOL
WINAPI
SetCurrentDirectoryA(IN LPCSTR lpPathName)
{
PUNICODE_STRING DirName;
NTSTATUS Status;
if (!lpPathName)
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}
DirName = Basep8BitStringToStaticUnicodeString(lpPathName);
if (!DirName) return FALSE;
if (CheckForSameCurdir(DirName)) return TRUE;
Status = RtlSetCurrentDirectory_U(DirName);
if (NT_SUCCESS(Status)) return TRUE;
if ((*DirName->Buffer != L'"') || (DirName->Length <= 2))
{
BaseSetLastNTError(Status);
return 0;
}
DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1);
if (!DirName) return FALSE;
Status = RtlSetCurrentDirectory_U(DirName);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
SetCurrentDirectoryW(IN LPCWSTR lpPathName)
{
NTSTATUS Status;
UNICODE_STRING UnicodeString;
if (!lpPathName)
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}
Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName);
if (NT_SUCCESS(Status))
{
if (!CheckForSameCurdir(&UnicodeString))
{
Status = RtlSetCurrentDirectory_U(&UnicodeString);
}
}
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return FALSE;
}
return TRUE;
}
/*
* @implemented
*/
UINT
WINAPI
GetSystemDirectoryA(OUT LPSTR lpBuffer,
IN UINT uSize)
{
ANSI_STRING AnsiString;
NTSTATUS Status;
ULONG AnsiLength;
/* Get the correct size of the Unicode Base directory */
Status = RtlUnicodeToMultiByteSize(&AnsiLength,
BaseWindowsSystemDirectory.Buffer,
BaseWindowsSystemDirectory.MaximumLength);
if (!NT_SUCCESS(Status)) return 0;
if (uSize < AnsiLength) return AnsiLength;
RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
Status = BasepUnicodeStringTo8BitString(&AnsiString,
&BaseWindowsSystemDirectory,
FALSE);
if (!NT_SUCCESS(Status)) return 0;
return AnsiString.Length;
}
/*
* @implemented
*/
UINT
WINAPI
GetSystemDirectoryW(OUT LPWSTR lpBuffer,
IN UINT uSize)
{
ULONG ReturnLength;
ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
if ((uSize * sizeof(WCHAR)) >= ReturnLength)
{
RtlCopyMemory(lpBuffer,
BaseWindowsSystemDirectory.Buffer,
BaseWindowsSystemDirectory.Length);
lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
ReturnLength = BaseWindowsSystemDirectory.Length;
}
return ReturnLength / sizeof(WCHAR);
}
/*
* @implemented
*/
UINT
WINAPI
GetWindowsDirectoryA(OUT LPSTR lpBuffer,
IN UINT uSize)
{
/* Is this a TS installation? */
if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
/* Otherwise, call the System API */
return GetSystemWindowsDirectoryA(lpBuffer, uSize);
}
/*
* @implemented
*/
UINT
WINAPI
GetWindowsDirectoryW(OUT LPWSTR lpBuffer,
IN UINT uSize)
{
/* Is this a TS installation? */
if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
/* Otherwise, call the System API */
return GetSystemWindowsDirectoryW(lpBuffer, uSize);
}
/*
* @implemented
*/
UINT
WINAPI
GetSystemWindowsDirectoryA(OUT LPSTR lpBuffer,
IN UINT uSize)
{
ANSI_STRING AnsiString;
NTSTATUS Status;
ULONG AnsiLength;
/* Get the correct size of the Unicode Base directory */
Status = RtlUnicodeToMultiByteSize(&AnsiLength,
BaseWindowsDirectory.Buffer,
BaseWindowsDirectory.MaximumLength);
if (!NT_SUCCESS(Status)) return 0;
if (uSize < AnsiLength) return AnsiLength;
RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
Status = BasepUnicodeStringTo8BitString(&AnsiString,
&BaseWindowsDirectory,
FALSE);
if (!NT_SUCCESS(Status)) return 0;
return AnsiString.Length;
}
/*
* @implemented
*/
UINT
WINAPI
GetSystemWindowsDirectoryW(OUT LPWSTR lpBuffer,
IN UINT uSize)
{
ULONG ReturnLength;
ReturnLength = BaseWindowsDirectory.MaximumLength;
if ((uSize * sizeof(WCHAR)) >= ReturnLength)
{
RtlCopyMemory(lpBuffer,
BaseWindowsDirectory.Buffer,
BaseWindowsDirectory.Length);
lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
ReturnLength = BaseWindowsDirectory.Length;
}
return ReturnLength / sizeof(WCHAR);
}
/*
* @unimplemented
*/
UINT
WINAPI
GetSystemWow64DirectoryW(OUT LPWSTR lpBuffer,
IN UINT uSize)
{
#ifdef _WIN64
UNIMPLEMENTED;
return 0;
#else
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
#endif
}
/*
* @unimplemented
*/
UINT
WINAPI
GetSystemWow64DirectoryA(OUT LPSTR lpBuffer,
IN UINT uSize)
{
#ifdef _WIN64
UNIMPLEMENTED;
return 0;
#else
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
#endif
}
/* EOF */