From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/nt/nthlpfs.c | 636 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 src/lib/nt/nthlpfs.c (limited to 'src/lib/nt/nthlpfs.c') diff --git a/src/lib/nt/nthlpfs.c b/src/lib/nt/nthlpfs.c new file mode 100644 index 0000000..a85c517 --- /dev/null +++ b/src/lib/nt/nthlpfs.c @@ -0,0 +1,636 @@ +/* $Id: nthlpfs.c 3223 2018-03-31 02:29:56Z bird $ */ +/** @file + * MSC + NT helpers for file system related functions. + */ + +/* + * Copyright (c) 2005-2013 knut st. osmundsen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Alternatively, the content of this file may be used under the terms of the + * GPL version 2 or later, or LGPL version 2.1 or later. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "nthlp.h" +#include +#include +#include +#include + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +static int g_fHaveOpenReparsePoint = -1; + + + +static int birdHasTrailingSlash(const char *pszPath) +{ + char ch, ch2; + + /* Skip leading slashes. */ + while ((ch = *pszPath) == '/' || ch == '\\') + pszPath++; + if (ch == '\0') + return 0; + + /* Find the last char. */ + while ((ch2 = *++pszPath) != '\0') + ch = ch2; + + return ch == '/' || ch == '\\' || ch == ':'; +} + + +static int birdHasTrailingSlashW(const wchar_t *pwszPath) +{ + wchar_t wc, wc2; + + /* Skip leading slashes. */ + while ((wc = *pwszPath) == '/' || wc == '\\') + pwszPath++; + if (wc == '\0') + return 0; + + /* Find the last char. */ + while ((wc2 = *++pwszPath) != '\0') + wc = wc2; + + return wc == '/' || wc == '\\' || wc == ':'; +} + + +int birdIsPathDirSpec(const char *pszPath) +{ + char ch, ch2; + + /* Check for empty string. */ + ch = *pszPath; + if (ch == '\0') + return 0; + + /* Find the last char. */ + while ((ch2 = *++pszPath) != '\0') + ch = ch2; + + return ch == '/' || ch == '\\' || ch == ':'; +} + + +static int birdIsPathDirSpecW(const wchar_t *pwszPath) +{ + wchar_t wc, wc2; + + /* Check for empty string. */ + wc = *pwszPath; + if (wc == '\0') + return 0; + + /* Find the last char. */ + while ((wc2 = *++pwszPath) != '\0') + wc = wc2; + + return wc == '/' || wc == '\\' || wc == ':'; +} + + +int birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath) +{ + MY_NTSTATUS rcNt; + WCHAR wszTmp[4096]; + MY_UNICODE_STRING TmpUniStr; + MY_ANSI_STRING Src; + + birdResolveImports(); + + pNtPath->Length = pNtPath->MaximumLength = 0; + pNtPath->Buffer = NULL; + + /* + * Convert the input to wide char. + */ + Src.Buffer = (PCHAR)pszPath; + Src.MaximumLength = Src.Length = (USHORT)strlen(pszPath); + + TmpUniStr.Length = 0; + TmpUniStr.MaximumLength = sizeof(wszTmp) - sizeof(WCHAR); + TmpUniStr.Buffer = wszTmp; + + rcNt = g_pfnRtlAnsiStringToUnicodeString(&TmpUniStr, &Src, FALSE); + if (MY_NT_SUCCESS(rcNt)) + { + if (TmpUniStr.Length > 0 && !(TmpUniStr.Length & 1)) + { + wszTmp[TmpUniStr.Length / sizeof(WCHAR)] = '\0'; + + /* + * Convert the wide DOS path to an NT path. + */ + if (g_pfnRtlDosPathNameToNtPathName_U(wszTmp, pNtPath, NULL, FALSE)) + return 0; + } + rcNt = -1; + } + return birdSetErrnoFromNt(rcNt); +} + + +int birdDosToNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath) +{ + birdResolveImports(); + + pNtPath->Length = pNtPath->MaximumLength = 0; + pNtPath->Buffer = NULL; + + /* + * Convert the wide DOS path to an NT path. + */ + if (g_pfnRtlDosPathNameToNtPathName_U(pwszPath, pNtPath, NULL, FALSE)) + return 0; + return birdSetErrnoFromNt(STATUS_NO_MEMORY); +} + + +/** + * Converts UNIX slashes to DOS ones. + * + * @returns 0 + * @param pNtPath The relative NT path to fix up. + */ +static int birdFixRelativeNtPathSlashesAndReturn0(MY_UNICODE_STRING *pNtPath) +{ + size_t cwcLeft = pNtPath->Length / sizeof(wchar_t); + wchar_t *pwcStart = pNtPath->Buffer; + wchar_t *pwcHit; + + /* Convert slashes. */ + while ((pwcHit = wmemchr(pwcStart, '/', cwcLeft)) != NULL) + { + *pwcHit = '\\'; + cwcLeft -= pwcHit - pwcStart; + pwcHit = pwcStart; + } + +#if 0 + /* Strip trailing slashes (NT doesn't like them). */ + while ( pNtPath->Length >= sizeof(wchar_t) + && pNtPath->Buffer[(pNtPath->Length - sizeof(wchar_t)) / sizeof(wchar_t)] == '\\') + { + pNtPath->Length -= sizeof(wchar_t); + pNtPath->Buffer[pNtPath->Length / sizeof(wchar_t)] = '\0'; + } + + /* If it was all trailing slashes we convert it to a dot path. */ + if ( pNtPath->Length == 0 + && pNtPath->MaximumLength >= sizeof(wchar_t) * 2) + { + pNtPath->Length = sizeof(wchar_t); + pNtPath->Buffer[0] = '.'; + pNtPath->Buffer[1] = '\0'; + } +#endif + + return 0; +} + + +/** + * Similar to birdDosToNtPath, but it does call RtlDosPathNameToNtPathName_U. + * + * @returns 0 on success, -1 + errno on failure. + * @param pszPath The relative path. + * @param pNtPath Where to return the NT path. Call birdFreeNtPath when done. + */ +int birdDosToRelativeNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath) +{ + MY_NTSTATUS rcNt; + MY_ANSI_STRING Src; + + birdResolveImports(); + + /* + * Just convert to wide char. + */ + pNtPath->Length = pNtPath->MaximumLength = 0; + pNtPath->Buffer = NULL; + + Src.Buffer = (PCHAR)pszPath; + Src.MaximumLength = Src.Length = (USHORT)strlen(pszPath); + + rcNt = g_pfnRtlAnsiStringToUnicodeString(pNtPath, &Src, TRUE /* Allocate */); + if (MY_NT_SUCCESS(rcNt)) + return birdFixRelativeNtPathSlashesAndReturn0(pNtPath); + return birdSetErrnoFromNt(rcNt); +} + + +/** + * Similar to birdDosToNtPathW, but it does call RtlDosPathNameToNtPathName_U. + * + * @returns 0 on success, -1 + errno on failure. + * @param pwszPath The relative path. + * @param pNtPath Where to return the NT path. Call birdFreeNtPath when done. + */ +int birdDosToRelativeNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath) +{ + size_t cwcPath = wcslen(pwszPath); + if (cwcPath < 0xfffe) + { + pNtPath->Length = (USHORT)(cwcPath * sizeof(wchar_t)); + pNtPath->MaximumLength = pNtPath->Length + sizeof(wchar_t); + pNtPath->Buffer = HeapAlloc(GetProcessHeap(), 0, pNtPath->MaximumLength); + if (pNtPath->Buffer) + { + memcpy(pNtPath->Buffer, pwszPath, pNtPath->MaximumLength); + return birdFixRelativeNtPathSlashesAndReturn0(pNtPath); + } + errno = ENOMEM; + } + else + errno = ENAMETOOLONG; + return -1; +} + + +/** + * Frees a string returned by birdDosToNtPath, birdDosToNtPathW or + * birdDosToRelativeNtPath. + * + * @param pNtPath The the NT path to free. + */ +void birdFreeNtPath(MY_UNICODE_STRING *pNtPath) +{ + HeapFree(GetProcessHeap(), 0, pNtPath->Buffer); + pNtPath->Buffer = NULL; + pNtPath->Length = 0; + pNtPath->MaximumLength = 0; +} + + +MY_NTSTATUS birdOpenFileUniStr(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + HANDLE *phFile) +{ + MY_IO_STATUS_BLOCK Ios; + MY_OBJECT_ATTRIBUTES ObjAttr; + MY_NTSTATUS rcNt; + + birdResolveImports(); + + if ( (fCreateOptions & FILE_OPEN_REPARSE_POINT) + && g_fHaveOpenReparsePoint == 0) + fCreateOptions &= ~FILE_OPEN_REPARSE_POINT; + + Ios.Information = -1; + Ios.u.Status = 0; + MyInitializeObjectAttributes(&ObjAttr, pNtPath, fObjAttribs, hRoot, NULL /*pSecAttr*/); + + rcNt = g_pfnNtCreateFile(phFile, + fDesiredAccess, + &ObjAttr, + &Ios, + NULL, /* cbFileInitialAlloc */ + fFileAttribs, + fShareAccess, + fCreateDisposition, + fCreateOptions, + NULL, /* pEaBuffer */ + 0); /* cbEaBuffer*/ + if ( rcNt == STATUS_INVALID_PARAMETER + && g_fHaveOpenReparsePoint < 0 + && (fCreateOptions & FILE_OPEN_REPARSE_POINT)) + { + fCreateOptions &= ~FILE_OPEN_REPARSE_POINT; + + Ios.Information = -1; + Ios.u.Status = 0; + MyInitializeObjectAttributes(&ObjAttr, pNtPath, fObjAttribs, NULL /*hRoot*/, NULL /*pSecAttr*/); + + rcNt = g_pfnNtCreateFile(phFile, + fDesiredAccess, + &ObjAttr, + &Ios, + NULL, /* cbFileInitialAlloc */ + fFileAttribs, + fShareAccess, + fCreateDisposition, + fCreateOptions, + NULL, /* pEaBuffer */ + 0); /* cbEaBuffer*/ + if (rcNt != STATUS_INVALID_PARAMETER) + g_fHaveOpenReparsePoint = 0; + } + return rcNt; +} + + +HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs) +{ + MY_UNICODE_STRING NtPath; + MY_NTSTATUS rcNt; + + /* + * Adjust inputs. + */ + if (birdIsPathDirSpec(pszPath)) + fCreateOptions |= FILE_DIRECTORY_FILE; + + /* + * Convert the path and call birdOpenFileUniStr to do the real work. + */ + if (birdDosToNtPath(pszPath, &NtPath) == 0) + { + HANDLE hFile; + rcNt = birdOpenFileUniStr(NULL /*hRoot*/, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, &hFile); + birdFreeNtPath(&NtPath); + if (MY_NT_SUCCESS(rcNt)) + return hFile; + birdSetErrnoFromNt(rcNt); + } + + return INVALID_HANDLE_VALUE; +} + + +HANDLE birdOpenFileW(const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs) +{ + MY_UNICODE_STRING NtPath; + MY_NTSTATUS rcNt; + + /* + * Adjust inputs. + */ + if (birdIsPathDirSpecW(pwszPath)) + fCreateOptions |= FILE_DIRECTORY_FILE; + + /* + * Convert the path and call birdOpenFileUniStr to do the real work. + */ + if (birdDosToNtPathW(pwszPath, &NtPath) == 0) + { + HANDLE hFile; + rcNt = birdOpenFileUniStr(NULL /*hRoot*/, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, &hFile); + birdFreeNtPath(&NtPath); + if (MY_NT_SUCCESS(rcNt)) + return hFile; + birdSetErrnoFromNt(rcNt); + } + + return INVALID_HANDLE_VALUE; +} + + +HANDLE birdOpenFileEx(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs) +{ + MY_UNICODE_STRING NtPath; + MY_NTSTATUS rcNt; + + /* + * Adjust inputs. + */ + if (birdIsPathDirSpec(pszPath)) + fCreateOptions |= FILE_DIRECTORY_FILE; + + /* + * Convert the path and call birdOpenFileUniStr to do the real work. + */ + if (hRoot == INVALID_HANDLE_VALUE) + hRoot = NULL; + if ((hRoot != NULL ? birdDosToRelativeNtPath(pszPath, &NtPath) : birdDosToNtPath(pszPath, &NtPath)) == 0) + { + HANDLE hFile; + rcNt = birdOpenFileUniStr(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, &hFile); + birdFreeNtPath(&NtPath); + if (MY_NT_SUCCESS(rcNt)) + return hFile; + birdSetErrnoFromNt(rcNt); + } + + return INVALID_HANDLE_VALUE; +} + + +HANDLE birdOpenFileExW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs) +{ + MY_UNICODE_STRING NtPath; + MY_NTSTATUS rcNt; + + /* + * Adjust inputs. + */ + if (birdIsPathDirSpecW(pwszPath)) + fCreateOptions |= FILE_DIRECTORY_FILE; + + /* + * Convert the path (could save ourselves this if pwszPath is perfect) and + * call birdOpenFileUniStr to do the real work. + */ + if (hRoot == INVALID_HANDLE_VALUE) + hRoot = NULL; + if ((hRoot != NULL ? birdDosToRelativeNtPathW(pwszPath, &NtPath) : birdDosToNtPathW(pwszPath, &NtPath)) == 0) + { + HANDLE hFile; + rcNt = birdOpenFileUniStr(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, &hFile); + birdFreeNtPath(&NtPath); + if (MY_NT_SUCCESS(rcNt)) + return hFile; + birdSetErrnoFromNt(rcNt); + } + + return INVALID_HANDLE_VALUE; +} + + +static HANDLE birdOpenParentDirCommon(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + MY_UNICODE_STRING *pNameUniStr) +{ + MY_NTSTATUS rcNt; + + /* + * Strip the path down to the directory. + */ + USHORT offName = pNtPath->Length / sizeof(WCHAR); + USHORT cwcName = offName; + WCHAR wc = 0; + while ( offName > 0 + && (wc = pNtPath->Buffer[offName - 1]) != '\\' + && wc != '/' + && wc != ':') + offName--; + if ( offName > 0 + || (hRoot != NULL && cwcName > 0)) + { + cwcName -= offName; + + /* Make a copy of the file name, if requested. */ + rcNt = STATUS_SUCCESS; + if (pNameUniStr) + { + pNameUniStr->Length = cwcName * sizeof(WCHAR); + pNameUniStr->MaximumLength = pNameUniStr->Length + sizeof(WCHAR); + pNameUniStr->Buffer = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, pNameUniStr->MaximumLength); + if (pNameUniStr->Buffer) + { + memcpy(pNameUniStr->Buffer, &pNtPath->Buffer[offName], pNameUniStr->Length); + pNameUniStr->Buffer[cwcName] = '\0'; + } + else + rcNt = STATUS_NO_MEMORY; + } + + /* Chop, chop. */ + // Bad idea, breaks \\?\c:\pagefile.sys. //while ( offName > 0 + // Bad idea, breaks \\?\c:\pagefile.sys. // && ( (wc = pNtPath->Buffer[offName - 1]) == '\\' + // Bad idea, breaks \\?\c:\pagefile.sys. // || wc == '/')) + // Bad idea, breaks \\?\c:\pagefile.sys. // offName--; + if (offName == 0) + pNtPath->Buffer[offName++] = '.'; /* Hack for dir handle + dir entry name. */ + pNtPath->Length = offName * sizeof(WCHAR); + pNtPath->Buffer[offName] = '\0'; + if (MY_NT_SUCCESS(rcNt)) + { + /* + * Finally, try open the directory. + */ + HANDLE hFile; + fCreateOptions |= FILE_DIRECTORY_FILE; + rcNt = birdOpenFileUniStr(hRoot, pNtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, &hFile); + if (MY_NT_SUCCESS(rcNt)) + { + birdFreeNtPath(pNtPath); + return hFile; + } + } + + if (pNameUniStr) + birdFreeNtPath(pNameUniStr); + } + else + rcNt = STATUS_INVALID_PARAMETER; + + birdFreeNtPath(pNtPath); + birdSetErrnoFromNt(rcNt); + return INVALID_HANDLE_VALUE; +} + + +HANDLE birdOpenParentDir(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + MY_UNICODE_STRING *pNameUniStr) +{ + /* + * Convert the path and join up with the UTF-16 version (it'll free NtPath). + */ + MY_UNICODE_STRING NtPath; + if (hRoot == INVALID_HANDLE_VALUE) + hRoot = NULL; + if ( hRoot == NULL + ? birdDosToNtPath(pszPath, &NtPath) == 0 + : birdDosToRelativeNtPath(pszPath, &NtPath) == 0) + return birdOpenParentDirCommon(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, pNameUniStr); + return INVALID_HANDLE_VALUE; +} + + +HANDLE birdOpenParentDirW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, + ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs, + MY_UNICODE_STRING *pNameUniStr) +{ + /* + * Convert the path and join up with the ansi version (it'll free NtPath). + */ + MY_UNICODE_STRING NtPath; + if (hRoot == INVALID_HANDLE_VALUE) + hRoot = NULL; + if ( hRoot == NULL + ? birdDosToNtPathW(pwszPath, &NtPath) == 0 + : birdDosToRelativeNtPathW(pwszPath, &NtPath) == 0) + return birdOpenParentDirCommon(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess, + fCreateDisposition, fCreateOptions, fObjAttribs, pNameUniStr); + return INVALID_HANDLE_VALUE; +} + + +/** + * Returns a handle to the current working directory of the process. + * + * @returns CWD handle with FILE_TRAVERSE and SYNCHRONIZE access. May return + * INVALID_HANDLE_VALUE w/ errno for invalid CWD. + */ +HANDLE birdOpenCurrentDirectory(void) +{ + PMY_RTL_USER_PROCESS_PARAMETERS pProcParams; + MY_NTSTATUS rcNt; + HANDLE hRet = INVALID_HANDLE_VALUE; + + birdResolveImports(); + + /* + * We'll try get this from the PEB. + */ + g_pfnRtlAcquirePebLock(); + pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)MY_NT_CURRENT_PEB()->ProcessParameters; + if (pProcParams != NULL) + rcNt = g_pfnNtDuplicateObject(MY_NT_CURRENT_PROCESS, pProcParams->CurrentDirectory.Handle, + MY_NT_CURRENT_PROCESS, &hRet, + FILE_TRAVERSE | SYNCHRONIZE, + 0 /*fAttribs*/, + 0 /*fOptions*/); + else + rcNt = STATUS_INVALID_PARAMETER; + g_pfnRtlReleasePebLock(); + if (MY_NT_SUCCESS(rcNt)) + return hRet; + + /* + * Fallback goes thru birdOpenFileW. + */ + return birdOpenFileW(L".", + FILE_TRAVERSE | SYNCHRONIZE, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, + OBJ_CASE_INSENSITIVE); +} + + +void birdCloseFile(HANDLE hFile) +{ + birdResolveImports(); + g_pfnNtClose(hFile); +} + -- cgit v1.2.3