summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/nt
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/nt')
-rw-r--r--src/VBox/Runtime/nt/Makefile.kup0
-rw-r--r--src/VBox/Runtime/nt/NtProcessStartup-stub.cpp69
-rw-r--r--src/VBox/Runtime/nt/RTErrConvertFromNtStatus.cpp120
-rw-r--r--src/VBox/Runtime/nt/RTNtPathExpand8dot3Path.cpp209
-rw-r--r--src/VBox/Runtime/nt/RTNtPathExpand8dot3PathA.cpp97
-rw-r--r--src/VBox/Runtime/nt/RTNtPathFindPossible8dot3Name.cpp82
-rw-r--r--src/VBox/Runtime/nt/RTSemEventGetResolution-nt.cpp95
-rw-r--r--src/VBox/Runtime/nt/RTSemEventMultiGetResolution-nt.cpp95
-rw-r--r--src/VBox/Runtime/nt/fileioutils-nt.cpp189
-rw-r--r--src/VBox/Runtime/nt/semevent-nt.cpp490
-rw-r--r--src/VBox/Runtime/nt/semeventmulti-nt.cpp520
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 */