/* * 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 #define NDEBUG #include /* 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; SIZE_T 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, (ULONG)(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, (ULONG)(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; SIZE_T 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; SIZE_T Length, ReturnLength; WCHAR LastChar; HANDLE FindHandle; 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; SIZE_T Length, ReturnLength; WCHAR LastChar; HANDLE FindHandle; 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 */