diff options
Diffstat (limited to 'src/VBox/Runtime/nt')
-rw-r--r-- | src/VBox/Runtime/nt/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/NtProcessStartup-stub.cpp | 69 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTErrConvertFromNtStatus.cpp | 120 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTNtPathExpand8dot3Path.cpp | 209 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTNtPathExpand8dot3PathA.cpp | 97 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTNtPathFindPossible8dot3Name.cpp | 82 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTSemEventGetResolution-nt.cpp | 95 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/RTSemEventMultiGetResolution-nt.cpp | 95 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/fileioutils-nt.cpp | 189 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/semevent-nt.cpp | 490 | ||||
-rw-r--r-- | src/VBox/Runtime/nt/semeventmulti-nt.cpp | 520 |
11 files changed, 1966 insertions, 0 deletions
diff --git a/src/VBox/Runtime/nt/Makefile.kup b/src/VBox/Runtime/nt/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/nt/Makefile.kup diff --git a/src/VBox/Runtime/nt/NtProcessStartup-stub.cpp b/src/VBox/Runtime/nt/NtProcessStartup-stub.cpp new file mode 100644 index 00000000..bd9dea16 --- /dev/null +++ b/src/VBox/Runtime/nt/NtProcessStartup-stub.cpp @@ -0,0 +1,69 @@ +/* $Id: NtProcessStartup-stub.cpp $ */ +/** @file + * IPRT - NtProcessStartup stub to make the link happy. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/asm.h> + + +/** + * This is the entrypoint the linker picks, however it is never called for + * ring-0 binaries. + */ +extern "C" void __cdecl NtProcessStartup(void *pvIgnored); +extern "C" void __cdecl NtProcessStartup(void *pvIgnored) +{ + ASMBreakpoint(); + NOREF(pvIgnored); +} + + +#ifdef IN_RING0 +/** + * This dummy entry point is required for using BufferOverflowK.lib and + * /guard:cf and /GS. It is never called. + */ +extern "C" long __stdcall DriverEntry(void *pvDrvObjIgn, void *pvRegPathIgn); +extern "C" long __stdcall DriverEntry(void *pvDrvObjIgn, void *pvRegPathIgn) +{ + ASMBreakpoint(); + RT_NOREF(pvDrvObjIgn, pvRegPathIgn); + return UINT32_C(0xc0000022); +} +#endif + diff --git a/src/VBox/Runtime/nt/RTErrConvertFromNtStatus.cpp b/src/VBox/Runtime/nt/RTErrConvertFromNtStatus.cpp new file mode 100644 index 00000000..aaef2023 --- /dev/null +++ b/src/VBox/Runtime/nt/RTErrConvertFromNtStatus.cpp @@ -0,0 +1,120 @@ +/* $Id: RTErrConvertFromNtStatus.cpp $ */ +/** @file + * IPRT - Convert NT status codes to iprt status codes. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <ntstatus.h> +typedef long NTSTATUS; /** @todo figure out which headers to include to get this one typedef... */ + +#include <iprt/err.h> +#ifdef VBOX +# include <VBox/err.h> +#endif +#include <iprt/log.h> +#include <iprt/assert.h> + + + +RTDECL(int) RTErrConvertFromNtStatus(long lNativeCode) +{ + switch (lNativeCode) + { + case STATUS_SUCCESS: return VINF_SUCCESS; + + case STATUS_ALERTED: return VERR_INTERRUPTED; + case STATUS_USER_APC: return VERR_INTERRUPTED; + + case STATUS_INVALID_INFO_CLASS: return VERR_INVALID_FUNCTION; + case STATUS_DATATYPE_MISALIGNMENT: return VERR_INVALID_POINTER; + case STATUS_NO_MORE_FILES: return VERR_NO_MORE_FILES; + case STATUS_NO_MORE_ENTRIES: return VERR_NO_MORE_FILES; + case STATUS_NO_MEMORY: return VERR_NO_MEMORY; + + case STATUS_INVALID_HANDLE: return VERR_INVALID_HANDLE; + case STATUS_INVALID_PARAMETER: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_1: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_2: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_3: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_4: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_5: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_6: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_7: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_8: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_9: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_10: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_11: return VERR_INVALID_PARAMETER; + case STATUS_INVALID_PARAMETER_12: return VERR_INVALID_PARAMETER; + case STATUS_NO_SUCH_DEVICE: return VERR_FILE_NOT_FOUND; + case STATUS_NO_SUCH_FILE: return VERR_FILE_NOT_FOUND; + case STATUS_INVALID_DEVICE_REQUEST: return VERR_IO_BAD_COMMAND; + case STATUS_ACCESS_DENIED: return VERR_ACCESS_DENIED; + case STATUS_OBJECT_TYPE_MISMATCH: return VERR_UNEXPECTED_FS_OBJ_TYPE; + case STATUS_OBJECT_NAME_INVALID: return VERR_INVALID_NAME; + case STATUS_OBJECT_NAME_NOT_FOUND: return VERR_FILE_NOT_FOUND; + case STATUS_OBJECT_PATH_INVALID: return VERR_INVALID_NAME; + case STATUS_OBJECT_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND; + case STATUS_OBJECT_PATH_SYNTAX_BAD: return VERR_INVALID_NAME; + case STATUS_BAD_NETWORK_PATH: return VERR_NET_PATH_NOT_FOUND; + case STATUS_NOT_A_DIRECTORY: return VERR_NOT_A_DIRECTORY; + case STATUS_DIRECTORY_NOT_EMPTY: return VERR_DIR_NOT_EMPTY; + case STATUS_SHARING_VIOLATION: return VERR_SHARING_VIOLATION; + case STATUS_NO_MEDIA_IN_DEVICE: return VERR_DRIVE_IS_EMPTY; + case STATUS_ACCESS_VIOLATION: return VERR_INVALID_POINTER; + case STATUS_FILE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY; + + case STATUS_REPARSE_POINT_NOT_RESOLVED: + return VERR_TOO_MANY_SYMLINKS; + case STATUS_UNEXPECTED_NETWORK_ERROR: + return VERR_NET_IO_ERROR; + case STATUS_INVALID_IMAGE_HASH: return VERR_LDR_IMAGE_HASH; + case STATUS_LOGON_FAILURE: return VERR_AUTHENTICATION_FAILURE; +#ifdef VBOX + case STATUS_TRUST_FAILURE: return VERR_SUPLIB_NT_PROCESS_UNTRUSTED_5; +#endif + } + + /* unknown error. */ +#ifndef IN_SUP_HARDENED_R3 + AssertLogRelMsgFailed(("Unhandled error %#lx (%lu)\n", lNativeCode, lNativeCode)); +#else + /* hardened main has no LogRel */ + AssertMsgFailed(("Unhandled error %#lx (%lu)\n", lNativeCode, lNativeCode)); +#endif + return VERR_UNRESOLVED_ERROR; +} + diff --git a/src/VBox/Runtime/nt/RTNtPathExpand8dot3Path.cpp b/src/VBox/Runtime/nt/RTNtPathExpand8dot3Path.cpp new file mode 100644 index 00000000..22e2903b --- /dev/null +++ b/src/VBox/Runtime/nt/RTNtPathExpand8dot3Path.cpp @@ -0,0 +1,209 @@ +/* $Id: RTNtPathExpand8dot3Path.cpp $ */ +/** @file + * IPRT - Native NT, RTNtPathExpand8dot3Path. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FS +#if !defined(IPRT_NT_MAP_TO_ZW) && defined(IN_RING0) +# define IPRT_NT_MAP_TO_ZW +#endif +#ifdef IN_SUP_HARDENED_R3 +# include <iprt/nt/nt-and-windows.h> +#else +# include <iprt/nt/nt.h> +#endif + +#include <iprt/mem.h> +#include <iprt/errcore.h> +#include <iprt/string.h> + + + +/** + * Fixes up a path possibly containing one or more alternative 8-dot-3 style + * components. + * + * The path is fixed up in place. Errors are ignored. + * + * @returns VINF_SUCCESS if it all went smoothly, informational status codes + * indicating the nature of last problem we ran into. + * + * @param pUniStr The path to fix up. MaximumLength is the max buffer + * length. + * @param fPathOnly Whether to only process the path and leave the filename + * as passed in. + */ +RTDECL(int) RTNtPathExpand8dot3Path(PUNICODE_STRING pUniStr, bool fPathOnly) +{ + int rc = VINF_SUCCESS; + + /* + * We could use FileNormalizedNameInformation here and slap the volume device + * path in front of the result, but it's only supported since windows 8.0 + * according to some docs... So we expand all supicious names. + */ + union fix8dot3tmp + { + FILE_BOTH_DIR_INFORMATION Info; + uint8_t abBuffer[sizeof(FILE_BOTH_DIR_INFORMATION) + 2048 * sizeof(WCHAR)]; + } *puBuf = NULL; + + + PRTUTF16 pwszFix = pUniStr->Buffer; + while (*pwszFix) + { + pwszFix = RTNtPathFindPossible8dot3Name(pwszFix); + if (pwszFix == NULL) + break; + + RTUTF16 wc; + PRTUTF16 pwszFixEnd = pwszFix; + while ((wc = *pwszFixEnd) != '\0' && wc != '\\' && wc != '/') + pwszFixEnd++; + if (wc == '\0' && fPathOnly) + break; + + if (!puBuf) + { + puBuf = (union fix8dot3tmp *)RTMemAlloc(sizeof(*puBuf)); + if (!puBuf) + { + rc = -VERR_NO_MEMORY; + break; + } + } + + RTUTF16 const wcSaved = *pwszFix; + *pwszFix = '\0'; /* paranoia. */ + + UNICODE_STRING NtDir; + NtDir.Buffer = pUniStr->Buffer; + NtDir.Length = NtDir.MaximumLength = (USHORT)((pwszFix - pUniStr->Buffer) * sizeof(WCHAR)); + + HANDLE hDir = RTNT_INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, &NtDir, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/); +#ifdef IN_RING0 + ObjAttr.Attributes |= OBJ_KERNEL_HANDLE; +#endif + + NTSTATUS rcNt = NtCreateFile(&hDir, + FILE_LIST_DIRECTORY | SYNCHRONIZE, + &ObjAttr, + &Ios, + NULL /* Allocation Size*/, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, + NULL /*EaBuffer*/, + 0 /*EaLength*/); + *pwszFix = wcSaved; + if (NT_SUCCESS(rcNt)) + { + RT_ZERO(*puBuf); + + RTNT_IO_STATUS_BLOCK_REINIT(&Ios); + UNICODE_STRING NtFilterStr; + NtFilterStr.Buffer = pwszFix; + NtFilterStr.Length = (USHORT)((uintptr_t)pwszFixEnd - (uintptr_t)pwszFix); + NtFilterStr.MaximumLength = NtFilterStr.Length; + rcNt = NtQueryDirectoryFile(hDir, + NULL /* Event */, + NULL /* ApcRoutine */, + NULL /* ApcContext */, + &Ios, + puBuf, + sizeof(*puBuf) - sizeof(WCHAR), + FileBothDirectoryInformation, + FALSE /*ReturnSingleEntry*/, + &NtFilterStr, + FALSE /*RestartScan */); + if (NT_SUCCESS(rcNt) && puBuf->Info.NextEntryOffset == 0) /* There shall only be one entry matching... */ + { + uint32_t offName = puBuf->Info.FileNameLength / sizeof(WCHAR); + while (offName > 0 && puBuf->Info.FileName[offName - 1] != '\\' && puBuf->Info.FileName[offName - 1] != '/') + offName--; + uint32_t cwcNameNew = (puBuf->Info.FileNameLength / sizeof(WCHAR)) - offName; + uint32_t cwcNameOld = (uint32_t)(pwszFixEnd - pwszFix); + + if (cwcNameOld == cwcNameNew) + memcpy(pwszFix, &puBuf->Info.FileName[offName], cwcNameNew * sizeof(WCHAR)); + else if ( pUniStr->Length + cwcNameNew * sizeof(WCHAR) - cwcNameOld * sizeof(WCHAR) + sizeof(WCHAR) + <= pUniStr->MaximumLength) + { + size_t cwcLeft = pUniStr->Length - (pwszFixEnd - pUniStr->Buffer) * sizeof(WCHAR) + sizeof(WCHAR); + memmove(&pwszFix[cwcNameNew], pwszFixEnd, cwcLeft * sizeof(WCHAR)); + pUniStr->Length -= (USHORT)(cwcNameOld * sizeof(WCHAR)); + pUniStr->Length += (USHORT)(cwcNameNew * sizeof(WCHAR)); + pwszFixEnd -= cwcNameOld; + pwszFixEnd += cwcNameNew; + memcpy(pwszFix, &puBuf->Info.FileName[offName], cwcNameNew * sizeof(WCHAR)); + } + else + rc = VINF_BUFFER_OVERFLOW; + } + else if (NT_SUCCESS(rcNt)) + rc = -VERR_DUPLICATE; + else + { + rc = -RTErrConvertFromNtStatus(rcNt); + if (rc < 0) + rc = -rc; + } + + NtClose(hDir); + } + else + rc = -RTErrConvertFromNtStatus(rcNt); + + /* Advance */ + pwszFix = pwszFixEnd; + } + + if (puBuf) + RTMemFree(puBuf); + + if (pUniStr->Length < pUniStr->MaximumLength) + pUniStr->Buffer[pUniStr->Length / sizeof(WCHAR)] = '\0'; + + return rc; +} + diff --git a/src/VBox/Runtime/nt/RTNtPathExpand8dot3PathA.cpp b/src/VBox/Runtime/nt/RTNtPathExpand8dot3PathA.cpp new file mode 100644 index 00000000..5d83f729 --- /dev/null +++ b/src/VBox/Runtime/nt/RTNtPathExpand8dot3PathA.cpp @@ -0,0 +1,97 @@ +/* $Id: RTNtPathExpand8dot3PathA.cpp $ */ +/** @file + * IPRT - Native NT, RTNtPathExpand8dot3PathA. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FS +#ifdef IN_SUP_HARDENED_R3 +# include <iprt/nt/nt-and-windows.h> +#else +# include <iprt/nt/nt.h> +#endif + +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/path.h> +#include <iprt/utf16.h> + + + +/** + * Wrapper around RTNtPathExpand8dot3Path that allocates a buffer instead of + * working on the input buffer. + * + * @returns IPRT status code, see RTNtPathExpand8dot3Path(). + * @param pUniStrSrc The path to fix up. MaximumLength is the max buffer + * length. + * @param fPathOnly Whether to only process the path and leave the filename + * as passed in. + * @param pUniStrDst Output string. On success, the caller must use + * RTUtf16Free to free what the Buffer member points to. + * This is all zeros and NULL on failure. + */ +RTDECL(int) RTNtPathExpand8dot3PathA(PCUNICODE_STRING pUniStrSrc, bool fPathOnly, PUNICODE_STRING pUniStrDst) +{ + /* Guess a reasonable size for the final version. */ + size_t const cbShort = pUniStrSrc->Length; + size_t cbLong = RT_MIN(_64K - 1, cbShort * 8); + if (cbLong < RTPATH_MAX) + cbLong = RTPATH_MAX * 2; + AssertCompile(RTPATH_MAX * 2 < _64K); + + pUniStrDst->Buffer = (WCHAR *)RTUtf16Alloc(cbLong); + if (pUniStrDst->Buffer != NULL) + { + /* Copy over the short name and fix it up. */ + pUniStrDst->MaximumLength = (uint16_t)cbLong; + pUniStrDst->Length = (uint16_t)cbShort; + memcpy(pUniStrDst->Buffer, pUniStrSrc->Buffer, cbShort); + pUniStrDst->Buffer[cbShort / sizeof(WCHAR)] = '\0'; + int rc = RTNtPathExpand8dot3Path(pUniStrDst, fPathOnly); + if (RT_SUCCESS(rc)) + return rc; + + /* We failed, bail. */ + RTUtf16Free(pUniStrDst->Buffer); + pUniStrDst->Buffer = NULL; + } + pUniStrDst->Length = 0; + pUniStrDst->MaximumLength = 0; + return VERR_NO_UTF16_MEMORY; +} + diff --git a/src/VBox/Runtime/nt/RTNtPathFindPossible8dot3Name.cpp b/src/VBox/Runtime/nt/RTNtPathFindPossible8dot3Name.cpp new file mode 100644 index 00000000..55e02bcf --- /dev/null +++ b/src/VBox/Runtime/nt/RTNtPathFindPossible8dot3Name.cpp @@ -0,0 +1,82 @@ +/* $Id: RTNtPathFindPossible8dot3Name.cpp $ */ +/** @file + * IPRT - Native NT, RTNtPathFindPossible8dot3Name. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FS +#ifdef IN_SUP_HARDENED_R3 +# include <iprt/nt/nt-and-windows.h> +#else +# include <iprt/nt/nt.h> +#endif + + + +/** + * Checks whether the path could be containing alternative 8.3 names generated + * by NTFS, FAT, or other similar file systems. + * + * @returns Pointer to the first component that might be an 8.3 name, NULL if + * not 8.3 path. + * @param pwszPath The path to check. + * + * @remarks This is making bad ASSUMPTION wrt to the naming scheme of 8.3 names, + * however, non-tilde 8.3 aliases are probably rare enough to not be + * worth all the extra code necessary to open each path component and + * check if we've got the short name or not. + */ +RTDECL(PRTUTF16) RTNtPathFindPossible8dot3Name(PCRTUTF16 pwszPath) +{ + PCRTUTF16 pwszName = pwszPath; + for (;;) + { + RTUTF16 wc = *pwszPath++; + if (wc == '~') + { + /* Could check more here before jumping to conclusions... */ + if (pwszPath - pwszName <= 8+1+3) + return (PRTUTF16)pwszName; + } + else if (wc == '\\' || wc == '/' || wc == ':') + pwszName = pwszPath; + else if (wc == 0) + break; + } + return NULL; +} + diff --git a/src/VBox/Runtime/nt/RTSemEventGetResolution-nt.cpp b/src/VBox/Runtime/nt/RTSemEventGetResolution-nt.cpp new file mode 100644 index 00000000..b7fe22aa --- /dev/null +++ b/src/VBox/Runtime/nt/RTSemEventGetResolution-nt.cpp @@ -0,0 +1,95 @@ +/* $Id: RTSemEventGetResolution-nt.cpp $ */ +/** @file + * IPRT - Single Release Event Semaphores, RTSemEventGetResolution. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENT_WITHOUT_REMAPPING +#ifdef IN_RING0 +# include "../r0drv/nt/the-nt-kernel.h" +#else +# include <iprt/nt/nt.h> +#endif +#include <iprt/semaphore.h> + +#include <iprt/assert.h> +#include <iprt/timer.h> +#ifdef IN_RING3 +# include <iprt/system.h> +#endif + + +RTDECL(uint32_t) RTSemEventGetResolution(void) +{ + /* + * We need to figure the KeWaitForSingleObject / NtWaitForSingleObject timeout + * resolution, i.e. if we wish to wait for 1000ns how long are we likely to + * actually wait before woken up. + * + * In older versions of NT, these timeout were implemented using KTIMERs and + * have the same resolution as what them. This should be found using + * ExSetTimerResolution or NtQueryTimerResolution. + * + * Probably since windows 8.1 the value returned by NtQueryTimerResolution (and + * set NtSetTimerResolution) have been virtualized and no longer reflects the + * timer wheel resolution, at least from what I can tell. ExSetTimerResolution + * still works as before, but it accesses variable that I cannot find out how + * to access from user land. So, kernel will get (and be able to set) the right + * granularity, while in user land we'll be forced to reporting the max value. + * + * (The reason why I suspect it's since 8.1 is because the high resolution + * ExSetTimer APIs were introduced back then.) + */ +#ifdef IN_RING0 + return RTTimerGetSystemGranularity(); +#else + ULONG cNtTicksMin = 0; + ULONG cNtTicksMax = 0; + ULONG cNtTicksCur = 0; + NTSTATUS rcNt = NtQueryTimerResolution(&cNtTicksMin, &cNtTicksMax, &cNtTicksCur); + if (NT_SUCCESS(rcNt)) + { + Assert(cNtTicksMin >= cNtTicksMax); + if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,3,9600)) /** @todo check when the switch happened, might be much later... */ + return cNtTicksMin * 100; + return cNtTicksCur * 100; + } + AssertFailed(); + return 16 * RT_NS_1MS; /* the default on 64-bit windows 10 */ +#endif +} + diff --git a/src/VBox/Runtime/nt/RTSemEventMultiGetResolution-nt.cpp b/src/VBox/Runtime/nt/RTSemEventMultiGetResolution-nt.cpp new file mode 100644 index 00000000..b2532b34 --- /dev/null +++ b/src/VBox/Runtime/nt/RTSemEventMultiGetResolution-nt.cpp @@ -0,0 +1,95 @@ +/* $Id: RTSemEventMultiGetResolution-nt.cpp $ */ +/** @file + * IPRT - Single Release Event Semaphores, RTSemEventMultiGetResolution. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENT_WITHOUT_REMAPPING +#ifdef IN_RING0 +# include "../r0drv/nt/the-nt-kernel.h" +#else +# include <iprt/nt/nt.h> +#endif +#include <iprt/semaphore.h> + +#include <iprt/assert.h> +#include <iprt/timer.h> +#ifdef IN_RING3 +# include <iprt/system.h> +#endif + + +RTDECL(uint32_t) RTSemEventMultiGetResolution(void) +{ + /* + * We need to figure the KeWaitForSingleObject / NtWaitForSingleObject timeout + * resolution, i.e. if we wish to wait for 1000ns how long are we likely to + * actually wait before woken up. + * + * In older versions of NT, these timeout were implemented using KTIMERs and + * have the same resolution as what them. This should be found using + * ExSetTimerResolution or NtQueryTimerResolution. + * + * Probably since windows 8.1 the value returned by NtQueryTimerResolution (and + * set NtSetTimerResolution) have been virtualized and no longer reflects the + * timer wheel resolution, at least from what I can tell. ExSetTimerResolution + * still works as before, but it accesses variable that I cannot find out how + * to access from user land. So, kernel will get (and be able to set) the right + * granularity, while in user land we'll be forced to reporting the max value. + * + * (The reason why I suspect it's since 8.1 is because the high resolution + * ExSetTimer APIs were introduced back then.) + */ +#ifdef IN_RING0 + return RTTimerGetSystemGranularity(); +#else + ULONG cNtTicksMin = 0; + ULONG cNtTicksMax = 0; + ULONG cNtTicksCur = 0; + NTSTATUS rcNt = NtQueryTimerResolution(&cNtTicksMin, &cNtTicksMax, &cNtTicksCur); + if (NT_SUCCESS(rcNt)) + { + Assert(cNtTicksMin >= cNtTicksMax); + if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,3,9600)) /** @todo check when the switch happened, might be much later... */ + return cNtTicksMin * 100; + return cNtTicksCur * 100; + } + AssertFailed(); + return 16 * RT_NS_1MS; /* the default on 64-bit windows 10 */ +#endif +} + diff --git a/src/VBox/Runtime/nt/fileioutils-nt.cpp b/src/VBox/Runtime/nt/fileioutils-nt.cpp new file mode 100644 index 00000000..a38dcae0 --- /dev/null +++ b/src/VBox/Runtime/nt/fileioutils-nt.cpp @@ -0,0 +1,189 @@ +/* $Id: fileioutils-nt.cpp $ */ +/** @file + * IPRT - File I/O, common NT helpers. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/nt/nt.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include "internal/file.h" + + + +/** + * Helper for converting RTFILE_O_XXX to the various NtCreateFile flags. + * + * @returns IPRT status code + * @param fOpen The RTFILE_O_XXX flags to convert. + * @param pfDesiredAccess Where to return the desired access mask. + * @param pfObjAttribs Where to return the NT object attributes. + * @param pfFileAttribs Where to return the file attributes (create). + * @param pfShareAccess Where to return the file sharing access mask. + * @param pfCreateDisposition Where to return the file create disposition. + * @param pfCreateOptions Where to return the file open/create options. + */ +DECLHIDDEN(int) rtFileNtValidateAndConvertFlags(uint64_t fOpen, uint32_t *pfDesiredAccess, uint32_t *pfObjAttribs, + uint32_t *pfFileAttribs, uint32_t *pfShareAccess, uint32_t *pfCreateDisposition, + uint32_t *pfCreateOptions) +{ + /* + * Merge forced open flags and validate them. + */ + int rc = rtFileRecalcAndValidateFlags(&fOpen); + if (RT_FAILURE(rc)) + return rc; + + /* + * Determine disposition, access, share mode, creation flags, and security attributes + * for the CreateFile API call. + */ + ULONG fCreateDisposition; + switch (fOpen & RTFILE_O_ACTION_MASK) + { + case RTFILE_O_OPEN: + fCreateDisposition = fOpen & RTFILE_O_TRUNCATE ? FILE_OVERWRITE : FILE_OPEN; + break; + case RTFILE_O_OPEN_CREATE: + fCreateDisposition = FILE_OPEN_IF; + break; + case RTFILE_O_CREATE: + fCreateDisposition = FILE_CREATE; + break; + case RTFILE_O_CREATE_REPLACE: + fCreateDisposition = FILE_SUPERSEDE; + break; + default: + AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen)); + return VERR_INVALID_PARAMETER; + } + + ACCESS_MASK fDesiredAccess; + switch (fOpen & RTFILE_O_ACCESS_MASK) + { + case RTFILE_O_READ: + fDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */ + break; + case RTFILE_O_WRITE: + fDesiredAccess = fOpen & RTFILE_O_APPEND + ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA + : FILE_GENERIC_WRITE; + break; + case RTFILE_O_READWRITE: + fDesiredAccess = fOpen & RTFILE_O_APPEND + ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) + : FILE_GENERIC_READ | FILE_GENERIC_WRITE; + break; + case RTFILE_O_ATTR_ONLY: + if (fOpen & RTFILE_O_ACCESS_ATTR_MASK) + { + fDesiredAccess = 0; + break; + } + RT_FALL_THRU(); + default: + AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen)); + return VERR_INVALID_PARAMETER; + } + if (fCreateDisposition == FILE_OVERWRITE) + /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */ + fDesiredAccess |= GENERIC_WRITE; + + /* RTFileSetMode needs following rights as well. */ + switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK) + { + case RTFILE_O_ACCESS_ATTR_READ: fDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_ACCESS_ATTR_WRITE: fDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_ACCESS_ATTR_READWRITE: fDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + default: + /* Attributes access is the same as the file access. */ + switch (fOpen & RTFILE_O_ACCESS_MASK) + { + case RTFILE_O_READ: fDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_WRITE: fDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_READWRITE: fDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + default: + AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen)); + return VERR_INVALID_PARAMETER; + } + } + + ULONG fShareAccess; + switch (fOpen & RTFILE_O_DENY_MASK) + { + case RTFILE_O_DENY_NONE: fShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_READ: fShareAccess = FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_WRITE: fShareAccess = FILE_SHARE_READ; break; + case RTFILE_O_DENY_READWRITE: fShareAccess = 0; break; + + case RTFILE_O_DENY_NOT_DELETE: fShareAccess = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: fShareAccess = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: fShareAccess = FILE_SHARE_DELETE | FILE_SHARE_READ; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:fShareAccess = FILE_SHARE_DELETE; break; + default: + AssertMsgFailed(("Impossible fOpen=%#llx\n", fOpen)); + return VERR_INVALID_PARAMETER; + } + + ULONG fObjAttribs = 0; + if (fOpen & RTFILE_O_INHERIT) + fObjAttribs = OBJ_INHERIT; + + ULONG fCreateOptions = FILE_NON_DIRECTORY_FILE; + if (fOpen & RTFILE_O_WRITE_THROUGH) + fCreateOptions |= FILE_WRITE_THROUGH; + if (!(fOpen & RTFILE_O_ASYNC_IO)) + fCreateOptions |= FILE_SYNCHRONOUS_IO_NONALERT; + if (fOpen & RTFILE_O_NO_CACHE) + { + fCreateOptions |= FILE_NO_INTERMEDIATE_BUFFERING; + fDesiredAccess &= ~FILE_APPEND_DATA; + } + + /* + * Done. + */ + *pfDesiredAccess = fDesiredAccess; + *pfObjAttribs = fObjAttribs; + *pfFileAttribs = FILE_ATTRIBUTE_NORMAL; + *pfShareAccess = fShareAccess; + *pfCreateDisposition = fCreateDisposition; + *pfCreateOptions = fCreateOptions; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/nt/semevent-nt.cpp b/src/VBox/Runtime/nt/semevent-nt.cpp new file mode 100644 index 00000000..c1dfa72b --- /dev/null +++ b/src/VBox/Runtime/nt/semevent-nt.cpp @@ -0,0 +1,490 @@ +/* $Id: semevent-nt.cpp $ */ +/** @file + * IPRT - Single Release Event Semaphores, Ring-0 Driver & Ring-3 Userland, NT. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENT_WITHOUT_REMAPPING +#ifdef IN_RING0 +# include "../r0drv/nt/the-nt-kernel.h" +#else +# include <iprt/nt/nt.h> +#endif +#include <iprt/semaphore.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/lockvalidator.h> +#include <iprt/mem.h> +#include <iprt/time.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * NT event semaphore. + */ +typedef struct RTSEMEVENTINTERNAL +{ + /** Magic value (RTSEMEVENT_MAGIC). */ + uint32_t volatile u32Magic; + /** Reference counter. */ + uint32_t volatile cRefs; +#ifdef IN_RING0 + /** The NT event object. */ + KEVENT Event; +#elif defined(IN_RING3) + /** Handle to the NT event object. */ + HANDLE hEvent; +#else +# error "Unknown context" +#endif +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + /** Signallers. */ + RTLOCKVALRECSHRD Signallers; + /** Indicates that lock validation should be performed. */ + bool volatile fEverHadSignallers; +#endif + +} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL; + + +RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) +{ + return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); + Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); + AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *)); + + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMEVENT_MAGIC; + pThis->cRefs = 1; +#ifdef IN_RING0 + KeInitializeEvent(&pThis->Event, SynchronizationEvent, FALSE /* not signalled */); +#else + NTSTATUS rcNt = NtCreateEvent(&pThis->hEvent, EVENT_ALL_ACCESS, NULL /*pObjAttr*/, + SynchronizationEvent, FALSE /*not signalled*/); + if (NT_SUCCESS(rcNt)) +#endif + { +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + if (!pszNameFmt) + { + static uint32_t volatile s_iSemEventAnon = 0; + RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL), + "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1); + } + else + { + va_list va; + va_start(va, pszNameFmt); + RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL), + pszNameFmt, va); + va_end(va); + } + pThis->fEverHadSignallers = false; +#else + RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt); +#endif + *phEventSem = pThis; + return VINF_SUCCESS; + } +#ifdef IN_RING3 + RTMemFree(pThis); + return RTErrConvertFromNtStatus(rcNt); +#endif + } + return VERR_NO_MEMORY; +} + + +/** + * Retains a reference to the semaphore. + * + * @param pThis The semaphore to retain. + */ +DECLINLINE(void) rtR0SemEventNtRetain(PRTSEMEVENTINTERNAL pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < 100000); NOREF(cRefs); +} + + +/** + * Releases a reference to the semaphore. + * + * @param pThis The semaphore to release + */ +DECLINLINE(void) rtR0SemEventNtRelease(PRTSEMEVENTINTERNAL pThis) +{ + if (ASMAtomicDecU32(&pThis->cRefs) == 0) + { +#ifdef IN_RING3 + NTSTATUS rcNt = NtClose(pThis->hEvent); + AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt)); RT_NOREF(rcNt); + pThis->hEvent = NULL; +#endif +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + RTLockValidatorRecSharedDelete(&pThis->Signallers); +#endif + RTMemFree(pThis); + } +} + + +RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTINTERNAL pThis = hEventSem; + if (pThis == NIL_RTSEMEVENT) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + + /* + * Invalidate it and signal the object just in case. + */ + ASMAtomicIncU32(&pThis->u32Magic); +#ifdef IN_RING0 + KeSetEvent(&pThis->Event, 0xfff, FALSE); +#else + NtSetEvent(pThis->hEvent, NULL); +#endif + + rtR0SemEventNtRelease(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + rtR0SemEventNtRetain(pThis); + +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + if (pThis->fEverHadSignallers) + { + int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD); + if (RT_FAILURE(rc9)) + return rc9; + } +#endif + + /* + * Signal the event object. + */ +#ifdef IN_RING0 + KeSetEvent(&pThis->Event, 1, FALSE); +#else + NTSTATUS rcNt = NtSetEvent(pThis->hEvent, NULL); +#endif + + rtR0SemEventNtRelease(pThis); +#ifdef IN_RING3 + AssertMsgReturn(NT_SUCCESS(rcNt), ("Signaling hEventSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt)); +#endif + return VINF_SUCCESS; +} + + + +/** + * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug. + * + * @returns VBox status code. + * @param pThis The event semaphore. + * @param fFlags See RTSemEventWaitEx. + * @param uTimeout See RTSemEventWaitEx. + * @param pSrcPos The source code position of the wait. + */ +DECLINLINE(int) rtR0SemEventNtWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + /* + * Validate input. + */ + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_FLAGS); + NOREF(pSrcPos); + + rtR0SemEventNtRetain(pThis); + + /* + * Lock validation needs to be done only when not polling. + */ +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + RTTHREAD const hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) ? RTThreadSelfAutoAdopt() : RTThreadSelf(); + if ( pThis->fEverHadSignallers + && ( uTimeout != 0 + || (fFlags & (RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_ABSOLUTE))) ) + { + int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false, + fFlags & RTSEMWAIT_FLAGS_INDEFINITE + ? RT_INDEFINITE_WAIT : RT_MS_30SEC /*whatever*/, + RTTHREADSTATE_EVENT, true); + if (RT_FAILURE(rc9)) + return rc9; + } +#elif defined(IN_RING3) + RTTHREAD const hThreadSelf = RTThreadSelf(); +#endif + + /* + * Convert the timeout to a relative one because KeWaitForSingleObject + * takes system time instead of interrupt time as input for absolute + * timeout specifications. So, we're best off by giving it relative time. + * + * Lazy bird converts uTimeout to relative nanoseconds and then to Nt time. + */ +#ifdef IN_RING3 + uint64_t nsStartNow = 0; +#endif + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { + if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) + uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS + ? uTimeout * RT_NS_1MS + : UINT64_MAX; + if (uTimeout == UINT64_MAX) + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + { +#ifdef IN_RING3 + if (fFlags & (RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_ABSOLUTE)) + nsStartNow = RTTimeSystemNanoTS(); +#endif + if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE) + { +#ifdef IN_RING0 + uint64_t const nsStartNow = RTTimeSystemNanoTS(); +#endif + uTimeout = nsStartNow < uTimeout + ? uTimeout - nsStartNow + : 0; + } + } + } + + /* + * Wait for it. + * We're assuming interruptible waits should happen at UserMode level. + */ + int rc; +#ifdef IN_RING3 + for (;;) +#endif + { +#ifdef IN_RING0 + BOOLEAN fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE); + KPROCESSOR_MODE WaitMode = fInterruptible ? UserMode : KernelMode; +#endif + NTSTATUS rcNt; +#ifdef IN_RING3 + RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true); +#endif + if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) +#ifdef IN_RING0 + rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, NULL); +#else + rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, NULL); +#endif + else + { + LARGE_INTEGER Timeout; + Timeout.QuadPart = -(int64_t)(uTimeout / 100); +#ifdef IN_RING0 + rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, &Timeout); +#else + rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, &Timeout); +#endif + } +#ifdef IN_RING3 + RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT); +#endif + if (pThis->u32Magic == RTSEMEVENT_MAGIC) + { + switch (rcNt) + { + case STATUS_SUCCESS: + rc = VINF_SUCCESS; + break; + + case STATUS_TIMEOUT: + Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)); + rc = VERR_TIMEOUT; + break; + + case STATUS_USER_APC: + case STATUS_ALERTED: + rc = VERR_INTERRUPTED; +#ifdef IN_RING3 + /* Loop if when automatically resuming on interruption, adjusting the timeout. */ + if (fFlags & RTSEMWAIT_FLAGS_RESUME) + { + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE) && uTimeout > 0) + { + uint64_t const nsNewNow = RTTimeSystemNanoTS(); + uint64_t const cNsElapsed = nsNewNow - nsStartNow; + if (cNsElapsed < uTimeout) + uTimeout -= cNsElapsed; + else + uTimeout = 0; + nsStartNow = nsNewNow; + } + continue; + } +#endif + break; + +#ifdef IN_RING3 + case STATUS_ABANDONED_WAIT_0: + rc = VERR_SEM_OWNER_DIED; + break; +#endif + default: + AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %x!\n", pThis->u32Magic, pThis, rcNt)); + rc = VERR_INTERNAL_ERROR_4; + break; + } + } + else + rc = VERR_SEM_DESTROYED; +#ifdef IN_RING3 + break; +#endif + } + + rtR0SemEventNtRelease(pThis); + return rc; +} + + +RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos); +#endif +} + + +RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtR0SemEventNtWait(hEventSem, fFlags, uTimeout, &SrcPos); +} + + +#ifdef IN_RING0 +RTR0DECL(bool) RTSemEventIsSignalSafe(void) +{ + return KeGetCurrentIrql() <= DISPATCH_LEVEL; +} +#endif + +#ifdef IN_RING3 + +RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL); +# else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +# endif +} + + +RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL); +# else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +# endif +} + + +RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread); +# else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +# endif +} + +#endif /* IN_RING3 */ diff --git a/src/VBox/Runtime/nt/semeventmulti-nt.cpp b/src/VBox/Runtime/nt/semeventmulti-nt.cpp new file mode 100644 index 00000000..b5be9fc6 --- /dev/null +++ b/src/VBox/Runtime/nt/semeventmulti-nt.cpp @@ -0,0 +1,520 @@ +/* $Id: semeventmulti-nt.cpp $ */ +/** @file + * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, NT. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENTMULTI_WITHOUT_REMAPPING +#ifdef IN_RING0 +# include "../r0drv/nt/the-nt-kernel.h" +#else +# include <iprt/nt/nt.h> +#endif +#include <iprt/semaphore.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/lockvalidator.h> +#include <iprt/mem.h> +#include <iprt/time.h> +#include <iprt/timer.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * NT event semaphore. + */ +typedef struct RTSEMEVENTMULTIINTERNAL +{ + /** Magic value (RTSEMEVENTMULTI_MAGIC). */ + uint32_t volatile u32Magic; + /** Reference counter. */ + uint32_t volatile cRefs; +#ifdef IN_RING0 + /** The NT event object. */ + KEVENT Event; +#elif defined(IN_RING3) + /** Handle to the NT event object. */ + HANDLE hEvent; +#endif +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + /** Signallers. */ + RTLOCKVALRECSHRD Signallers; + /** Indicates that lock validation should be performed. */ + bool volatile fEverHadSignallers; +#endif +} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL; + + +RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem) +{ + return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass, + const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + RT_NOREF2(hClass, pszNameFmt); + + AssertCompile(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *)); + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMEVENTMULTI_MAGIC; + pThis->cRefs = 1; +#ifdef IN_RING0 + KeInitializeEvent(&pThis->Event, NotificationEvent, FALSE /* not signalled */); +#else + NTSTATUS rcNt = NtCreateEvent(&pThis->hEvent, EVENT_ALL_ACCESS, NULL /*pObjAttr*/, + NotificationEvent, FALSE /*not signalled*/); + if (NT_SUCCESS(rcNt)) +#endif + { +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + if (!pszNameFmt) + { + static uint32_t volatile s_iSemEventMultiAnon = 0; + RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), + "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1); + } + else + { + va_list va; + va_start(va, pszNameFmt); + RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), + pszNameFmt, va); + va_end(va); + } + pThis->fEverHadSignallers = false; +#else + RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt); +#endif + + *phEventMultiSem = pThis; + return VINF_SUCCESS; + } +#ifdef IN_RING3 + RTMemFree(pThis); + return RTErrConvertFromNtStatus(rcNt); +#endif + } + return VERR_NO_MEMORY; +} + + +/** + * Retains a reference to the semaphore. + * + * @param pThis The semaphore to retain. + */ +DECLINLINE(void) rtR0SemEventMultiNtRetain(PRTSEMEVENTMULTIINTERNAL pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < 100000); NOREF(cRefs); +} + + +/** + * Releases a reference to the semaphore. + * + * @param pThis The semaphore to release + */ +DECLINLINE(void) rtR0SemEventMultiNtRelease(PRTSEMEVENTMULTIINTERNAL pThis) +{ + if (ASMAtomicDecU32(&pThis->cRefs) == 0) + { +#ifdef IN_RING3 + NTSTATUS rcNt = NtClose(pThis->hEvent); + AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt)); RT_NOREF(rcNt); + pThis->hEvent = NULL; +#endif +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + RTLockValidatorRecSharedDelete(&pThis->Signallers); +#endif + RTMemFree(pThis); + } +} + + +RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (pThis == NIL_RTSEMEVENTMULTI) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + + /* + * Invalidate it and signal the object just in case. + */ + ASMAtomicIncU32(&pThis->u32Magic); +#ifdef IN_RING0 + KeSetEvent(&pThis->Event, 0xfff, FALSE); +#else + NtSetEvent(pThis->hEvent, NULL); +#endif + + rtR0SemEventMultiNtRelease(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + rtR0SemEventMultiNtRetain(pThis); + +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + if (pThis->fEverHadSignallers) + { + int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD); + if (RT_FAILURE(rc9)) + return rc9; + } +#endif + + /* + * Signal the event object. + */ +#ifdef IN_RING0 + KeSetEvent(&pThis->Event, 1, FALSE); +#else + NTSTATUS rcNt = NtSetEvent(pThis->hEvent, NULL); +#endif + + rtR0SemEventMultiNtRelease(pThis); +#ifdef IN_RING3 + AssertMsgReturn(NT_SUCCESS(rcNt), ("Signaling hEventMultiSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt)); +#endif + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + rtR0SemEventMultiNtRetain(pThis); + + /* + * Reset the event object. + */ +#ifdef IN_RING0 + KeResetEvent(&pThis->Event); +#else + NTSTATUS rcNt = NtResetEvent(pThis->hEvent, NULL); +#endif + + rtR0SemEventMultiNtRelease(pThis); +#ifdef IN_RING3 + AssertMsgReturn(NT_SUCCESS(rcNt), ("Resetting hEventMultiSem %p failed: %#x\n", pThis, rcNt), RTErrConvertFromNtStatus(rcNt)); +#endif + return VINF_SUCCESS; +} + + +/** + * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug. + * + * @returns VBox status code. + * @param pThis The event semaphore. + * @param fFlags See RTSemEventMultiWaitEx. + * @param uTimeout See RTSemEventMultiWaitEx. + * @param pSrcPos The source code position of the wait. + */ +DECLINLINE(int) rtR0SemEventMultiNtWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + /* + * Validate input. + */ + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); + RT_NOREF1(pSrcPos); + + rtR0SemEventMultiNtRetain(pThis); + + /* + * Lock validation needs to be done only when not polling. + */ +#if defined(RTSEMEVENT_STRICT) && defined(IN_RING3) + RTTHREAD const hThreadSelf = RTThreadSelfAutoAdopt(); + if ( pThis->fEverHadSignallers + && ( uTimeout != 0 + || (fFlags & (RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_ABSOLUTE))) ) + { + int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false, + fFlags & RTSEMWAIT_FLAGS_INDEFINITE + ? RT_INDEFINITE_WAIT : RT_MS_30SEC /*whatever*/, + RTTHREADSTATE_EVENT_MULTI, true); + if (RT_FAILURE(rc9)) + return rc9; + } +#elif defined(IN_RING3) + RTTHREAD const hThreadSelf = RTThreadSelf(); +#endif + + /* + * Convert the timeout to a relative one because KeWaitForSingleObject + * takes system time instead of interrupt time as input for absolute + * timeout specifications. So, we're best of by giving it relative time. + * + * Lazy bird converts uTimeout to relative nanoseconds and then to Nt time. + */ +#ifdef IN_RING3 + uint64_t nsStartNow = 0; +#endif + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { + if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) + uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS + ? uTimeout * RT_NS_1MS + : UINT64_MAX; + if (uTimeout == UINT64_MAX) + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + { +#ifdef IN_RING3 + if (fFlags & (RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_ABSOLUTE)) + nsStartNow = RTTimeSystemNanoTS(); +#endif + if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE) + { +#ifdef IN_RING0 + uint64_t const nsStartNow = RTTimeSystemNanoTS(); +#endif + uTimeout = nsStartNow < uTimeout + ? uTimeout - nsStartNow + : 0; + } + } + } + + /* + * Wait for it. + * We're assuming interruptible waits should happen at UserMode level. + */ + int rc; +#ifdef IN_RING3 + for (;;) +#endif + { +#ifdef IN_RING0 + BOOLEAN fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE); + KPROCESSOR_MODE WaitMode = fInterruptible ? UserMode : KernelMode; +#endif + NTSTATUS rcNt; +#ifdef IN_RING3 + RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true); +#endif + if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) +#ifdef IN_RING0 + rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, NULL); +#else + rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, NULL); +#endif + else + { + LARGE_INTEGER Timeout; + Timeout.QuadPart = -(int64_t)(uTimeout / 100); +#ifdef IN_RING0 + rcNt = KeWaitForSingleObject(&pThis->Event, Executive, WaitMode, fInterruptible, &Timeout); +#else + rcNt = NtWaitForSingleObject(pThis->hEvent, TRUE /*Alertable*/, &Timeout); +#endif + } +#ifdef IN_RING3 + RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI); +#endif + if (pThis->u32Magic == RTSEMEVENTMULTI_MAGIC) + { + switch (rcNt) + { + case STATUS_SUCCESS: + rc = VINF_SUCCESS; + break; + + case STATUS_TIMEOUT: + Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)); + rc = VERR_TIMEOUT; + break; + + case STATUS_USER_APC: + case STATUS_ALERTED: + rc = VERR_INTERRUPTED; +#ifdef IN_RING3 + /* Loop if when automatically resuming on interruption, adjusting the timeout. */ + if (fFlags & RTSEMWAIT_FLAGS_RESUME) + { + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE) && uTimeout > 0) + { + uint64_t const nsNewNow = RTTimeSystemNanoTS(); + uint64_t const cNsElapsed = nsNewNow - nsStartNow; + if (cNsElapsed < uTimeout) + uTimeout -= cNsElapsed; + else + uTimeout = 0; + nsStartNow = nsNewNow; + } + continue; + } +#endif + break; + +#ifdef IN_RING3 + case STATUS_ABANDONED_WAIT_0: + rc = VERR_SEM_OWNER_DIED; + break; +#endif + default: + AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %x!\n", pThis->u32Magic, pThis, rcNt)); + rc = VERR_INTERNAL_ERROR_4; + break; + } + } + else + rc = VERR_SEM_DESTROYED; +#ifdef IN_RING3 + break; +#endif + } + + rtR0SemEventMultiNtRelease(pThis); + return rc; +} + + +RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +#endif +} + + +RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtR0SemEventMultiNtWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +} + + +#ifdef IN_RING0 +RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void) +{ + return KeGetCurrentIrql() <= DISPATCH_LEVEL; +} +#endif + +#ifdef IN_RING3 + +RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL); +# else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +# endif +} + + +RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL); +# else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +# endif +} + + +RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +# ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread); +# else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +# endif +} + +#endif /* IN_RING3 */ |