/* $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); }