summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/generic
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/generic')
-rw-r--r--src/VBox/Runtime/generic/Makefile.kup0
-rw-r--r--src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp67
-rw-r--r--src/VBox/Runtime/generic/RTCrStoreCreateSnapshotById-generic.cpp158
-rw-r--r--src/VBox/Runtime/generic/RTDirCreateUniqueNumbered-generic.cpp152
-rw-r--r--src/VBox/Runtime/generic/RTDirExists-generic.cpp59
-rw-r--r--src/VBox/Runtime/generic/RTDirQueryInfo-generic.cpp66
-rw-r--r--src/VBox/Runtime/generic/RTDirSetMode-generic.cpp74
-rw-r--r--src/VBox/Runtime/generic/RTDirSetTimes-generic.cpp68
-rw-r--r--src/VBox/Runtime/generic/RTEnvDupEx-generic.cpp87
-rw-r--r--src/VBox/Runtime/generic/RTFileCopy-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTFileCopyAttributes-generic.cpp72
-rw-r--r--src/VBox/Runtime/generic/RTFileCopyByHandlesEx-generic.cpp171
-rw-r--r--src/VBox/Runtime/generic/RTFileCopyEx-generic.cpp97
-rw-r--r--src/VBox/Runtime/generic/RTFileCopyPart-generic.cpp60
-rw-r--r--src/VBox/Runtime/generic/RTFileCopyPartEx-generic.cpp166
-rw-r--r--src/VBox/Runtime/generic/RTFileExists-generic.cpp60
-rw-r--r--src/VBox/Runtime/generic/RTFileMove-generic.cpp124
-rw-r--r--src/VBox/Runtime/generic/RTFileQuerySectorSize-generic.cpp55
-rw-r--r--src/VBox/Runtime/generic/RTFileQuerySize-generic.cpp70
-rw-r--r--src/VBox/Runtime/generic/RTFileReadAll-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTFileReadAllByHandle-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTFileReadAllByHandleEx-generic.cpp127
-rw-r--r--src/VBox/Runtime/generic/RTFileReadAllEx-generic.cpp62
-rw-r--r--src/VBox/Runtime/generic/RTFileReadAllFree-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTFileSetAllocationSize-generic.cpp61
-rw-r--r--src/VBox/Runtime/generic/RTLogDefaultInit-generic.cpp49
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteDebugger-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteStdErr-generic.cpp51
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteStdOut-generic.cpp53
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTLogWriteVmm-stub-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpCpuId-generic.cpp58
-rw-r--r--src/VBox/Runtime/generic/RTMpCpuIdFromSetIndex-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpCpuIdToSetIndex-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTMpGetArraySize-generic.cpp72
-rw-r--r--src/VBox/Runtime/generic/RTMpGetCoreCount-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpGetCount-generic.cpp54
-rw-r--r--src/VBox/Runtime/generic/RTMpGetCurFrequency-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTMpGetDescription-generic-stub.cpp64
-rw-r--r--src/VBox/Runtime/generic/RTMpGetDescription-generic.cpp124
-rw-r--r--src/VBox/Runtime/generic/RTMpGetMaxCpuId-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpGetMaxFrequency-generic.cpp52
-rw-r--r--src/VBox/Runtime/generic/RTMpGetOnlineCoreCount-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpGetOnlineCount-generic.cpp54
-rw-r--r--src/VBox/Runtime/generic/RTMpGetOnlineSet-generic.cpp61
-rw-r--r--src/VBox/Runtime/generic/RTMpGetSet-generic.cpp61
-rw-r--r--src/VBox/Runtime/generic/RTMpIsCpuOnline-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpIsCpuPossible-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTMpOnPair-generic.cpp148
-rw-r--r--src/VBox/Runtime/generic/RTPathGetCurrentDrive-generic.cpp133
-rw-r--r--src/VBox/Runtime/generic/RTPathGetCurrentOnDrive-generic.cpp94
-rw-r--r--src/VBox/Runtime/generic/RTPathIsSame-generic.cpp107
-rw-r--r--src/VBox/Runtime/generic/RTProcDaemonize-generic.cpp106
-rw-r--r--src/VBox/Runtime/generic/RTProcIsRunningByName-generic.cpp53
-rw-r--r--src/VBox/Runtime/generic/RTProcSignalName-generic.cpp226
-rw-r--r--src/VBox/Runtime/generic/RTProcessQueryUsernameA-generic.cpp79
-rw-r--r--src/VBox/Runtime/generic/RTRandAdvCreateSystemFaster-generic.cpp53
-rw-r--r--src/VBox/Runtime/generic/RTRandAdvCreateSystemTruer-generic.cpp53
-rw-r--r--src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTSemEventMultiWait-generic.cpp77
-rw-r--r--src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTSemEventWait-generic.cpp77
-rw-r--r--src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTSemMutexRequest-generic.cpp77
-rw-r--r--src/VBox/Runtime/generic/RTSemMutexRequestDebug-generic.cpp77
-rw-r--r--src/VBox/Runtime/generic/RTSystemFirmware-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTSystemIsInsideVM-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTSystemQueryDmiString-generic.cpp57
-rw-r--r--src/VBox/Runtime/generic/RTSystemQueryOSInfo-generic.cpp66
-rw-r--r--src/VBox/Runtime/generic/RTSystemShutdown-generic.cpp56
-rw-r--r--src/VBox/Runtime/generic/RTThreadGetAffinity-stub-generic.cpp54
-rw-r--r--src/VBox/Runtime/generic/RTThreadGetNativeState-generic.cpp59
-rw-r--r--src/VBox/Runtime/generic/RTThreadQueryTerminationStatus-stub-generic.cpp53
-rw-r--r--src/VBox/Runtime/generic/RTThreadSetAffinity-stub-generic.cpp56
-rw-r--r--src/VBox/Runtime/generic/RTThreadSetAffinityToCpu-generic.cpp69
-rw-r--r--src/VBox/Runtime/generic/RTTimeLocalDeltaNano-generic.cpp50
-rw-r--r--src/VBox/Runtime/generic/RTTimeLocalDeltaNanoFor-generic.cpp51
-rw-r--r--src/VBox/Runtime/generic/RTTimeLocalExplode-generic.cpp59
-rw-r--r--src/VBox/Runtime/generic/RTTimeLocalNow-generic.cpp59
-rw-r--r--src/VBox/Runtime/generic/RTTimerCreate-generic.cpp63
-rw-r--r--src/VBox/Runtime/generic/RTTimerLRCreate-generic.cpp62
-rw-r--r--src/VBox/Runtime/generic/RTUuidCreate-generic.cpp66
-rw-r--r--src/VBox/Runtime/generic/cdrom-generic.cpp128
-rw-r--r--src/VBox/Runtime/generic/createtemp-generic.cpp291
-rw-r--r--src/VBox/Runtime/generic/critsect-generic.cpp591
-rw-r--r--src/VBox/Runtime/generic/critsectrw-generic.cpp1080
-rw-r--r--src/VBox/Runtime/generic/env-generic.cpp1409
-rw-r--r--src/VBox/Runtime/generic/errvars-generic.cpp78
-rw-r--r--src/VBox/Runtime/generic/fileio-at-generic.cpp64
-rw-r--r--src/VBox/Runtime/generic/fileio-sg-at-generic.cpp112
-rw-r--r--src/VBox/Runtime/generic/fileio-sg-generic.cpp110
-rw-r--r--src/VBox/Runtime/generic/fs-stubs-generic.cpp103
-rw-r--r--src/VBox/Runtime/generic/http-curl.cpp4173
-rw-r--r--src/VBox/Runtime/generic/http.cpp409
-rw-r--r--src/VBox/Runtime/generic/mempool-generic.cpp420
-rw-r--r--src/VBox/Runtime/generic/memsafer-generic.cpp238
-rw-r--r--src/VBox/Runtime/generic/mppresent-generic-online.cpp71
-rw-r--r--src/VBox/Runtime/generic/mppresent-generic.cpp71
-rw-r--r--src/VBox/Runtime/generic/new-delete-generic.cpp144
-rw-r--r--src/VBox/Runtime/generic/pathhost-generic.cpp101
-rw-r--r--src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp84
-rw-r--r--src/VBox/Runtime/generic/sched-generic.cpp77
-rw-r--r--src/VBox/Runtime/generic/semfastmutex-generic.cpp92
-rw-r--r--src/VBox/Runtime/generic/semrw-generic.cpp988
-rw-r--r--src/VBox/Runtime/generic/semrw-lockless-generic.cpp981
-rw-r--r--src/VBox/Runtime/generic/semxroads-generic.cpp451
-rw-r--r--src/VBox/Runtime/generic/spinlock-generic.cpp237
-rw-r--r--src/VBox/Runtime/generic/strcache-stubs-generic.cpp151
-rw-r--r--src/VBox/Runtime/generic/timer-generic.cpp347
-rw-r--r--src/VBox/Runtime/generic/timerlr-generic.cpp452
-rw-r--r--src/VBox/Runtime/generic/tls-generic.cpp169
-rw-r--r--src/VBox/Runtime/generic/utf16locale-generic.cpp51
-rw-r--r--src/VBox/Runtime/generic/uuid-generic.cpp532
116 files changed, 20294 insertions, 0 deletions
diff --git a/src/VBox/Runtime/generic/Makefile.kup b/src/VBox/Runtime/generic/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/generic/Makefile.kup
diff --git a/src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp b/src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp
new file mode 100644
index 00000000..76a87800
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTAssertShouldPanic-generic.cpp
@@ -0,0 +1,67 @@
+/* $Id: RTAssertShouldPanic-generic.cpp $ */
+/** @file
+ * IPRT - Assertions, generic RTAssertShouldPanic.
+ */
+
+/*
+ * 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/assert.h>
+#include "internal/iprt.h"
+
+#ifdef IN_RING
+# if 0
+# include <iprt/asm.h>
+# include <iprt/asm-amd64-x86.h>
+# endif
+#endif
+
+
+RTDECL(bool) RTAssertShouldPanic(void)
+{
+#ifdef IN_RING0
+# if 0 /* this can be useful when debugging guests. */
+ ASMIntDisable();
+ ASMHalt();
+# endif
+#endif
+#if 0 /* Enable this to not panic on assertions. (Make sure this code is used!) */
+ return false;
+#else
+ return RTAssertMayPanic();
+#endif
+}
+RT_EXPORT_SYMBOL(RTAssertShouldPanic);
+
diff --git a/src/VBox/Runtime/generic/RTCrStoreCreateSnapshotById-generic.cpp b/src/VBox/Runtime/generic/RTCrStoreCreateSnapshotById-generic.cpp
new file mode 100644
index 00000000..89451a1d
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTCrStoreCreateSnapshotById-generic.cpp
@@ -0,0 +1,158 @@
+/* $Id: RTCrStoreCreateSnapshotById-generic.cpp $ */
+/** @file
+ * IPRT - Generic RTCrStoreCreateSnapshotById implementation.
+ */
+
+/*
+ * 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/crypto/store.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/dir.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Unix root prefix. */
+#ifdef RT_OS_OS2
+# define UNIX_ROOT "/@unixroot@"
+#elif defined(RT_OS_WINDOWS)
+# define UNIX_ROOT "C:/cygwin"
+#else
+# define UNIX_ROOT
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** System PEM files worth looking at.
+ * @remarks Several of these could be symlinks to one of the others.
+ */
+static const char *g_apszSystemPemFiles[] =
+{
+ UNIX_ROOT "/etc/ssl/certs/ca-certificates.crt",
+ UNIX_ROOT "/etc/ssl/cert.pem",
+ UNIX_ROOT "/etc/ca-certificates/extracted/tls-ca-bundle.pem", /* Arch linux (ca 2015-08-xx) */
+ UNIX_ROOT "/etc/ca-certificates/extracted/email-ca-bundle.pem",
+ UNIX_ROOT "/etc/ca-certificates/extracted/objsign-ca-bundle.pem",
+ UNIX_ROOT "/etc/ca-certificates/extracted/ca-bundle.trust.crt",
+ UNIX_ROOT "/etc/ca-certificates/extracted/ca-bundle.trust.crt",
+ UNIX_ROOT "/etc/pki/tls/certs/ca-bundle.crt", /* Oracle Linux 5 */
+ UNIX_ROOT "/etc/pki/tls/cert.pem",
+ UNIX_ROOT "/etc/certs/ca-certificates.crt", /* Solaris 11 */
+ UNIX_ROOT "/etc/curl/curlCA",
+};
+
+/**
+ * System directories containing lots of pem/crt files.
+ */
+static const char *g_apszSystemPemDirs[] =
+{
+ UNIX_ROOT "/etc/openssl/certs/",
+ UNIX_ROOT "/etc/ssl/certs/",
+ UNIX_ROOT "/etc/ca-certificates/extracted/cadir/",
+ UNIX_ROOT "/etc/certs/CA/", /* Solaris 11 */
+};
+
+
+RTDECL(int) RTCrStoreCreateSnapshotById(PRTCRSTORE phStore, RTCRSTOREID enmStoreId, PRTERRINFO pErrInfo)
+{
+ AssertReturn(enmStoreId > RTCRSTOREID_INVALID && enmStoreId < RTCRSTOREID_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Create an empty in-memory store.
+ */
+ RTCRSTORE hStore;
+ uint32_t cExpected = enmStoreId == RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES ? 256 : 0;
+ int rc = RTCrStoreCreateInMem(&hStore, cExpected);
+ if (RT_SUCCESS(rc))
+ {
+ *phStore = hStore;
+
+ /*
+ * Add system certificates if part of the given store ID.
+ */
+ bool fFound = false;
+ rc = VINF_SUCCESS;
+ if (enmStoreId == RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSystemPemFiles); i++)
+ if (RTFileExists(g_apszSystemPemFiles[i]))
+ {
+ fFound = true;
+ int rc2 = RTCrStoreCertAddFromFile(hStore,
+ RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ g_apszSystemPemFiles[i], pErrInfo);
+ if (RT_FAILURE(rc2))
+ rc = -rc2;
+ }
+
+ /*
+ * If we didn't find any of the certificate collection files, go hunting
+ * for directories containing PEM/CRT files with single certificates.
+ */
+ if (!fFound)
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_apszSystemPemDirs); i++)
+ if (RTDirExists(g_apszSystemPemDirs[i]))
+ {
+ static RTSTRTUPLE const s_aSuffixes[] =
+ {
+ { RT_STR_TUPLE(".crt") },
+ { RT_STR_TUPLE(".pem") },
+ { RT_STR_TUPLE(".PEM") },
+ { RT_STR_TUPLE(".CRT") },
+ };
+ fFound = true;
+ int rc2 = RTCrStoreCertAddFromDir(hStore,
+ RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ g_apszSystemPemDirs[i], &s_aSuffixes[0], RT_ELEMENTS(s_aSuffixes),
+ pErrInfo);
+ if (RT_FAILURE(rc2))
+ rc = -rc2;
+ }
+ }
+ }
+ else
+ RTErrInfoAdd(pErrInfo, rc, " RTCrStoreCreateInMem failed");
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateSnapshotById);
+
diff --git a/src/VBox/Runtime/generic/RTDirCreateUniqueNumbered-generic.cpp b/src/VBox/Runtime/generic/RTDirCreateUniqueNumbered-generic.cpp
new file mode 100644
index 00000000..c2317836
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTDirCreateUniqueNumbered-generic.cpp
@@ -0,0 +1,152 @@
+/* $Id: RTDirCreateUniqueNumbered-generic.cpp $ */
+/** @file
+ * IPRT - RTDirCreateUniqueNumbered, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2011-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/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTDirCreateUniqueNumbered(char *pszPath, size_t cbSize, RTFMODE fMode, size_t cchDigits, char chSep)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cbSize, VERR_BUFFER_OVERFLOW);
+ AssertReturn(cchDigits > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cchDigits < 64, VERR_INVALID_PARAMETER);
+
+ /* Check that there is sufficient space. */
+ char *pszEnd = RTStrEnd(pszPath, cbSize);
+ AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
+ size_t cbLeft = cbSize - (pszEnd - pszPath);
+ AssertReturn(cbLeft > (chSep ? 1U : 0U) + cchDigits, VERR_BUFFER_OVERFLOW);
+
+ /*
+ * First try the pretty name without any numbers appended.
+ */
+ int rc = RTDirCreate(pszPath, fMode, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (rc == VERR_ALREADY_EXISTS)
+ {
+ /*
+ * Already exist, apply template specification.
+ */
+
+ /* Max 10000 tries (like RTDirCreateTemp), but stop earlier if we haven't got enough digits to work with. */
+ uint32_t cMaxTries;
+ switch (cchDigits)
+ {
+ case 1: cMaxTries = 40; break;
+ case 2: cMaxTries = 400; break;
+ case 3: cMaxTries = 4000; break;
+ default: cMaxTries = 10000; break;
+ }
+
+ static uint64_t const s_aEndSeqs[] =
+ {
+ UINT64_C(0),
+ UINT64_C(9),
+ UINT64_C(99),
+ UINT64_C(999),
+ UINT64_C(9999),
+ UINT64_C(99999),
+ UINT64_C(999999),
+ UINT64_C(9999999),
+ UINT64_C(99999999),
+ UINT64_C(999999999),
+ UINT64_C(9999999999),
+ UINT64_C(99999999999),
+ UINT64_C(999999999999),
+ UINT64_C(9999999999999),
+ UINT64_C(99999999999999),
+ UINT64_C(999999999999999),
+ UINT64_C(9999999999999999),
+ UINT64_C(99999999999999999),
+ UINT64_C(999999999999999999),
+ UINT64_C(9999999999999999999),
+ };
+ uint64_t const uEndSeq = cchDigits < RT_ELEMENTS(s_aEndSeqs) ? s_aEndSeqs[cchDigits] : UINT64_MAX;
+
+ /* Add separator if requested. */
+ if (chSep != '\0')
+ {
+ *pszEnd++ = chSep;
+ *pszEnd = '\0';
+ cbLeft--;
+ }
+
+ Assert(cbLeft > cchDigits);
+ for (uint32_t iTry = 0; iTry <= cMaxTries; iTry++)
+ {
+ /* Try sequentially first for a little bit, then switch to random numbers. */
+ uint64_t iSeq;
+ if (iTry > 20)
+ iSeq = RTRandU64Ex(0, uEndSeq);
+ else
+ {
+ iSeq = iTry;
+ if (uEndSeq < UINT64_MAX)
+ iSeq %= uEndSeq + 1;
+ }
+ ssize_t cchRet = RTStrFormatU64(pszEnd, cbLeft, iSeq, 10 /*uiBase*/,
+ (int)cchDigits /*cchWidth*/, 0 /*cchPrecision*/, RTSTR_F_WIDTH | RTSTR_F_ZEROPAD);
+ Assert((size_t)cchRet == cchDigits); NOREF(cchRet);
+
+ rc = RTDirCreate(pszPath, fMode, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (rc != VERR_ALREADY_EXISTS)
+ break;
+ }
+ }
+
+ /* We've given up or failed. */
+ *pszPath = '\0';
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDirCreateUniqueNumbered);
+
diff --git a/src/VBox/Runtime/generic/RTDirExists-generic.cpp b/src/VBox/Runtime/generic/RTDirExists-generic.cpp
new file mode 100644
index 00000000..95f1fa37
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTDirExists-generic.cpp
@@ -0,0 +1,59 @@
+/* $Id: RTDirExists-generic.cpp $ */
+/** @file
+ * IPRT - RTDirExists, generic implementation.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include "internal/iprt.h"
+
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+
+
+RTDECL(bool) RTDirExists(const char *pszPath)
+{
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ bool fRc = RT_SUCCESS(rc)
+ && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode);
+ LogFlow(("RTDirExists(%p:{%s}): returns %RTbool (%Rrc)\n", pszPath, pszPath, fRc, rc));
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTDirQueryInfo-generic.cpp b/src/VBox/Runtime/generic/RTDirQueryInfo-generic.cpp
new file mode 100644
index 00000000..0283242e
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTDirQueryInfo-generic.cpp
@@ -0,0 +1,66 @@
+/* $Id: RTDirQueryInfo-generic.cpp $ */
+/** @file
+ * IPRT - RTDirQueryInfo, generic implementation.
+ */
+
+/*
+ * 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_DIR
+#ifdef RT_OS_WINDOWS /* dir.h has host specific stuff */
+# include <iprt/win/windows.h>
+#else
+# include <dirent.h>
+#endif
+
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/dir.h"
+
+
+RTR3DECL(int) RTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(hDir))
+ return VERR_INVALID_PARAMETER;
+ return RTPathQueryInfoEx(hDir->pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_FOLLOW_LINK);
+}
+
diff --git a/src/VBox/Runtime/generic/RTDirSetMode-generic.cpp b/src/VBox/Runtime/generic/RTDirSetMode-generic.cpp
new file mode 100644
index 00000000..9bbc1255
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTDirSetMode-generic.cpp
@@ -0,0 +1,74 @@
+/* $Id: RTDirSetMode-generic.cpp $ */
+/** @file
+ * IPRT - RTDirSetMode, generic implementation.
+ */
+
+/*
+ * 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_DIR
+#ifdef RT_OS_WINDOWS /* dir.h has host specific stuff */
+# include <iprt/win/windows.h>
+#else
+# include <dirent.h>
+#endif
+
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/dir.h"
+
+
+
+RTR3DECL(int) RTDirSetMode(RTDIR hDir, RTFMODE fMode)
+{
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(hDir))
+ return VERR_INVALID_PARAMETER;
+
+ /* Make sure we flag it as a directory so that it will be converted correctly: */
+ if (!(fMode & RTFS_UNIX_MASK))
+ fMode |= RTFS_DOS_DIRECTORY;
+ else if (!(fMode & RTFS_TYPE_MASK))
+ fMode |= RTFS_TYPE_DIRECTORY;
+
+ return RTPathSetMode(hDir->pszPath, fMode);
+}
+
diff --git a/src/VBox/Runtime/generic/RTDirSetTimes-generic.cpp b/src/VBox/Runtime/generic/RTDirSetTimes-generic.cpp
new file mode 100644
index 00000000..6e2d1573
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTDirSetTimes-generic.cpp
@@ -0,0 +1,68 @@
+/* $Id: RTDirSetTimes-generic.cpp $ */
+/** @file
+ * IPRT - RTDirSetTimes, generic implementation.
+ */
+
+/*
+ * 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_DIR
+#ifdef RT_OS_WINDOWS /* dir.h has host specific stuff */
+# include <iprt/win/windows.h>
+#else
+# include <dirent.h>
+#endif
+
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/dir.h"
+
+
+
+RTR3DECL(int) RTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(hDir))
+ return VERR_INVALID_PARAMETER;
+ return RTPathSetTimes(hDir->pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+}
+
diff --git a/src/VBox/Runtime/generic/RTEnvDupEx-generic.cpp b/src/VBox/Runtime/generic/RTEnvDupEx-generic.cpp
new file mode 100644
index 00000000..e6bcb6d2
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTEnvDupEx-generic.cpp
@@ -0,0 +1,87 @@
+/* $Id: RTEnvDupEx-generic.cpp $ */
+/** @file
+ * IPRT - Environment, RTEnvDupEx, generic.
+ */
+
+/*
+ * Copyright (C) 2010-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/env.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+
+RTDECL(char *) RTEnvDupEx(RTENV Env, const char *pszVar)
+{
+ /*
+ * Try with a small buffer. This helps avoid allocating a heap buffer for
+ * variables that doesn't exist.
+ */
+ char szSmall[256];
+ int rc = RTEnvGetEx(Env, pszVar, szSmall, sizeof(szSmall), NULL);
+ if (RT_SUCCESS(rc))
+ return RTStrDup(szSmall);
+ if (rc != VERR_BUFFER_OVERFLOW)
+ return NULL;
+
+ /*
+ * It's a big bugger.
+ */
+ size_t cbBuf = _1K;
+ do
+ {
+ char *pszBuf = RTStrAlloc(cbBuf);
+ AssertBreak(pszBuf);
+
+ rc = RTEnvGetEx(Env, pszVar, pszBuf, cbBuf, NULL);
+ if (RT_SUCCESS(rc))
+ return pszBuf;
+
+ RTStrFree(pszBuf);
+
+ /* If overflow double the buffer. */
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ cbBuf *= 2;
+ } while (cbBuf < _64M);
+
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTEnvDupEx);
+
diff --git a/src/VBox/Runtime/generic/RTFileCopy-generic.cpp b/src/VBox/Runtime/generic/RTFileCopy-generic.cpp
new file mode 100644
index 00000000..efcc2982
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopy-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTFileCopy-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopy, generic implementation.
+ */
+
+/*
+ * 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/file.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int) RTFileCopy(const char *pszSrc, const char *pszDst)
+{
+ return RTFileCopyEx(pszSrc, pszDst, 0, NULL, NULL);
+}
+RT_EXPORT_SYMBOL(RTFileCopy);
+
diff --git a/src/VBox/Runtime/generic/RTFileCopyAttributes-generic.cpp b/src/VBox/Runtime/generic/RTFileCopyAttributes-generic.cpp
new file mode 100644
index 00000000..6f431db8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopyAttributes-generic.cpp
@@ -0,0 +1,72 @@
+/* $Id: RTFileCopyAttributes-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopyAttributes, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2019-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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileCopyAttributes(RTFILE hFileSrc, RTFILE hFileDst, uint32_t fFlags)
+{
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+
+ RTFSOBJINFO ObjInfo;
+ int rc = RTFileQueryInfo(hFileSrc, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * The file mode.
+ */
+ rc = RTFileSetMode(hFileDst, ObjInfo.Attr.fMode);
+ if (RT_FAILURE(rc) && (ObjInfo.Attr.fMode & (RTFS_UNIX_ALL_ACCESS_PERMS & ~RTFS_UNIX_ALL_ACCESS_PERMS)))
+ rc = RTFileSetMode(hFileDst, ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS & ~RTFS_UNIX_ALL_ACCESS_PERMS));
+
+ /*
+ * As many of the timestamps as we are able to, except the birth time.
+ * Ignoring the status here because of hardend builds on older linux
+ * systems (see RTFileSetTimes).
+ */
+ RTFileSetTimes(hFileDst, &ObjInfo.AccessTime, &ObjInfo.ModificationTime, &ObjInfo.ChangeTime, NULL /*pBirthTime*/);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileCopyByHandlesEx-generic.cpp b/src/VBox/Runtime/generic/RTFileCopyByHandlesEx-generic.cpp
new file mode 100644
index 00000000..11e0aa31
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopyByHandlesEx-generic.cpp
@@ -0,0 +1,171 @@
+/* $Id: RTFileCopyByHandlesEx-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopyByHandlesEx, generic implementation.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileCopyByHandlesEx(RTFILE hFileSrc, RTFILE hFileDst, PFNRTPROGRESS pfnProgress, void *pvUser)
+{
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(RTFileIsValid(hFileSrc), ("hFileSrc=%RTfile\n", hFileSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTFileIsValid(hFileDst), ("hFileDst=%RTfile\n", hFileDst), VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
+
+ /*
+ * Save file offset.
+ */
+ uint64_t offSrcSaved;
+ int rc = RTFileSeek(hFileSrc, 0, RTFILE_SEEK_CURRENT, &offSrcSaved);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Get the file size and figure out how much we'll copy at a time.
+ */
+ uint64_t cbSrc;
+ rc = RTFileQuerySize(hFileSrc, &cbSrc);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t cbChunk = cbSrc;
+ if (pfnProgress && cbSrc > _1M)
+ {
+ cbChunk /= 100;
+ if (cbChunk > _64M)
+ cbChunk = RT_ALIGN_64(cbChunk, _2M);
+ else
+ cbChunk = RT_ALIGN_64(cbChunk, _128K);
+ }
+
+ /*
+ * Prepare buffers.
+ */
+ RTFILECOPYPARTBUFSTATE BufState;
+ rc = RTFileCopyPartPrep(&BufState, cbChunk);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Prepare the destination file.
+ */
+ uint64_t cbDst;
+ rc = RTFileQuerySize(hFileDst, &cbDst);
+ if (RT_SUCCESS(rc) && cbDst > cbSrc)
+ rc = RTFileSetSize(hFileDst, cbSrc);
+ if (RT_SUCCESS(rc) && cbDst < cbSrc)
+ {
+ rc = RTFileSetAllocationSize(hFileDst, cbSrc, RTFILE_ALLOC_SIZE_F_DEFAULT);
+ if (rc == VERR_NOT_SUPPORTED)
+ rc = RTFileSetSize(hFileDst, cbSrc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy loop that works till we reach EOF.
+ */
+ RTFOFF off = 0;
+ RTFOFF cbPercent = cbSrc / 100;
+ RTFOFF offNextPercent = pfnProgress ? cbPercent : RTFOFF_MAX;
+ unsigned uPercentage = pfnProgress ? 0 : 100;
+ for (;;)
+ {
+ /*
+ * Copy a block.
+ */
+ uint64_t cbCopied = 0;
+ rc = RTFileCopyPartEx(hFileSrc, off, hFileDst, off, cbChunk, 0 /*fFlags*/, &BufState, &cbCopied);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbCopied == 0)
+ {
+ /*
+ * We reached the EOF. Complete the copy operation.
+ */
+ rc = RTFileSetSize(hFileDst, off);
+ if (RT_SUCCESS(rc))
+ rc = RTFileCopyAttributes(hFileSrc, hFileDst, 0);
+ break;
+ }
+
+ /*
+ * Advance and work the progress callback.
+ */
+ off += cbCopied;
+ if ( off >= offNextPercent
+ && pfnProgress
+ && uPercentage < 99)
+ {
+ do
+ {
+ uPercentage++;
+ offNextPercent += cbPercent;
+ } while ( offNextPercent <= off
+ && uPercentage < 99);
+ rc = pfnProgress(uPercentage, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ }
+
+ RTFileCopyPartCleanup(&BufState);
+
+ /*
+ * 100%.
+ */
+ if ( pfnProgress
+ && RT_SUCCESS(rc))
+ rc = pfnProgress(100, pvUser);
+ }
+
+ /*
+ * Restore source position.
+ */
+ RTFileSeek(hFileSrc, offSrcSaved, RTFILE_SEEK_BEGIN, NULL);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileCopyEx-generic.cpp b/src/VBox/Runtime/generic/RTFileCopyEx-generic.cpp
new file mode 100644
index 00000000..cc20b4b8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopyEx-generic.cpp
@@ -0,0 +1,97 @@
+/* $Id: RTFileCopyEx-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopyEx, generic implementation.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileCopyEx(const char *pszSrc, const char *pszDst, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("pszSrc=%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszDst, ("pszDst=%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fFlags & ~RTFILECOPY_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Open the files.
+ */
+ RTFILE FileSrc;
+ int rc = RTFileOpen(&FileSrc, pszSrc,
+ RTFILE_O_READ | RTFILE_O_OPEN
+ | (fFlags & RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE FileDst;
+ rc = RTFileOpen(&FileDst, pszDst,
+ RTFILE_O_WRITE | RTFILE_O_CREATE
+ | (fFlags & RTFILECOPY_FLAGS_NO_DST_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Call the ByHandles version and let it do the job.
+ */
+ rc = RTFileCopyByHandlesEx(FileSrc, FileDst, pfnProgress, pvUser);
+
+ /*
+ * Close the files regardless of the result.
+ * Don't bother cleaning up or anything like that.
+ */
+ int rc2 = RTFileClose(FileDst);
+ AssertRC(rc2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ int rc2 = RTFileClose(FileSrc);
+ AssertRC(rc2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileCopyPart-generic.cpp b/src/VBox/Runtime/generic/RTFileCopyPart-generic.cpp
new file mode 100644
index 00000000..382db0f0
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopyPart-generic.cpp
@@ -0,0 +1,60 @@
+/* $Id: RTFileCopyPart-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopyPart, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2019-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/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileCopyPart(RTFILE hFileSrc, RTFOFF offSrc, RTFILE hFileDst, RTFOFF offDst, uint64_t cbToCopy,
+ uint32_t fFlags, uint64_t *pcbCopied)
+{
+ RTFILECOPYPARTBUFSTATE BufState;
+ int rc = RTFileCopyPartPrep(&BufState, cbToCopy);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileCopyPartEx(hFileSrc, offSrc, hFileDst, offDst, cbToCopy, fFlags, &BufState, pcbCopied);
+ RTFileCopyPartCleanup(&BufState);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileCopyPart);
+
diff --git a/src/VBox/Runtime/generic/RTFileCopyPartEx-generic.cpp b/src/VBox/Runtime/generic/RTFileCopyPartEx-generic.cpp
new file mode 100644
index 00000000..48e52954
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileCopyPartEx-generic.cpp
@@ -0,0 +1,166 @@
+/* $Id: RTFileCopyPartEx-generic.cpp $ */
+/** @file
+ * IPRT - RTFileCopyPartEx, generic implementation.
+ */
+
+/*
+ * 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/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+
+
+#ifndef IPRT_FALLBACK_VERSION
+RTDECL(int) RTFileCopyPartPrep(PRTFILECOPYPARTBUFSTATE pBufState, uint64_t cbToCopy)
+#else
+static int rtFileCopyPartPrepFallback(PRTFILECOPYPARTBUFSTATE pBufState, uint64_t cbToCopy)
+#endif
+{
+ /*
+ * Allocate a fitting buffer.
+ *
+ * We're a little careful with using RTMemPageAlloc here as it does require
+ * kernel calls on some hosts. If we could be sure the heap was able to
+ * handle it most of the time, we would be a lot more aggressive.
+ */
+ size_t cbBuf = 0;
+ void *pvBuf = NULL;
+ if ( cbToCopy >= _512K
+ && (pvBuf = RTMemPageAlloc(cbBuf = _128K)) != NULL)
+ pBufState->iAllocType = 2;
+ else if ( cbToCopy >= _128K
+ && (pvBuf = RTMemTmpAlloc(cbBuf = _128K)) != NULL)
+ pBufState->iAllocType = 1;
+ else if ( cbToCopy < _128K
+ && cbToCopy >= _4K
+ && (pvBuf = RTMemTmpAlloc(cbBuf = RT_ALIGN_Z(cbToCopy, 32))) != NULL)
+ pBufState->iAllocType = 1;
+ else if ( cbToCopy >= _4K
+ && (pvBuf = RTMemTmpAlloc(cbBuf = _4K)) != NULL)
+ pBufState->iAllocType = 1;
+ else
+ pBufState->iAllocType = 0;
+ pBufState->pbBuf = (uint8_t *)pvBuf;
+ pBufState->cbBuf = cbBuf;
+ pBufState->uMagic = RTFILECOPYPARTBUFSTATE_MAGIC;
+ return VINF_SUCCESS;
+}
+
+
+#ifndef IPRT_FALLBACK_VERSION
+RTDECL(void) RTFileCopyPartCleanup(PRTFILECOPYPARTBUFSTATE pBufState)
+#else
+static void rtFileCopyPartCleanupFallback(PRTFILECOPYPARTBUFSTATE pBufState)
+#endif
+{
+ AssertReturnVoid(pBufState->uMagic == RTFILECOPYPARTBUFSTATE_MAGIC);
+ if (pBufState->iAllocType == 1)
+ RTMemTmpFree(pBufState->pbBuf);
+ else if (pBufState->iAllocType == 2)
+ RTMemPageFree(pBufState->pbBuf, pBufState->cbBuf);
+ pBufState->pbBuf = NULL;
+ pBufState->cbBuf = 0;
+ pBufState->uMagic = ~RTFILECOPYPARTBUFSTATE_MAGIC;
+}
+
+
+#ifndef IPRT_FALLBACK_VERSION
+RTDECL(int) RTFileCopyPartEx(RTFILE hFileSrc, RTFOFF offSrc, RTFILE hFileDst, RTFOFF offDst, uint64_t cbToCopy,
+ uint32_t fFlags, PRTFILECOPYPARTBUFSTATE pBufState, uint64_t *pcbCopied)
+#else
+static int rtFileCopyPartExFallback(RTFILE hFileSrc, RTFOFF offSrc, RTFILE hFileDst, RTFOFF offDst, uint64_t cbToCopy,
+ uint32_t fFlags, PRTFILECOPYPARTBUFSTATE pBufState, uint64_t *pcbCopied)
+#endif
+{
+ /*
+ * Validate input.
+ */
+ if (pcbCopied)
+ *pcbCopied = 0;
+ AssertReturn(offSrc >= 0, VERR_NEGATIVE_SEEK);
+ AssertReturn(offDst >= 0, VERR_NEGATIVE_SEEK);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+ AssertReturn(pBufState->uMagic == RTFILECOPYPARTBUFSTATE_MAGIC, VERR_INVALID_FLAGS);
+
+ /*
+ * If nothing to copy, return right away.
+ */
+ if (!cbToCopy)
+ return VINF_SUCCESS;
+
+ if (pBufState->iAllocType == 0)
+ {
+ pBufState->cbBuf = (size_t)RT_MIN(_4K, cbToCopy);
+ pBufState->pbBuf = (uint8_t *)alloca(pBufState->cbBuf);
+ AssertReturn(pBufState->pbBuf, VERR_NO_MEMORY);
+ }
+
+ /*
+ * Do the copying.
+ */
+ uint64_t cbCopied = 0;
+ int rc = VINF_SUCCESS;
+ do
+ {
+ size_t cbThisCopy = (size_t)RT_MIN(cbToCopy - cbCopied, pBufState->cbBuf);
+ size_t cbActual = 0;
+ rc = RTFileReadAt(hFileSrc, offSrc + cbCopied, pBufState->pbBuf, cbThisCopy, &cbActual);
+ if (RT_FAILURE(rc))
+ break;
+ if (cbActual == 0)
+ {
+ if (!pcbCopied)
+ rc = VERR_EOF;
+ break;
+ }
+
+ rc = RTFileWriteAt(hFileDst, offDst + cbCopied, pBufState->pbBuf, cbActual, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ cbCopied += cbActual;
+ } while (cbCopied < cbToCopy);
+
+ if (pcbCopied)
+ *pcbCopied = cbCopied;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileExists-generic.cpp b/src/VBox/Runtime/generic/RTFileExists-generic.cpp
new file mode 100644
index 00000000..c3e22579
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileExists-generic.cpp
@@ -0,0 +1,60 @@
+/* $Id: RTFileExists-generic.cpp $ */
+/** @file
+ * IPRT - RTFileExists, generic implementation.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal/iprt.h"
+
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+
+
+RTDECL(bool) RTFileExists(const char *pszPath)
+{
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ bool fRc = RT_SUCCESS(rc)
+ && RTFS_IS_FILE(ObjInfo.Attr.fMode)
+ && !(ObjInfo.Attr.fMode & (RTFS_DOS_NT_DEVICE | RTFS_DOS_NT_REPARSE_POINT)); /* paranoia */
+ LogFlow(("RTFileExists(%p:{%s}): returns %RTbool (%Rrc)\n", pszPath, pszPath, fRc, rc));
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileMove-generic.cpp b/src/VBox/Runtime/generic/RTFileMove-generic.cpp
new file mode 100644
index 00000000..3adae09f
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileMove-generic.cpp
@@ -0,0 +1,124 @@
+/* $Id: RTFileMove-generic.cpp $ */
+/** @file
+ * IPRT - RTFileMove, Generic.
+ */
+
+/*
+ * 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_FILE
+#include <iprt/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+
+
+RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER);
+
+ /*
+ * Try RTFileRename first.
+ */
+ Assert(RTPATHRENAME_FLAGS_REPLACE == RTFILEMOVE_FLAGS_REPLACE);
+ unsigned fRename = fMove;
+ int rc = RTFileRename(pszSrc, pszDst, fRename);
+ if (rc == VERR_NOT_SAME_DEVICE)
+ {
+ const char *pszDelete = NULL;
+
+ /*
+ * The source and target are not on the same device, darn.
+ * We'll try open both ends and perform a copy.
+ */
+ RTFILE FileSrc;
+ rc = RTFileOpen(&FileSrc, pszSrc, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE FileDst;
+ rc = RTFileOpen(&FileDst, pszDst, RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE_REPLACE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileCopyByHandles(FileSrc, FileDst);
+ if (RT_SUCCESS(rc))
+ pszDelete = pszSrc;
+ else
+ {
+ pszDelete = pszDst;
+ Log(("RTFileMove('%s', '%s', %#x): copy failed, rc=%Rrc\n",
+ pszSrc, pszDst, fMove, rc));
+ }
+
+ /* try delete without closing, and could perhaps avoid some trouble */
+ int rc2 = RTFileDelete(pszDelete);
+ if (RT_SUCCESS(rc2))
+ pszDelete = NULL;
+ RTFileClose(FileDst);
+ }
+ else
+ Log(("RTFileMove('%s', '%s', %#x): failed to create destination, rc=%Rrc\n",
+ pszSrc, pszDst, fMove, rc));
+ RTFileClose(FileSrc);
+ }
+ else
+ Log(("RTFileMove('%s', '%s', %#x): failed to open source, rc=%Rrc\n",
+ pszSrc, pszDst, fMove, rc));
+
+ /* if we failed to close it while open, close it now */
+ if (pszDelete)
+ {
+ int rc2 = RTFileDelete(pszDelete);
+ if (RT_FAILURE(rc2))
+ Log(("RTFileMove('%s', '%s', %#x): failed to delete '%s', rc2=%Rrc (rc=%Rrc)\n",
+ pszSrc, pszDst, fMove, pszDelete, rc2, rc));
+ }
+ }
+
+ LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fMove, rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileMove);
+
diff --git a/src/VBox/Runtime/generic/RTFileQuerySectorSize-generic.cpp b/src/VBox/Runtime/generic/RTFileQuerySectorSize-generic.cpp
new file mode 100644
index 00000000..9ee4e9d2
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileQuerySectorSize-generic.cpp
@@ -0,0 +1,55 @@
+/* $Id: RTFileQuerySectorSize-generic.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, generic.
+ */
+
+/*
+ * Copyright (C) 2020-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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
+ AssertFailedReturn(VERR_INVALID_FUNCTION);
+}
+
diff --git a/src/VBox/Runtime/generic/RTFileQuerySize-generic.cpp b/src/VBox/Runtime/generic/RTFileQuerySize-generic.cpp
new file mode 100644
index 00000000..d62fd569
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileQuerySize-generic.cpp
@@ -0,0 +1,70 @@
+/* $Id: RTFileQuerySize-generic.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySize, generic implementation.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal/iprt.h"
+
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+
+
+RTDECL(int) RTFileQuerySizeByPath(const char *pszPath, uint64_t *pcbFile)
+{
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
+ {
+ *pcbFile = ObjInfo.cbObject;
+ LogFlow(("RTFileQuerySize(%p:{%s}): returns %Rrc (%#RX64)\n", pszPath, pszPath, rc, *pcbFile));
+ return rc;
+ }
+
+ if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ rc = VERR_IS_A_DIRECTORY;
+ else
+ rc = VERR_FILE_NOT_FOUND; /** @todo VERR_NOT_A_FILE... */
+ }
+ LogFlow(("RTFileQuerySize(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc));
+ return rc;
+}
diff --git a/src/VBox/Runtime/generic/RTFileReadAll-generic.cpp b/src/VBox/Runtime/generic/RTFileReadAll-generic.cpp
new file mode 100644
index 00000000..8561ee43
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileReadAll-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTFileReadAll-generic.cpp $ */
+/** @file
+ * IPRT - RTFileReadAll, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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/file.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int) RTFileReadAll(const char *pszFilename, void **ppvFile, size_t *pcbFile)
+{
+ return RTFileReadAllEx(pszFilename, 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_NONE, ppvFile, pcbFile);
+}
+RT_EXPORT_SYMBOL(RTFileReadAll);
+
diff --git a/src/VBox/Runtime/generic/RTFileReadAllByHandle-generic.cpp b/src/VBox/Runtime/generic/RTFileReadAllByHandle-generic.cpp
new file mode 100644
index 00000000..1e859ff8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileReadAllByHandle-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTFileReadAllByHandle-generic.cpp $ */
+/** @file
+ * IPRT - RTFileReadAllByHandle, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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/file.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int) RTFileReadAllByHandle(RTFILE File, void **ppvFile, size_t *pcbFile)
+{
+ return RTFileReadAllByHandleEx(File, 0, RTFOFF_MAX, 0, ppvFile, pcbFile);
+}
+RT_EXPORT_SYMBOL(RTFileReadAllByHandle);
+
diff --git a/src/VBox/Runtime/generic/RTFileReadAllByHandleEx-generic.cpp b/src/VBox/Runtime/generic/RTFileReadAllByHandleEx-generic.cpp
new file mode 100644
index 00000000..7202ab75
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileReadAllByHandleEx-generic.cpp
@@ -0,0 +1,127 @@
+/* $Id: RTFileReadAllByHandleEx-generic.cpp $ */
+/** @file
+ * IPRT - RTFileReadAllByHandleEx, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileReadAllByHandleEx(RTFILE File, RTFOFF off, RTFOFF cbMax, uint32_t fFlags, void **ppvFile, size_t *pcbFile)
+{
+ AssertReturn(!(fFlags & ~RTFILE_RDALL_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Save the current offset first.
+ */
+ RTFOFF offOrg;
+ int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, (uint64_t *)&offOrg);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the file size, adjust it and check that it might fit into memory.
+ */
+ RTFOFF cbFile;
+ AssertCompile(sizeof(cbFile) == sizeof(uint64_t));
+ rc = RTFileSeek(File, 0, RTFILE_SEEK_END, (uint64_t *)&cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ RTFOFF cbAllocFile = cbFile > off ? cbFile - off : 0;
+ if (cbAllocFile <= cbMax)
+ { /* likely */ }
+ else if (!(fFlags & RTFILE_RDALL_F_FAIL_ON_MAX_SIZE))
+ cbAllocFile = cbMax;
+ else
+ rc = VERR_OUT_OF_RANGE;
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbAllocMem = (size_t)cbAllocFile;
+ if ((RTFOFF)cbAllocMem == cbAllocFile)
+ {
+ /*
+ * Try allocate the required memory and initialize the header (hardcoded fun).
+ */
+ void *pvHdr = RTMemAlloc(cbAllocMem + 32 + (fFlags & RTFILE_RDALL_F_TRAILING_ZERO_BYTE ? 1 : 0));
+ if (pvHdr)
+ {
+ memset(pvHdr, 0xff, 32);
+ *(size_t *)pvHdr = cbAllocMem;
+
+ /*
+ * Seek and read.
+ */
+ rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvFile = (uint8_t *)pvHdr + 32;
+ rc = RTFileRead(File, pvFile, cbAllocMem, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTFILE_RDALL_F_TRAILING_ZERO_BYTE)
+ ((uint8_t *)pvFile)[cbAllocFile] = '\0';
+
+ /*
+ * Success - fill in the return values.
+ */
+ *ppvFile = pvFile;
+ *pcbFile = cbAllocMem;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pvHdr);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_TOO_MUCH_DATA;
+ }
+ }
+ /* restore the position. */
+ RTFileSeek(File, offOrg, RTFILE_SEEK_BEGIN, NULL);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileReadAllByHandleEx);
+
diff --git a/src/VBox/Runtime/generic/RTFileReadAllEx-generic.cpp b/src/VBox/Runtime/generic/RTFileReadAllEx-generic.cpp
new file mode 100644
index 00000000..6557153b
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileReadAllEx-generic.cpp
@@ -0,0 +1,62 @@
+/* $Id: RTFileReadAllEx-generic.cpp $ */
+/** @file
+ * IPRT - RTFileReadAllEx, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTFileReadAllEx(const char *pszFilename, RTFOFF off, RTFOFF cbMax, uint32_t fFlags, void **ppvFile, size_t *pcbFile)
+{
+ AssertReturn(!(fFlags & ~RTFILE_RDALL_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ RTFILE File;
+ int rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | (fFlags & RTFILE_RDALL_O_DENY_MASK));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileReadAllByHandleEx(File, off, cbMax, fFlags, ppvFile, pcbFile);
+ RTFileClose(File);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileReadAllEx);
+
diff --git a/src/VBox/Runtime/generic/RTFileReadAllFree-generic.cpp b/src/VBox/Runtime/generic/RTFileReadAllFree-generic.cpp
new file mode 100644
index 00000000..0151b4f8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileReadAllFree-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTFileReadAllFree-generic.cpp $ */
+/** @file
+ * IPRT - RTFileReadAllFree, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTFileReadAllFree(void *pvFile, size_t cbFile)
+{
+ AssertPtrReturnVoid(pvFile);
+
+ /*
+ * We've got a 32 byte header to make sure the caller isn't screwing us.
+ * It's all hardcoded fun for now...
+ */
+ pvFile = (void *)((uintptr_t)pvFile - 32);
+ Assert(*(size_t *)pvFile == cbFile); RT_NOREF_PV(cbFile);
+ *(size_t *)pvFile = ~(size_t)1;
+
+ RTMemFree(pvFile);
+}
+RT_EXPORT_SYMBOL(RTFileReadAllFree);
+
diff --git a/src/VBox/Runtime/generic/RTFileSetAllocationSize-generic.cpp b/src/VBox/Runtime/generic/RTFileSetAllocationSize-generic.cpp
new file mode 100644
index 00000000..6a4a9259
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTFileSetAllocationSize-generic.cpp
@@ -0,0 +1,61 @@
+/* $Id: RTFileSetAllocationSize-generic.cpp $ */
+/** @file
+ * IPRT - RTFileSetAllocationSize, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2016-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/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+
+#include "internal/iprt.h"
+
+
+RTDECL(int) RTFileSetAllocationSize(RTFILE hFile, uint64_t cbSize, uint32_t fFlags)
+{
+ /*
+ * Quick validation.
+ */
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTFILE_ALLOC_SIZE_F_VALID), VERR_INVALID_PARAMETER);
+
+ NOREF(cbSize);
+
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTFileSetAllocationSize);
+
diff --git a/src/VBox/Runtime/generic/RTLogDefaultInit-generic.cpp b/src/VBox/Runtime/generic/RTLogDefaultInit-generic.cpp
new file mode 100644
index 00000000..aedae6d3
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogDefaultInit-generic.cpp
@@ -0,0 +1,49 @@
+/* $Id: RTLogDefaultInit-generic.cpp $ */
+/** @file
+ * IPRT - Default Log Init, Generic Dummy.
+ */
+
+/*
+ * 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/log.h>
+
+
+RTDECL(PRTLOGGER) RTLogDefaultInit(void)
+{
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTLogDefaultInit);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteDebugger-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteDebugger-generic.cpp
new file mode 100644
index 00000000..e10dda7c
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteDebugger-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteDebugger-generic.cpp $ */
+/** @file
+ * IPRT - Log To Debugger, Generic Dummy.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ NOREF(pch);
+ NOREF(cb);
+ return;
+}
+RT_EXPORT_SYMBOL(RTLogWriteDebugger);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteStdErr-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteStdErr-generic.cpp
new file mode 100644
index 00000000..8554f4fa
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteStdErr-generic.cpp
@@ -0,0 +1,51 @@
+/* $Id: RTLogWriteStdErr-generic.cpp $ */
+/** @file
+ * IPRT - Log To StdErr, Generic.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+#include <stdio.h>
+
+RTDECL(void) RTLogWriteStdErr(const char *pch, size_t cb)
+{
+ size_t cbWritten = fwrite(pch, 1, cb, stderr);
+ (void)cbWritten;
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdErr);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp
new file mode 100644
index 00000000..b4d9ade3
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteStdErr-stub-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteStdErr-stub-generic.cpp $ */
+/** @file
+ * IPRT - Log To StdErr, Generic Dummy.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+
+
+RTDECL(void) RTLogWriteStdErr(const char *pch, size_t cb)
+{
+ NOREF(pch);
+ NOREF(cb);
+ return;
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdErr);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteStdOut-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteStdOut-generic.cpp
new file mode 100644
index 00000000..1a72542d
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteStdOut-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTLogWriteStdOut-generic.cpp $ */
+/** @file
+ * IPRT - Log To StdOut, Generic.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+#include <stdio.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ size_t cbWritten = fwrite(pch, 1, cb, stdout);
+ (void)cbWritten;
+ fflush(stdout);
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdOut);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp
new file mode 100644
index 00000000..342cf97d
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteStdOut-stub-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteStdOut-stub-generic.cpp $ */
+/** @file
+ * IPRT - Log To StdOut, Generic Dummy.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ NOREF(pch);
+ NOREF(cb);
+ return;
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdOut);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp
new file mode 100644
index 00000000..3694e81b
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteUser-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteUser-generic.cpp $ */
+/** @file
+ * IPRT - Log User Specific Output, Generic Dummy.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+
+
+RTDECL(void) RTLogWriteUser(const char *pch, size_t cb)
+{
+ NOREF(pch);
+ NOREF(cb);
+ return;
+}
+RT_EXPORT_SYMBOL(RTLogWriteUser);
+
diff --git a/src/VBox/Runtime/generic/RTLogWriteVmm-stub-generic.cpp b/src/VBox/Runtime/generic/RTLogWriteVmm-stub-generic.cpp
new file mode 100644
index 00000000..e9542dcb
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTLogWriteVmm-stub-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTLogWriteVmm-stub-generic.cpp $ */
+/** @file
+ * IPRT - Log to VMM, stub.
+ */
+
+/*
+ * 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/log.h>
+#include "internal/iprt.h"
+
+
+RTDECL(void) RTLogWriteVmm(const char *pch, size_t cb, bool fRelease)
+{
+ RT_NOREF(pch, cb, fRelease);
+}
+RT_EXPORT_SYMBOL(RTLogWriteVmm);
+
diff --git a/src/VBox/Runtime/generic/RTMpCpuId-generic.cpp b/src/VBox/Runtime/generic/RTMpCpuId-generic.cpp
new file mode 100644
index 00000000..299244b8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpCpuId-generic.cpp
@@ -0,0 +1,58 @@
+/* $Id: RTMpCpuId-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpCpuId.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) || defined(RT_ARCH_SPARC) || defined(RT_ARCH_SPARC64)
+ return ASMGetApicId();
+#else
+# error "Not ported to this architecture."
+ return NIL_RTAPICID;
+#endif
+}
+RT_EXPORT_SYMBOL(RTMpCpuId);
+
diff --git a/src/VBox/Runtime/generic/RTMpCpuIdFromSetIndex-generic.cpp b/src/VBox/Runtime/generic/RTMpCpuIdFromSetIndex-generic.cpp
new file mode 100644
index 00000000..58e94050
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpCpuIdFromSetIndex-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpCpuIdFromSetIndex-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpCpuIdFromSetIndex.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return iCpu >= 0 ? iCpu : NIL_RTCPUID;
+}
+RT_EXPORT_SYMBOL(RTMpCpuIdFromSetIndex);
+
diff --git a/src/VBox/Runtime/generic/RTMpCpuIdToSetIndex-generic.cpp b/src/VBox/Runtime/generic/RTMpCpuIdToSetIndex-generic.cpp
new file mode 100644
index 00000000..58b3c433
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpCpuIdToSetIndex-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTMpCpuIdToSetIndex-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpCpuIdToSetIndex.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu != NIL_RTCPUID ? (int) idCpu : -1;
+}
+RT_EXPORT_SYMBOL(RTMpCpuIdToSetIndex);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetArraySize-generic.cpp b/src/VBox/Runtime/generic/RTMpGetArraySize-generic.cpp
new file mode 100644
index 00000000..2e61c071
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetArraySize-generic.cpp
@@ -0,0 +1,72 @@
+/* $Id: RTMpGetArraySize-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetArraySize.
+ */
+
+/*
+ * Copyright (C) 2010-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/cpuset.h>
+
+
+RTDECL(uint32_t) RTMpGetArraySize(void)
+{
+ /*
+ * Cache the result here. This whole point of this function is that it
+ * will always return the same value, so that should be safe.
+ *
+ * Note! Because RTCPUSET may be to small to represent all the CPUs, we
+ * check with RTMpGetCount() as well.
+ */
+ static uint32_t s_cMaxCpus = 0;
+ uint32_t cCpus = s_cMaxCpus;
+ if (RT_UNLIKELY(cCpus == 0))
+ {
+ RTCPUSET CpuSet;
+ uint32_t cCpus1 = RTCpuLastIndex(RTMpGetSet(&CpuSet)) + 1;
+ uint32_t cCpus2 = RTMpGetCount();
+ cCpus = RT_MAX(cCpus1, cCpus2);
+ ASMAtomicCmpXchgU32(&s_cMaxCpus, cCpus, 0);
+ return cCpus;
+ }
+ return s_cMaxCpus;
+
+}
+RT_EXPORT_SYMBOL(RTMpGetArraySize);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetCoreCount-generic.cpp b/src/VBox/Runtime/generic/RTMpGetCoreCount-generic.cpp
new file mode 100644
index 00000000..a44f1b8b
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetCoreCount-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpGetCoreCount-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetCoreCount.
+ */
+
+/*
+ * Copyright (C) 2013-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ return RTMpGetCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetCoreCount);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetCount-generic.cpp b/src/VBox/Runtime/generic/RTMpGetCount-generic.cpp
new file mode 100644
index 00000000..e1ade25c
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetCount-generic.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTMpGetCount-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetCount.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+RT_EXPORT_SYMBOL(RTMpGetCount);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetCurFrequency-generic.cpp b/src/VBox/Runtime/generic/RTMpGetCurFrequency-generic.cpp
new file mode 100644
index 00000000..992b16d7
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetCurFrequency-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTMpGetCurFrequency-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetCurFrequency.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ NOREF(idCpu);
+ /* this is a generic stub which returns "unknown". */
+ return 0;
+}
+RT_EXPORT_SYMBOL(RTMpGetCurFrequency);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetDescription-generic-stub.cpp b/src/VBox/Runtime/generic/RTMpGetDescription-generic-stub.cpp
new file mode 100644
index 00000000..6604333e
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetDescription-generic-stub.cpp
@@ -0,0 +1,64 @@
+/* $Id: RTMpGetDescription-generic-stub.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetDescription stub.
+ */
+
+/*
+ * 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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/string.h>
+#include <iprt/err.h>
+
+
+RTDECL(int) RTMpGetDescription(RTCPUID idCpu, char *pszBuf, size_t cbBuf)
+{
+ static const char s_szUnknown[] = "Unknown";
+
+ if (idCpu != NIL_RTCPUID && !RTMpIsCpuOnline(idCpu))
+ return RTMpIsCpuPossible(idCpu)
+ ? VERR_CPU_OFFLINE
+ : VERR_CPU_NOT_FOUND;
+
+ if (cbBuf < sizeof(s_szUnknown))
+ return VERR_BUFFER_OVERFLOW;
+
+ memcpy(pszBuf, s_szUnknown, sizeof(s_szUnknown));
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpGetDescription);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetDescription-generic.cpp b/src/VBox/Runtime/generic/RTMpGetDescription-generic.cpp
new file mode 100644
index 00000000..efbe5e16
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetDescription-generic.cpp
@@ -0,0 +1,124 @@
+/* $Id: RTMpGetDescription-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetDescription.
+ */
+
+/*
+ * 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/mp.h>
+#include "internal/iprt.h"
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+
+
+/**
+ * Returns "Unknown" as description.
+ *
+ * @returns VERR_BUFFER_OVERFLOW or VINF_SUCCESS.
+ * @param pszBuf Output buffer.
+ * @param cbBuf Buffer size.
+ */
+static int rtMpGetDescriptionUnknown(char *pszBuf, size_t cbBuf)
+{
+ static const char s_szUnknown[] = "Unknown";
+ if (cbBuf < sizeof(s_szUnknown))
+ return VERR_BUFFER_OVERFLOW;
+ memcpy(pszBuf, s_szUnknown, sizeof(s_szUnknown));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTMpGetDescription(RTCPUID idCpu, char *pszBuf, size_t cbBuf)
+{
+ /*
+ * Check that the specified cpu is valid & online.
+ */
+ if (idCpu != NIL_RTCPUID && !RTMpIsCpuOnline(idCpu))
+ return RTMpIsCpuPossible(idCpu)
+ ? VERR_CPU_OFFLINE
+ : VERR_CPU_NOT_FOUND;
+
+ /*
+ * Construct the description string in a temporary buffer.
+ */
+ char szString[4*4*3+1];
+ RT_ZERO(szString);
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ if (!ASMHasCpuId())
+ return rtMpGetDescriptionUnknown(pszBuf, cbBuf);
+
+ uint32_t uMax;
+ uint32_t uEBX, uECX, uEDX;
+ ASMCpuId(0x80000000, &uMax, &uEBX, &uECX, &uEDX);
+ if (uMax >= 0x80000002)
+ {
+ ASMCpuId(0x80000002, &szString[0 + 0], &szString[0 + 4], &szString[0 + 8], &szString[0 + 12]);
+ if (uMax >= 0x80000003)
+ ASMCpuId(0x80000003, &szString[16 + 0], &szString[16 + 4], &szString[16 + 8], &szString[16 + 12]);
+ if (uMax >= 0x80000004)
+ ASMCpuId(0x80000004, &szString[32 + 0], &szString[32 + 4], &szString[32 + 8], &szString[32 + 12]);
+ }
+ else
+ {
+ ASMCpuId(0x00000000, &uMax, &uEBX, &uECX, &uEDX);
+ ((uint32_t *)&szString[0])[0] = uEBX;
+ ((uint32_t *)&szString[0])[1] = uEDX;
+ ((uint32_t *)&szString[0])[2] = uECX;
+ }
+
+#elif defined(RT_ARCH_ARM64)
+ RTCCINTREG uFreq;
+
+#else
+# error "PORTME or use RTMpGetDescription-generic-stub.cpp."
+#endif
+
+ /*
+ * Copy it out into the buffer supplied by the caller.
+ */
+ char *pszSrc = RTStrStrip(szString);
+ size_t cchSrc = strlen(pszSrc);
+ if (cchSrc >= cbBuf)
+ return VERR_BUFFER_OVERFLOW;
+ memcpy(pszBuf, pszSrc, cchSrc + 1);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpGetDescription);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetMaxCpuId-generic.cpp b/src/VBox/Runtime/generic/RTMpGetMaxCpuId-generic.cpp
new file mode 100644
index 00000000..921acdd1
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetMaxCpuId-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpGetMaxCpuId-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetMaxCpuId.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return RTMpCpuId();
+}
+RT_EXPORT_SYMBOL(RTMpGetMaxCpuId);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetMaxFrequency-generic.cpp b/src/VBox/Runtime/generic/RTMpGetMaxFrequency-generic.cpp
new file mode 100644
index 00000000..e9a36c7a
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetMaxFrequency-generic.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTMpGetMaxFrequency-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetMaxFrequency.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ NOREF(idCpu);
+ /* this is a generic stub which returns "unknown". */
+ return 0;
+}
+RT_EXPORT_SYMBOL(RTMpGetMaxFrequency);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetOnlineCoreCount-generic.cpp b/src/VBox/Runtime/generic/RTMpGetOnlineCoreCount-generic.cpp
new file mode 100644
index 00000000..050c630b
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetOnlineCoreCount-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpGetOnlineCoreCount-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetCoreCount.
+ */
+
+/*
+ * Copyright (C) 2013-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ return RTMpGetOnlineCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetOnlineCoreCount);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetOnlineCount-generic.cpp b/src/VBox/Runtime/generic/RTMpGetOnlineCount-generic.cpp
new file mode 100644
index 00000000..974dd009
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetOnlineCount-generic.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTMpGetOnlineCount-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetOnlineCount.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+RT_EXPORT_SYMBOL(RTMpGetOnlineCount);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetOnlineSet-generic.cpp b/src/VBox/Runtime/generic/RTMpGetOnlineSet-generic.cpp
new file mode 100644
index 00000000..9b7bea9e
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetOnlineSet-generic.cpp
@@ -0,0 +1,61 @@
+/* $Id: RTMpGetOnlineSet-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetOnlineSet.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+RT_EXPORT_SYMBOL(RTMpGetOnlineSet);
+
diff --git a/src/VBox/Runtime/generic/RTMpGetSet-generic.cpp b/src/VBox/Runtime/generic/RTMpGetSet-generic.cpp
new file mode 100644
index 00000000..3b67ea5c
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpGetSet-generic.cpp
@@ -0,0 +1,61 @@
+/* $Id: RTMpGetSet-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpGetSet.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+RT_EXPORT_SYMBOL(RTMpGetSet);
+
diff --git a/src/VBox/Runtime/generic/RTMpIsCpuOnline-generic.cpp b/src/VBox/Runtime/generic/RTMpIsCpuOnline-generic.cpp
new file mode 100644
index 00000000..83e45710
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpIsCpuOnline-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpIsCpuOnline-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpIsCpuOnline.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ return RTMpCpuId() == idCpu;
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuOnline);
+
diff --git a/src/VBox/Runtime/generic/RTMpIsCpuPossible-generic.cpp b/src/VBox/Runtime/generic/RTMpIsCpuPossible-generic.cpp
new file mode 100644
index 00000000..b3587093
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpIsCpuPossible-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTMpIsCpuPossible-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Generic RTMpIsCpuPossible.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return RTMpCpuId() == idCpu;
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuPossible);
+
diff --git a/src/VBox/Runtime/generic/RTMpOnPair-generic.cpp b/src/VBox/Runtime/generic/RTMpOnPair-generic.cpp
new file mode 100644
index 00000000..f6000b0c
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTMpOnPair-generic.cpp
@@ -0,0 +1,148 @@
+/* $Id: RTMpOnPair-generic.cpp $ */
+/** @file
+ * IPRT - RTMpOnPair, generic implementation using RTMpOnAll.
+ */
+
+/*
+ * Copyright (C) 2015-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 "internal/iprt.h"
+#include <iprt/mp.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Argument package for the generic RTMpOnPair implemenetation.
+ */
+typedef struct RTMPONPAIRGENERIC
+{
+ RTCPUID idCpu1;
+ RTCPUID idCpu2;
+ PFNRTMPWORKER pfnWorker;
+ void *pvUser1;
+ void *pvUser2;
+ /** Count of how many CPUs actually showed up. */
+ uint32_t volatile cPresent;
+} RTMPONPAIRGENERIC;
+/** Pointer to the an argument package for the generic RTMpOnPair
+ * implemenation. */
+typedef RTMPONPAIRGENERIC *PRTMPONPAIRGENERIC;
+
+
+/**
+ * @callback_method_impl{FNRTMPWORKER,
+ * Used by RTMpOnPair to call the worker on the two specified CPUs.}
+ */
+static DECLCALLBACK(void) rtMpOnPairGenericWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ RT_NOREF(pvUser2);
+ PRTMPONPAIRGENERIC pArgs = (PRTMPONPAIRGENERIC)pvUser1;
+
+ /*
+ * Only the two choosen CPUs should call the worker function, count how
+ * many of them that showed up.
+ */
+ if ( idCpu == pArgs->idCpu1
+ || idCpu == pArgs->idCpu2)
+ {
+ ASMAtomicIncU32(&pArgs->cPresent);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ }
+}
+
+
+RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ int rc;
+ AssertReturn(idCpu1 != idCpu2, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTMPON_F_VALID_MASK), VERR_INVALID_FLAGS);
+ if ((fFlags & RTMPON_F_CONCURRENT_EXEC) && !RTMpOnAllIsConcurrentSafe())
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Check that both CPUs are online before doing the broadcast call.
+ */
+ if ( RTMpIsCpuOnline(idCpu1)
+ && RTMpIsCpuOnline(idCpu2))
+ {
+ RTMPONPAIRGENERIC Args;
+ Args.idCpu1 = idCpu1;
+ Args.idCpu2 = idCpu2;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.cPresent = 0;
+ rc = RTMpOnAll(rtMpOnPairGenericWorker, &Args, pvUser2);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Let's see if both of the CPUs showed up.
+ */
+ if (RT_LIKELY(Args.cPresent == 2))
+ { /* likely */ }
+ else if (Args.cPresent == 0)
+ rc = VERR_CPU_OFFLINE;
+ else if (Args.cPresent == 1)
+ rc = VERR_NOT_ALL_CPUS_SHOWED;
+ else
+ {
+ rc = VERR_CPU_IPE_1;
+ AssertMsgFailed(("cPresent=%#x\n", Args.cPresent));
+ }
+ }
+ }
+ /*
+ * A CPU must be present to be considered just offline.
+ */
+ else if ( RTMpIsCpuPresent(idCpu1)
+ && RTMpIsCpuPresent(idCpu2))
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_NOT_FOUND;
+ return rc;
+}
+
+
+RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void)
+{
+ return RTMpOnAllIsConcurrentSafe();
+}
+
diff --git a/src/VBox/Runtime/generic/RTPathGetCurrentDrive-generic.cpp b/src/VBox/Runtime/generic/RTPathGetCurrentDrive-generic.cpp
new file mode 100644
index 00000000..1d2d4698
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTPathGetCurrentDrive-generic.cpp
@@ -0,0 +1,133 @@
+/* $Id: RTPathGetCurrentDrive-generic.cpp $ */
+/** @file
+ * IPRT - RTPathGetCurrentDrive, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2014-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_PATH
+#include <iprt/path.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+
+
+RTDECL(int) RTPathGetCurrentDrive(char *pszPath, size_t cbPath)
+{
+#ifdef HAVE_DRIVE
+ /*
+ * Query the current directroy and extract the wanted information from it.
+ */
+ char *pszPathFree = NULL;
+ char *pszCwd = pszPath;
+ int rc = RTPathGetCurrent(pszCwd, cbPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc != VERR_BUFFER_OVERFLOW)
+ return rc;
+ else
+ {
+ pszPathFree = pszCwd = (char *)RTMemTmpAlloc(RTPATH_BIG_MAX);
+ AssertReturn(pszPathFree, VERR_NO_TMP_MEMORY);
+ rc = RTPathGetCurrent(pszCwd, RTPATH_BIG_MAX);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Drive letter? Chop off at root slash.
+ */
+ if (pszCwd[0] && RTPATH_IS_VOLSEP(pszCwd[1]))
+ pszCwd[2] = '\0';
+ /*
+ * UNC? Chop off after share.
+ */
+ else if ( RTPATH_IS_SLASH(pszCwd[0])
+ && RTPATH_IS_SLASH(pszCwd[1])
+ && !RTPATH_IS_SLASH(pszCwd[2])
+ && pszCwd[2])
+ {
+ /* Work thru the server name. */
+ size_t off = 3;
+ while (!RTPATH_IS_SLASH(pszCwd[off]) && pszCwd[off])
+ off++;
+ size_t offServerSlash = off;
+
+ /* Is there a share name? */
+ if (RTPATH_IS_SLASH(pszCwd[off]))
+ {
+ while (RTPATH_IS_SLASH(pszCwd[off]))
+ off++;
+ if (pszCwd[off])
+ {
+ /* Work thru the share name. */
+ while (!RTPATH_IS_SLASH(pszCwd[off]) && pszCwd[off])
+ off++;
+ }
+ /* No share name, chop at server name. */
+ else
+ off = offServerSlash;
+ }
+ pszCwd[off] = '\0';
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_4;
+ }
+ if (pszPathFree)
+ {
+ if (RT_SUCCESS(rc))
+ rc = RTStrCopy(pszPath, cbPath, pszCwd);
+ RTMemTmpFree(pszPathFree);
+ }
+ return rc;
+
+#else /* !HAVE_DRIVE */
+ /*
+ * No drive letters on this system, return empty string.
+ */
+ if (cbPath > 0)
+ {
+ *pszPath = '\0';
+ return VINF_SUCCESS;
+ }
+ return VERR_BUFFER_OVERFLOW;
+#endif /* !HAVE_DRIVE */
+}
+RT_EXPORT_SYMBOL(RTPathGetCurrentDrive);
+
diff --git a/src/VBox/Runtime/generic/RTPathGetCurrentOnDrive-generic.cpp b/src/VBox/Runtime/generic/RTPathGetCurrentOnDrive-generic.cpp
new file mode 100644
index 00000000..d04a5c71
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTPathGetCurrentOnDrive-generic.cpp
@@ -0,0 +1,94 @@
+/* $Id: RTPathGetCurrentOnDrive-generic.cpp $ */
+/** @file
+ * IPRT - RTPathGetCurrentOnDrive, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2014-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_PATH
+#include <iprt/path.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+
+
+RTDECL(int) RTPathGetCurrentOnDrive(char chDrive, char *pszPath, size_t cbPath)
+{
+#ifdef HAVE_DRIVE
+ /*
+ * Check if it's the same drive as the current directory.
+ */
+ int rc = RTPathGetCurrent(pszPath, cbPath);
+ if (RT_SUCCESS(rc))
+ {
+ if ( ( chDrive == *pszPath
+ || RT_C_TO_LOWER(chDrive) == RT_C_TO_LOWER(*pszPath))
+ && RTPATH_IS_VOLSEP(pszPath[1]))
+ return rc;
+
+ /*
+ * Different drive, indicate root.
+ */
+ if (cbPath >= 4)
+ {
+ pszPath[0] = RT_C_TO_UPPER(chDrive);
+ pszPath[1] = ':';
+ pszPath[2] = RTPATH_SLASH;
+ pszPath[3] = '\0';
+ return VINF_SUCCESS;
+ }
+ }
+ return rc;
+
+#else
+ /*
+ * No driver letters, just return root slash on whatever we're asked.
+ */
+ NOREF(chDrive);
+ if (cbPath >= 2)
+ {
+ pszPath[0] = RTPATH_SLASH;
+ pszPath[1] = '\0';
+ return VINF_SUCCESS;
+ }
+ return VERR_BUFFER_OVERFLOW;
+#endif
+}
+RT_EXPORT_SYMBOL(RTPathGetCurrentOnDrive);
+
diff --git a/src/VBox/Runtime/generic/RTPathIsSame-generic.cpp b/src/VBox/Runtime/generic/RTPathIsSame-generic.cpp
new file mode 100644
index 00000000..3f8a20bb
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTPathIsSame-generic.cpp
@@ -0,0 +1,107 @@
+/* $Id: RTPathIsSame-generic.cpp $ */
+/** @file
+ * IPRT - Assertions, generic RTPathIsSame.
+ */
+
+/*
+ * Copyright (C) 2013-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/path.h>
+#include "internal/iprt.h"
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTPathIsSame(const char *pszPath1, const char *pszPath2)
+{
+ /*
+ * Simple checks based on the path values.
+ */
+ if (pszPath1 == pszPath2)
+ return true;
+ if (!pszPath1)
+ return false;
+ if (!pszPath2)
+ return false;
+ if (!strcmp(pszPath1, pszPath2))
+ return true;
+
+ /*
+ * If the files exist, try use the attributes.
+ */
+ RTFSOBJINFO ObjInfo1;
+ int rc = RTPathQueryInfoEx(pszPath1, &ObjInfo1, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO ObjInfo2;
+ rc = RTPathQueryInfoEx(pszPath2, &ObjInfo2, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ if ((ObjInfo1.Attr.fMode & RTFS_TYPE_MASK) != (ObjInfo2.Attr.fMode & RTFS_TYPE_MASK))
+ return false;
+ if (ObjInfo1.Attr.u.Unix.INodeIdDevice != ObjInfo2.Attr.u.Unix.INodeIdDevice)
+ return false;
+ if (ObjInfo1.Attr.u.Unix.INodeId != ObjInfo2.Attr.u.Unix.INodeId)
+ return false;
+ if (ObjInfo1.Attr.u.Unix.GenerationId != ObjInfo2.Attr.u.Unix.GenerationId)
+ return false;
+ if ( ObjInfo1.Attr.u.Unix.INodeIdDevice != 0
+ && ObjInfo1.Attr.u.Unix.INodeId != 0)
+ return true;
+ }
+ }
+
+ /*
+ * Fallback, compare absolute/real paths. Return failure on paths that are
+ * too long.
+ */
+ char szPath1[RTPATH_MAX];
+ rc = RTPathAbs(pszPath1, szPath1, sizeof(szPath1));
+ AssertRCReturn(rc, VERR_FILENAME_TOO_LONG);
+
+ char szPath2[RTPATH_MAX];
+ rc = RTPathAbs(pszPath2, szPath2, sizeof(szPath2)); AssertRC(rc);
+ AssertRCReturn(rc, VERR_FILENAME_TOO_LONG);
+
+ if (RTPathCompare(szPath1, szPath2) == 0)
+ return true;
+
+ /** @todo Relsolve any symbolic links in the paths. Too lazy for that right
+ * now. */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTPathIsSame);
+
diff --git a/src/VBox/Runtime/generic/RTProcDaemonize-generic.cpp b/src/VBox/Runtime/generic/RTProcDaemonize-generic.cpp
new file mode 100644
index 00000000..5c951411
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTProcDaemonize-generic.cpp
@@ -0,0 +1,106 @@
+/* $Id: RTProcDaemonize-generic.cpp $ */
+/** @file
+ * IPRT - RTProcDaemonize, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2010-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_PROCESS
+#include <iprt/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "internal/process.h"
+
+
+RTR3DECL(int) RTProcDaemonize(const char * const *papszArgs, const char *pszDaemonizedOpt)
+{
+ /*
+ * Get the executable name.
+ * If this asserts, it's probably because rtR3Init hasn't been called.
+ */
+ char szExecPath[RTPATH_MAX];
+ AssertReturn(RTProcGetExecutablePath(szExecPath, sizeof(szExecPath)) == szExecPath, VERR_WRONG_ORDER);
+
+ /*
+ * Create a copy of the argument list with the daemonized option appended.
+ */
+ unsigned cArgs = 0;
+ while (papszArgs[cArgs])
+ cArgs++;
+
+ char const **papszNewArgs = (char const **)RTMemAlloc(sizeof(const char *) * (cArgs + 2));
+ if (!papszNewArgs)
+ return VERR_NO_MEMORY;
+ for (unsigned i = 0; i < cArgs; i++)
+ papszNewArgs[i] = papszArgs[i];
+ papszNewArgs[cArgs] = pszDaemonizedOpt;
+ papszNewArgs[cArgs + 1] = NULL;
+
+ /*
+ * Open the bitbucket handles and create the detached process.
+ */
+ RTHANDLE hStdIn;
+ int rc = RTFileOpenBitBucket(&hStdIn.u.hFile, RTFILE_O_READ);
+ if (RT_SUCCESS(rc))
+ {
+ hStdIn.enmType = RTHANDLETYPE_FILE;
+
+ RTHANDLE hStdOutAndErr;
+ rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
+
+ rc = RTProcCreateEx(szExecPath, papszNewArgs, RTENV_DEFAULT,
+ RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SAME_CONTRACT,
+ &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
+ NULL /*pszAsUser*/, NULL /*pszPassword*/, NULL /*pExtraData*/, NULL /*phProcess*/);
+
+ RTFileClose(hStdOutAndErr.u.hFile);
+ }
+
+ RTFileClose(hStdIn.u.hFile);
+ }
+ RTMemFree(papszNewArgs);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTProcIsRunningByName-generic.cpp b/src/VBox/Runtime/generic/RTProcIsRunningByName-generic.cpp
new file mode 100644
index 00000000..bddaec98
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTProcIsRunningByName-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTProcIsRunningByName-generic.cpp $ */
+/** @file
+ * IPRT - RTProcIsRunningByName, generic stub.
+ */
+
+/*
+ * 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/process.h>
+#include "internal/iprt.h"
+
+
+RTR3DECL(bool) RTProcIsRunningByName(const char *pszName)
+{
+ /*
+ * No other info here, so return false;
+ */
+ NOREF(pszName);
+ return false;
+}
+
diff --git a/src/VBox/Runtime/generic/RTProcSignalName-generic.cpp b/src/VBox/Runtime/generic/RTProcSignalName-generic.cpp
new file mode 100644
index 00000000..841eee62
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTProcSignalName-generic.cpp
@@ -0,0 +1,226 @@
+/* $Id: RTProcSignalName-generic.cpp $ */
+/** @file
+ * IPRT - RTProcSignalName, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2022-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/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/string.h>
+
+#include <signal.h>
+
+
+RTDECL(const char *) RTProcSignalName(int iSignal)
+{
+#if !defined(IPRT_NO_CRT) || !defined(RT_OS_WINDOWS)
+ switch (iSignal)
+ {
+ /*
+ * Typical bsd/xnu ones:
+ */
+# ifdef SIGHUP
+ RT_CASE_RET_STR(SIGHUP);
+# endif
+# ifdef SIGINT
+ RT_CASE_RET_STR(SIGINT);
+# endif
+# ifdef SIGQUIT
+ RT_CASE_RET_STR(SIGQUIT);
+# endif
+# ifdef SIGILL
+ RT_CASE_RET_STR(SIGILL);
+# endif
+# ifdef SIGTRAP
+ RT_CASE_RET_STR(SIGTRAP);
+# endif
+# ifdef SIGABRT
+ RT_CASE_RET_STR(SIGABRT);
+# endif
+# ifdef SIGEMT
+ RT_CASE_RET_STR(SIGEMT);
+# endif
+# ifdef SIGPOLL
+ RT_CASE_RET_STR(SIGPOLL);
+# endif
+# ifdef SIGFPE
+ RT_CASE_RET_STR(SIGFPE);
+# endif
+# ifdef SIGKILL
+ RT_CASE_RET_STR(SIGKILL);
+# endif
+# ifdef SIGBUS
+ RT_CASE_RET_STR(SIGBUS);
+# endif
+# ifdef SIGSEGV
+ RT_CASE_RET_STR(SIGSEGV);
+# endif
+# ifdef SIGSYS
+ RT_CASE_RET_STR(SIGSYS);
+# endif
+# ifdef SIGPIPE
+ RT_CASE_RET_STR(SIGPIPE);
+# endif
+# ifdef SIGALRM
+ RT_CASE_RET_STR(SIGALRM);
+# endif
+# ifdef SIGTERM
+ RT_CASE_RET_STR(SIGTERM);
+# endif
+# ifdef SIGURG
+ RT_CASE_RET_STR(SIGURG);
+# endif
+# ifdef SIGSTOP
+ RT_CASE_RET_STR(SIGSTOP);
+# endif
+# ifdef SIGTSTP
+ RT_CASE_RET_STR(SIGTSTP);
+# endif
+# ifdef SIGCONT
+ RT_CASE_RET_STR(SIGCONT);
+# endif
+# ifdef SIGCHLD
+ RT_CASE_RET_STR(SIGCHLD);
+# endif
+# ifdef SIGTTIN
+ RT_CASE_RET_STR(SIGTTIN);
+# endif
+# ifdef SIGTTOU
+ RT_CASE_RET_STR(SIGTTOU);
+# endif
+# ifdef SIGIO
+# if !defined(SIGPOLL) || (SIGPOLL+0) != SIGIO
+ RT_CASE_RET_STR(SIGIO);
+# endif
+# endif
+# ifdef SIGXCPU
+ RT_CASE_RET_STR(SIGXCPU);
+# endif
+# ifdef SIGXFSZ
+ RT_CASE_RET_STR(SIGXFSZ);
+# endif
+# ifdef SIGVTALRM
+ RT_CASE_RET_STR(SIGVTALRM);
+# endif
+# ifdef SIGPROF
+ RT_CASE_RET_STR(SIGPROF);
+# endif
+# ifdef SIGWINCH
+ RT_CASE_RET_STR(SIGWINCH);
+# endif
+# ifdef SIGINFO
+ RT_CASE_RET_STR(SIGINFO);
+# endif
+# ifdef SIGUSR1
+ RT_CASE_RET_STR(SIGUSR1);
+# endif
+# ifdef SIGUSR2
+ RT_CASE_RET_STR(SIGUSR2);
+# endif
+# ifdef SIGTHR
+ RT_CASE_RET_STR(SIGTHR);
+# endif
+# ifdef SIGLIBRT
+ RT_CASE_RET_STR(SIGLIBRT);
+# endif
+
+ /*
+ * Additional linux ones:
+ */
+# ifdef SIGIOT
+# if !defined(SIGABRT) || (SIGABRT+0) != SIGIOT
+ RT_CASE_RET_STR(SIGIOT);
+# endif
+# endif
+# ifdef SIGSTKFLT
+ RT_CASE_RET_STR(SIGSTKFLT);
+# endif
+# ifdef SIGLOST
+ RT_CASE_RET_STR(SIGLOST);
+# endif
+# ifdef SIGPWR
+ RT_CASE_RET_STR(SIGPWR);
+# endif
+# ifdef SIGUNUSED
+# if !defined(SIGSYS) || (SIGSYS+0) != SIGUNUSED
+ RT_CASE_RET_STR(SIGUNUSED);
+# endif
+# endif
+ }
+#endif
+
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+ /*
+ * Real time signals.
+ *
+ * This cannot be done in the switch/case setup because glibc maps SIGRTMIN
+ * and SIGRTMAX to internal libc calls that dynamically resolves their values.
+ */
+ static char const s_szSigRt[] =
+ "SIGRT00\0" "SIGRT01\0" "SIGRT02\0" "SIGRT03\0" "SIGRT04\0" "SIGRT05\0" "SIGRT06\0" "SIGRT07\0" "SIGRT08\0" "SIGRT09\0"
+ "SIGRT10\0" "SIGRT11\0" "SIGRT12\0" "SIGRT13\0" "SIGRT14\0" "SIGRT15\0" "SIGRT16\0" "SIGRT17\0" "SIGRT18\0" "SIGRT19\0"
+ "SIGRT20\0" "SIGRT21\0" "SIGRT22\0" "SIGRT23\0" "SIGRT24\0" "SIGRT25\0" "SIGRT26\0" "SIGRT27\0" "SIGRT28\0" "SIGRT29\0"
+ "SIGRT30\0" "SIGRT31\0" "SIGRT32\0" "SIGRT33\0" "SIGRT34\0" "SIGRT35\0" "SIGRT36\0" "SIGRT37\0" "SIGRT38\0" "SIGRT39\0"
+ "SIGRT40\0" "SIGRT41\0" "SIGRT42\0" "SIGRT43\0" "SIGRT44\0" "SIGRT45\0" "SIGRT46\0" "SIGRT47\0" "SIGRT48\0" "SIGRT49\0"
+ "SIGRT50\0" "SIGRT51\0" "SIGRT52\0" "SIGRT53\0" "SIGRT54\0" "SIGRT55\0" "SIGRT56\0" "SIGRT57\0" "SIGRT58\0" "SIGRT59\0"
+ "SIGRT60\0" "SIGRT61\0" "SIGRT62\0" "SIGRT63\0" "SIGRT64\0";
+ int const iSigRtMin = SIGRTMIN;
+ if (iSignal >= iSigRtMin && iSignal <= SIGRTMAX)
+ {
+ unsigned uRtSig = (unsigned)(iSignal - iSigRtMin);
+ if (uRtSig < (sizeof(s_szSigRt) - 1) / sizeof("SIGRT00"))
+ return &s_szSigRt[uRtSig * sizeof("SIGRT00")];
+ }
+#endif /* SIGRTMIN */
+
+ /*
+ * Do fallback: SIG+nnnn.
+ */
+ static struct { char sz[16]; } s_aFallback[16];
+ static uint32_t volatile s_iFallback = 0;
+ uint32_t const iFallback = ASMAtomicIncU32(&s_iFallback) % RT_ELEMENTS(s_aFallback);
+ s_aFallback[iFallback].sz[0] = 'S';
+ s_aFallback[iFallback].sz[1] = 'I';
+ s_aFallback[iFallback].sz[2] = 'G';
+ RTStrFormatU32(&s_aFallback[iFallback].sz[3], sizeof(s_aFallback[iFallback].sz) - 3, iSignal,
+ 10, 0, 0, RTSTR_F_PLUS | RTSTR_F_VALSIGNED);
+ return s_aFallback[iFallback].sz;
+}
+RT_EXPORT_SYMBOL(RTProcSignalName);
+
diff --git a/src/VBox/Runtime/generic/RTProcessQueryUsernameA-generic.cpp b/src/VBox/Runtime/generic/RTProcessQueryUsernameA-generic.cpp
new file mode 100644
index 00000000..a317fb0d
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTProcessQueryUsernameA-generic.cpp
@@ -0,0 +1,79 @@
+/* $Id: RTProcessQueryUsernameA-generic.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * Copyright (C) 2008-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/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+
+
+RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser)
+{
+ /*
+ * Validation.
+ */
+ AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ size_t cbUser = 0;
+
+ rc = RTProcQueryUsername(hProcess, NULL, cbUser, &cbUser);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ char *pszUser = (char *)RTStrAlloc(cbUser);
+ if (pszUser)
+ {
+ rc = RTProcQueryUsername(hProcess, pszUser, cbUser, NULL);
+ Assert(rc != VERR_BUFFER_OVERFLOW);
+ if (RT_SUCCESS(rc))
+ *ppszUser = pszUser;
+ else
+ RTStrFree(pszUser);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTProcQueryUsernameA);
+
diff --git a/src/VBox/Runtime/generic/RTRandAdvCreateSystemFaster-generic.cpp b/src/VBox/Runtime/generic/RTRandAdvCreateSystemFaster-generic.cpp
new file mode 100644
index 00000000..ba46d3af
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTRandAdvCreateSystemFaster-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTRandAdvCreateSystemFaster-generic.cpp $ */
+/** @file
+ * IPRT - Random Numbers, generic RTRandAdvCreateSystemFaster.
+ */
+
+/*
+ * Copyright (C) 2008-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/rand.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTRandAdvCreateSystemFaster(PRTRAND phRand) RT_NO_THROW_DEF
+{
+ NOREF(phRand);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTRandAdvCreateSystemFaster);
+
diff --git a/src/VBox/Runtime/generic/RTRandAdvCreateSystemTruer-generic.cpp b/src/VBox/Runtime/generic/RTRandAdvCreateSystemTruer-generic.cpp
new file mode 100644
index 00000000..0bd38545
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTRandAdvCreateSystemTruer-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTRandAdvCreateSystemTruer-generic.cpp $ */
+/** @file
+ * IPRT - Random Numbers, generic RTRandAdvCreateSystemTruer.
+ */
+
+/*
+ * Copyright (C) 2008-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/rand.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTRandAdvCreateSystemTruer(PRTRAND phRand) RT_NO_THROW_DEF
+{
+ NOREF(phRand);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTRandAdvCreateSystemTruer);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp b/src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp
new file mode 100644
index 00000000..c43f4185
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventMultiWait-2-ex-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTSemEventMultiWait-2-ex-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventMultiWait, implementation based on RTSemEventMultiWaitEx.
+ */
+
+/*
+ * Copyright (C) 2010-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_SEM
+#define RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI hEventMultiSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = RTSemEventMultiWaitEx(hEventMultiSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0);
+ else
+ rc = RTSemEventMultiWaitEx(hEventMultiSem,
+ RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies);
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWait);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventMultiWait-generic.cpp b/src/VBox/Runtime/generic/RTSemEventMultiWait-generic.cpp
new file mode 100644
index 00000000..24ddf432
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventMultiWait-generic.cpp
@@ -0,0 +1,77 @@
+/* $Id: RTSemEventMultiWait-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventMultiWait, generic RTSemEventMultiWaitNoResume wrapper.
+ */
+
+/*
+ * 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_SEM
+#define RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ do rc = RTSemEventMultiWaitNoResume(EventSem, cMillies);
+ while (rc == VERR_INTERRUPTED);
+ }
+ else
+ {
+ const uint64_t u64Start = RTTimeMilliTS();
+ rc = RTSemEventMultiWaitNoResume(EventSem, cMillies);
+ if (rc == VERR_INTERRUPTED)
+ {
+ do
+ {
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64Start;
+ if (u64Elapsed >= cMillies)
+ return VERR_TIMEOUT;
+ rc = RTSemEventMultiWaitNoResume(EventSem, cMillies - (RTMSINTERVAL)u64Elapsed);
+ } while (rc == VERR_INTERRUPTED);
+ }
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWait);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp b/src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp
new file mode 100644
index 00000000..cd95b502
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventMultiWaitNoResume-2-ex-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTSemEventMultiWaitNoResume-2-ex-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventMultiWaitNoResume, generic implementation based
+ * on RTSemEventMultiWaitEx.
+ */
+
+/*
+ * Copyright (C) 2010-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_SEM
+#define RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI hEventMultiSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = RTSemEventMultiWaitEx(hEventMultiSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0);
+ else
+ rc = RTSemEventMultiWaitEx(hEventMultiSem,
+ RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitNoResume);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp b/src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp
new file mode 100644
index 00000000..afc6e7ba
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventWait-2-ex-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTSemEventWait-2-ex-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventWait, implementation based on RTSemEventWaitEx.
+ */
+
+/*
+ * Copyright (C) 2010-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_SEM
+#define RTSEMEVENT_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = RTSemEventWaitEx(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0);
+ else
+ rc = RTSemEventWaitEx(hEventSem,
+ RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies);
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventWait);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventWait-generic.cpp b/src/VBox/Runtime/generic/RTSemEventWait-generic.cpp
new file mode 100644
index 00000000..86bfc77f
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventWait-generic.cpp
@@ -0,0 +1,77 @@
+/* $Id: RTSemEventWait-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventWait, generic RTSemEventWaitNoResume wrapper.
+ */
+
+/*
+ * 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_SEM
+#define RTSEMEVENT_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ do rc = RTSemEventWaitNoResume(EventSem, cMillies);
+ while (rc == VERR_INTERRUPTED);
+ }
+ else
+ {
+ const uint64_t u64Start = RTTimeMilliTS();
+ rc = RTSemEventWaitNoResume(EventSem, cMillies);
+ if (rc == VERR_INTERRUPTED)
+ {
+ do
+ {
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64Start;
+ if (u64Elapsed >= cMillies)
+ return VERR_TIMEOUT;
+ rc = RTSemEventWaitNoResume(EventSem, cMillies - (RTMSINTERVAL)u64Elapsed);
+ } while (rc == VERR_INTERRUPTED);
+ }
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventWait);
+
diff --git a/src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp b/src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp
new file mode 100644
index 00000000..0591cca4
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemEventWaitNoResume-2-ex-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTSemEventWaitNoResume-2-ex-generic.cpp $ */
+/** @file
+ * IPRT - RTSemEventWaitNoResume, generic implementation based
+ * on RTSemEventWaitEx.
+ */
+
+/*
+ * Copyright (C) 2010-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_SEM
+#define RTSEMEVENT_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = RTSemEventWaitEx(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0);
+ else
+ rc = RTSemEventWaitEx(hEventSem,
+ RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitNoResume);
+
diff --git a/src/VBox/Runtime/generic/RTSemMutexRequest-generic.cpp b/src/VBox/Runtime/generic/RTSemMutexRequest-generic.cpp
new file mode 100644
index 00000000..91d4ffb8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemMutexRequest-generic.cpp
@@ -0,0 +1,77 @@
+/* $Id: RTSemMutexRequest-generic.cpp $ */
+/** @file
+ * IPRT - RTSemMutexRequest, generic RTSemMutexRequestNoResume wrapper.
+ */
+
+/*
+ * 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 RTSEMMUTEX_WITHOUT_REMAPPING
+#define LOG_GROUP RTLOGGROUP_SEM
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX Mutex, RTMSINTERVAL cMillies)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ do rc = RTSemMutexRequestNoResume(Mutex, cMillies);
+ while (rc == VERR_INTERRUPTED);
+ }
+ else
+ {
+ const uint64_t u64Start = RTTimeMilliTS();
+ rc = RTSemMutexRequestNoResume(Mutex, cMillies);
+ if (rc == VERR_INTERRUPTED)
+ {
+ do
+ {
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64Start;
+ if (u64Elapsed >= cMillies)
+ return VERR_TIMEOUT;
+ rc = RTSemMutexRequestNoResume(Mutex, cMillies - (RTMSINTERVAL)u64Elapsed);
+ } while (rc == VERR_INTERRUPTED);
+ }
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequest);
+
diff --git a/src/VBox/Runtime/generic/RTSemMutexRequestDebug-generic.cpp b/src/VBox/Runtime/generic/RTSemMutexRequestDebug-generic.cpp
new file mode 100644
index 00000000..60002abc
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSemMutexRequestDebug-generic.cpp
@@ -0,0 +1,77 @@
+/* $Id: RTSemMutexRequestDebug-generic.cpp $ */
+/** @file
+ * IPRT - RTSemMutexRequestDebug, generic RTSemMutexRequestNoResumeDebug wrapper.
+ */
+
+/*
+ * 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_SEM
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX Mutex, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ do rc = RTSemMutexRequestNoResumeDebug(Mutex, cMillies, uId, RT_SRC_POS_ARGS);
+ while (rc == VERR_INTERRUPTED);
+ }
+ else
+ {
+ const uint64_t u64Start = RTTimeMilliTS();
+ rc = RTSemMutexRequestNoResumeDebug(Mutex, cMillies, uId, RT_SRC_POS_ARGS);
+ if (rc == VERR_INTERRUPTED)
+ {
+ do
+ {
+ uint64_t u64Elapsed = RTTimeMilliTS() - u64Start;
+ if (u64Elapsed >= cMillies)
+ return VERR_TIMEOUT;
+ rc = RTSemMutexRequestNoResumeDebug(Mutex, cMillies - (RTMSINTERVAL)u64Elapsed, uId, RT_SRC_POS_ARGS);
+ } while (rc == VERR_INTERRUPTED);
+ }
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequestDebug);
+
diff --git a/src/VBox/Runtime/generic/RTSystemFirmware-generic.cpp b/src/VBox/Runtime/generic/RTSystemFirmware-generic.cpp
new file mode 100644
index 00000000..f8187fbe
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSystemFirmware-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTSystemFirmware-generic.cpp $ */
+/** @file
+ * IPRT - System firmware information, Generic stub.
+ */
+
+/*
+ * Copyright (C) 2019-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 "internal/iprt.h"
+#include <iprt/system.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType)
+{
+ RT_NOREF(penmFirmwareType);
+ *penmFirmwareType = RTSYSFWTYPE_INVALID;
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareType);
+
+
+RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue)
+{
+ RT_NOREF(enmBoolean, pfValue);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareBoolean);
+
diff --git a/src/VBox/Runtime/generic/RTSystemIsInsideVM-generic.cpp b/src/VBox/Runtime/generic/RTSystemIsInsideVM-generic.cpp
new file mode 100644
index 00000000..0ffff261
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSystemIsInsideVM-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTSystemIsInsideVM-generic.cpp $ */
+/** @file
+ * IPRT -
+ */
+
+/*
+ * Copyright (C) 2013-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 "internal/iprt.h"
+#include <iprt/system.h>
+
+
+RTDECL(bool) RTSystemIsInsideVM(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSystemIsInsideVM);
+
diff --git a/src/VBox/Runtime/generic/RTSystemQueryDmiString-generic.cpp b/src/VBox/Runtime/generic/RTSystemQueryDmiString-generic.cpp
new file mode 100644
index 00000000..0d67ddc0
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSystemQueryDmiString-generic.cpp
@@ -0,0 +1,57 @@
+/* $Id: RTSystemQueryDmiString-generic.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, generic stub.
+ */
+
+/*
+ * Copyright (C) 2010-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/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryDmiString);
+
diff --git a/src/VBox/Runtime/generic/RTSystemQueryOSInfo-generic.cpp b/src/VBox/Runtime/generic/RTSystemQueryOSInfo-generic.cpp
new file mode 100644
index 00000000..43ca2b8e
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSystemQueryOSInfo-generic.cpp
@@ -0,0 +1,66 @@
+/* $Id: RTSystemQueryOSInfo-generic.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * Copyright (C) 2008-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/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemQueryOSInfo(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Quick validation.
+ */
+ AssertReturn(enmInfo > RTSYSOSINFO_INVALID && enmInfo < RTSYSOSINFO_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszInfo, VERR_INVALID_POINTER);
+
+
+ /*
+ * Return empty string and VERR_NOT_SUPPORTED.
+ */
+ if (!cchInfo)
+ return VERR_BUFFER_OVERFLOW;
+ *pszInfo = '\0';
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryOSInfo);
+
diff --git a/src/VBox/Runtime/generic/RTSystemShutdown-generic.cpp b/src/VBox/Runtime/generic/RTSystemShutdown-generic.cpp
new file mode 100644
index 00000000..32d1edfe
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTSystemShutdown-generic.cpp
@@ -0,0 +1,56 @@
+/* $Id: RTSystemShutdown-generic.cpp $ */
+/** @file
+ * IPRT - RTSystemShutdown, generic stub.
+ */
+
+/*
+ * Copyright (C) 2012-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/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg)
+{
+ RT_NOREF(cMsDelay, pszLogMsg);
+ AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemShutdown);
+
diff --git a/src/VBox/Runtime/generic/RTThreadGetAffinity-stub-generic.cpp b/src/VBox/Runtime/generic/RTThreadGetAffinity-stub-generic.cpp
new file mode 100644
index 00000000..3c7cf6dc
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTThreadGetAffinity-stub-generic.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTThreadGetAffinity-stub-generic.cpp $ */
+/** @file
+ * IPRT - Generic RTThreadGetAffinity stub.
+ */
+
+/*
+ * Copyright (C) 2011-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/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+#include <iprt/errcore.h>
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ RTCpuSetEmpty(pCpuSet);
+ RTCpuSetAddByIndex(pCpuSet, 0);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/generic/RTThreadGetNativeState-generic.cpp b/src/VBox/Runtime/generic/RTThreadGetNativeState-generic.cpp
new file mode 100644
index 00000000..0c92dabd
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTThreadGetNativeState-generic.cpp
@@ -0,0 +1,59 @@
+/* $Id: RTThreadGetNativeState-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadGetNativeState, generic stub implementation.
+ */
+
+/*
+ * Copyright (C) 2010-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_PROCESS
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include "internal/thread.h"
+
+
+RTDECL(RTTHREADNATIVESTATE) RTThreadGetNativeState(RTTHREAD hThread)
+{
+ RTTHREADNATIVESTATE enmRet = RTTHREADNATIVESTATE_INVALID;
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ if (pThread)
+ {
+ enmRet = RTTHREADNATIVESTATE_UNKNOWN;
+ rtThreadRelease(pThread);
+ }
+ return enmRet;
+}
+
diff --git a/src/VBox/Runtime/generic/RTThreadQueryTerminationStatus-stub-generic.cpp b/src/VBox/Runtime/generic/RTThreadQueryTerminationStatus-stub-generic.cpp
new file mode 100644
index 00000000..e0ee6bbd
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTThreadQueryTerminationStatus-stub-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTThreadQueryTerminationStatus-stub-generic.cpp $ */
+/** @file
+ * IPRT - Generic RTThreadQueryTerminationStatus stub.
+ */
+
+/*
+ * Copyright (C) 2021-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/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTThreadQueryTerminationStatus(RTTHREAD hThread)
+{
+ RT_NOREF(hThread);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTThreadQueryTerminationStatus);
+
diff --git a/src/VBox/Runtime/generic/RTThreadSetAffinity-stub-generic.cpp b/src/VBox/Runtime/generic/RTThreadSetAffinity-stub-generic.cpp
new file mode 100644
index 00000000..079c7ba1
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTThreadSetAffinity-stub-generic.cpp
@@ -0,0 +1,56 @@
+/* $Id: RTThreadSetAffinity-stub-generic.cpp $ */
+/** @file
+ * IPRT - Generic RTThreadSetAffinity stub.
+ */
+
+/*
+ * Copyright (C) 2011-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/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+#include <iprt/errcore.h>
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ RTCPUSET CurSet;
+ RTThreadGetAffinity(&CurSet);
+ if (pCpuSet && !RTCpuSetIsEqual(&CurSet, pCpuSet))
+ return VERR_INVALID_PARAMETER;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/generic/RTThreadSetAffinityToCpu-generic.cpp b/src/VBox/Runtime/generic/RTThreadSetAffinityToCpu-generic.cpp
new file mode 100644
index 00000000..aec8ed41
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTThreadSetAffinityToCpu-generic.cpp
@@ -0,0 +1,69 @@
+/* $Id: RTThreadSetAffinityToCpu-generic.cpp $ */
+/** @file
+ * IPRT - Generic RTThreadSetAffinityToCpu implementation.
+ */
+
+/*
+ * Copyright (C) 2011-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/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+
+
+
+RTR3DECL(int) RTThreadSetAffinityToCpu(RTCPUID idCpu)
+{
+ int rc;
+ if (idCpu == NIL_RTCPUID)
+ rc = RTThreadSetAffinity(NULL);
+ else
+ {
+ int iCpu = RTMpCpuIdToSetIndex(idCpu);
+ if (iCpu >= 0)
+ {
+ RTCPUSET CpuSet;
+ RTCpuSetEmpty(&CpuSet);
+ RTCpuSetAddByIndex(&CpuSet, iCpu);
+ rc = RTThreadSetAffinity(&CpuSet);
+ }
+ else
+ rc = VERR_CPU_NOT_FOUND;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/RTTimeLocalDeltaNano-generic.cpp b/src/VBox/Runtime/generic/RTTimeLocalDeltaNano-generic.cpp
new file mode 100644
index 00000000..9f3d7c89
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimeLocalDeltaNano-generic.cpp
@@ -0,0 +1,50 @@
+/* $Id: RTTimeLocalDeltaNano-generic.cpp $ */
+/** @file
+ * IPRT - Time, generic RTTimeLocalDeltaNano.
+ */
+
+/*
+ * 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/time.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ return 0;
+}
+RT_EXPORT_SYMBOL(RTTimeLocalDeltaNano);
+
diff --git a/src/VBox/Runtime/generic/RTTimeLocalDeltaNanoFor-generic.cpp b/src/VBox/Runtime/generic/RTTimeLocalDeltaNanoFor-generic.cpp
new file mode 100644
index 00000000..a9058ac6
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimeLocalDeltaNanoFor-generic.cpp
@@ -0,0 +1,51 @@
+/* $Id: RTTimeLocalDeltaNanoFor-generic.cpp $ */
+/** @file
+ * IPRT - Time, generic RTTimeLocalDeltaNanoFor.
+ */
+
+/*
+ * 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/time.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNanoFor(PCRTTIMESPEC pTimeSpec)
+{
+ RT_NOREF(pTimeSpec);
+ return RTTimeLocalDeltaNano();
+}
+RT_EXPORT_SYMBOL(RTTimeLocalDeltaNano);
+
diff --git a/src/VBox/Runtime/generic/RTTimeLocalExplode-generic.cpp b/src/VBox/Runtime/generic/RTTimeLocalExplode-generic.cpp
new file mode 100644
index 00000000..fb7ed09d
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimeLocalExplode-generic.cpp
@@ -0,0 +1,59 @@
+/* $Id: RTTimeLocalExplode-generic.cpp $ */
+/** @file
+ * IPRT - Time, generic RTTimeLocalExplode.
+ */
+
+/*
+ * 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/time.h>
+#include "internal/iprt.h"
+
+
+RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime = *pTimeSpec;
+ int64_t cNsUtcOffset = RTTimeLocalDeltaNano(); /**< @todo this is obviously wrong. Need RTTimeLocalDeltaNanoFor(pTimeSpec); */
+ RTTimeSpecAddNano(&LocalTime, cNsUtcOffset);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = cNsUtcOffset / RT_NS_1MIN;
+ }
+ return pTime;
+}
+RT_EXPORT_SYMBOL(RTTimeLocalExplode);
+
diff --git a/src/VBox/Runtime/generic/RTTimeLocalNow-generic.cpp b/src/VBox/Runtime/generic/RTTimeLocalNow-generic.cpp
new file mode 100644
index 00000000..f2467749
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimeLocalNow-generic.cpp
@@ -0,0 +1,59 @@
+/* $Id $ */
+/** @file
+ * IPRT - Time, generic RTTimeLocalNow.
+ */
+
+/*
+ * 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/time.h>
+#include "internal/iprt.h"
+
+
+RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime)
+{
+ int64_t i64PreDelta;
+ int64_t i64PostDelta;
+ do
+ {
+ i64PreDelta = RTTimeLocalDeltaNano();
+ RTTimeNow(pTime);
+ i64PostDelta = RTTimeLocalDeltaNano();
+ } while (i64PreDelta != i64PostDelta);
+
+ return RTTimeSpecAddNano(pTime, i64PostDelta);
+}
+RT_EXPORT_SYMBOL(RTTimeLocalNow);
+
diff --git a/src/VBox/Runtime/generic/RTTimerCreate-generic.cpp b/src/VBox/Runtime/generic/RTTimerCreate-generic.cpp
new file mode 100644
index 00000000..49069345
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimerCreate-generic.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTTimerCreate-generic.cpp $ */
+/** @file
+ * IPRT - Timers, Generic RTTimerCreate() Implementation.
+ */
+
+/*
+ * 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/timer.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTTimerCreate(PRTTIMER *ppTimer, unsigned uMilliesInterval, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ int rc = RTTimerCreateEx(ppTimer, uMilliesInterval * RT_NS_1MS_64, 0 /* fFlags */, pfnTimer, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(*ppTimer, 0 /* u64First */);
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = RTTimerDestroy(*ppTimer); AssertRC(rc2);
+ *ppTimer = NULL;
+ }
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerCreate);
+
diff --git a/src/VBox/Runtime/generic/RTTimerLRCreate-generic.cpp b/src/VBox/Runtime/generic/RTTimerLRCreate-generic.cpp
new file mode 100644
index 00000000..b5cac8c8
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTTimerLRCreate-generic.cpp
@@ -0,0 +1,62 @@
+/* $Id: RTTimerLRCreate-generic.cpp $ */
+/** @file
+ * IPRT - Low Resolution Timers, Generic RTTimerLRCreate() Implementation.
+ */
+
+/*
+ * 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/timer.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+RTDECL(int) RTTimerLRCreate(PRTTIMERLR phTimerLR, uint32_t uMilliesInterval, PFNRTTIMERLR pfnTimer, void *pvUser)
+{
+ int rc = RTTimerLRCreateEx(phTimerLR, uMilliesInterval * UINT64_C(1000000), 0, pfnTimer, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerLRStart(*phTimerLR, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+ int rc2 = RTTimerLRDestroy(*phTimerLR); AssertRC(rc2);
+ *phTimerLR = NIL_RTTIMERLR;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerLRCreate);
+
diff --git a/src/VBox/Runtime/generic/RTUuidCreate-generic.cpp b/src/VBox/Runtime/generic/RTUuidCreate-generic.cpp
new file mode 100644
index 00000000..f47f2443
--- /dev/null
+++ b/src/VBox/Runtime/generic/RTUuidCreate-generic.cpp
@@ -0,0 +1,66 @@
+/* $Id: RTUuidCreate-generic.cpp $ */
+/** @file
+ * IPRT - UUID, Generic RTUuidCreate implementation.
+ */
+
+/*
+ * 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/uuid.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/rand.h>
+
+
+/* WARNING: This implementation ASSUMES little endian. Does not work on big endian! */
+
+/* Remember, the time fields in the UUID must be little endian. */
+
+
+RTDECL(int) RTUuidCreate(PRTUUID pUuid)
+{
+ /* validate input. */
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+
+ RTRandBytes(pUuid, sizeof(*pUuid));
+ pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidCreate);
+
diff --git a/src/VBox/Runtime/generic/cdrom-generic.cpp b/src/VBox/Runtime/generic/cdrom-generic.cpp
new file mode 100644
index 00000000..09c7230c
--- /dev/null
+++ b/src/VBox/Runtime/generic/cdrom-generic.cpp
@@ -0,0 +1,128 @@
+/* $Id: cdrom-generic.cpp $ */
+/** @file
+ * IPRT - CD/DVD/BD-ROM Drive, Generic.
+ */
+
+/*
+ * Copyright (C) 2012-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/cdrom.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTCdromOpen(const char *psz, uint32_t fFlags, PRTCDROM phCdrom)
+{
+ RT_NOREF_PV(psz); RT_NOREF_PV(fFlags); RT_NOREF_PV(phCdrom);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(uint32_t) RTCdromRetain(RTCDROM hCdrom)
+{
+ RT_NOREF_PV(hCdrom);
+ AssertFailedReturn(UINT32_MAX);
+}
+
+
+RTDECL(uint32_t) RTCdromRelease(RTCDROM hCdrom)
+{
+ RT_NOREF_PV(hCdrom);
+ AssertFailedReturn(UINT32_MAX);
+}
+
+
+RTDECL(int) RTCdromQueryMountPoint(RTCDROM hCdrom, char *pszMountPoint, size_t cbMountPoint)
+{
+ RT_NOREF_PV(hCdrom);
+ RT_NOREF_PV(pszMountPoint);
+ RT_NOREF_PV(cbMountPoint);
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+RTDECL(int) RTCdromUnmount(RTCDROM hCdrom)
+{
+ RT_NOREF_PV(hCdrom);
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+RTDECL(int) RTCdromEject(RTCDROM hCdrom, bool fForce)
+{
+ RT_NOREF_PV(hCdrom);
+ RT_NOREF_PV(fForce);
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+RTDECL(int) RTCdromLock(RTCDROM hCdrom)
+{
+ RT_NOREF_PV(hCdrom);
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+RTDECL(int) RTCdromUnlock(RTCDROM hCdrom)
+{
+ RT_NOREF_PV(hCdrom);
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+RTDECL(unsigned) RTCdromCount(void)
+{
+ return 0;
+}
+
+RTDECL(int) RTCdromOrdinalToName(unsigned iCdrom, char *pszName, size_t cbName)
+{
+ RT_NOREF_PV(iCdrom);
+ if (cbName)
+ *pszName = '\0';
+ return VERR_OUT_OF_RANGE;
+}
+
+
+RTDECL(int) RTCdromOpenByOrdinal(unsigned iCdrom, uint32_t fFlags, PRTCDROM phCdrom)
+{
+ RT_NOREF_PV(iCdrom);
+ RT_NOREF_PV(fFlags);
+ RT_NOREF_PV(phCdrom);
+ return VERR_OUT_OF_RANGE;
+}
+
diff --git a/src/VBox/Runtime/generic/createtemp-generic.cpp b/src/VBox/Runtime/generic/createtemp-generic.cpp
new file mode 100644
index 00000000..6630e249
--- /dev/null
+++ b/src/VBox/Runtime/generic/createtemp-generic.cpp
@@ -0,0 +1,291 @@
+/* $Id: createtemp-generic.cpp $ */
+/** @file
+ * IPRT - temporary file and directory creation, generic implementation.
+ */
+
+/*
+ * 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/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+
+
+/**
+ * The X'es may be trailing, or they may be a cluster of 3 or more inside
+ * the file name.
+ */
+static int rtCreateTempValidateTemplate(char *pszTemplate, char **ppszX, unsigned *pcXes)
+{
+ AssertPtr(pszTemplate);
+ AssertPtr(ppszX);
+ AssertPtr(pcXes);
+
+ unsigned cXes = 0;
+ char *pszX = strchr(pszTemplate, '\0');
+ if ( pszX != pszTemplate
+ && pszX[-1] != 'X')
+ {
+ /* look inside the file name. */
+ char *pszFilename = RTPathFilename(pszTemplate);
+ if ( pszFilename
+ && (size_t)(pszX - pszFilename) > 3)
+ {
+ char *pszXEnd = pszX - 1;
+ pszFilename += 3;
+ do
+ {
+ if ( pszXEnd[-1] == 'X'
+ && pszXEnd[-2] == 'X'
+ && pszXEnd[-3] == 'X')
+ {
+ pszX = pszXEnd - 3;
+ cXes = 3;
+ break;
+ }
+ } while (pszXEnd-- != pszFilename);
+ }
+ }
+
+ /* count them */
+ while ( pszX != pszTemplate
+ && pszX[-1] == 'X')
+ {
+ pszX--;
+ cXes++;
+ }
+
+ /* fail if none found. */
+ *ppszX = pszX;
+ *pcXes = cXes;
+ AssertReturn(cXes > 0, VERR_INVALID_PARAMETER);
+ return VINF_SUCCESS;
+}
+
+
+static void rtCreateTempFillTemplate(char *pszX, unsigned cXes)
+{
+ static char const s_sz[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ unsigned j = cXes;
+ while (j-- > 0)
+ pszX[j] = s_sz[RTRandU32Ex(0, RT_ELEMENTS(s_sz) - 2)];
+}
+
+
+RTDECL(int) RTDirCreateTemp(char *pszTemplate, RTFMODE fMode)
+{
+ char *pszX = NULL;
+ unsigned cXes = 0;
+ int rc = rtCreateTempValidateTemplate(pszTemplate, &pszX, &cXes);
+ if (RT_FAILURE(rc))
+ {
+ *pszTemplate = '\0';
+ return rc;
+ }
+ /*
+ * Try ten thousand times.
+ */
+ int i = 10000;
+ while (i-- > 0)
+ {
+ rtCreateTempFillTemplate(pszX, cXes);
+ rc = RTDirCreate(pszTemplate, fMode, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (rc != VERR_ALREADY_EXISTS)
+ {
+ *pszTemplate = '\0';
+ return rc;
+ }
+ }
+
+ /* we've given up. */
+ *pszTemplate = '\0';
+ return VERR_ALREADY_EXISTS;
+}
+RT_EXPORT_SYMBOL(RTDirCreateTemp);
+
+
+/** @todo Test case for this once it is implemented. */
+RTDECL(int) RTDirCreateTempSecure(char *pszTemplate)
+{
+ /* bool fSafe; */
+
+ /* Temporarily convert pszTemplate to a path. */
+ size_t cchDir = 0;
+ RTPathParseSimple(pszTemplate, &cchDir, NULL, NULL);
+ char chOld = pszTemplate[cchDir];
+ pszTemplate[cchDir] = '\0';
+ /** @todo Implement this. */
+ int rc = /* RTPathIsSecure(pszTemplate, &fSafe) */ VERR_NOT_SUPPORTED;
+ pszTemplate[cchDir] = chOld;
+ if (RT_SUCCESS(rc) /* && fSafe */)
+ return RTDirCreateTemp(pszTemplate, 0700);
+
+ *pszTemplate = '\0';
+ /** @todo Replace VERR_PERMISSION_DENIED. VERR_INSECURE? */
+ return RT_FAILURE(rc) ? rc : VERR_PERMISSION_DENIED;
+}
+RT_EXPORT_SYMBOL(RTDirCreateTempSecure);
+
+
+RTDECL(int) RTFileCreateUnique(PRTFILE phFile, char *pszTemplate, uint64_t fOpen)
+{
+ /*
+ * Validate input.
+ */
+ *phFile = NIL_RTFILE;
+ AssertReturn((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE, VERR_INVALID_FLAGS);
+ char *pszX = NULL;
+ unsigned cXes = 0;
+ int rc = rtCreateTempValidateTemplate(pszTemplate, &pszX, &cXes);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try ten thousand times.
+ */
+ int i = 10000;
+ while (i-- > 0)
+ {
+ rtCreateTempFillTemplate(pszX, cXes);
+ RTFILE hFile = NIL_RTFILE;
+ rc = RTFileOpen(&hFile, pszTemplate, fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ *phFile = hFile;
+ return rc;
+ }
+ /** @todo Anything else to consider? */
+ if (rc != VERR_ALREADY_EXISTS)
+ {
+ *pszTemplate = '\0';
+ return rc;
+ }
+ }
+
+ /* we've given up. */
+ rc = VERR_ALREADY_EXISTS;
+ }
+ *pszTemplate = '\0';
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileCreateUnique);
+
+
+RTDECL(int) RTFileCreateTemp(char *pszTemplate, RTFMODE fMode)
+{
+ RTFILE hFile = NIL_RTFILE;
+ int rc = RTFileCreateUnique(&hFile, pszTemplate,
+ RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE | RTFILE_O_NOT_CONTENT_INDEXED
+ | fMode << RTFILE_O_CREATE_MODE_SHIFT);
+ if (RT_SUCCESS(rc))
+ RTFileClose(hFile);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileCreateTemp);
+
+
+/** @todo Test case for this once it is implemented. */
+RTDECL(int) RTFileCreateTempSecure(char *pszTemplate)
+{
+ /* bool fSafe; */
+
+ /* Temporarily convert pszTemplate to a path. */
+ size_t cchDir = 0;
+ RTPathParseSimple(pszTemplate, &cchDir, NULL, NULL);
+ char chOld = pszTemplate[cchDir];
+ pszTemplate[cchDir] = '\0';
+ /** @todo Implement this. */
+ int rc = /* RTPathIsSecure(pszTemplate, &fSafe) */ VERR_NOT_SUPPORTED;
+ pszTemplate[cchDir] = chOld;
+ if (RT_SUCCESS(rc) /* && fSafe */)
+ return RTFileCreateTemp(pszTemplate, 0600);
+
+ *pszTemplate = '\0';
+ /** @todo Replace VERR_PERMISSION_DENIED. VERR_INSECURE? */
+ return RT_FAILURE(rc) ? rc : VERR_PERMISSION_DENIED;
+}
+RT_EXPORT_SYMBOL(RTFileCreateTempSecure);
+
+
+RTDECL(int) RTFileOpenTemp(PRTFILE phFile, char *pszFilename, size_t cbFilename, uint64_t fOpen)
+{
+ AssertReturn((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE, VERR_INVALID_FLAGS);
+ AssertReturn(fOpen & RTFILE_O_WRITE, VERR_INVALID_FLAGS);
+
+ /*
+ * Start by obtaining the path to the temporary directory.
+ */
+ int rc = RTPathTemp(pszFilename, cbFilename);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add a filename pattern.
+ */
+ static char const s_szTemplate[] = "IPRT-XXXXXXXXXXXX.tmp";
+ rc = RTPathAppend(pszFilename, cbFilename, s_szTemplate);
+ if (RT_SUCCESS(rc))
+ {
+ char * const pszX = RTStrEnd(pszFilename, cbFilename) - (sizeof(s_szTemplate) - 1) + 5;
+ unsigned cXes = sizeof(s_szTemplate) - 1 - 4 - 5;
+ Assert(pszX[0] == 'X'); Assert(pszX[-1] == '-'); Assert(pszX[cXes] == '.');
+
+ /*
+ * Try 10000 times with random names.
+ */
+ unsigned cTriesLeft = 10000;
+ while (cTriesLeft-- > 0)
+ {
+ rtCreateTempFillTemplate(pszX, cXes);
+ rc = RTFileOpen(phFile, pszFilename, fOpen);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ }
+ }
+
+ if (cbFilename)
+ *pszFilename = '\0';
+ *phFile = NIL_RTFILE;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileOpenTemp);
+
diff --git a/src/VBox/Runtime/generic/critsect-generic.cpp b/src/VBox/Runtime/generic/critsect-generic.cpp
new file mode 100644
index 00000000..cfabdd85
--- /dev/null
+++ b/src/VBox/Runtime/generic/critsect-generic.cpp
@@ -0,0 +1,591 @@
+/* $Id: critsect-generic.cpp $ */
+/** @file
+ * IPRT - Critical Section, Generic.
+ */
+
+/*
+ * 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 RTCRITSECT_WITHOUT_REMAPPING
+#include <iprt/critsect.h>
+#include "internal/iprt.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include "internal/thread.h"
+#include "internal/strict.h"
+
+/* Two issues here, (1) the tracepoint generator uses IPRT, and (2) only one .d
+ file per module. */
+#ifdef IPRT_WITH_DTRACE
+# include IPRT_DTRACE_INCLUDE
+# ifdef IPRT_DTRACE_PREFIX
+# define IPRT_CRITSECT_ENTERED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECT_ENTERED)
+# define IPRT_CRITSECT_LEAVING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECT_LEAVING)
+# define IPRT_CRITSECT_BUSY RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECT_BUSY)
+# define IPRT_CRITSECT_WAITING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECT_WAITING)
+# endif
+#else
+# define IPRT_CRITSECT_ENTERED(a_pvCritSect, a_pszName, a_cLockers, a_cNestings) do {} while (0)
+# define IPRT_CRITSECT_LEAVING(a_pvCritSect, a_pszName, a_cLockers, a_cNestings) do {} while (0)
+# define IPRT_CRITSECT_BUSY( a_pvCritSect, a_pszName, a_cLockers, a_pvNativeOwnerThread) do {} while (0)
+# define IPRT_CRITSECT_WAITING(a_pvCritSect, a_pszName, a_cLockers, a_pvNativeOwnerThread) do {} while (0)
+#endif
+
+
+
+RTDECL(int) RTCritSectInit(PRTCRITSECT pCritSect)
+{
+ return RTCritSectInitEx(pCritSect, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTCritSect");
+}
+RT_EXPORT_SYMBOL(RTCritSectInit);
+
+
+RTDECL(int) RTCritSectInitEx(PRTCRITSECT pCritSect, uint32_t fFlags, RTLOCKVALCLASS hClass, uint32_t uSubClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_BOOTSTRAP_HACK | RTCRITSECT_FLAGS_NOP)),
+ VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+
+ /*
+ * Initialize the structure and
+ */
+ pCritSect->u32Magic = RTCRITSECT_MAGIC;
+#ifdef IN_RING0
+ pCritSect->fFlags = fFlags | RTCRITSECT_FLAGS_RING0;
+#else
+ pCritSect->fFlags = fFlags & ~RTCRITSECT_FLAGS_RING0;
+#endif
+ pCritSect->cNestings = 0;
+ pCritSect->cLockers = -1;
+ pCritSect->NativeThreadOwner = NIL_RTNATIVETHREAD;
+ pCritSect->pValidatorRec = NULL;
+ int rc = VINF_SUCCESS;
+#ifdef RTCRITSECT_STRICT
+ if (!(fFlags & (RTCRITSECT_FLAGS_BOOTSTRAP_HACK | RTCRITSECT_FLAGS_NOP)))
+ {
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iCritSectAnon = 0;
+ rc = RTLockValidatorRecExclCreate(&pCritSect->pValidatorRec, hClass, uSubClass, pCritSect,
+ !(fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL),
+ "RTCritSect-%u", ASMAtomicIncU32(&s_iCritSectAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ rc = RTLockValidatorRecExclCreateV(&pCritSect->pValidatorRec, hClass, uSubClass, pCritSect,
+ !(fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
+ va_end(va);
+ }
+ }
+#endif
+ if (RT_SUCCESS(rc))
+ {
+#ifdef IN_RING0
+ rc = RTSemEventCreate(&pCritSect->EventSem);
+
+#else
+ rc = RTSemEventCreateEx(&pCritSect->EventSem,
+ fFlags & RTCRITSECT_FLAGS_BOOTSTRAP_HACK
+ ? RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK
+ : RTSEMEVENT_FLAGS_NO_LOCK_VAL,
+ NIL_RTLOCKVALCLASS,
+ NULL);
+#endif
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+#ifdef RTCRITSECT_STRICT
+ RTLockValidatorRecExclDestroy(&pCritSect->pValidatorRec);
+#endif
+ }
+
+ AssertRC(rc);
+ pCritSect->EventSem = NULL;
+ pCritSect->u32Magic = (uint32_t)rc;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCritSectInitEx);
+
+
+RTDECL(uint32_t) RTCritSectSetSubClass(PRTCRITSECT pCritSect, uint32_t uSubClass)
+{
+# ifdef RTCRITSECT_STRICT
+ AssertPtrReturn(pCritSect, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pCritSect->u32Magic == RTCRITSECT_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(!(pCritSect->fFlags & RTCRITSECT_FLAGS_NOP), RTLOCKVAL_SUB_CLASS_INVALID);
+ return RTLockValidatorRecExclSetSubClass(pCritSect->pValidatorRec, uSubClass);
+# else
+ RT_NOREF_PV(pCritSect); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+# endif
+}
+
+
+DECL_FORCE_INLINE(int) rtCritSectTryEnter(PRTCRITSECT pCritSect, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ Assert(pCritSect);
+ Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
+ /*AssertReturn(pCritSect->u32Magic == RTCRITSECT_MAGIC, VERR_SEM_DESTROYED);*/
+#ifdef IN_RING0
+ Assert(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Return straight away if NOP.
+ */
+ if (pCritSect->fFlags & RTCRITSECT_FLAGS_NOP)
+ return VINF_SUCCESS;
+
+ /*
+ * Try take the lock. (cLockers is -1 if it's free)
+ */
+ RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf();
+ if (!ASMAtomicCmpXchgS32(&pCritSect->cLockers, 0, -1))
+ {
+ /*
+ * Somebody is owning it (or will be soon). Perhaps it's us?
+ */
+ if (pCritSect->NativeThreadOwner == NativeThreadSelf)
+ {
+ if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING))
+ {
+#ifdef RTCRITSECT_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(pCritSect->pValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ int32_t cLockers = ASMAtomicIncS32(&pCritSect->cLockers); NOREF(cLockers);
+ pCritSect->cNestings++;
+ IPRT_CRITSECT_ENTERED(pCritSect, NULL, cLockers, pCritSect->cNestings);
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("Nested entry of critsect %p\n", pCritSect));
+ return VERR_SEM_NESTED;
+ }
+ IPRT_CRITSECT_BUSY(pCritSect, NULL, pCritSect->cLockers, (void *)pCritSect->NativeThreadOwner);
+ return VERR_SEM_BUSY;
+ }
+
+ /*
+ * First time
+ */
+ pCritSect->cNestings = 1;
+ ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NativeThreadSelf);
+#ifdef RTCRITSECT_STRICT
+ RTLockValidatorRecExclSetOwner(pCritSect->pValidatorRec, NIL_RTTHREAD, pSrcPos, true);
+#endif
+ IPRT_CRITSECT_ENTERED(pCritSect, NULL, 0, 1);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCritSectTryEnter(PRTCRITSECT pCritSect)
+{
+#ifndef RTCRTISECT_STRICT
+ return rtCritSectTryEnter(pCritSect, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectTryEnter(pCritSect, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectTryEnter);
+
+
+RTDECL(int) RTCritSectTryEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectTryEnter(pCritSect, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTCritSectTryEnterDebug);
+
+
+DECL_FORCE_INLINE(int) rtCritSectEnter(PRTCRITSECT pCritSect, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ AssertPtr(pCritSect);
+ AssertReturn(pCritSect->u32Magic == RTCRITSECT_MAGIC, VERR_SEM_DESTROYED);
+#ifdef IN_RING0
+ Assert(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Return straight away if NOP.
+ */
+ if (pCritSect->fFlags & RTCRITSECT_FLAGS_NOP)
+ return VINF_SUCCESS;
+
+ /*
+ * How is calling and is the order right?
+ */
+ RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf();
+#ifdef RTCRITSECT_STRICT
+ RTTHREAD hThreadSelf = pCritSect->pValidatorRec
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+ int rc9;
+ if (pCritSect->pValidatorRec) /* (bootstap) */
+ {
+ rc9 = RTLockValidatorRecExclCheckOrder(pCritSect->pValidatorRec, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Increment the waiter counter.
+ * This becomes 0 when the section is free.
+ */
+ int32_t cLockers = ASMAtomicIncS32(&pCritSect->cLockers);
+ if (cLockers > 0)
+ {
+ /*
+ * Nested?
+ */
+ if (pCritSect->NativeThreadOwner == NativeThreadSelf)
+ {
+ if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING))
+ {
+#ifdef RTCRITSECT_STRICT
+ rc9 = RTLockValidatorRecExclRecursion(pCritSect->pValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ {
+ ASMAtomicDecS32(&pCritSect->cLockers);
+ return rc9;
+ }
+#endif
+ pCritSect->cNestings++;
+ IPRT_CRITSECT_ENTERED(pCritSect, NULL, cLockers, pCritSect->cNestings);
+ return VINF_SUCCESS;
+ }
+
+ AssertBreakpoint(); /* don't do normal assertion here, the logger uses this code too. */
+ ASMAtomicDecS32(&pCritSect->cLockers);
+ return VERR_SEM_NESTED;
+ }
+
+ /*
+ * Wait for the current owner to release it.
+ */
+ IPRT_CRITSECT_WAITING(pCritSect, NULL, cLockers, (void *)pCritSect->NativeThreadOwner);
+#if !defined(RTCRITSECT_STRICT) && defined(IN_RING3)
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ for (;;)
+ {
+#ifdef RTCRITSECT_STRICT
+ rc9 = RTLockValidatorRecExclCheckBlocking(pCritSect->pValidatorRec, hThreadSelf, pSrcPos,
+ !(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING),
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_CRITSECT, false);
+ if (RT_FAILURE(rc9))
+ {
+ ASMAtomicDecS32(&pCritSect->cLockers);
+ return rc9;
+ }
+#elif defined(IN_RING3)
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_CRITSECT, false);
+#endif
+ int rc = RTSemEventWait(pCritSect->EventSem, RT_INDEFINITE_WAIT);
+#ifdef IN_RING3
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_CRITSECT);
+#endif
+
+ if (pCritSect->u32Magic != RTCRITSECT_MAGIC)
+ return VERR_SEM_DESTROYED;
+ if (rc == VINF_SUCCESS)
+ break;
+ AssertMsg(rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED, ("rc=%Rrc\n", rc));
+ }
+ AssertMsg(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD, ("pCritSect->NativeThreadOwner=%p\n", pCritSect->NativeThreadOwner));
+ }
+
+ /*
+ * First time
+ */
+ pCritSect->cNestings = 1;
+ ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NativeThreadSelf);
+#ifdef RTCRITSECT_STRICT
+ RTLockValidatorRecExclSetOwner(pCritSect->pValidatorRec, hThreadSelf, pSrcPos, true);
+#endif
+ IPRT_CRITSECT_ENTERED(pCritSect, NULL, 0, 1);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCritSectEnter(PRTCRITSECT pCritSect)
+{
+#ifndef RTCRITSECT_STRICT
+ return rtCritSectEnter(pCritSect, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectEnter(pCritSect, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectEnter);
+
+
+RTDECL(int) RTCritSectEnterDebug(PRTCRITSECT pCritSect, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectEnter(pCritSect, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTCritSectEnterDebug);
+
+
+RTDECL(int) RTCritSectLeave(PRTCRITSECT pCritSect)
+{
+ /*
+ * Assert sanity and check for NOP.
+ */
+ Assert(pCritSect);
+ Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
+#ifdef IN_RING0
+ Assert(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+ if (pCritSect->fFlags & RTCRITSECT_FLAGS_NOP)
+ return VINF_SUCCESS;
+
+ /*
+ * Assert ownership and so on.
+ */
+ Assert(pCritSect->cNestings > 0);
+ Assert(pCritSect->cLockers >= 0);
+ Assert(pCritSect->NativeThreadOwner == RTThreadNativeSelf());
+
+#ifdef RTCRITSECT_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(pCritSect->pValidatorRec, pCritSect->cNestings == 1);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+
+ /*
+ * Decrement nestings, if <= 0 when we'll release the critsec.
+ */
+ uint32_t cNestings = --pCritSect->cNestings;
+ IPRT_CRITSECT_LEAVING(pCritSect, NULL, ASMAtomicUoReadS32(&pCritSect->cLockers) - 1, cNestings);
+ if (cNestings > 0)
+ ASMAtomicDecS32(&pCritSect->cLockers);
+ else
+ {
+ /*
+ * Set owner to zero.
+ * Decrement waiters, if >= 0 then we have to wake one of them up.
+ */
+ ASMAtomicWriteHandle(&pCritSect->NativeThreadOwner, NIL_RTNATIVETHREAD);
+ if (ASMAtomicDecS32(&pCritSect->cLockers) >= 0)
+ {
+ int rc = RTSemEventSignal(pCritSect->EventSem);
+ AssertReleaseMsgRC(rc, ("RTSemEventSignal -> %Rrc\n", rc));
+ }
+ }
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTCritSectLeave);
+
+
+
+#ifdef IN_RING3
+
+static int rtCritSectEnterMultiple(size_t cCritSects, PRTCRITSECT *papCritSects, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ Assert(cCritSects > 0);
+ AssertPtr(papCritSects);
+
+ /*
+ * Try get them all.
+ */
+ int rc = VERR_INVALID_PARAMETER;
+ size_t i;
+ for (i = 0; i < cCritSects; i++)
+ {
+ rc = rtCritSectTryEnter(papCritSects[i], pSrcPos);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /*
+ * The retry loop.
+ */
+ for (unsigned cTries = 0; ; cTries++)
+ {
+ /*
+ * We've failed, release any locks we might have gotten. ('i' is the lock that failed btw.)
+ */
+ size_t j = i;
+ while (j-- > 0)
+ {
+ int rc2 = RTCritSectLeave(papCritSects[j]);
+ AssertRC(rc2);
+ }
+ if (rc != VERR_SEM_BUSY)
+ return rc;
+
+ /*
+ * Try prevent any theoretical synchronous races with other threads.
+ */
+ Assert(cTries < 1000000);
+ if (cTries > 10000)
+ RTThreadSleep(cTries % 3);
+
+ /*
+ * Wait on the one we failed to get.
+ */
+ rc = rtCritSectEnter(papCritSects[i], pSrcPos);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try take the others.
+ */
+ for (j = 0; j < cCritSects; j++)
+ {
+ if (j != i)
+ {
+ rc = rtCritSectTryEnter(papCritSects[j], pSrcPos);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /*
+ * We failed.
+ */
+ if (i > j)
+ {
+ int rc2 = RTCritSectLeave(papCritSects[i]);
+ AssertRC(rc2);
+ }
+ i = j;
+ }
+}
+
+
+RTDECL(int) RTCritSectEnterMultiple(size_t cCritSects, PRTCRITSECT *papCritSects)
+{
+#ifndef RTCRITSECT_STRICT
+ return rtCritSectEnterMultiple(cCritSects, papCritSects, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectEnterMultiple(cCritSects, papCritSects, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectEnterMultiple);
+
+
+RTDECL(int) RTCritSectEnterMultipleDebug(size_t cCritSects, PRTCRITSECT *papCritSects, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectEnterMultiple(cCritSects, papCritSects, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTCritSectEnterMultipleDebug);
+
+
+
+RTDECL(int) RTCritSectLeaveMultiple(size_t cCritSects, PRTCRITSECT *papCritSects)
+{
+ int rc = VINF_SUCCESS;
+ for (size_t i = 0; i < cCritSects; i++)
+ {
+ int rc2 = RTCritSectLeave(papCritSects[i]);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCritSectLeaveMultiple);
+
+#endif /* IN_RING3 */
+
+
+
+RTDECL(int) RTCritSectDelete(PRTCRITSECT pCritSect)
+{
+ /*
+ * Assert free waiters and so on.
+ */
+ Assert(pCritSect);
+ Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
+ Assert(pCritSect->cNestings == 0);
+ Assert(pCritSect->cLockers == -1);
+ Assert(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD);
+#ifdef IN_RING0
+ Assert(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pCritSect->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ /*
+ * Invalidate the structure and free the mutex.
+ * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
+ */
+ ASMAtomicWriteU32(&pCritSect->u32Magic, ~RTCRITSECT_MAGIC);
+ pCritSect->fFlags = 0;
+ pCritSect->cNestings = 0;
+ pCritSect->NativeThreadOwner= NIL_RTNATIVETHREAD;
+ RTSEMEVENT EventSem = pCritSect->EventSem;
+ pCritSect->EventSem = NIL_RTSEMEVENT;
+
+ while (pCritSect->cLockers-- >= 0)
+ RTSemEventSignal(EventSem);
+ ASMAtomicWriteS32(&pCritSect->cLockers, -1);
+ int rc = RTSemEventDestroy(EventSem);
+ AssertRC(rc);
+
+#ifdef RTCRITSECT_STRICT
+ RTLockValidatorRecExclDestroy(&pCritSect->pValidatorRec);
+#endif
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCritSectDelete);
+
diff --git a/src/VBox/Runtime/generic/critsectrw-generic.cpp b/src/VBox/Runtime/generic/critsectrw-generic.cpp
new file mode 100644
index 00000000..caffd045
--- /dev/null
+++ b/src/VBox/Runtime/generic/critsectrw-generic.cpp
@@ -0,0 +1,1080 @@
+/* $Id: critsectrw-generic.cpp $ */
+/** @file
+ * IPRT - Read/Write Critical Section, Generic.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define RTCRITSECTRW_WITHOUT_REMAPPING
+#define RTASSERT_QUIET
+#include <iprt/critsect.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+/* Two issues here, (1) the tracepoint generator uses IPRT, and (2) only one .d
+ file per module. */
+#ifdef IPRT_WITH_DTRACE
+# include IPRT_DTRACE_INCLUDE
+# ifdef IPRT_DTRACE_PREFIX
+# define IPRT_CRITSECTRW_EXCL_ENTERED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_ENTERED)
+# define IPRT_CRITSECTRW_EXCL_ENTERED_ENABLED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_ENTERED_ENABLED)
+# define IPRT_CRITSECTRW_EXCL_LEAVING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_LEAVING)
+# define IPRT_CRITSECTRW_EXCL_LEAVING_ENABLED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_LEAVING_ENABLED)
+# define IPRT_CRITSECTRW_EXCL_BUSY RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_BUSY)
+# define IPRT_CRITSECTRW_EXCL_WAITING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_WAITING)
+# define IPRT_CRITSECTRW_EXCL_ENTERED_SHARED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_ENTERED_SHARED)
+# define IPRT_CRITSECTRW_EXCL_LEAVING_SHARED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_EXCL_LEAVING_SHARED)
+# define IPRT_CRITSECTRW_SHARED_ENTERED RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_SHARED_ENTERED)
+# define IPRT_CRITSECTRW_SHARED_LEAVING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_SHARED_LEAVING)
+# define IPRT_CRITSECTRW_SHARED_BUSY RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_SHARED_BUSY)
+# define IPRT_CRITSECTRW_SHARED_WAITING RT_CONCAT(IPRT_DTRACE_PREFIX,IPRT_CRITSECTRW_SHARED_WAITING)
+# endif
+#else
+# define IPRT_CRITSECTRW_EXCL_ENTERED(a_pvCritSect, a_pszName, a_cNestings, a_cWaitingReaders, a_cWriters) do {} while (0)
+# define IPRT_CRITSECTRW_EXCL_ENTERED_ENABLED() (false)
+# define IPRT_CRITSECTRW_EXCL_LEAVING(a_pvCritSect, a_pszName, a_cNestings, a_cWaitingReaders, a_cWriters) do {} while (0)
+# define IPRT_CRITSECTRW_EXCL_LEAVING_ENABLED() (false)
+# define IPRT_CRITSECTRW_EXCL_BUSY( a_pvCritSect, a_pszName, a_fWriteMode, a_cWaitingReaders, a_cReaders, cWriters, a_pvNativeOwnerThread) do {} while (0)
+# define IPRT_CRITSECTRW_EXCL_WAITING(a_pvCritSect, a_pszName, a_fWriteMode, a_cWaitingReaders, a_cReaders, cWriters, a_pvNativeOwnerThread) do {} while (0)
+# define IPRT_CRITSECTRW_EXCL_ENTERED_SHARED(a_pvCritSect, a_pszName, a_cNestings, a_cWaitingReaders, a_cWriters) do {} while (0)
+# define IPRT_CRITSECTRW_EXCL_LEAVING_SHARED(a_pvCritSect, a_pszName, a_cNestings, a_cWaitingReaders, a_cWriters) do {} while (0)
+# define IPRT_CRITSECTRW_SHARED_ENTERED(a_pvCritSect, a_pszName, a_cReaders, a_cWaitingWriters) do {} while (0)
+# define IPRT_CRITSECTRW_SHARED_LEAVING(a_pvCritSect, a_pszName, a_cReaders, a_cWaitingWriters) do {} while (0)
+# define IPRT_CRITSECTRW_SHARED_BUSY( a_pvCritSect, a_pszName, a_pvNativeOwnerThread, a_cWaitingReaders, a_cWriters) do {} while (0)
+# define IPRT_CRITSECTRW_SHARED_WAITING(a_pvCritSect, a_pszName, a_pvNativeOwnerThread, a_cWaitingReaders, a_cWriters) do {} while (0)
+#endif
+
+
+
+RTDECL(int) RTCritSectRwInit(PRTCRITSECTRW pThis)
+{
+ return RTCritSectRwInitEx(pThis, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTCritSectRw");
+}
+RT_EXPORT_SYMBOL(RTCritSectRwInit);
+
+
+RTDECL(int) RTCritSectRwInitEx(PRTCRITSECTRW pThis, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ int rc;
+ AssertReturn(!(fFlags & ~( RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_BOOTSTRAP_HACK
+ | RTCRITSECT_FLAGS_NOP )),
+ VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+
+
+ /*
+ * Initialize the structure, allocate the lock validator stuff and sems.
+ */
+ pThis->u32Magic = RTCRITSECTRW_MAGIC_DEAD;
+ pThis->fNeedReset = false;
+#ifdef IN_RING0
+ pThis->fFlags = (uint16_t)(fFlags | RTCRITSECT_FLAGS_RING0);
+#else
+ pThis->fFlags = (uint16_t)(fFlags & ~RTCRITSECT_FLAGS_RING0);
+#endif
+ pThis->u.u128.s.Hi = 0;
+ pThis->u.u128.s.Lo = 0;
+ pThis->u.s.hNativeWriter= NIL_RTNATIVETHREAD;
+ AssertCompile(sizeof(pThis->u.u128) >= sizeof(pThis->u.s));
+ pThis->cWriterReads = 0;
+ pThis->cWriteRecursions = 0;
+ pThis->hEvtWrite = NIL_RTSEMEVENT;
+ pThis->hEvtRead = NIL_RTSEMEVENTMULTI;
+ pThis->pValidatorWrite = NULL;
+ pThis->pValidatorRead = NULL;
+
+#ifdef RTCRITSECTRW_STRICT
+ bool const fLVEnabled = !(fFlags & RTCRITSECT_FLAGS_NO_LOCK_VAL);
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iAnon = 0;
+ uint32_t i = ASMAtomicIncU32(&s_iAnon) - 1;
+ rc = RTLockValidatorRecExclCreate(&pThis->pValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, "RTCritSectRw-%u", i);
+ if (RT_SUCCESS(rc))
+ rc = RTLockValidatorRecSharedCreate(&pThis->pValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, "RTCritSectRw-%u", i);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ rc = RTLockValidatorRecExclCreateV(&pThis->pValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ if (RT_SUCCESS(rc))
+ {
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedCreateV(&pThis->pValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ rc = RTLockValidatorRecMakeSiblings(&pThis->pValidatorWrite->Core, &pThis->pValidatorRead->Core);
+
+ if (RT_SUCCESS(rc))
+#endif
+ {
+ rc = RTSemEventMultiCreate(&pThis->hEvtRead);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pThis->hEvtWrite);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTCRITSECTRW_MAGIC;
+ return VINF_SUCCESS;
+ }
+ RTSemEventMultiDestroy(pThis->hEvtRead);
+ }
+ }
+
+#ifdef RTCRITSECTRW_STRICT
+ RTLockValidatorRecSharedDestroy(&pThis->pValidatorRead);
+ RTLockValidatorRecExclDestroy(&pThis->pValidatorWrite);
+#endif
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwInitEx);
+
+
+RTDECL(uint32_t) RTCritSectRwSetSubClass(PRTCRITSECTRW pThis, uint32_t uSubClass)
+{
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+#ifdef RTCRITSECTRW_STRICT
+ AssertReturn(!(pThis->fFlags & RTCRITSECT_FLAGS_NOP), RTLOCKVAL_SUB_CLASS_INVALID);
+
+ RTLockValidatorRecSharedSetSubClass(pThis->pValidatorRead, uSubClass);
+ return RTLockValidatorRecExclSetSubClass(pThis->pValidatorWrite, uSubClass);
+#else
+ NOREF(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwSetSubClass);
+
+
+static int rtCritSectRwEnterShared(PRTCRITSECTRW pThis, PCRTLOCKVALSRCPOS pSrcPos, bool fTryOnly)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, VERR_SEM_DESTROYED);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+ RT_NOREF_PV(pSrcPos);
+
+#ifdef RTCRITSECTRW_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (!fTryOnly)
+ {
+ int rc9;
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ if (hNativeWriter != NIL_RTTHREAD && hNativeWriter == RTThreadNativeSelf())
+ rc9 = RTLockValidatorRecExclCheckOrder(pThis->pValidatorWrite, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT);
+ else
+ rc9 = RTLockValidatorRecSharedCheckOrder(pThis->pValidatorRead, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Get cracking...
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ uint64_t u64OldState = u64State;
+
+ for (;;)
+ {
+ if ((u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT))
+ {
+ /* It flows in the right direction, try follow it before it changes. */
+ uint64_t c = (u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT;
+ c++;
+ Assert(c < RTCSRW_CNT_MASK / 2);
+ u64State &= ~RTCSRW_CNT_RD_MASK;
+ u64State |= c << RTCSRW_CNT_RD_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+#ifdef RTCRITSECTRW_STRICT
+ RTLockValidatorRecSharedAddOwner(pThis->pValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+ else if ((u64State & (RTCSRW_CNT_RD_MASK | RTCSRW_CNT_WR_MASK)) == 0)
+ {
+ /* Wrong direction, but we're alone here and can simply try switch the direction. */
+ u64State &= ~(RTCSRW_CNT_RD_MASK | RTCSRW_CNT_WR_MASK | RTCSRW_DIR_MASK);
+ u64State |= (UINT64_C(1) << RTCSRW_CNT_RD_SHIFT) | (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ Assert(!pThis->fNeedReset);
+#ifdef RTCRITSECTRW_STRICT
+ RTLockValidatorRecSharedAddOwner(pThis->pValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+ else
+ {
+ /* Is the writer perhaps doing a read recursion? */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ if (hNativeSelf == hNativeWriter)
+ {
+#ifdef RTCRITSECTRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursionMixed(pThis->pValidatorWrite, &pThis->pValidatorRead->Core, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWriterReads < UINT32_MAX / 2);
+ uint32_t const cReads = ASMAtomicIncU32(&pThis->cWriterReads); NOREF(cReads);
+ IPRT_CRITSECTRW_EXCL_ENTERED_SHARED(pThis, NULL,
+ cReads + pThis->cWriteRecursions,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+
+ return VINF_SUCCESS; /* don't break! */
+ }
+
+ /* If we're only trying, return already. */
+ if (fTryOnly)
+ {
+ IPRT_CRITSECTRW_SHARED_BUSY(pThis, NULL,
+ (void *)pThis->u.s.hNativeWriter,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ return VERR_SEM_BUSY;
+ }
+
+ /* Add ourselves to the queue and wait for the direction to change. */
+ uint64_t c = (u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT;
+ c++;
+ Assert(c < RTCSRW_CNT_MASK / 2);
+
+ uint64_t cWait = (u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT;
+ cWait++;
+ Assert(cWait <= c);
+ Assert(cWait < RTCSRW_CNT_MASK / 2);
+
+ u64State &= ~(RTCSRW_CNT_RD_MASK | RTCSRW_WAIT_CNT_RD_MASK);
+ u64State |= (c << RTCSRW_CNT_RD_SHIFT) | (cWait << RTCSRW_WAIT_CNT_RD_SHIFT);
+
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ IPRT_CRITSECTRW_SHARED_WAITING(pThis, NULL,
+ (void *)pThis->u.s.hNativeWriter,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ for (uint32_t iLoop = 0; ; iLoop++)
+ {
+ int rc;
+#ifdef RTCRITSECTRW_STRICT
+ rc = RTLockValidatorRecSharedCheckBlocking(pThis->pValidatorRead, hThreadSelf, pSrcPos, true,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_RW_READ, false);
+ if (RT_SUCCESS(rc))
+#elif defined(IN_RING3)
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_READ, false);
+#endif
+ {
+ rc = RTSemEventMultiWait(pThis->hEvtRead, RT_INDEFINITE_WAIT);
+#ifdef IN_RING3
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_READ);
+#endif
+ if (pThis->u32Magic != RTCRITSECTRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* Decrement the counts and return the error. */
+ for (;;)
+ {
+ u64OldState = u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ c = (u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT; Assert(c > 0);
+ c--;
+ cWait = (u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT; Assert(cWait > 0);
+ cWait--;
+ u64State &= ~(RTCSRW_CNT_RD_MASK | RTCSRW_WAIT_CNT_RD_MASK);
+ u64State |= (c << RTCSRW_CNT_RD_SHIFT) | (cWait << RTCSRW_WAIT_CNT_RD_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ return rc;
+ }
+
+ Assert(pThis->fNeedReset);
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ if ((u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT))
+ break;
+ AssertMsg(iLoop < 1, ("%u\n", iLoop));
+ }
+
+ /* Decrement the wait count and maybe reset the semaphore (if we're last). */
+ for (;;)
+ {
+ u64OldState = u64State;
+
+ cWait = (u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT;
+ Assert(cWait > 0);
+ cWait--;
+ u64State &= ~RTCSRW_WAIT_CNT_RD_MASK;
+ u64State |= cWait << RTCSRW_WAIT_CNT_RD_SHIFT;
+
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ if (cWait == 0)
+ {
+ if (ASMAtomicXchgBool(&pThis->fNeedReset, false))
+ {
+ int rc = RTSemEventMultiReset(pThis->hEvtRead);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ break;
+ }
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ }
+
+#ifdef RTCRITSECTRW_STRICT
+ RTLockValidatorRecSharedAddOwner(pThis->pValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+
+ if (pThis->u32Magic != RTCRITSECTRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ u64OldState = u64State;
+ }
+
+ /* got it! */
+ Assert((ASMAtomicReadU64(&pThis->u.s.u64State) & RTCSRW_DIR_MASK) == (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT));
+ IPRT_CRITSECTRW_SHARED_ENTERED(pThis, NULL,
+ (uint32_t)((u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCritSectRwEnterShared(PRTCRITSECTRW pThis)
+{
+#ifndef RTCRITSECTRW_STRICT
+ return rtCritSectRwEnterShared(pThis, NULL, false /*fTryOnly*/);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectRwEnterShared(pThis, &SrcPos, false /*fTryOnly*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterShared);
+
+
+RTDECL(int) RTCritSectRwEnterSharedDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectRwEnterShared(pThis, &SrcPos, false /*fTryOnly*/);
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterSharedDebug);
+
+
+RTDECL(int) RTCritSectRwTryEnterShared(PRTCRITSECTRW pThis)
+{
+#ifndef RTCRITSECTRW_STRICT
+ return rtCritSectRwEnterShared(pThis, NULL, true /*fTryOnly*/);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectRwEnterShared(pThis, &SrcPos, true /*fTryOnly*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterShared);
+
+
+RTDECL(int) RTCritSectRwTryEnterSharedDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectRwEnterShared(pThis, &SrcPos, true /*fTryOnly*/);
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterSharedDebug);
+
+
+
+RTDECL(int) RTCritSectRwLeaveShared(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, VERR_SEM_DESTROYED);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ /*
+ * Check the direction and take action accordingly.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ uint64_t u64OldState = u64State;
+ if ((u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT))
+ {
+#ifdef RTCRITSECTRW_STRICT
+ int rc9 = RTLockValidatorRecSharedCheckAndRelease(pThis->pValidatorRead, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ IPRT_CRITSECTRW_SHARED_LEAVING(pThis, NULL,
+ (uint32_t)((u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT) - 1,
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+
+ for (;;)
+ {
+ uint64_t c = (u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT;
+ AssertReturn(c > 0, VERR_NOT_OWNER);
+ c--;
+
+ if ( c > 0
+ || (u64State & RTCSRW_CNT_WR_MASK) == 0)
+ {
+ /* Don't change the direction. */
+ u64State &= ~RTCSRW_CNT_RD_MASK;
+ u64State |= c << RTCSRW_CNT_RD_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ else
+ {
+ /* Reverse the direction and signal the reader threads. */
+ u64State &= ~(RTCSRW_CNT_RD_MASK | RTCSRW_DIR_MASK);
+ u64State |= RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ int rc = RTSemEventSignal(pThis->hEvtWrite);
+ AssertRC(rc);
+ break;
+ }
+ }
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ u64OldState = u64State;
+ }
+ }
+ else
+ {
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ AssertReturn(hNativeSelf == hNativeWriter, VERR_NOT_OWNER);
+ AssertReturn(pThis->cWriterReads > 0, VERR_NOT_OWNER);
+#ifdef RTCRITSECTRW_STRICT
+ int rc = RTLockValidatorRecExclUnwindMixed(pThis->pValidatorWrite, &pThis->pValidatorRead->Core);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+ uint32_t cReads = ASMAtomicDecU32(&pThis->cWriterReads); NOREF(cReads);
+ IPRT_CRITSECTRW_EXCL_LEAVING_SHARED(pThis, NULL,
+ cReads + pThis->cWriteRecursions,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwLeaveShared);
+
+
+static int rtCritSectRwEnterExcl(PRTCRITSECTRW pThis, PCRTLOCKVALSRCPOS pSrcPos, bool fTryOnly)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, VERR_SEM_DESTROYED);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+ RT_NOREF_PV(pSrcPos);
+
+#ifdef RTCRITSECTRW_STRICT
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (!fTryOnly)
+ {
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrder(pThis->pValidatorWrite, hThreadSelf, pSrcPos, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Check if we're already the owner and just recursing.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ if (hNativeSelf == hNativeWriter)
+ {
+ Assert((ASMAtomicReadU64(&pThis->u.s.u64State) & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT));
+#ifdef RTCRITSECTRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(pThis->pValidatorWrite, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWriteRecursions < UINT32_MAX / 2);
+ uint32_t cNestings = ASMAtomicIncU32(&pThis->cWriteRecursions); NOREF(cNestings);
+
+#ifdef IPRT_WITH_DTRACE
+ if (IPRT_CRITSECTRW_EXCL_ENTERED_ENABLED())
+ {
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ IPRT_CRITSECTRW_EXCL_ENTERED(pThis, NULL, cNestings + pThis->cWriterReads,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ }
+#endif
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Get cracking.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ uint64_t u64OldState = u64State;
+
+ for (;;)
+ {
+ if ( (u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT)
+ || (u64State & (RTCSRW_CNT_RD_MASK | RTCSRW_CNT_WR_MASK)) != 0)
+ {
+ /* It flows in the right direction, try follow it before it changes. */
+ uint64_t c = (u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT;
+ c++;
+ Assert(c < RTCSRW_CNT_MASK / 2);
+ u64State &= ~RTCSRW_CNT_WR_MASK;
+ u64State |= c << RTCSRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ else if ((u64State & (RTCSRW_CNT_RD_MASK | RTCSRW_CNT_WR_MASK)) == 0)
+ {
+ /* Wrong direction, but we're alone here and can simply try switch the direction. */
+ u64State &= ~(RTCSRW_CNT_RD_MASK | RTCSRW_CNT_WR_MASK | RTCSRW_DIR_MASK);
+ u64State |= (UINT64_C(1) << RTCSRW_CNT_WR_SHIFT) | (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ else if (fTryOnly)
+ /* Wrong direction and we're not supposed to wait, just return. */
+ return VERR_SEM_BUSY;
+ else
+ {
+ /* Add ourselves to the write count and break out to do the wait. */
+ uint64_t c = (u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT;
+ c++;
+ Assert(c < RTCSRW_CNT_MASK / 2);
+ u64State &= ~RTCSRW_CNT_WR_MASK;
+ u64State |= c << RTCSRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+
+ if (pThis->u32Magic != RTCRITSECTRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ u64OldState = u64State;
+ }
+
+ /*
+ * If we're in write mode now try grab the ownership. Play fair if there
+ * are threads already waiting.
+ */
+ bool fDone = (u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT)
+ && ( ((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT) == 1
+ || fTryOnly);
+ if (fDone)
+ ASMAtomicCmpXchgHandle(&pThis->u.s.hNativeWriter, hNativeSelf, NIL_RTNATIVETHREAD, fDone);
+ if (!fDone)
+ {
+ /*
+ * If only trying, undo the above writer incrementation and return.
+ */
+ if (fTryOnly)
+ {
+ for (;;)
+ {
+ u64OldState = u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ uint64_t c = (u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT; Assert(c > 0);
+ c--;
+ u64State &= ~RTCSRW_CNT_WR_MASK;
+ u64State |= c << RTCSRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ IPRT_CRITSECTRW_EXCL_BUSY(pThis, NULL,
+ (u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT) /*fWrite*/,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT),
+ (void *)pThis->u.s.hNativeWriter);
+ return VERR_SEM_BUSY;
+ }
+
+ /*
+ * Wait for our turn.
+ */
+ IPRT_CRITSECTRW_EXCL_WAITING(pThis, NULL,
+ (u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT) /*fWrite*/,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT),
+ (void *)pThis->u.s.hNativeWriter);
+ for (uint32_t iLoop = 0; ; iLoop++)
+ {
+ int rc;
+#ifdef RTCRITSECTRW_STRICT
+ if (hThreadSelf == NIL_RTTHREAD)
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ rc = RTLockValidatorRecExclCheckBlocking(pThis->pValidatorWrite, hThreadSelf, pSrcPos, true,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_RW_WRITE, false);
+ if (RT_SUCCESS(rc))
+#elif defined(IN_RING3)
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_WRITE, false);
+#endif
+ {
+ rc = RTSemEventWait(pThis->hEvtWrite, RT_INDEFINITE_WAIT);
+#ifdef IN_RING3
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_WRITE);
+#endif
+ if (pThis->u32Magic != RTCRITSECTRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* Decrement the counts and return the error. */
+ for (;;)
+ {
+ u64OldState = u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ uint64_t c = (u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT; Assert(c > 0);
+ c--;
+ u64State &= ~RTCSRW_CNT_WR_MASK;
+ u64State |= c << RTCSRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ break;
+ }
+ return rc;
+ }
+
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ if ((u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT))
+ {
+ ASMAtomicCmpXchgHandle(&pThis->u.s.hNativeWriter, hNativeSelf, NIL_RTNATIVETHREAD, fDone);
+ if (fDone)
+ break;
+ }
+ AssertMsg(iLoop < 1000, ("%u\n", iLoop)); /* may loop a few times here... */
+ }
+ }
+
+ /*
+ * Got it!
+ */
+ Assert((ASMAtomicReadU64(&pThis->u.s.u64State) & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT));
+ ASMAtomicWriteU32(&pThis->cWriteRecursions, 1);
+ Assert(pThis->cWriterReads == 0);
+#ifdef RTCRITSECTRW_STRICT
+ RTLockValidatorRecExclSetOwner(pThis->pValidatorWrite, hThreadSelf, pSrcPos, true);
+#endif
+ IPRT_CRITSECTRW_EXCL_ENTERED(pThis, NULL, 1,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCritSectRwEnterExcl(PRTCRITSECTRW pThis)
+{
+#ifndef RTCRITSECTRW_STRICT
+ return rtCritSectRwEnterExcl(pThis, NULL, false /*fTryAgain*/);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectRwEnterExcl(pThis, &SrcPos, false /*fTryAgain*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterExcl);
+
+
+RTDECL(int) RTCritSectRwEnterExclDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectRwEnterExcl(pThis, &SrcPos, false /*fTryAgain*/);
+}
+RT_EXPORT_SYMBOL(RTCritSectRwEnterExclDebug);
+
+
+RTDECL(int) RTCritSectRwTryEnterExcl(PRTCRITSECTRW pThis)
+{
+#ifndef RTCRITSECTRW_STRICT
+ return rtCritSectRwEnterExcl(pThis, NULL, true /*fTryAgain*/);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtCritSectRwEnterExcl(pThis, &SrcPos, true /*fTryAgain*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwTryEnterExcl);
+
+
+RTDECL(int) RTCritSectRwTryEnterExclDebug(PRTCRITSECTRW pThis, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtCritSectRwEnterExcl(pThis, &SrcPos, true /*fTryAgain*/);
+}
+RT_EXPORT_SYMBOL(RTCritSectRwTryEnterExclDebug);
+
+
+RTDECL(int) RTCritSectRwLeaveExcl(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, VERR_SEM_DESTROYED);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ AssertReturn(hNativeSelf == hNativeWriter, VERR_NOT_OWNER);
+
+ /*
+ * Unwind a recursion.
+ */
+ if (pThis->cWriteRecursions == 1)
+ {
+ AssertReturn(pThis->cWriterReads == 0, VERR_WRONG_ORDER); /* (must release all read recursions before the final write.) */
+#ifdef RTCRITSECTRW_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(pThis->pValidatorWrite, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ /*
+ * Update the state.
+ */
+ ASMAtomicWriteU32(&pThis->cWriteRecursions, 0);
+ ASMAtomicWriteHandle(&pThis->u.s.hNativeWriter, NIL_RTNATIVETHREAD);
+
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ IPRT_CRITSECTRW_EXCL_LEAVING(pThis, NULL, 0,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+
+ for (;;)
+ {
+ uint64_t u64OldState = u64State;
+
+ uint64_t c = (u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT;
+ Assert(c > 0);
+ c--;
+
+ if ( c > 0
+ || (u64State & RTCSRW_CNT_RD_MASK) == 0)
+ {
+ /* Don't change the direction, wait up the next writer if any. */
+ u64State &= ~RTCSRW_CNT_WR_MASK;
+ u64State |= c << RTCSRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ if (c > 0)
+ {
+ int rc = RTSemEventSignal(pThis->hEvtWrite);
+ AssertRC(rc);
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* Reverse the direction and signal the reader threads. */
+ u64State &= ~(RTCSRW_CNT_WR_MASK | RTCSRW_DIR_MASK);
+ u64State |= RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u.s.u64State, u64State, u64OldState))
+ {
+ Assert(!pThis->fNeedReset);
+ ASMAtomicWriteBool(&pThis->fNeedReset, true);
+ int rc = RTSemEventMultiSignal(pThis->hEvtRead);
+ AssertRC(rc);
+ break;
+ }
+ }
+
+ ASMNopPause();
+ if (pThis->u32Magic != RTCRITSECTRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ }
+ }
+ else
+ {
+ Assert(pThis->cWriteRecursions != 0);
+#ifdef RTCRITSECTRW_STRICT
+ int rc9 = RTLockValidatorRecExclUnwind(pThis->pValidatorWrite);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ uint32_t cNestings = ASMAtomicDecU32(&pThis->cWriteRecursions); NOREF(cNestings);
+#ifdef IPRT_WITH_DTRACE
+ if (IPRT_CRITSECTRW_EXCL_LEAVING_ENABLED())
+ {
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ IPRT_CRITSECTRW_EXCL_LEAVING(pThis, NULL, cNestings + pThis->cWriterReads,
+ (uint32_t)((u64State & RTCSRW_WAIT_CNT_RD_MASK) >> RTCSRW_WAIT_CNT_RD_SHIFT),
+ (uint32_t)((u64State & RTCSRW_CNT_WR_MASK) >> RTCSRW_CNT_WR_SHIFT));
+ }
+#endif
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwLeaveExcl);
+
+
+RTDECL(bool) RTCritSectRwIsWriteOwner(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, false);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ /*
+ * Check ownership.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hNativeWriter);
+ return hNativeWriter == hNativeSelf;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwIsWriteOwner);
+
+
+RTDECL(bool) RTCritSectRwIsReadOwner(PRTCRITSECTRW pThis, bool fWannaHear)
+{
+ RT_NOREF_PV(fWannaHear);
+
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, false);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ /*
+ * Inspect the state.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ if ((u64State & RTCSRW_DIR_MASK) == (RTCSRW_DIR_WRITE << RTCSRW_DIR_SHIFT))
+ {
+ /*
+ * It's in write mode, so we can only be a reader if we're also the
+ * current writer.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hWriter;
+ ASMAtomicUoReadHandle(&pThis->u.s.hNativeWriter, &hWriter);
+ return hWriter == hNativeSelf;
+ }
+
+ /*
+ * Read mode. If there are no current readers, then we cannot be a reader.
+ */
+ if (!(u64State & RTCSRW_CNT_RD_MASK))
+ return false;
+
+#ifdef RTCRITSECTRW_STRICT
+ /*
+ * Ask the lock validator.
+ */
+ return RTLockValidatorRecSharedIsOwner(pThis->pValidatorRead, NIL_RTTHREAD);
+#else
+ /*
+ * Ok, we don't know, just tell the caller what he want to hear.
+ */
+ return fWannaHear;
+#endif
+}
+RT_EXPORT_SYMBOL(RTCritSectRwIsReadOwner);
+
+
+RTDECL(uint32_t) RTCritSectRwGetWriteRecursion(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriteRecursions;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwGetWriteRecursion);
+
+
+RTDECL(uint32_t) RTCritSectRwGetWriterReadRecursion(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate handle.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriterReads;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwGetWriterReadRecursion);
+
+
+RTDECL(uint32_t) RTCritSectRwGetReadCount(PRTCRITSECTRW pThis)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pThis);
+ AssertReturn(pThis->u32Magic == RTCRITSECTRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u.s.u64State);
+ if ((u64State & RTCSRW_DIR_MASK) != (RTCSRW_DIR_READ << RTCSRW_DIR_SHIFT))
+ return 0;
+ return (u64State & RTCSRW_CNT_RD_MASK) >> RTCSRW_CNT_RD_SHIFT;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwGetReadCount);
+
+
+RTDECL(int) RTCritSectRwDelete(PRTCRITSECTRW pThis)
+{
+ /*
+ * Assert free waiters and so on.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTCRITSECTRW_MAGIC);
+ //Assert(pThis->cNestings == 0);
+ //Assert(pThis->cLockers == -1);
+ Assert(pThis->u.s.hNativeWriter == NIL_RTNATIVETHREAD);
+#ifdef IN_RING0
+ Assert(pThis->fFlags & RTCRITSECT_FLAGS_RING0);
+#else
+ Assert(!(pThis->fFlags & RTCRITSECT_FLAGS_RING0));
+#endif
+
+ /*
+ * Invalidate the structure and free the semaphores.
+ */
+ if (!ASMAtomicCmpXchgU32(&pThis->u32Magic, RTCRITSECTRW_MAGIC_DEAD, RTCRITSECTRW_MAGIC))
+ return VERR_INVALID_PARAMETER;
+
+ pThis->fFlags = 0;
+ pThis->u.s.u64State = 0;
+
+ RTSEMEVENT hEvtWrite = pThis->hEvtWrite;
+ pThis->hEvtWrite = NIL_RTSEMEVENT;
+ RTSEMEVENTMULTI hEvtRead = pThis->hEvtRead;
+ pThis->hEvtRead = NIL_RTSEMEVENTMULTI;
+
+ int rc1 = RTSemEventDestroy(hEvtWrite); AssertRC(rc1);
+ int rc2 = RTSemEventMultiDestroy(hEvtRead); AssertRC(rc2);
+
+#ifndef IN_RING0
+ RTLockValidatorRecSharedDestroy(&pThis->pValidatorRead);
+ RTLockValidatorRecExclDestroy(&pThis->pValidatorWrite);
+#endif
+
+ return RT_SUCCESS(rc1) ? rc2 : rc1;
+}
+RT_EXPORT_SYMBOL(RTCritSectRwDelete);
+
diff --git a/src/VBox/Runtime/generic/env-generic.cpp b/src/VBox/Runtime/generic/env-generic.cpp
new file mode 100644
index 00000000..f7892dce
--- /dev/null
+++ b/src/VBox/Runtime/generic/env-generic.cpp
@@ -0,0 +1,1409 @@
+/* $Id: env-generic.cpp $ */
+/** @file
+ * IPRT - Environment, Generic.
+ */
+
+/*
+ * 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/env.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/err.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/magics.h"
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/nt/nt.h>
+#else
+# include <stdlib.h>
+# if !defined(RT_OS_WINDOWS)
+# include <unistd.h>
+# endif
+# ifdef RT_OS_DARWIN
+# include <crt_externs.h>
+# endif
+# if defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
+RT_C_DECLS_BEGIN
+extern char **environ;
+RT_C_DECLS_END
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The allocation granularity of the RTENVINTERNAL::papszEnv memory. */
+#define RTENV_GROW_SIZE 16
+
+/** Macro that unlocks the specified environment block. */
+#define RTENV_LOCK(pEnvInt) do { } while (0)
+/** Macro that unlocks the specified environment block. */
+#define RTENV_UNLOCK(pEnvInt) do { } while (0)
+
+/** @def RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ * Indicates the RTEnv*Utf8 APIs are implemented. */
+#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
+# define RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API 1
+#endif
+
+/** @def RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ * Allows a variable to start with an '=' sign by default. This is used by
+ * windows to maintain CWDs of non-current drives.
+ * @note Not supported by _wputenv AFAIK. */
+#if defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
+# define RTENV_ALLOW_EQUAL_FIRST_IN_VAR 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a (non-default) environment.
+ */
+typedef struct RTENVINTERNAL
+{
+ /** Magic value . */
+ uint32_t u32Magic;
+ /** Set if this is a record of environment changes, putenv style. */
+ bool fPutEnvBlock;
+ /** Set if starting a variable with an equal sign is okay, clear if not okay
+ * (RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR). */
+ bool fFirstEqual;
+ /** Number of variables in the array.
+ * This does not include the terminating NULL entry. */
+ size_t cVars;
+ /** Capacity (allocated size) of the array.
+ * This includes space for the terminating NULL element (for compatibility
+ * with the C library), so that c <= cCapacity - 1. */
+ size_t cAllocated;
+ /** Array of environment variables.
+ * These are always in "NAME=VALUE" form, where the value can be empty. If
+ * fPutEnvBlock is set though, there will be "NAME" entries too for variables
+ * that need to be removed when merged with another environment block. */
+ char **papszEnv;
+ /** Array of environment variables in the process CP.
+ * This get (re-)constructed when RTEnvGetExecEnvP method is called. */
+ char **papszEnvOtherCP;
+
+ /** The compare function we're using. */
+ DECLCALLBACKMEMBER(int, pfnCompare,(const char *psz1, const char *psz2, size_t cchMax));
+} RTENVINTERNAL, *PRTENVINTERNAL;
+
+
+#ifndef RT_OS_WINDOWS
+/**
+ * Internal worker that resolves the pointer to the default
+ * process environment. (environ)
+ *
+ * @returns Pointer to the default environment.
+ * This may be NULL.
+ */
+static const char * const *rtEnvDefault(void)
+{
+# ifdef RT_OS_DARWIN
+ return *(_NSGetEnviron());
+# else
+ return environ;
+# endif
+}
+#endif
+
+
+/**
+ * Internal worker that creates an environment handle with a specified capacity.
+ *
+ * @returns IPRT status code.
+ * @param ppIntEnv Where to store the result.
+ * @param cAllocated The initial array size.
+ * @param fCaseSensitive Whether the environment block is case sensitive or
+ * not.
+ * @param fPutEnvBlock Indicates whether this is a special environment
+ * block that will be used to record change another
+ * block. We will keep unsets in putenv format, i.e.
+ * just the variable name without any equal sign.
+ * @param fFirstEqual The RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR value.
+ */
+static int rtEnvCreate(PRTENVINTERNAL *ppIntEnv, size_t cAllocated, bool fCaseSensitive, bool fPutEnvBlock, bool fFirstEqual)
+{
+ /*
+ * Allocate environment handle.
+ */
+ PRTENVINTERNAL pIntEnv = (PRTENVINTERNAL)RTMemAlloc(sizeof(*pIntEnv));
+ if (pIntEnv)
+ {
+ /*
+ * Pre-allocate the variable array.
+ */
+ pIntEnv->u32Magic = RTENV_MAGIC;
+ pIntEnv->fPutEnvBlock = fPutEnvBlock;
+ pIntEnv->fFirstEqual = fFirstEqual;
+ pIntEnv->pfnCompare = fCaseSensitive ? RTStrNCmp : RTStrNICmp;
+ pIntEnv->papszEnvOtherCP = NULL;
+ pIntEnv->cVars = 0;
+ pIntEnv->cAllocated = RT_ALIGN_Z(RT_MAX(cAllocated, RTENV_GROW_SIZE), RTENV_GROW_SIZE);
+ pIntEnv->papszEnv = (char **)RTMemAllocZ(sizeof(pIntEnv->papszEnv[0]) * pIntEnv->cAllocated);
+ if (pIntEnv->papszEnv)
+ {
+ *ppIntEnv = pIntEnv;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pIntEnv);
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTEnvCreate(PRTENV pEnv)
+{
+ AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ return rtEnvCreate(pEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/, false/*fFirstEqual*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTEnvCreate);
+
+
+RTDECL(int) RTEnvCreateEx(PRTENV phEnv, uint32_t fFlags)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, false /*fPutEnvBlock*/,
+ RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
+}
+RT_EXPORT_SYMBOL(RTEnvCreateEx);
+
+
+RTDECL(int) RTEnvDestroy(RTENV Env)
+{
+ /*
+ * Ignore NIL_RTENV and validate input.
+ */
+ if ( Env == NIL_RTENV
+ || Env == RTENV_DEFAULT)
+ return VINF_SUCCESS;
+
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ RTENV_LOCK(pIntEnv);
+ pIntEnv->u32Magic++;
+ size_t iVar = pIntEnv->cVars;
+ while (iVar-- > 0)
+ RTStrFree(pIntEnv->papszEnv[iVar]);
+ RTMemFree(pIntEnv->papszEnv);
+ pIntEnv->papszEnv = NULL;
+
+ if (pIntEnv->papszEnvOtherCP)
+ {
+ for (iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
+ {
+ RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
+ pIntEnv->papszEnvOtherCP[iVar] = NULL;
+ }
+ RTMemFree(pIntEnv->papszEnvOtherCP);
+ pIntEnv->papszEnvOtherCP = NULL;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ /*RTCritSectDelete(&pIntEnv->CritSect) */
+ RTMemFree(pIntEnv);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTEnvDestroy);
+
+
+static int rtEnvCloneDefault(PRTENV phEnv)
+{
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ bool const fFirstEqual = true;
+#else
+ bool const fFirstEqual = false;
+#endif
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Lock the PEB, get the process environment.
+ *
+ * On older windows version GetEnviornmentStringsW will not copy the
+ * environment block, but return the pointer stored in the PEB. This
+ * should be safer wrt to concurrent changes.
+ */
+ PPEB pPeb = RTNtCurrentPeb();
+
+ RtlAcquirePebLock();
+
+ /* Count variables in the block: */
+ size_t cVars = 0;
+ PCRTUTF16 pwszzEnv = pPeb->ProcessParameters ? pPeb->ProcessParameters->Environment : NULL;
+ if (pwszzEnv)
+ {
+ PCRTUTF16 pwsz = pwszzEnv;
+ while (*pwsz)
+ {
+ cVars++;
+ pwsz += RTUtf16Len(pwsz) + 1;
+ }
+ }
+
+ PRTENVINTERNAL pIntEnv;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ size_t iDst;
+ for (iDst = 0; iDst < cVars && *pwszzEnv; iDst++, pwszzEnv += RTUtf16Len(pwszzEnv) + 1)
+ {
+ int rc2 = RTUtf16ToUtf8(pwszzEnv, &pIntEnv->papszEnv[iDst]);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Make sure it contains an '='. */
+ if (strchr(pIntEnv->papszEnv[iDst], '='))
+ continue;
+ rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
+ if (RT_SUCCESS(rc2))
+ continue;
+ }
+
+ /* failed fatally. */
+ pIntEnv->cVars = iDst + 1;
+ RtlReleasePebLock();
+ RTEnvDestroy(pIntEnv);
+ return rc2;
+ }
+
+ Assert(!*pwszzEnv); Assert(iDst == cVars);
+ pIntEnv->cVars = iDst;
+ pIntEnv->papszEnv[iDst] = NULL;
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+
+ RtlReleasePebLock();
+ return rc;
+
+#else /* !RT_OS_WINDOWS */
+
+ /*
+ * Figure out how many variable to clone.
+ */
+ const char * const *papszEnv = rtEnvDefault();
+ size_t cVars = 0;
+ if (papszEnv)
+ while (papszEnv[cVars])
+ cVars++;
+
+ bool fCaseSensitive = true;
+# if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ /* DOS systems was case insensitive. A prime example is the 'Path'
+ variable on windows which turns into the 'PATH' variable. */
+ fCaseSensitive = false;
+# endif
+
+ /*
+ * Create the duplicate.
+ */
+ PRTENVINTERNAL pIntEnv;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, fCaseSensitive, false /*fPutEnvBlock*/, fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ pIntEnv->cVars = cVars;
+ pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
+
+ /* ASSUMES the default environment is in the current codepage. */
+ size_t iDst = 0;
+ for (size_t iSrc = 0; iSrc < cVars; iSrc++)
+ {
+ int rc2 = RTStrCurrentCPToUtf8(&pIntEnv->papszEnv[iDst], papszEnv[iSrc]);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Make sure it contains an '='. */
+ iDst++;
+ if (strchr(pIntEnv->papszEnv[iDst - 1], '='))
+ continue;
+ rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst - 1], "=");
+ if (RT_SUCCESS(rc2))
+ continue;
+ }
+ else if (rc2 == VERR_NO_TRANSLATION)
+ {
+ rc = VWRN_ENV_NOT_FULLY_TRANSLATED;
+ continue;
+ }
+
+ /* failed fatally. */
+ pIntEnv->cVars = iDst;
+ RTEnvDestroy(pIntEnv);
+ return rc2;
+ }
+ pIntEnv->cVars = iDst;
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+
+ return rc;
+#endif /* !RT_OS_WINDOWS */
+}
+
+
+/**
+ * Clones a non-default environment instance.
+ *
+ * @param phEnv Where to return the handle to the cloned environment.
+ * @param pIntEnvToClone The source environment. Caller takes care of
+ * locking.
+ */
+static int rtEnvCloneNonDefault(PRTENV phEnv, PRTENVINTERNAL pIntEnvToClone)
+{
+ PRTENVINTERNAL pIntEnv;
+ size_t const cVars = pIntEnvToClone->cVars;
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */,
+ pIntEnvToClone->pfnCompare != RTStrNICmp,
+ pIntEnvToClone->fPutEnvBlock,
+ pIntEnvToClone->fFirstEqual);
+ if (RT_SUCCESS(rc))
+ {
+ pIntEnv->cVars = cVars;
+ pIntEnv->papszEnv[cVars] = NULL;
+
+ const char * const * const papszEnv = pIntEnvToClone->papszEnv;
+ for (size_t iVar = 0; iVar < cVars; iVar++)
+ {
+ char *pszVar = RTStrDup(papszEnv[iVar]);
+ if (RT_UNLIKELY(!pszVar))
+ {
+ pIntEnv->cVars = iVar;
+ RTEnvDestroy(pIntEnv);
+ return VERR_NO_STR_MEMORY;
+ }
+ pIntEnv->papszEnv[iVar] = pszVar;
+ }
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvClone(PRTENV phEnv, RTENV hEnvToClone)
+{
+ /*
+ * Validate input and what kind of source block we're working with.
+ */
+ int rc;
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ if (hEnvToClone == RTENV_DEFAULT)
+ rc = rtEnvCloneDefault(phEnv);
+ else
+ {
+ PRTENVINTERNAL pIntEnvToClone = hEnvToClone;
+ AssertPtrReturn(pIntEnvToClone, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnvToClone->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ RTENV_LOCK(pIntEnvToClone);
+ rc = rtEnvCloneNonDefault(phEnv, pIntEnvToClone);
+ RTENV_UNLOCK(pIntEnvToClone);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvClone);
+
+
+RTDECL(int) RTEnvCloneUtf16Block(PRTENV phEnv, PCRTUTF16 pwszzBlock, uint32_t fFlags)
+{
+ AssertPtrReturn(pwszzBlock, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Count the number of variables in the block.
+ */
+ uint32_t cVars = 0;
+ PCRTUTF16 pwsz = pwszzBlock;
+ while (*pwsz != '\0')
+ {
+ cVars++;
+ pwsz += RTUtf16Len(pwsz) + 1;
+ AssertReturn(cVars < _256K, VERR_OUT_OF_RANGE);
+ }
+
+ /*
+ * Create the duplicate.
+ */
+ PRTENVINTERNAL pIntEnv;
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ int rc = rtEnvCreate(&pIntEnv, cVars + 1 /* NULL */, false /*fCaseSensitive*/, false /*fPutEnvBlock*/, false /*fFirstEqual*/);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ pIntEnv->cVars = cVars;
+ pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
+
+ size_t iDst = 0;
+ for (pwsz = pwszzBlock; *pwsz != '\0'; pwsz += RTUtf16Len(pwsz) + 1)
+ {
+ int rc2 = RTUtf16ToUtf8(pwsz, &pIntEnv->papszEnv[iDst]);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Make sure it contains an '='. */
+ const char *pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
+ if (!pszEqual)
+ {
+ rc2 = RTStrAAppend(&pIntEnv->papszEnv[iDst], "=");
+ if (RT_SUCCESS(rc2))
+ pszEqual = strchr(pIntEnv->papszEnv[iDst], '=');
+
+ }
+ if (pszEqual)
+ {
+ /* Check for duplicates, keep the last version. */
+ const char *pchVar = pIntEnv->papszEnv[iDst];
+ size_t cchVarNmAndEq = pszEqual - pchVar;
+ for (size_t iDst2 = 0; iDst2 < iDst; iDst2++)
+ if (pIntEnv->pfnCompare(pIntEnv->papszEnv[iDst2], pchVar, cchVarNmAndEq) == 0)
+ {
+ RTStrFree(pIntEnv->papszEnv[iDst2]);
+ pIntEnv->papszEnv[iDst2] = pIntEnv->papszEnv[iDst];
+ pIntEnv->papszEnv[iDst] = NULL;
+ iDst--;
+ break;
+ }
+ iDst++;
+ continue;
+ }
+ iDst++;
+ }
+
+ /* failed fatally. */
+ pIntEnv->cVars = iDst;
+ RTEnvDestroy(pIntEnv);
+ return rc2;
+ }
+ Assert(iDst <= pIntEnv->cVars);
+ pIntEnv->cVars = iDst;
+
+ /* done */
+ *phEnv = pIntEnv;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvCloneUtf16Block);
+
+
+
+RTDECL(int) RTEnvReset(RTENV hEnv)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ RTENV_LOCK(pIntEnv);
+
+ size_t iVar = pIntEnv->cVars;
+ pIntEnv->cVars = 0;
+ while (iVar-- > 0)
+ {
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->papszEnv[iVar] = NULL;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTEnvReset);
+
+
+/**
+ * Appends an already allocated string to papszEnv.
+ *
+ * @returns IPRT status code
+ * @param pIntEnv The environment block to append it to.
+ * @param pszEntry The string to add. Already duplicated, caller
+ * does error cleanup.
+ */
+static int rtEnvIntAppend(PRTENVINTERNAL pIntEnv, char *pszEntry)
+{
+ /*
+ * Do we need to resize the array?
+ */
+ int rc = VINF_SUCCESS;
+ size_t iVar = pIntEnv->cVars;
+ if (iVar + 2 > pIntEnv->cAllocated)
+ {
+ void *pvNew = RTMemRealloc(pIntEnv->papszEnv, sizeof(char *) * (pIntEnv->cAllocated + RTENV_GROW_SIZE));
+ if (!pvNew)
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ pIntEnv->papszEnv = (char **)pvNew;
+ pIntEnv->cAllocated += RTENV_GROW_SIZE;
+ for (size_t iNewVar = pIntEnv->cVars; iNewVar < pIntEnv->cAllocated; iNewVar++)
+ pIntEnv->papszEnv[iNewVar] = NULL;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Append it.
+ */
+ pIntEnv->papszEnv[iVar] = pszEntry;
+ pIntEnv->papszEnv[iVar + 1] = NULL; /* this isn't really necessary, but doesn't hurt. */
+ pIntEnv->cVars = iVar + 1;
+ }
+ return rc;
+}
+
+
+/**
+ * Worker for RTEnvSetEx and RTEnvPutEx.
+ */
+static int rtEnvSetExWorker(RTENV Env, const char *pchVar, size_t cchVar, const char *pszValue)
+{
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RT_OS_WINDOWS
+ extern int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
+ rc = rtEnvSetUtf8Worker(pchVar, cchVar, pszValue);
+#else
+ /*
+ * Since RTEnvPut isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCPEx(&pszVarOtherCP, pchVar, cchVar);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszValueOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszValueOtherCP, pszValue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvSet(pszVarOtherCP, pszValueOtherCP);
+ RTStrFree(pszValueOtherCP);
+ }
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the variable string.
+ */
+ const size_t cchValue = strlen(pszValue);
+ char *pszEntry = (char *)RTMemAlloc(cchVar + cchValue + 2);
+ if (pszEntry)
+ {
+ memcpy(pszEntry, pchVar, cchVar);
+ pszEntry[cchVar] = '=';
+ memcpy(&pszEntry[cchVar + 1], pszValue, cchValue + 1);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Find the location of the variable. (iVar = cVars if new)
+ */
+ rc = VINF_SUCCESS;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pchVar, cchVar)
+ && ( pIntEnv->papszEnv[iVar][cchVar] == '='
+ || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
+ break;
+ if (iVar < pIntEnv->cVars)
+ {
+ /*
+ * Replace the current entry. Simple.
+ */
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->papszEnv[iVar] = pszEntry;
+ }
+ else
+ {
+ /*
+ * New variable, append it.
+ */
+ Assert(pIntEnv->cVars == iVar);
+ rc = rtEnvIntAppend(pIntEnv, pszEntry);
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pszEntry);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvSetEx(RTENV Env, const char *pszVar, const char *pszValue)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+ size_t const cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ char const *pszEq = (char const *)memchr(pszVar, '=', cchVar);
+ if (!pszEq)
+ { /* likely */ }
+ else
+ {
+ AssertReturn(Env != RTENV_DEFAULT, VERR_ENV_INVALID_VAR_NAME);
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->fFirstEqual, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(memchr(pszVar + 1, '=', cchVar - 1) == NULL, VERR_ENV_INVALID_VAR_NAME);
+ }
+
+ return rtEnvSetExWorker(Env, pszVar, cchVar, pszValue);
+}
+RT_EXPORT_SYMBOL(RTEnvSetEx);
+
+
+RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertReturn(*pszVar, VERR_ENV_INVALID_VAR_NAME);
+
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ rc = RTEnvUnsetUtf8(pszVar);
+#else
+ /*
+ * Since RTEnvUnset isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvUnset(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Remove all variable by the given name.
+ */
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if ( !pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar)
+ && ( pIntEnv->papszEnv[iVar][cchVar] == '='
+ || pIntEnv->papszEnv[iVar][cchVar] == '\0') )
+ {
+ if (!pIntEnv->fPutEnvBlock)
+ {
+ RTMemFree(pIntEnv->papszEnv[iVar]);
+ pIntEnv->cVars--;
+ if (pIntEnv->cVars > 0)
+ pIntEnv->papszEnv[iVar] = pIntEnv->papszEnv[pIntEnv->cVars];
+ pIntEnv->papszEnv[pIntEnv->cVars] = NULL;
+ }
+ else
+ {
+ /* Record this unset by keeping the variable without any equal sign. */
+ pIntEnv->papszEnv[iVar][cchVar] = '\0';
+ }
+ rc = VINF_SUCCESS;
+ /* no break, there could be more. */
+ }
+
+ /*
+ * If this is a change record, we may need to add it.
+ */
+ if (rc == VINF_ENV_VAR_NOT_FOUND && pIntEnv->fPutEnvBlock)
+ {
+ char *pszEntry = (char *)RTMemDup(pszVar, cchVar + 1);
+ if (pszEntry)
+ {
+ rc = rtEnvIntAppend(pIntEnv, pszEntry);
+ if (RT_SUCCESS(rc))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ RTMemFree(pszEntry);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTEnvUnsetEx);
+
+
+RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue)
+{
+ int rc;
+ AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER);
+ const char *pszEq = strchr(pszVarEqualValue, '=');
+ if ( pszEq == pszVarEqualValue
+ && Env != RTENV_DEFAULT)
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ if (pIntEnv->fFirstEqual)
+ pszEq = strchr(pszVarEqualValue + 1, '=');
+ }
+ if (!pszEq)
+ rc = RTEnvUnsetEx(Env, pszVarEqualValue);
+ else
+ {
+ AssertReturn(pszEq != pszVarEqualValue, VERR_ENV_INVALID_VAR_NAME);
+ rc = rtEnvSetExWorker(Env, pszVarEqualValue, pszEq - pszVarEqualValue, pszEq + 1);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvPutEx);
+
+
+RTDECL(int) RTEnvGetEx(RTENV Env, const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
+ AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
+
+ if (pcchActual)
+ *pcchActual = 0;
+ int rc;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ rc = RTEnvGetUtf8(pszVar, pszValue, cbValue, pcchActual);
+#else
+ /*
+ * Since RTEnvGet isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszValueOtherCP = RTEnvGet(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ if (pszValueOtherCP)
+ {
+ char *pszValueUtf8;
+ rc = RTStrCurrentCPToUtf8(&pszValueUtf8, pszValueOtherCP);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VINF_SUCCESS;
+ size_t cch = strlen(pszValueUtf8);
+ if (pcchActual)
+ *pcchActual = cch;
+ if (pszValue && cbValue)
+ {
+ if (cch < cbValue)
+ memcpy(pszValue, pszValueUtf8, cch + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ RTStrFree(pszValueUtf8);
+ }
+ }
+ else
+ rc = VERR_ENV_VAR_NOT_FOUND;
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, VERR_ENV_INVALID_VAR_NAME);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Locate the first variable and return it to the caller.
+ */
+ rc = VERR_ENV_VAR_NOT_FOUND;
+ size_t iVar;
+ for (iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
+ {
+ if (pIntEnv->papszEnv[iVar][cchVar] == '=')
+ {
+ rc = VINF_SUCCESS;
+ const char *pszValueOrg = pIntEnv->papszEnv[iVar] + cchVar + 1;
+ size_t cch = strlen(pszValueOrg);
+ if (pcchActual)
+ *pcchActual = cch;
+ if (pszValue && cbValue)
+ {
+ if (cch < cbValue)
+ memcpy(pszValue, pszValueOrg, cch + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ break;
+ }
+ if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
+ {
+ Assert(pIntEnv->fPutEnvBlock);
+ rc = VERR_ENV_VAR_UNSET;
+ break;
+ }
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvGetEx);
+
+
+RTDECL(bool) RTEnvExistEx(RTENV Env, const char *pszVar)
+{
+ AssertPtrReturn(pszVar, false);
+
+ bool fExists = false;
+ if (Env == RTENV_DEFAULT)
+ {
+#ifdef RTENV_IMPLEMENTS_UTF8_DEFAULT_ENV_API
+ fExists = RTEnvExistsUtf8(pszVar);
+#else
+ /*
+ * Since RTEnvExist isn't UTF-8 clean and actually expects the strings
+ * to be in the current code page (codeset), we'll do the necessary
+ * conversions here.
+ */
+ char *pszVarOtherCP;
+ int rc = RTStrUtf8ToCurrentCP(&pszVarOtherCP, pszVar);
+ if (RT_SUCCESS(rc))
+ {
+ fExists = RTEnvExist(pszVarOtherCP);
+ RTStrFree(pszVarOtherCP);
+ }
+#endif
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, false);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
+ const size_t cchVar = strlen(pszVar);
+ AssertReturn(cchVar > 0, false);
+ AssertReturn(strchr(pIntEnv->fFirstEqual ? pszVar + 1 : pszVar, '=') == NULL, false);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Simple search.
+ */
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ if (!pIntEnv->pfnCompare(pIntEnv->papszEnv[iVar], pszVar, cchVar))
+ {
+ if (pIntEnv->papszEnv[iVar][cchVar] == '=')
+ {
+ fExists = true;
+ break;
+ }
+ if (pIntEnv->papszEnv[iVar][cchVar] == '\0')
+ break;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return fExists;
+}
+RT_EXPORT_SYMBOL(RTEnvExistEx);
+
+
+#ifndef RT_OS_WINDOWS
+RTDECL(char const * const *) RTEnvGetExecEnvP(RTENV Env)
+{
+ const char * const *papszRet;
+ if (Env == RTENV_DEFAULT)
+ {
+ /** @todo fix this API it's fundamentally wrong! */
+ papszRet = rtEnvDefault();
+ if (!papszRet)
+ {
+ static const char * const s_papszDummy[2] = { NULL, NULL };
+ papszRet = &s_papszDummy[0];
+ }
+ }
+ else
+ {
+ PRTENVINTERNAL pIntEnv = Env;
+ AssertPtrReturn(pIntEnv, NULL);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Free any old envp.
+ */
+ if (pIntEnv->papszEnvOtherCP)
+ {
+ for (size_t iVar = 0; pIntEnv->papszEnvOtherCP[iVar]; iVar++)
+ {
+ RTStrFree(pIntEnv->papszEnvOtherCP[iVar]);
+ pIntEnv->papszEnvOtherCP[iVar] = NULL;
+ }
+ RTMemFree(pIntEnv->papszEnvOtherCP);
+ pIntEnv->papszEnvOtherCP = NULL;
+ }
+
+ /*
+ * Construct a new envp with the strings in the process code set.
+ */
+ char **papsz;
+ papszRet = pIntEnv->papszEnvOtherCP = papsz = (char **)RTMemAlloc(sizeof(char *) * (pIntEnv->cVars + 1));
+ if (papsz)
+ {
+ papsz[pIntEnv->cVars] = NULL;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ int rc = RTStrUtf8ToCurrentCP(&papsz[iVar], pIntEnv->papszEnv[iVar]);
+ if (RT_FAILURE(rc))
+ {
+ /* RTEnvDestroy / we cleans up later. */
+ papsz[iVar] = NULL;
+ AssertRC(rc);
+ papszRet = NULL;
+ break;
+ }
+ }
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+ }
+ return papszRet;
+}
+RT_EXPORT_SYMBOL(RTEnvGetExecEnvP);
+#endif /* !RT_OS_WINDOWS */
+
+
+/**
+ * RTSort callback for comparing two environment variables.
+ *
+ * @returns -1, 0, 1. See PFNRTSORTCMP.
+ * @param pvElement1 Variable 1.
+ * @param pvElement2 Variable 2.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) rtEnvSortCompare(const void *pvElement1, const void *pvElement2, void *pvUser)
+{
+ NOREF(pvUser);
+ int iDiff = strcmp((const char *)pvElement1, (const char *)pvElement2);
+ if (iDiff < 0)
+ iDiff = -1;
+ else if (iDiff > 0)
+ iDiff = 1;
+ return iDiff;
+}
+
+
+RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock)
+{
+ RTENV hClone = NIL_RTENV;
+ PRTENVINTERNAL pIntEnv;
+ int rc;
+
+ /*
+ * Validate / simplify input.
+ */
+ if (hEnv == RTENV_DEFAULT)
+ {
+ rc = RTEnvClone(&hClone, RTENV_DEFAULT);
+ if (RT_FAILURE(rc))
+ return rc;
+ pIntEnv = hClone;
+ }
+ else
+ {
+ pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ rc = VINF_SUCCESS;
+ }
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Sort it first.
+ */
+ RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
+
+ /*
+ * Calculate the size.
+ */
+ size_t cwc;
+ size_t cwcTotal = 2;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc);
+ AssertRCBreak(rc);
+ cwcTotal += cwc + 1;
+ }
+
+ PRTUTF16 pwszzBlock = NULL;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform the conversion.
+ */
+ PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16));
+ if (pwszz)
+ {
+ size_t cwcLeft = cwcTotal;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX,
+ &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc);
+ AssertRCBreak(rc);
+ pwszz += cwc + 1;
+ cwcLeft -= cwc + 1;
+ AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3);
+ }
+ AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2);
+ if (RT_SUCCESS(rc))
+ {
+ pwszz[0] = '\0';
+ pwszz[1] = '\0';
+ }
+ else
+ {
+ RTMemFree(pwszzBlock);
+ pwszzBlock = NULL;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (hClone != NIL_RTENV)
+ RTEnvDestroy(hClone);
+ if (RT_SUCCESS(rc))
+ *ppwszzBlock = pwszzBlock;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvQueryUtf16Block);
+
+
+RTDECL(void) RTEnvFreeUtf16Block(PRTUTF16 pwszzBlock)
+{
+ RTMemFree(pwszzBlock);
+}
+RT_EXPORT_SYMBOL(RTEnvFreeUtf16Block);
+
+
+RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock)
+{
+ RTENV hClone = NIL_RTENV;
+ PRTENVINTERNAL pIntEnv;
+ int rc;
+
+ /*
+ * Validate / simplify input.
+ */
+ if (hEnv == RTENV_DEFAULT)
+ {
+ rc = RTEnvClone(&hClone, RTENV_DEFAULT);
+ if (RT_FAILURE(rc))
+ return rc;
+ pIntEnv = hClone;
+ }
+ else
+ {
+ pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+ rc = VINF_SUCCESS;
+ }
+
+ RTENV_LOCK(pIntEnv);
+
+ /*
+ * Sort it, if requested.
+ */
+ if (fSorted)
+ RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv);
+
+ /*
+ * Calculate the size. We add one extra terminator just to be on the safe side.
+ */
+ size_t cbBlock = 2;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1;
+
+ if (pcbBlock)
+ *pcbBlock = cbBlock - 1;
+
+ /*
+ * Allocate memory and copy out the variables.
+ */
+ char *pszzBlock;
+ char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock);
+ if (pszz)
+ {
+ size_t cbLeft = cbBlock;
+ for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++)
+ {
+ size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1;
+ AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3);
+ memcpy(pszz, pIntEnv->papszEnv[iVar], cb);
+ pszz += cb;
+ cbLeft -= cb;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pszz[0] = '\0';
+ pszz[1] = '\0'; /* The extra one. */
+ }
+ else
+ {
+ RTMemFree(pszzBlock);
+ pszzBlock = NULL;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ if (hClone != NIL_RTENV)
+ RTEnvDestroy(hClone);
+ if (RT_SUCCESS(rc))
+ *ppszzBlock = pszzBlock;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvQueryUtf8Block);
+
+
+RTDECL(void) RTEnvFreeUtf8Block(char *pszzBlock)
+{
+ RTMemFree(pszzBlock);
+}
+RT_EXPORT_SYMBOL(RTEnvFreeUtf8Block);
+
+
+RTDECL(uint32_t) RTEnvCountEx(RTENV hEnv)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, UINT32_MAX);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
+
+ RTENV_LOCK(pIntEnv);
+ uint32_t cVars = (uint32_t)pIntEnv->cVars;
+ RTENV_UNLOCK(pIntEnv);
+
+ return cVars;
+}
+RT_EXPORT_SYMBOL(RTEnvCountEx);
+
+
+RTDECL(int) RTEnvGetByIndexEx(RTENV hEnv, uint32_t iVar, char *pszVar, size_t cbVar, char *pszValue, size_t cbValue)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, UINT32_MAX);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, UINT32_MAX);
+ if (cbVar)
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ if (cbValue)
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+
+ RTENV_LOCK(pIntEnv);
+
+ int rc;
+ if (iVar < pIntEnv->cVars)
+ {
+ const char *pszSrcVar = pIntEnv->papszEnv[iVar];
+ const char *pszSrcValue = strchr(pszSrcVar, '=');
+ if (pszSrcValue == pszSrcVar && pIntEnv->fFirstEqual)
+ pszSrcValue = strchr(pszSrcVar + 1, '=');
+ bool fHasEqual = pszSrcValue != NULL;
+ if (pszSrcValue)
+ {
+ pszSrcValue++;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ pszSrcValue = strchr(pszSrcVar, '\0');
+ rc = VINF_ENV_VAR_UNSET;
+ }
+ if (cbVar)
+ {
+ int rc2 = RTStrCopyEx(pszVar, cbVar, pszSrcVar, pszSrcValue - pszSrcVar - fHasEqual);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ if (cbValue)
+ {
+ int rc2 = RTStrCopy(pszValue, cbValue, pszSrcValue);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ }
+ else
+ rc = VERR_ENV_VAR_NOT_FOUND;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvGetByIndexEx);
+
+
+RTDECL(const char *) RTEnvGetByIndexRawEx(RTENV hEnv, uint32_t iVar)
+{
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, NULL);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, NULL);
+
+ RTENV_LOCK(pIntEnv);
+
+ const char *pszRet;
+ if (iVar < pIntEnv->cVars)
+ pszRet = pIntEnv->papszEnv[iVar];
+ else
+ pszRet = NULL;
+
+ RTENV_UNLOCK(pIntEnv);
+
+ return pszRet;
+}
+RT_EXPORT_SYMBOL(RTEnvGetByIndexRawEx);
+
+
+RTDECL(int) RTEnvCreateChangeRecord(PRTENV phEnv)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+#ifdef RTENV_ALLOW_EQUAL_FIRST_IN_VAR
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, true /*fFirstEqual*/);
+#else
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/, false /*fFirstEqual*/);
+#endif
+}
+RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
+
+
+RTDECL(int) RTEnvCreateChangeRecordEx(PRTENV phEnv, uint32_t fFlags)
+{
+ AssertPtrReturn(phEnv, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTENV_CREATE_F_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtEnvCreate(phEnv, RTENV_GROW_SIZE, true /*fCaseSensitive*/, true /*fPutEnvBlock*/,
+ RT_BOOL(fFlags & RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR));
+}
+RT_EXPORT_SYMBOL(RTEnvCreateChangeRecord);
+
+
+RTDECL(bool) RTEnvIsChangeRecord(RTENV hEnv)
+{
+ if (hEnv == RTENV_DEFAULT)
+ return false;
+
+ PRTENVINTERNAL pIntEnv = hEnv;
+ AssertPtrReturn(pIntEnv, false);
+ AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, false);
+ return pIntEnv->fPutEnvBlock;
+}
+RT_EXPORT_SYMBOL(RTEnvIsChangeRecord);
+
+
+RTDECL(int) RTEnvApplyChanges(RTENV hEnvDst, RTENV hEnvChanges)
+{
+ PRTENVINTERNAL pIntEnvChanges = hEnvChanges;
+ AssertPtrReturn(pIntEnvChanges, VERR_INVALID_HANDLE);
+ AssertReturn(pIntEnvChanges->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE);
+
+ /** @todo lock validator trouble ahead here! */
+ RTENV_LOCK(pIntEnvChanges);
+
+ int rc = VINF_SUCCESS;
+ for (uint32_t iChange = 0; iChange < pIntEnvChanges->cVars && RT_SUCCESS(rc); iChange++)
+ rc = RTEnvPutEx(hEnvDst, pIntEnvChanges->papszEnv[iChange]);
+
+ RTENV_UNLOCK(pIntEnvChanges);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTEnvApplyChanges);
+
diff --git a/src/VBox/Runtime/generic/errvars-generic.cpp b/src/VBox/Runtime/generic/errvars-generic.cpp
new file mode 100644
index 00000000..672d122c
--- /dev/null
+++ b/src/VBox/Runtime/generic/errvars-generic.cpp
@@ -0,0 +1,78 @@
+/* $Id: errvars-generic.cpp $ */
+/** @file
+ * IPRT - Save and Restore Error Variables, generic stub implementation.
+ */
+
+/*
+ * Copyright (C) 2011-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/errcore.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include "internal/magics.h"
+
+
+
+RTDECL(PRTERRVARS) RTErrVarsSave(PRTERRVARS pVars)
+{
+ pVars->ai32Vars[0] = RTERRVARS_MAGIC;
+ return pVars;
+}
+
+
+RTDECL(void) RTErrVarsRestore(PCRTERRVARS pVars)
+{
+ Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+ RT_NOREF_PV(pVars);
+}
+
+
+RTDECL(bool) RTErrVarsAreEqual(PCRTERRVARS pVars1, PCRTERRVARS pVars2)
+{
+ Assert(pVars1->ai32Vars[0] == RTERRVARS_MAGIC);
+ Assert(pVars2->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars1->ai32Vars[0] == pVars2->ai32Vars[0];
+}
+
+
+RTDECL(bool) RTErrVarsHaveChanged(PCRTERRVARS pVars)
+{
+ Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+ RT_NOREF_PV(pVars);
+ return false;
+}
+
diff --git a/src/VBox/Runtime/generic/fileio-at-generic.cpp b/src/VBox/Runtime/generic/fileio-at-generic.cpp
new file mode 100644
index 00000000..8cfde508
--- /dev/null
+++ b/src/VBox/Runtime/generic/fileio-at-generic.cpp
@@ -0,0 +1,64 @@
+/* $Id: fileio-at-generic.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileReadAt and RTFileWriteAt, generic.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/errcore.h>
+
+
+
+RTDECL(int) RTFileReadAt(RTFILE File, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileRead(File, pvBuf, cbToRead, pcbRead);
+ return rc;
+}
+
+
+RTDECL(int) RTFileWriteAt(RTFILE File, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileWrite(File, pvBuf, cbToWrite, pcbWritten);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/fileio-sg-at-generic.cpp b/src/VBox/Runtime/generic/fileio-sg-at-generic.cpp
new file mode 100644
index 00000000..fcdd6434
--- /dev/null
+++ b/src/VBox/Runtime/generic/fileio-sg-at-generic.cpp
@@ -0,0 +1,112 @@
+/* $Id: fileio-sg-at-generic.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileSgReadAt & RTFileSgWriteAt, generic.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+
+RTDECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbRead = 0;
+ while (cbToRead)
+ {
+ size_t cbBuf = cbToRead;
+ void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf); /** @todo this is wrong as it may advance the buffer past what's actually read. */
+
+ size_t cbThisRead = cbBuf;
+ rc = RTFileReadAt(hFile, off, pvBuf, cbBuf, pcbRead ? &cbThisRead : NULL);
+ if (RT_SUCCESS(rc))
+ cbRead += cbThisRead;
+ else
+ break;
+ if (cbThisRead < cbBuf)
+ {
+ AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+ Assert(cbBuf == cbThisRead);
+
+ cbToRead -= cbBuf;
+ off += cbBuf;
+ }
+
+ if (pcbRead)
+ *pcbRead = cbRead;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbWritten = 0;
+ while (cbToWrite)
+ {
+ size_t cbBuf = cbToWrite;
+ void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf); /** @todo this is wrong as it may advance the buffer past what's actually read. */
+
+ size_t cbThisWritten = cbBuf;
+ rc = RTFileWriteAt(hFile, off, pvBuf, cbBuf, pcbWritten ? &cbThisWritten : NULL);
+ if (RT_SUCCESS(rc))
+ cbWritten += cbThisWritten;
+ else
+ break;
+ if (cbThisWritten < cbBuf)
+ {
+ AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbBuf == cbThisWritten);
+ cbToWrite -= cbBuf;
+ off += cbBuf;
+ }
+
+ if (pcbWritten)
+ *pcbWritten = cbWritten;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/fileio-sg-generic.cpp b/src/VBox/Runtime/generic/fileio-sg-generic.cpp
new file mode 100644
index 00000000..8322af3f
--- /dev/null
+++ b/src/VBox/Runtime/generic/fileio-sg-generic.cpp
@@ -0,0 +1,110 @@
+/* $Id: fileio-sg-generic.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileSgRead & RTFileSgWrite, generic.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+
+RTDECL(int) RTFileSgRead(RTFILE hFile, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbRead = 0;
+ while (cbToRead)
+ {
+ size_t cbBuf = cbToRead;
+ void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf); /** @todo this is wrong as it may advance the buffer past what's actually read. */
+
+ size_t cbThisRead = cbBuf;
+ rc = RTFileRead(hFile, pvBuf, cbBuf, pcbRead ? &cbThisRead : NULL);
+ if (RT_SUCCESS(rc))
+ cbRead += cbThisRead;
+ else
+ break;
+ if (cbThisRead < cbBuf)
+ {
+ AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+ Assert(cbBuf == cbThisRead);
+
+ cbToRead -= cbBuf;
+ }
+
+ if (pcbRead)
+ *pcbRead = cbRead;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileSgWrite(RTFILE hFile, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ int rc = VINF_SUCCESS;
+ size_t cbWritten = 0;
+ while (cbToWrite)
+ {
+ size_t cbBuf = cbToWrite;
+ void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf); /** @todo this is wrong as it may advance the buffer past what's actually read. */
+
+ size_t cbThisWritten = cbBuf;
+ rc = RTFileWrite(hFile, pvBuf, cbBuf, pcbWritten ? &cbThisWritten : NULL);
+ if (RT_SUCCESS(rc))
+ cbWritten += cbThisWritten;
+ else
+ break;
+ if (cbThisWritten < cbBuf)
+ {
+ AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbBuf == cbThisWritten);
+ cbToWrite -= cbBuf;
+ }
+
+ if (pcbWritten)
+ *pcbWritten = cbWritten;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/fs-stubs-generic.cpp b/src/VBox/Runtime/generic/fs-stubs-generic.cpp
new file mode 100644
index 00000000..b88fbe30
--- /dev/null
+++ b/src/VBox/Runtime/generic/fs-stubs-generic.cpp
@@ -0,0 +1,103 @@
+/* $Id: fs-stubs-generic.cpp $ */
+/** @file
+ * IPRT - File System, Generic Stubs.
+ */
+
+/*
+ * 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
+#include <iprt/fs.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/fs.h"
+
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ if (pcbTotal)
+ *pcbTotal = _2G;
+ if (pcbFree)
+ *pcbFree = _1G;
+ if (pcbBlock)
+ *pcbBlock = _4K;
+ if (pcbSector)
+ *pcbSector = 512;
+ LogFlow(("RTFsQuerySizes: success stub!\n"));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ if (pu32Serial)
+ *pu32Serial = 0xc0ffee;
+ LogFlow(("RTFsQuerySerial: success stub!\n"));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ pProperties->cbMaxComponent = 255;
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
+ pProperties->fCaseSensitive = false;
+#else
+ pProperties->fCaseSensitive = true;
+#endif
+ pProperties->fCompressed = false;
+ pProperties->fFileCompression = false;
+ pProperties->fReadOnly = false;
+ pProperties->fRemote = false;
+ pProperties->fSupportsUnicode = true;
+ LogFlow(("RTFsQueryProperties: success stub!\n"));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
+ return false;
+#else
+ return true;
+#endif
+}
+
diff --git a/src/VBox/Runtime/generic/http-curl.cpp b/src/VBox/Runtime/generic/http-curl.cpp
new file mode 100644
index 00000000..60e88269
--- /dev/null
+++ b/src/VBox/Runtime/generic/http-curl.cpp
@@ -0,0 +1,4173 @@
+/* $Id: http-curl.cpp $ */
+/** @file
+ * IPRT - HTTP client API, cURL based.
+ *
+ * Logging groups:
+ * Log4 - request headers.
+ * Log5 - request body.
+ * Log6 - response headers.
+ * Log7 - response body.
+ */
+
+/*
+ * Copyright (C) 2012-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_HTTP
+#include <iprt/http.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/cidr.h>
+#include <iprt/crypto/store.h>
+#include <iprt/ctype.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/net.h>
+#include <iprt/once.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+#include <iprt/uri.h>
+#include <iprt/utf16.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/pkix.h>
+#include <iprt/crypto/key.h>
+
+
+#include "internal/magics.h"
+
+#ifdef RT_OS_WINDOWS /* curl.h drags in windows.h which isn't necessarily -Wall clean. */
+# include <iprt/win/windows.h>
+#endif
+#include <curl/curl.h>
+
+#ifdef RT_OS_DARWIN
+# include <CoreFoundation/CoreFoundation.h>
+# include <SystemConfiguration/SystemConfiguration.h>
+# include <CoreServices/CoreServices.h>
+#endif
+#ifdef RT_OS_WINDOWS
+# include <Winhttp.h>
+# include "../r3/win/internal-r3-win.h"
+#endif
+
+#ifdef RT_OS_LINUX
+# define IPRT_USE_LIBPROXY
+#endif
+#ifdef IPRT_USE_LIBPROXY
+# include <stdlib.h> /* free */
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Output collection data. */
+typedef struct RTHTTPOUTPUTDATA
+{
+ /** Pointer to the HTTP client instance structure. */
+ struct RTHTTPINTERNAL *pHttp;
+ /** Callback specific data. */
+ union
+ {
+ /** For file destination. */
+ RTFILE hFile;
+ /** For memory destination. */
+ struct
+ {
+ /** The current size (sans terminator char). */
+ size_t cb;
+ /** The currently allocated size. */
+ size_t cbAllocated;
+ /** Pointer to the buffer. */
+ uint8_t *pb;
+ } Mem;
+ } uData;
+} RTHTTPOUTPUTDATA;
+
+/**
+ * HTTP header.
+ */
+typedef struct RTHTTPHEADER
+{
+ /** The core list structure. */
+ struct curl_slist Core;
+ /** The field name length. */
+ uint32_t cchName;
+ /** The value offset. */
+ uint32_t offValue;
+ /** The full header field. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ RT_GCC_EXTENSION char szData[RT_FLEXIBLE_ARRAY];
+} RTHTTPHEADER;
+/** Pointer to a HTTP header. */
+typedef RTHTTPHEADER *PRTHTTPHEADER;
+
+/**
+ * Internal HTTP client instance.
+ */
+typedef struct RTHTTPINTERNAL
+{
+ /** Magic value. */
+ uint32_t u32Magic;
+ /** cURL handle. */
+ CURL *pCurl;
+ /** The last response code. */
+ long lLastResp;
+ /** Custom headers (PRTHTTPHEADER).
+ * The list head is registered with curl, though we do all the allocating. */
+ struct curl_slist *pHeaders;
+ /** Where to append the next header. */
+ struct curl_slist **ppHeadersTail;
+
+ /** CA certificate file for HTTPS authentication. */
+ char *pszCaFile;
+ /** Whether to delete the CA on destruction. */
+ bool fDeleteCaFile;
+
+ /** Set if we've applied a CURLOTP_USERAGENT already. */
+ bool fHaveSetUserAgent;
+ /** Set if we've got a user agent header, otherwise clear. */
+ bool fHaveUserAgentHeader;
+
+ /** @name Proxy settings.
+ * When fUseSystemProxySettings is set, the other members will be updated each
+ * time we're presented with a new URL. The members reflect the cURL
+ * configuration.
+ *
+ * @{ */
+ /** Set if we should use the system proxy settings for a URL.
+ * This means reconfiguring cURL for each request. */
+ bool fUseSystemProxySettings;
+ /** Set if we've detected no proxy necessary. */
+ bool fNoProxy;
+ /** Set if we've reset proxy info in cURL and need to reapply it. */
+ bool fReapplyProxyInfo;
+ /** Proxy host name (RTStrFree). */
+ char *pszProxyHost;
+ /** Proxy port number (UINT32_MAX if not specified). */
+ uint32_t uProxyPort;
+ /** The proxy type (CURLPROXY_HTTP, CURLPROXY_SOCKS5, ++). */
+ curl_proxytype enmProxyType;
+ /** Proxy username (RTStrFree). */
+ char *pszProxyUsername;
+ /** Proxy password (RTStrFree). */
+ char *pszProxyPassword;
+ /** @} */
+
+ /** @name Cached settings.
+ * @{ */
+ /** Maximum number of redirects to follow.
+ * Zero if not automatically following (default). */
+ uint32_t cMaxRedirects;
+ /** Whether to check if Peer lies about his SSL certificate. */
+ bool fVerifyPeer;
+ /** @} */
+
+ /** Abort the current HTTP request if true. */
+ bool volatile fAbort;
+ /** Set if someone is preforming an HTTP operation. */
+ bool volatile fBusy;
+ /** The location field for 301 responses. */
+ char *pszRedirLocation;
+
+ union
+ {
+ struct
+ {
+ /** Pointer to the memory block we're feeding the cURL/server. */
+ void const *pvMem;
+ /** Size of the memory block. */
+ size_t cbMem;
+ /** Current memory block offset. */
+ size_t offMem;
+ } Mem;
+ } ReadData;
+
+ /** Body output callback data. */
+ RTHTTPOUTPUTDATA BodyOutput;
+ /** Headers output callback data. */
+ RTHTTPOUTPUTDATA HeadersOutput;
+ /** The output status.*/
+ int rcOutput;
+
+ /** @name Upload callback
+ * @{ */
+ /** Pointer to the upload callback function, if any. */
+ PFNRTHTTPUPLOADCALLBACK pfnUploadCallback;
+ /** The user argument for the upload callback function. */
+ void *pvUploadCallbackUser;
+ /** The expected upload size, UINT64_MAX if not known. */
+ uint64_t cbUploadContent;
+ /** The current upload offset. */
+ uint64_t offUploadContent;
+ /** @} */
+
+ /** @name Download callback.
+ * @{ */
+ /** Pointer to the download callback function, if any. */
+ PFNRTHTTPDOWNLOADCALLBACK pfnDownloadCallback;
+ /** The user argument for the download callback function. */
+ void *pvDownloadCallbackUser;
+ /** The flags for the download callback function. */
+ uint32_t fDownloadCallback;
+ /** HTTP status for passing to the download callback, UINT32_MAX if not known. */
+ uint32_t uDownloadHttpStatus;
+ /** The download content length, or UINT64_MAX. */
+ uint64_t cbDownloadContent;
+ /** The current download offset. */
+ uint64_t offDownloadContent;
+ /** @} */
+
+ /** @name Download progress callback.
+ * @{ */
+ /** Download size hint set by the progress callback. */
+ uint64_t cbDownloadHint;
+ /** Callback called during download. */
+ PFNRTHTTPDOWNLDPROGRCALLBACK pfnDownloadProgress;
+ /** User pointer parameter for pfnDownloadProgress. */
+ void *pvDownloadProgressUser;
+ /** @} */
+
+ /** @name Header callback.
+ * @{ */
+ /** Pointer to the header callback function, if any. */
+ PFNRTHTTPHEADERCALLBACK pfnHeaderCallback;
+ /** User pointer parameter for pfnHeaderCallback. */
+ void *pvHeaderCallbackUser;
+ /** @} */
+
+ /** Buffer for human readable error messages from curl on failures or problems. */
+ char szErrorBuffer[CURL_ERROR_SIZE];
+} RTHTTPINTERNAL;
+/** Pointer to an internal HTTP client instance. */
+typedef RTHTTPINTERNAL *PRTHTTPINTERNAL;
+
+
+#ifdef RT_OS_WINDOWS
+/** @name Windows: Types for dynamically resolved APIs
+ * @{ */
+typedef HINTERNET (WINAPI * PFNWINHTTPOPEN)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
+typedef BOOL (WINAPI * PFNWINHTTPCLOSEHANDLE)(HINTERNET);
+typedef BOOL (WINAPI * PFNWINHTTPGETPROXYFORURL)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS *, WINHTTP_PROXY_INFO *);
+typedef BOOL (WINAPI * PFNWINHTTPGETDEFAULTPROXYCONFIGURATION)(WINHTTP_PROXY_INFO *);
+typedef BOOL (WINAPI * PFNWINHTTPGETIEPROXYCONFIGFORCURRENTUSER)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *);
+/** @} */
+#endif
+
+#ifdef IPRT_USE_LIBPROXY
+typedef struct px_proxy_factory *PLIBPROXYFACTORY;
+typedef PLIBPROXYFACTORY (* PFNLIBPROXYFACTORYCTOR)(void);
+typedef void (* PFNLIBPROXYFACTORYDTOR)(PLIBPROXYFACTORY);
+typedef char ** (* PFNLIBPROXYFACTORYGETPROXIES)(PLIBPROXYFACTORY, const char *);
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTHTTP_MAX_MEM_DOWNLOAD_SIZE
+ * The max size we are allowed to download to a memory buffer.
+ *
+ * @remarks The minus 1 is for the trailing zero terminator we always add.
+ */
+#if ARCH_BITS == 64
+# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE (UINT32_C(64)*_1M - 1)
+#else
+# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE (UINT32_C(32)*_1M - 1)
+#endif
+
+/** Checks whether a cURL return code indicates success. */
+#define CURL_SUCCESS(rcCurl) RT_LIKELY(rcCurl == CURLE_OK)
+/** Checks whether a cURL return code indicates failure. */
+#define CURL_FAILURE(rcCurl) RT_UNLIKELY(rcCurl != CURLE_OK)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTP_VALID_RETURN_RC(hHttp, a_rc) \
+ do { \
+ AssertPtrReturn((hHttp), (a_rc)); \
+ AssertReturn((hHttp)->u32Magic == RTHTTP_MAGIC, (a_rc)); \
+ } while (0)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTP_VALID_RETURN(hHTTP) RTHTTP_VALID_RETURN_RC((hHttp), VERR_INVALID_HANDLE)
+
+/** Validates a handle and returns (void) if not valid. */
+#define RTHTTP_VALID_RETURN_VOID(hHttp) \
+ do { \
+ AssertPtrReturnVoid(hHttp); \
+ AssertReturnVoid((hHttp)->u32Magic == RTHTTP_MAGIC); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+/** @name Windows: Dynamically resolved APIs
+ * @{ */
+static RTONCE g_WinResolveImportsOnce = RTONCE_INITIALIZER;
+static PFNWINHTTPOPEN g_pfnWinHttpOpen = NULL;
+static PFNWINHTTPCLOSEHANDLE g_pfnWinHttpCloseHandle = NULL;
+static PFNWINHTTPGETPROXYFORURL g_pfnWinHttpGetProxyForUrl = NULL;
+static PFNWINHTTPGETDEFAULTPROXYCONFIGURATION g_pfnWinHttpGetDefaultProxyConfiguration = NULL;
+static PFNWINHTTPGETIEPROXYCONFIGFORCURRENTUSER g_pfnWinHttpGetIEProxyConfigForCurrentUser = NULL;
+/** @} */
+#endif
+
+#ifdef IPRT_USE_LIBPROXY
+/** @name Dynamaically resolved libproxy APIs.
+ * @{ */
+static RTONCE g_LibProxyResolveImportsOnce = RTONCE_INITIALIZER;
+static RTLDRMOD g_hLdrLibProxy = NIL_RTLDRMOD;
+static PFNLIBPROXYFACTORYCTOR g_pfnLibProxyFactoryCtor = NULL;
+static PFNLIBPROXYFACTORYDTOR g_pfnLibProxyFactoryDtor = NULL;
+static PFNLIBPROXYFACTORYGETPROXIES g_pfnLibProxyFactoryGetProxies = NULL;
+/** @} */
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis);
+#ifdef RT_OS_DARWIN
+static int rtHttpDarwinTryConfigProxies(PRTHTTPINTERNAL pThis, CFArrayRef hArrayProxies, CFURLRef hUrlTarget, bool fIgnorePacType);
+#endif
+static void rtHttpFreeHeaders(PRTHTTPINTERNAL pThis);
+
+
+RTR3DECL(int) RTHttpCreate(PRTHTTP phHttp)
+{
+ AssertPtrReturn(phHttp, VERR_INVALID_PARAMETER);
+
+ /** @todo r=bird: rainy day: curl_global_init is not thread safe, only a
+ * problem if multiple threads get here at the same time. */
+ int rc = VERR_HTTP_INIT_FAILED;
+ CURLcode rcCurl = curl_global_init(CURL_GLOBAL_ALL);
+ if (CURL_SUCCESS(rcCurl))
+ {
+ CURL *pCurl = curl_easy_init();
+ if (pCurl)
+ {
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)RTMemAllocZ(sizeof(RTHTTPINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTHTTP_MAGIC;
+ pThis->pCurl = pCurl;
+ pThis->ppHeadersTail = &pThis->pHeaders;
+ pThis->fHaveSetUserAgent = false;
+ pThis->fHaveUserAgentHeader = false;
+ pThis->fUseSystemProxySettings = true;
+ pThis->cMaxRedirects = 0; /* no automatic redir following */
+ pThis->fVerifyPeer = true;
+ pThis->BodyOutput.pHttp = pThis;
+ pThis->HeadersOutput.pHttp = pThis;
+ pThis->uDownloadHttpStatus = UINT32_MAX;
+ pThis->cbDownloadContent = UINT64_MAX;
+ pThis->offDownloadContent = 0;
+ pThis->cbUploadContent = UINT64_MAX;
+ pThis->offUploadContent = 0;
+
+ /* ask curl to give us back error messages */
+ curl_easy_setopt(pThis->pCurl, CURLOPT_ERRORBUFFER, pThis->szErrorBuffer);
+
+ *phHttp = (RTHTTP)pThis;
+
+ return VINF_SUCCESS;
+ }
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_HTTP_INIT_FAILED;
+ }
+ curl_global_cleanup();
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpReset(RTHTTP hHttp, uint32_t fFlags)
+{
+ /* Validate the instance handle, state and flags. */
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+ AssertReturn(!(fFlags & ~RTHTTP_RESET_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /* This resets options, but keeps open connections, cookies, etc. */
+ curl_easy_reset(pThis->pCurl);
+
+ /** @todo check if CURLOPT_SSL_VERIFYPEER is affected by curl_easy_reset. */
+
+ if (!(fFlags & RTHTTP_RESET_F_KEEP_HEADERS))
+ rtHttpFreeHeaders(pThis);
+
+ pThis->uDownloadHttpStatus = UINT32_MAX;
+ pThis->cbDownloadContent = UINT64_MAX;
+ pThis->offDownloadContent = 0;
+ pThis->cbUploadContent = UINT64_MAX;
+ pThis->offUploadContent = 0;
+ pThis->rcOutput = VINF_SUCCESS;
+
+ /* Tell the proxy configuration code to reapply settings even if they
+ didn't change as cURL has forgotten them: */
+ pThis->fReapplyProxyInfo = true;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpDestroy(RTHTTP hHttp)
+{
+ if (hHttp == NIL_RTHTTP)
+ return VINF_SUCCESS;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ Assert(!pThis->fBusy);
+
+ pThis->u32Magic = RTHTTP_MAGIC_DEAD;
+
+ curl_easy_cleanup(pThis->pCurl);
+ pThis->pCurl = NULL;
+
+ rtHttpFreeHeaders(pThis);
+
+ rtHttpUnsetCaFile(pThis);
+ Assert(!pThis->pszCaFile);
+
+ if (pThis->pszRedirLocation)
+ {
+ RTStrFree(pThis->pszRedirLocation);
+ pThis->pszRedirLocation = NULL;
+ }
+
+ RTStrFree(pThis->pszProxyHost);
+ pThis->pszProxyHost = NULL;
+ RTStrFree(pThis->pszProxyUsername);
+ pThis->pszProxyUsername = NULL;
+ if (pThis->pszProxyPassword)
+ {
+ RTMemWipeThoroughly(pThis->pszProxyPassword, strlen(pThis->pszProxyPassword), 2);
+ RTStrFree(pThis->pszProxyPassword);
+ pThis->pszProxyPassword = NULL;
+ }
+
+ RTMemFree(pThis);
+
+ curl_global_cleanup();
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpAbort(RTHTTP hHttp)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ pThis->fAbort = true;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpGetRedirLocation(RTHTTP hHttp, char **ppszRedirLocation)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ Assert(!pThis->fBusy);
+
+ if (!pThis->pszRedirLocation)
+ return VERR_HTTP_NOT_FOUND;
+
+ return RTStrDupEx(ppszRedirLocation, pThis->pszRedirLocation);
+}
+
+
+RTR3DECL(int) RTHttpSetFollowRedirects(RTHTTP hHttp, uint32_t cMaxRedirects)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Update the redirection settings.
+ */
+ if (pThis->cMaxRedirects != cMaxRedirects)
+ {
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_MAXREDIRS, (long)cMaxRedirects);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_MAXREDIRS=%u: %d (%#x)\n", cMaxRedirects, rcCurl, rcCurl),
+ VERR_HTTP_CURL_ERROR);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_FOLLOWLOCATION, (long)(cMaxRedirects > 0));
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_FOLLOWLOCATION=%d: %d (%#x)\n", cMaxRedirects > 0, rcCurl, rcCurl),
+ VERR_HTTP_CURL_ERROR);
+
+ pThis->cMaxRedirects = cMaxRedirects;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(uint32_t) RTHttpGetFollowRedirects(RTHTTP hHttp)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN_RC(pThis, 0);
+ return pThis->cMaxRedirects;
+}
+
+
+/*********************************************************************************************************************************
+* Proxy handling. *
+*********************************************************************************************************************************/
+
+RTR3DECL(int) RTHttpUseSystemProxySettings(RTHTTP hHttp)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Change the settings.
+ */
+ pThis->fUseSystemProxySettings = true;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * rtHttpConfigureProxyForUrl: Update cURL proxy settings as needed.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP client instance.
+ * @param enmProxyType The proxy type.
+ * @param pszHost The proxy host name.
+ * @param uPort The proxy port number.
+ * @param pszUsername The proxy username, or NULL if none.
+ * @param pszPassword The proxy password, or NULL if none.
+ */
+static int rtHttpUpdateProxyConfig(PRTHTTPINTERNAL pThis, curl_proxytype enmProxyType, const char *pszHost,
+ uint32_t uPort, const char *pszUsername, const char *pszPassword)
+{
+ CURLcode rcCurl;
+ AssertReturn(pszHost, VERR_INVALID_PARAMETER);
+ Log(("rtHttpUpdateProxyConfig: pThis=%p type=%d host='%s' port=%u user='%s'%s\n",
+ pThis, enmProxyType, pszHost, uPort, pszUsername, pszPassword ? " with password" : " without password"));
+
+ if (pThis->fNoProxy)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROXY, (const char *)NULL);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_NOPROXY=NULL: %d (%#x)\n", rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ pThis->fNoProxy = false;
+ }
+
+ if ( pThis->fReapplyProxyInfo
+ || enmProxyType != pThis->enmProxyType)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE, (long)enmProxyType);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_PROXYTYPE=%d: %d (%#x)\n", enmProxyType, rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ pThis->enmProxyType = enmProxyType;
+ }
+
+ if ( pThis->fReapplyProxyInfo
+ || uPort != pThis->uProxyPort)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_PROXYPORT=%d: %d (%#x)\n", uPort, rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ pThis->uProxyPort = uPort;
+ }
+
+ if ( pThis->fReapplyProxyInfo
+ || pszUsername != pThis->pszProxyUsername
+ || RTStrCmp(pszUsername, pThis->pszProxyUsername))
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pszUsername);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_PROXYUSERNAME=%s: %d (%#x)\n", pszUsername, rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ if (pThis->pszProxyUsername)
+ {
+ RTStrFree(pThis->pszProxyUsername);
+ pThis->pszProxyUsername = NULL;
+ }
+ if (pszUsername)
+ {
+ pThis->pszProxyUsername = RTStrDup(pszUsername);
+ AssertReturn(pThis->pszProxyUsername, VERR_NO_STR_MEMORY);
+ }
+ }
+
+ if ( pThis->fReapplyProxyInfo
+ || pszPassword != pThis->pszProxyPassword
+ || RTStrCmp(pszPassword, pThis->pszProxyPassword))
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pszPassword);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_PROXYPASSWORD=%s: %d (%#x)\n", pszPassword ? "xxx" : NULL, rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ if (pThis->pszProxyPassword)
+ {
+ RTMemWipeThoroughly(pThis->pszProxyPassword, strlen(pThis->pszProxyPassword), 2);
+ RTStrFree(pThis->pszProxyPassword);
+ pThis->pszProxyPassword = NULL;
+ }
+ if (pszPassword)
+ {
+ pThis->pszProxyPassword = RTStrDup(pszPassword);
+ AssertReturn(pThis->pszProxyPassword, VERR_NO_STR_MEMORY);
+ }
+ }
+
+ if ( pThis->fReapplyProxyInfo
+ || pszHost != pThis->pszProxyHost
+ || RTStrCmp(pszHost, pThis->pszProxyHost))
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pszHost);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_PROXY=%s: %d (%#x)\n", pszHost, rcCurl, rcCurl),
+ VERR_HTTP_CURL_PROXY_CONFIG);
+ if (pThis->pszProxyHost)
+ {
+ RTStrFree(pThis->pszProxyHost);
+ pThis->pszProxyHost = NULL;
+ }
+ if (pszHost)
+ {
+ pThis->pszProxyHost = RTStrDup(pszHost);
+ AssertReturn(pThis->pszProxyHost, VERR_NO_STR_MEMORY);
+ }
+ }
+
+ pThis->fReapplyProxyInfo = false;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * rtHttpConfigureProxyForUrl: Disables proxying.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP client instance.
+ */
+static int rtHttpUpdateAutomaticProxyDisable(PRTHTTPINTERNAL pThis)
+{
+ Log(("rtHttpUpdateAutomaticProxyDisable: pThis=%p\n", pThis));
+
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTP) == CURLE_OK, VERR_INTERNAL_ERROR_2);
+ pThis->enmProxyType = CURLPROXY_HTTP;
+
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)1080) == CURLE_OK, VERR_INTERNAL_ERROR_2);
+ pThis->uProxyPort = 1080;
+
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
+ if (pThis->pszProxyUsername)
+ {
+ RTStrFree(pThis->pszProxyUsername);
+ pThis->pszProxyUsername = NULL;
+ }
+
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
+ if (pThis->pszProxyPassword)
+ {
+ RTStrFree(pThis->pszProxyPassword);
+ pThis->pszProxyPassword = NULL;
+ }
+
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, "") == CURLE_OK, VERR_INTERNAL_ERROR_2);
+ if (pThis->pszProxyHost)
+ {
+ RTStrFree(pThis->pszProxyHost);
+ pThis->pszProxyHost = NULL;
+ }
+
+ /* No proxy for everything! */
+ AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROXY, "*") == CURLE_OK, CURLOPT_PROXY);
+ pThis->fNoProxy = true;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * See if the host name of the URL is included in the stripped no_proxy list.
+ *
+ * The no_proxy list is a colon or space separated list of domain names for
+ * which there should be no proxying. Given "no_proxy=oracle.com" neither the
+ * URL "http://www.oracle.com" nor "http://oracle.com" will not be proxied, but
+ * "http://notoracle.com" will be.
+ *
+ * @returns true if the URL is in the no_proxy list, otherwise false.
+ * @param pszUrl The URL.
+ * @param pszNoProxyList The stripped no_proxy list.
+ */
+static bool rtHttpUrlInNoProxyList(const char *pszUrl, const char *pszNoProxyList)
+{
+ /*
+ * Check for just '*', diabling proxying for everything.
+ * (Caller stripped pszNoProxyList.)
+ */
+ if (*pszNoProxyList == '*' && pszNoProxyList[1] == '\0')
+ return true;
+
+ /*
+ * Empty list? (Caller stripped it, remember).
+ */
+ if (!*pszNoProxyList)
+ return false;
+
+ /*
+ * We now need to parse the URL and extract the host name.
+ */
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, false);
+ char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
+ if (!pszHost) /* Don't assert, in case of file:///xxx or similar blunder. */
+ return false;
+
+ bool fRet = false;
+ size_t const cchHost = strlen(pszHost);
+ if (cchHost)
+ {
+ /*
+ * The list is comma or space separated, walk it and match host names.
+ */
+ while (*pszNoProxyList != '\0')
+ {
+ /* Strip leading slashes, commas and dots. */
+ char ch;
+ while ( (ch = *pszNoProxyList) == ','
+ || ch == '.'
+ || RT_C_IS_SPACE(ch))
+ pszNoProxyList++;
+
+ /* Find the end. */
+ size_t cch = RTStrOffCharOrTerm(pszNoProxyList, ',');
+ size_t offNext = RTStrOffCharOrTerm(pszNoProxyList, ' ');
+ cch = RT_MIN(cch, offNext);
+ offNext = cch;
+
+ /* Trip trailing spaces, well tabs and stuff. */
+ while (cch > 0 && RT_C_IS_SPACE(pszNoProxyList[cch - 1]))
+ cch--;
+
+ /* Do the matching, if we have anything to work with. */
+ if (cch > 0)
+ {
+ if ( ( cch == cchHost
+ && RTStrNICmp(pszNoProxyList, pszHost, cch) == 0)
+ || ( cch < cchHost
+ && pszHost[cchHost - cch - 1] == '.'
+ && RTStrNICmp(pszNoProxyList, &pszHost[cchHost - cch], cch) == 0) )
+ {
+ fRet = true;
+ break;
+ }
+ }
+
+ /* Next. */
+ pszNoProxyList += offNext;
+ }
+ }
+
+ RTStrFree(pszHost);
+ return fRet;
+}
+
+
+/**
+ * Configures a proxy given a "URL" like specification.
+ *
+ * The format is:
+ * @verbatim
+ * [<scheme>"://"][<userid>[@<password>]:]<server>[":"<port>]
+ * @endverbatim
+ *
+ * Where the scheme gives the type of proxy server we're dealing with rather
+ * than the protocol of the external server we wish to talk to.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP client instance.
+ * @param pszProxyUrl The proxy server "URL".
+ */
+static int rtHttpConfigureProxyFromUrl(PRTHTTPINTERNAL pThis, const char *pszProxyUrl)
+{
+ /*
+ * Make sure it can be parsed as an URL.
+ */
+ char *pszFreeMe = NULL;
+ if (!strstr(pszProxyUrl, "://"))
+ {
+ static const char s_szPrefix[] = "http://";
+ size_t cchProxyUrl = strlen(pszProxyUrl);
+ pszFreeMe = (char *)RTMemTmpAlloc(sizeof(s_szPrefix) + cchProxyUrl);
+ if (pszFreeMe)
+ {
+ memcpy(pszFreeMe, s_szPrefix, sizeof(s_szPrefix) - 1);
+ memcpy(&pszFreeMe[sizeof(s_szPrefix) - 1], pszProxyUrl, cchProxyUrl);
+ pszFreeMe[sizeof(s_szPrefix) - 1 + cchProxyUrl] = '\0';
+ pszProxyUrl = pszFreeMe;
+ }
+ else
+ return VERR_NO_TMP_MEMORY;
+ }
+
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(pszProxyUrl, &Parsed);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszHost = RTUriParsedAuthorityHost(pszProxyUrl, &Parsed);
+ if (pszHost)
+ {
+ /*
+ * We've got a host name, try get the rest.
+ */
+ char *pszUsername = RTUriParsedAuthorityUsername(pszProxyUrl, &Parsed);
+ char *pszPassword = RTUriParsedAuthorityPassword(pszProxyUrl, &Parsed);
+ uint32_t uProxyPort = RTUriParsedAuthorityPort(pszProxyUrl, &Parsed);
+ bool fUnknownProxyType = false;
+ curl_proxytype enmProxyType;
+ if (RTUriIsSchemeMatch(pszProxyUrl, "http"))
+ {
+ enmProxyType = CURLPROXY_HTTP;
+ if (uProxyPort == UINT32_MAX)
+ uProxyPort = 80;
+ }
+#ifdef CURL_AT_LEAST_VERSION
+# if CURL_AT_LEAST_VERSION(7,52,0)
+ else if (RTUriIsSchemeMatch(pszProxyUrl, "https"))
+ {
+ enmProxyType = CURLPROXY_HTTPS;
+ if (uProxyPort == UINT32_MAX)
+ uProxyPort = 443;
+ }
+# endif
+#endif
+ else if ( RTUriIsSchemeMatch(pszProxyUrl, "socks4")
+ || RTUriIsSchemeMatch(pszProxyUrl, "socks"))
+ enmProxyType = CURLPROXY_SOCKS4;
+ else if (RTUriIsSchemeMatch(pszProxyUrl, "socks4a"))
+ enmProxyType = CURLPROXY_SOCKS4A;
+ else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5"))
+ enmProxyType = CURLPROXY_SOCKS5;
+ else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5h"))
+ enmProxyType = CURLPROXY_SOCKS5_HOSTNAME;
+ else
+ {
+ fUnknownProxyType = true;
+ enmProxyType = CURLPROXY_HTTP;
+ if (uProxyPort == UINT32_MAX)
+ uProxyPort = 8080;
+ }
+
+ /* Guess the port from the proxy type if not given. */
+ if (uProxyPort == UINT32_MAX)
+ uProxyPort = 1080; /* CURL_DEFAULT_PROXY_PORT */
+
+ rc = rtHttpUpdateProxyConfig(pThis, enmProxyType, pszHost, uProxyPort, pszUsername, pszPassword);
+ if (RT_SUCCESS(rc) && fUnknownProxyType)
+ rc = VWRN_WRONG_TYPE;
+
+ RTStrFree(pszUsername);
+ RTStrFree(pszPassword);
+ RTStrFree(pszHost);
+ }
+ else
+ AssertMsgFailed(("RTUriParsedAuthorityHost('%s',) -> NULL\n", pszProxyUrl));
+ }
+ else
+ AssertMsgFailed(("RTUriParse('%s',) -> %Rrc\n", pszProxyUrl, rc));
+
+ if (pszFreeMe)
+ RTMemTmpFree(pszFreeMe);
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpSetProxyByUrl(RTHTTP hHttp, const char *pszUrl)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertPtrNullReturn(pszUrl, VERR_INVALID_PARAMETER);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+
+ if (!pszUrl || !*pszUrl)
+ return RTHttpUseSystemProxySettings(pThis);
+ if (RTStrNICmpAscii(pszUrl, RT_STR_TUPLE("direct://")) == 0)
+ return rtHttpUpdateAutomaticProxyDisable(pThis);
+ return rtHttpConfigureProxyFromUrl(pThis, pszUrl);
+}
+
+
+/**
+ * Consults enviornment variables that cURL/lynx/wget/lynx uses for figuring out
+ * the proxy config.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL to configure a proxy for.
+ */
+static int rtHttpConfigureProxyForUrlFromEnv(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ char szTmp[_1K];
+
+ /*
+ * First we consult the "no_proxy" / "NO_PROXY" environment variable.
+ */
+ const char *pszNoProxyVar;
+ size_t cchActual;
+ char *pszNoProxyFree = NULL;
+ char *pszNoProxy = szTmp;
+ int rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar = "no_proxy", szTmp, sizeof(szTmp), &cchActual);
+ if (rc == VERR_ENV_VAR_NOT_FOUND)
+ rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar = "NO_PROXY", szTmp, sizeof(szTmp), &cchActual);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ pszNoProxyFree = pszNoProxy = (char *)RTMemTmpAlloc(cchActual + _1K);
+ AssertReturn(pszNoProxy, VERR_NO_TMP_MEMORY);
+ rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar, pszNoProxy, cchActual + _1K, NULL);
+ }
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_ENV_VAR_NOT_FOUND, ("rc=%Rrc\n", rc));
+ bool fNoProxy = false;
+ if (RT_SUCCESS(rc))
+ fNoProxy = rtHttpUrlInNoProxyList(pszUrl, RTStrStrip(pszNoProxy));
+ RTMemTmpFree(pszNoProxyFree);
+ if (!fNoProxy)
+ {
+ /*
+ * Get the schema specific specific env var, falling back on the
+ * generic all_proxy if not found.
+ */
+ const char *apszEnvVars[4];
+ unsigned cEnvVars = 0;
+ if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("http:")))
+ apszEnvVars[cEnvVars++] = "http_proxy"; /* Skip HTTP_PROXY because of cgi paranoia */
+ else if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("https:")))
+ {
+ apszEnvVars[cEnvVars++] = "https_proxy";
+ apszEnvVars[cEnvVars++] = "HTTPS_PROXY";
+ }
+ else if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("ftp:")))
+ {
+ apszEnvVars[cEnvVars++] = "ftp_proxy";
+ apszEnvVars[cEnvVars++] = "FTP_PROXY";
+ }
+ else
+ AssertMsgFailedReturn(("Unknown/unsupported schema in URL: '%s'\n", pszUrl), VERR_NOT_SUPPORTED);
+ apszEnvVars[cEnvVars++] = "all_proxy";
+ apszEnvVars[cEnvVars++] = "ALL_PROXY";
+
+ /*
+ * We try the env vars out and goes with the first one we can make sense out of.
+ * If we cannot make sense of any, we return the first unexpected rc we got.
+ */
+ rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < cEnvVars; i++)
+ {
+ size_t cchValue;
+ int rc2 = RTEnvGetEx(RTENV_DEFAULT, apszEnvVars[i], szTmp, sizeof(szTmp) - sizeof("http://"), &cchValue);
+ if (RT_SUCCESS(rc2))
+ {
+ if (cchValue != 0)
+ {
+ /* Add a http:// prefix so RTUriParse groks it (cheaper to do it here). */
+ if (!strstr(szTmp, "://"))
+ {
+ memmove(&szTmp[sizeof("http://") - 1], szTmp, cchValue + 1);
+ memcpy(szTmp, RT_STR_TUPLE("http://"));
+ }
+
+ rc2 = rtHttpConfigureProxyFromUrl(pThis, szTmp);
+ if (RT_SUCCESS(rc2))
+ rc = rc2;
+ }
+ /*
+ * The variable is empty. Guess that means no proxying wanted.
+ */
+ else
+ {
+ rc = rtHttpUpdateAutomaticProxyDisable(pThis);
+ break;
+ }
+ }
+ else
+ AssertMsgStmt(rc2 == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n", rc2), if (RT_SUCCESS(rc)) rc = rc2);
+ }
+ }
+ /*
+ * The host is the no-proxy list, it seems.
+ */
+ else
+ rc = rtHttpUpdateAutomaticProxyDisable(pThis);
+
+ return rc;
+}
+
+#ifdef IPRT_USE_LIBPROXY
+
+/**
+ * @callback_method_impl{FNRTONCE,
+ * Attempts to load libproxy.so.1 and resolves APIs}
+ */
+static DECLCALLBACK(int) rtHttpLibProxyResolveImports(void *pvUser)
+{
+ RTLDRMOD hMod;
+ int rc = RTLdrLoadSystem("libproxy.so.1", false /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "px_proxy_factory_new", (void **)&g_pfnLibProxyFactoryCtor);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "px_proxy_factory_free", (void **)&g_pfnLibProxyFactoryDtor);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "px_proxy_factory_get_proxies", (void **)&g_pfnLibProxyFactoryGetProxies);
+ if (RT_SUCCESS(rc))
+ {
+ RTMEM_WILL_LEAK(hMod);
+ g_hLdrLibProxy = hMod;
+ }
+ else
+ RTLdrClose(hMod);
+ AssertRC(rc);
+ }
+
+ NOREF(pvUser);
+ return rc;
+}
+
+/**
+ * Reconfigures the cURL proxy settings for the given URL, libproxy style.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL.
+ */
+static int rtHttpLibProxyConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ int rcRet = VINF_NOT_SUPPORTED;
+
+ int rc = RTOnce(&g_LibProxyResolveImportsOnce, rtHttpLibProxyResolveImports, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Instance the factory and ask for a list of proxies.
+ */
+ PLIBPROXYFACTORY pFactory = g_pfnLibProxyFactoryCtor();
+ if (pFactory)
+ {
+ char **papszProxies = g_pfnLibProxyFactoryGetProxies(pFactory, pszUrl);
+ g_pfnLibProxyFactoryDtor(pFactory);
+ if (papszProxies)
+ {
+ /*
+ * Look for something we can use.
+ */
+ for (unsigned i = 0; papszProxies[i]; i++)
+ {
+ if (strncmp(papszProxies[i], RT_STR_TUPLE("direct://")) == 0)
+ rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
+ else if ( strncmp(papszProxies[i], RT_STR_TUPLE("http://")) == 0
+ || strncmp(papszProxies[i], RT_STR_TUPLE("socks5://")) == 0
+ || strncmp(papszProxies[i], RT_STR_TUPLE("socks4://")) == 0
+ || strncmp(papszProxies[i], RT_STR_TUPLE("socks://")) == 0 /** @todo same problem as on OS X. */
+ )
+ rcRet = rtHttpConfigureProxyFromUrl(pThis, papszProxies[i]);
+ else
+ continue;
+ if (rcRet != VINF_NOT_SUPPORTED)
+ break;
+ }
+
+ /* free the result. */
+ for (unsigned i = 0; papszProxies[i]; i++)
+ free(papszProxies[i]);
+ free(papszProxies);
+ }
+ }
+ }
+
+ return rcRet;
+}
+
+#endif /* IPRT_USE_LIBPROXY */
+
+#ifdef RT_OS_DARWIN
+
+/**
+ * Get a boolean like integer value from a dictionary.
+ *
+ * @returns true / false.
+ * @param hDict The dictionary.
+ * @param pvKey The dictionary value key.
+ */
+static bool rtHttpDarwinGetBooleanFromDict(CFDictionaryRef hDict, void const *pvKey, bool fDefault)
+{
+ CFNumberRef hNum = (CFNumberRef)CFDictionaryGetValue(hDict, pvKey);
+ if (hNum)
+ {
+ int fEnabled;
+ if (!CFNumberGetValue(hNum, kCFNumberIntType, &fEnabled))
+ return fDefault;
+ return fEnabled != 0;
+ }
+ return fDefault;
+}
+
+
+/**
+ * Creates a CFURL object for an URL.
+ *
+ * @returns CFURL object reference.
+ * @param pszUrl The URL.
+ */
+static CFURLRef rtHttpDarwinUrlToCFURL(const char *pszUrl)
+{
+ /* CFURLCreateStringByAddingPercentEscapes is deprecated, so try use CFURLCreateWithBytes
+ as it doesn't validate as much as as CFUrlCreateWithString does. */
+#if 0
+ CFURLRef hUrl = NULL;
+ CFStringRef hStrUrl = CFStringCreateWithCString(kCFAllocatorDefault, pszUrl, kCFStringEncodingUTF8);
+ if (hStrUrl)
+ {
+ CFStringRef hStrUrlEscaped = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, hStrUrl,
+ NULL /*charactersToLeaveUnescaped*/,
+ NULL /*legalURLCharactersToBeEscaped*/,
+ kCFStringEncodingUTF8);
+ if (hStrUrlEscaped)
+ {
+ hUrl = CFURLCreateWithString(kCFAllocatorDefault, hStrUrlEscaped, NULL /*baseURL*/);
+ Assert(hUrl);
+ CFRelease(hStrUrlEscaped);
+ }
+ else
+ AssertFailed();
+ CFRelease(hStrUrl);
+ }
+ else
+ AssertFailed();
+#else
+ CFURLRef hUrl = CFURLCreateWithBytes(kCFAllocatorDefault, (const uint8_t *)pszUrl, strlen(pszUrl),
+ kCFStringEncodingUTF8, NULL /*baseURL*/);
+ Assert(hUrl);
+#endif
+ return hUrl;
+}
+
+
+/**
+ * For passing results from rtHttpDarwinPacCallback to
+ * rtHttpDarwinExecuteProxyAutoConfigurationUrl.
+ */
+typedef struct RTHTTPDARWINPACRESULT
+{
+ CFArrayRef hArrayProxies;
+ CFErrorRef hError;
+} RTHTTPDARWINPACRESULT;
+typedef RTHTTPDARWINPACRESULT *PRTHTTPDARWINPACRESULT;
+
+/**
+ * Stupid callback for getting the result from
+ * CFNetworkExecuteProxyAutoConfigurationURL.
+ *
+ * @param pvUser Pointer to a RTHTTPDARWINPACRESULT on the stack of
+ * rtHttpDarwinExecuteProxyAutoConfigurationUrl.
+ * @param hArrayProxies The result array.
+ * @param hError Errors, if any.
+ */
+static void rtHttpDarwinPacCallback(void *pvUser, CFArrayRef hArrayProxies, CFErrorRef hError)
+{
+ PRTHTTPDARWINPACRESULT pResult = (PRTHTTPDARWINPACRESULT)pvUser;
+
+ Assert(pResult->hArrayProxies == NULL);
+ if (hArrayProxies)
+ pResult->hArrayProxies = (CFArrayRef)CFRetain(hArrayProxies);
+
+ Assert(pResult->hError == NULL);
+ if (hError)
+ pResult->hError = (CFErrorRef)CFRetain(hError);
+
+ CFRunLoopStop(CFRunLoopGetCurrent());
+}
+
+
+/**
+ * Executes a PAC script and returning the proxies it suggests.
+ *
+ * @returns Array of proxy configs (CFProxySupport.h style).
+ * @param hUrlTarget The URL we're about to use.
+ * @param hUrlScript The PAC script URL.
+ */
+static CFArrayRef rtHttpDarwinExecuteProxyAutoConfigurationUrl(CFURLRef hUrlTarget, CFURLRef hUrlScript)
+{
+ char szTmp[256];
+ if (LogIsFlowEnabled())
+ {
+ szTmp[0] = '\0';
+ CFStringGetCString(CFURLGetString(hUrlScript), szTmp, sizeof(szTmp), kCFStringEncodingUTF8);
+ LogFlow(("rtHttpDarwinExecuteProxyAutoConfigurationUrl: hUrlScript=%p:%s\n", hUrlScript, szTmp));
+ }
+
+ /*
+ * Use CFNetworkExecuteProxyAutoConfigurationURL here so we don't have to
+ * download the script ourselves and mess around with too many CF APIs.
+ */
+ CFRunLoopRef hRunLoop = CFRunLoopGetCurrent();
+ AssertReturn(hRunLoop, NULL);
+
+ RTHTTPDARWINPACRESULT Result = { NULL, NULL };
+ CFStreamClientContext Ctx = { 0, &Result, NULL, NULL, NULL };
+ CFRunLoopSourceRef hRunLoopSrc = CFNetworkExecuteProxyAutoConfigurationURL(hUrlScript, hUrlTarget,
+ rtHttpDarwinPacCallback, &Ctx);
+ AssertReturn(hRunLoopSrc, NULL);
+
+ CFStringRef kMode = CFSTR("com.apple.dts.CFProxySupportTool");
+ CFRunLoopAddSource(hRunLoop, hRunLoopSrc, kMode);
+ CFRunLoopRunInMode(kMode, 1.0e10, false); /* callback will force a return. */
+ CFRunLoopRemoveSource(hRunLoop, hRunLoopSrc, kMode);
+
+ /** @todo convert errors, maybe even fail. */
+
+ /*
+ * Autoconfig (or missing wpad server) typically results in:
+ * domain:kCFErrorDomainCFNetwork; code=kCFHostErrorUnknown (2).
+ *
+ * In the autoconfig case, it looks like we're getting two entries, first
+ * one that's http://wpad/wpad.dat and a noproxy entry. So, no reason to
+ * be very upset if this fails, just continue trying alternatives.
+ */
+ if (Result.hError)
+ {
+ if (LogIsEnabled())
+ {
+ szTmp[0] = '\0';
+ CFStringGetCString(CFErrorCopyDescription(Result.hError), szTmp, sizeof(szTmp), kCFStringEncodingUTF8);
+ Log(("rtHttpDarwinExecuteProxyAutoConfigurationUrl: error! code=%ld desc='%s'\n", (long)CFErrorGetCode(Result.hError), szTmp));
+ }
+ CFRelease(Result.hError);
+ }
+ return Result.hArrayProxies;
+}
+
+
+/**
+ * Attempt to configure the proxy according to @a hDictProxy.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if not able to configure it and
+ * the caller should try out alternative proxy configs and fallbacks.
+ * @param pThis The HTTP client instance.
+ * @param hDictProxy The proxy configuration (see CFProxySupport.h).
+ * @param hUrlTarget The URL we're about to use.
+ * @param fIgnorePacType Whether to ignore PAC type proxy entries (i.e.
+ * javascript URL). This is set when we're processing
+ * the output from a PAC script.
+ */
+static int rtHttpDarwinTryConfigProxy(PRTHTTPINTERNAL pThis, CFDictionaryRef hDictProxy, CFURLRef hUrlTarget, bool fIgnorePacType)
+{
+ CFStringRef hStrProxyType = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyTypeKey);
+ AssertReturn(hStrProxyType, VINF_NOT_SUPPORTED);
+
+ /*
+ * No proxy is fairly simple and common.
+ */
+ if (CFEqual(hStrProxyType, kCFProxyTypeNone))
+ return rtHttpUpdateAutomaticProxyDisable(pThis);
+
+ /*
+ * PAC URL means recursion, however we only do one level.
+ */
+ if (CFEqual(hStrProxyType, kCFProxyTypeAutoConfigurationURL))
+ {
+ AssertReturn(!fIgnorePacType, VINF_NOT_SUPPORTED);
+
+ CFURLRef hUrlScript = (CFURLRef)CFDictionaryGetValue(hDictProxy, kCFProxyAutoConfigurationURLKey);
+ AssertReturn(hUrlScript, VINF_NOT_SUPPORTED);
+
+ int rcRet = VINF_NOT_SUPPORTED;
+ CFArrayRef hArray = rtHttpDarwinExecuteProxyAutoConfigurationUrl(hUrlTarget, hUrlScript);
+ if (hArray)
+ {
+ rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, true /*fIgnorePacType*/);
+ CFRelease(hArray);
+ }
+ return rcRet;
+ }
+
+ /*
+ * Determine the proxy type (not entirely sure about type == proxy type and
+ * not scheme/protocol)...
+ */
+ curl_proxytype enmProxyType = CURLPROXY_HTTP;
+ uint32_t uDefaultProxyPort = 8080;
+ if ( CFEqual(hStrProxyType, kCFProxyTypeHTTP)
+ || CFEqual(hStrProxyType, kCFProxyTypeHTTPS))
+ { /* defaults */ }
+ else if (CFEqual(hStrProxyType, kCFProxyTypeSOCKS))
+ {
+ /** @todo All we get from darwin is 'SOCKS', no idea whether it's SOCK4 or
+ * SOCK5 on the other side... Selecting SOCKS5 for now. */
+ enmProxyType = CURLPROXY_SOCKS5;
+ uDefaultProxyPort = 1080;
+ }
+ /* Unknown proxy type. */
+ else
+ return VINF_NOT_SUPPORTED;
+
+ /*
+ * Extract the proxy configuration.
+ */
+ /* The proxy host name. */
+ char szHostname[_1K];
+ CFStringRef hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyHostNameKey);
+ AssertReturn(hStr, VINF_NOT_SUPPORTED);
+ AssertReturn(CFStringGetCString(hStr, szHostname, sizeof(szHostname), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
+
+ /* Get the port number (optional). */
+ SInt32 iProxyPort;
+ CFNumberRef hNum = (CFNumberRef)CFDictionaryGetValue(hDictProxy, kCFProxyPortNumberKey);
+ if (hNum && CFNumberGetValue(hNum, kCFNumberSInt32Type, &iProxyPort))
+ AssertMsgStmt(iProxyPort > 0 && iProxyPort < _64K, ("%d\n", iProxyPort), iProxyPort = uDefaultProxyPort);
+ else
+ iProxyPort = uDefaultProxyPort;
+
+ /* The proxy username. */
+ char szUsername[256];
+ hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyUsernameKey);
+ if (hStr)
+ AssertReturn(CFStringGetCString(hStr, szUsername, sizeof(szUsername), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
+ else
+ szUsername[0] = '\0';
+
+ /* The proxy password. */
+ char szPassword[384];
+ hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyPasswordKey);
+ if (hStr)
+ AssertReturn(CFStringGetCString(hStr, szPassword, sizeof(szPassword), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
+ else
+ szPassword[0] = '\0';
+
+ /*
+ * Apply the proxy config.
+ */
+ return rtHttpUpdateProxyConfig(pThis, enmProxyType, szHostname, iProxyPort,
+ szUsername[0] ? szUsername : NULL, szPassword[0] ? szPassword : NULL);
+}
+
+
+/**
+ * Try do proxy config for our HTTP client instance given an array of proxies.
+ *
+ * This is used with the output from a CFProxySupport.h API.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if not able to configure it and
+ * we might want to try out fallbacks.
+ * @param pThis The HTTP client instance.
+ * @param hArrayProxies The proxies CFPRoxySupport have given us.
+ * @param hUrlTarget The URL we're about to use.
+ * @param fIgnorePacType Whether to ignore PAC type proxy entries (i.e.
+ * javascript URL). This is set when we're processing
+ * the output from a PAC script.
+ */
+static int rtHttpDarwinTryConfigProxies(PRTHTTPINTERNAL pThis, CFArrayRef hArrayProxies, CFURLRef hUrlTarget, bool fIgnorePacType)
+{
+ int rcRet = VINF_NOT_SUPPORTED;
+ CFIndex const cEntries = CFArrayGetCount(hArrayProxies);
+ LogFlow(("rtHttpDarwinTryConfigProxies: cEntries=%d\n", cEntries));
+ for (CFIndex i = 0; i < cEntries; i++)
+ {
+ CFDictionaryRef hDictProxy = (CFDictionaryRef)CFArrayGetValueAtIndex(hArrayProxies, i);
+ AssertContinue(hDictProxy);
+
+ rcRet = rtHttpDarwinTryConfigProxy(pThis, hDictProxy, hUrlTarget, fIgnorePacType);
+ if (rcRet != VINF_NOT_SUPPORTED)
+ break;
+ }
+ return rcRet;
+}
+
+
+/**
+ * Inner worker for rtHttpWinConfigureProxyForUrl.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL.
+ */
+static int rtHttpDarwinConfigureProxyForUrlWorker(PRTHTTPINTERNAL pThis, CFDictionaryRef hDictProxies,
+ const char *pszUrl, const char *pszHost)
+{
+ CFArrayRef hArray;
+
+ /*
+ * From what I can tell, the CFNetworkCopyProxiesForURL API doesn't apply
+ * proxy exclusion rules (tested on 10.9). So, do that manually.
+ */
+ RTNETADDRU HostAddr;
+ int fIsHostIpv4Address = -1;
+ char szTmp[_4K];
+
+ /* If we've got a simple hostname, something containing no dots, we must check
+ whether such simple hostnames are excluded from proxying by default or not. */
+ if (strchr(pszHost, '.') == NULL)
+ {
+ if (rtHttpDarwinGetBooleanFromDict(hDictProxies, kSCPropNetProxiesExcludeSimpleHostnames, false))
+ return rtHttpUpdateAutomaticProxyDisable(pThis);
+ fIsHostIpv4Address = false;
+ }
+
+ /* Consult the exclusion list. This is an array of strings.
+ This is very similar to what we do on windows. */
+ hArray = (CFArrayRef)CFDictionaryGetValue(hDictProxies, kSCPropNetProxiesExceptionsList);
+ if (hArray)
+ {
+ CFIndex const cEntries = CFArrayGetCount(hArray);
+ for (CFIndex i = 0; i < cEntries; i++)
+ {
+ CFStringRef hStr = (CFStringRef)CFArrayGetValueAtIndex(hArray, i);
+ AssertContinue(hStr);
+ AssertContinue(CFStringGetCString(hStr, szTmp, sizeof(szTmp), kCFStringEncodingUTF8));
+ RTStrToLower(szTmp);
+
+ bool fRet;
+ if ( strchr(szTmp, '*')
+ || strchr(szTmp, '?'))
+ fRet = RTStrSimplePatternMatch(szTmp, pszHost);
+ else
+ {
+ if (fIsHostIpv4Address == -1)
+ fIsHostIpv4Address = RT_SUCCESS(RTNetStrToIPv4Addr(pszHost, &HostAddr.IPv4));
+ RTNETADDRIPV4 Network, Netmask;
+ if ( fIsHostIpv4Address
+ && RT_SUCCESS(RTCidrStrToIPv4(szTmp, &Network, &Netmask)) )
+ fRet = (HostAddr.IPv4.u & Netmask.u) == Network.u;
+ else
+ fRet = strcmp(szTmp, pszHost) == 0;
+ }
+ if (fRet)
+ return rtHttpUpdateAutomaticProxyDisable(pThis);
+ }
+ }
+
+#if 0 /* The start of a manual alternative to CFNetworkCopyProxiesForURL below, hopefully we won't need this. */
+ /*
+ * Is proxy auto config (PAC) enabled? If so, we must consult it first.
+ */
+ if (rtHttpDarwinGetBooleanFromDict(hDictProxies, kSCPropNetProxiesProxyAutoConfigEnable, false))
+ {
+ /* Convert the auto config url string to a CFURL object. */
+ CFStringRef hStrAutoConfigUrl = (CFStringRef)CFDictionaryGetValue(hDictProxies, kSCPropNetProxiesProxyAutoConfigURLString);
+ if (hStrAutoConfigUrl)
+ {
+ if (CFStringGetCString(hStrAutoConfigUrl, szTmp, sizeof(szTmp), kCFStringEncodingUTF8))
+ {
+ CFURLRef hUrlScript = rtHttpDarwinUrlToCFURL(szTmp);
+ if (hUrlScript)
+ {
+ int rcRet = VINF_NOT_SUPPORTED;
+ CFURLRef hUrlTarget = rtHttpDarwinUrlToCFURL(pszUrl);
+ if (hUrlTarget)
+ {
+ /* Work around for <rdar://problem/5530166>, whatever that is. Initializes
+ some internal CFNetwork state, they say. See CFPRoxySupportTool example. */
+ hArray = CFNetworkCopyProxiesForURL(hUrlTarget, NULL);
+ if (hArray)
+ CFRelease(hArray);
+
+ hArray = rtHttpDarwinExecuteProxyAutoConfigurationUrl(hUrlTarget, hUrlScript);
+ if (hArray)
+ {
+ rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, true /*fIgnorePacType*/);
+ CFRelease(hArray);
+ }
+ }
+ CFRelease(hUrlScript);
+ if (rcRet != VINF_NOT_SUPPORTED)
+ return rcRet;
+ }
+ }
+ }
+ }
+
+ /*
+ * Try static proxy configs.
+ */
+ /** @todo later if needed. */
+ return VERR_NOT_SUPPORTED;
+
+#else
+ /*
+ * Simple solution - "just" use CFNetworkCopyProxiesForURL.
+ */
+ CFURLRef hUrlTarget = rtHttpDarwinUrlToCFURL(pszUrl);
+ AssertReturn(hUrlTarget, VERR_INTERNAL_ERROR);
+ int rcRet = VINF_NOT_SUPPORTED;
+
+ /* Work around for <rdar://problem/5530166>, whatever that is. Initializes
+ some internal CFNetwork state, they say. See CFPRoxySupportTool example. */
+ CFDictionaryRef hDictNull = (CFDictionaryRef)(42-42); /*workaround for -Wnonnull warning in Clang 11. */
+ hArray = CFNetworkCopyProxiesForURL(hUrlTarget, hDictNull);
+ if (hArray)
+ CFRelease(hArray);
+
+ /* The actual run. */
+ hArray = CFNetworkCopyProxiesForURL(hUrlTarget, hDictProxies);
+ if (hArray)
+ {
+ rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, false /*fIgnorePacType*/);
+ CFRelease(hArray);
+ }
+ CFRelease(hUrlTarget);
+
+ return rcRet;
+#endif
+}
+
+/**
+ * Reconfigures the cURL proxy settings for the given URL, OS X style.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL.
+ */
+static int rtHttpDarwinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ /*
+ * Parse the URL, if there isn't any host name (like for file:///xxx.txt)
+ * we don't need to run thru proxy settings to know what to do.
+ */
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, false);
+ if (Parsed.cchAuthorityHost == 0)
+ return rtHttpUpdateAutomaticProxyDisable(pThis);
+ char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
+ AssertReturn(pszHost, VERR_NO_STR_MEMORY);
+ RTStrToLower(pszHost);
+
+ /*
+ * Get a copy of the proxy settings (10.6 API).
+ */
+ CFDictionaryRef hDictProxies = CFNetworkCopySystemProxySettings(); /* Alt for 10.5: SCDynamicStoreCopyProxies(NULL); */
+ if (hDictProxies)
+ rc = rtHttpDarwinConfigureProxyForUrlWorker(pThis, hDictProxies, pszUrl, pszHost);
+ else
+ rc = VINF_NOT_SUPPORTED;
+ CFRelease(hDictProxies);
+
+ RTStrFree(pszHost);
+ return rc;
+}
+
+#endif /* RT_OS_DARWIN */
+
+#ifdef RT_OS_WINDOWS
+
+/**
+ * @callback_method_impl{FNRTONCE, Loads WinHttp.dll and resolves APIs}
+ */
+static DECLCALLBACK(int) rtHttpWinResolveImports(void *pvUser)
+{
+ /*
+ * winhttp.dll is not present on NT4 and probably was first introduced with XP.
+ */
+ RTLDRMOD hMod;
+ int rc = RTLdrLoadSystem("winhttp.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "WinHttpOpen", (void **)&g_pfnWinHttpOpen);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "WinHttpCloseHandle", (void **)&g_pfnWinHttpCloseHandle);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "WinHttpGetProxyForUrl", (void **)&g_pfnWinHttpGetProxyForUrl);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "WinHttpGetDefaultProxyConfiguration", (void **)&g_pfnWinHttpGetDefaultProxyConfiguration);
+ if (RT_SUCCESS(rc))
+ rc = RTLdrGetSymbol(hMod, "WinHttpGetIEProxyConfigForCurrentUser", (void **)&g_pfnWinHttpGetIEProxyConfigForCurrentUser);
+ RTLdrClose(hMod);
+ AssertRC(rc);
+ }
+ else
+ AssertMsg(g_enmWinVer < kRTWinOSType_XP, ("%Rrc\n", rc));
+
+ NOREF(pvUser);
+ return rc;
+}
+
+
+/**
+ * Matches the URL against the given Windows by-pass list.
+ *
+ * @returns true if we should by-pass the proxy for this URL, false if not.
+ * @param pszUrl The URL.
+ * @param pwszBypass The Windows by-pass list.
+ */
+static bool rtHttpWinIsUrlInBypassList(const char *pszUrl, PCRTUTF16 pwszBypass)
+{
+ /*
+ * Don't bother parsing the URL if we've actually got nothing to work with
+ * in the by-pass list.
+ */
+ if (!pwszBypass)
+ return false;
+
+ RTUTF16 wc;
+ while ( (wc = *pwszBypass) != '\0'
+ && ( RTUniCpIsSpace(wc)
+ || wc == ';') )
+ pwszBypass++;
+ if (wc == '\0')
+ return false;
+
+ /*
+ * We now need to parse the URL and extract the host name.
+ */
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, false);
+ char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
+ if (!pszHost) /* Don't assert, in case of file:///xxx or similar blunder. */
+ return false;
+ RTStrToLower(pszHost);
+
+ bool fRet = false;
+ char *pszBypassFree;
+ rc = RTUtf16ToUtf8(pwszBypass, &pszBypassFree);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Walk the by-pass list.
+ *
+ * According to https://msdn.microsoft.com/en-us/library/aa384098(v=vs.85).aspx
+ * a by-pass list is semicolon delimited list. The entries are either host
+ * names or IP addresses, and may use wildcard ('*', '?', I guess). There
+ * special "<local>" entry matches anything without a dot.
+ */
+ RTNETADDRU HostAddr = { 0, 0 };
+ int fIsHostIpv4Address = -1;
+ char *pszEntry = pszBypassFree;
+ while (*pszEntry != '\0')
+ {
+ /*
+ * Find end of entry.
+ */
+ char ch;
+ size_t cchEntry = 1;
+ while ( (ch = pszEntry[cchEntry]) != '\0'
+ && ch != ';'
+ && !RT_C_IS_SPACE(ch))
+ cchEntry++;
+
+ char chSaved = pszEntry[cchEntry];
+ pszEntry[cchEntry] = '\0';
+ RTStrToLower(pszEntry);
+
+ if ( cchEntry == sizeof("<local>") - 1
+ && memcmp(pszEntry, RT_STR_TUPLE("<local>")) == 0)
+ fRet = strchr(pszHost, '.') == NULL;
+ else if ( memchr(pszEntry, '*', cchEntry) != NULL
+ || memchr(pszEntry, '?', cchEntry) != NULL)
+ fRet = RTStrSimplePatternMatch(pszEntry, pszHost);
+ else
+ {
+ if (fIsHostIpv4Address == -1)
+ fIsHostIpv4Address = RT_SUCCESS(RTNetStrToIPv4Addr(pszHost, &HostAddr.IPv4));
+ RTNETADDRIPV4 Network, Netmask;
+ if ( fIsHostIpv4Address
+ && RT_SUCCESS(RTCidrStrToIPv4(pszEntry, &Network, &Netmask)) )
+ fRet = (HostAddr.IPv4.u & Netmask.u) == Network.u;
+ else
+ fRet = strcmp(pszEntry, pszHost) == 0;
+ }
+
+ pszEntry[cchEntry] = chSaved;
+ if (fRet)
+ break;
+
+ /*
+ * Next entry.
+ */
+ pszEntry += cchEntry;
+ while ( (ch = *pszEntry) != '\0'
+ && ( ch == ';'
+ || RT_C_IS_SPACE(ch)) )
+ pszEntry++;
+ }
+
+ RTStrFree(pszBypassFree);
+ }
+
+ RTStrFree(pszHost);
+ return false;
+}
+
+
+/**
+ * Searches a Windows proxy server list for the best fitting proxy to use, then
+ * reconfigures the HTTP client instance to use it.
+ *
+ * @returns IPRT status code, VINF_NOT_SUPPORTED if we need to consult fallback.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL needing proxying.
+ * @param pwszProxies The list of proxy servers to choose from.
+ */
+static int rtHttpWinSelectProxyFromList(PRTHTTPINTERNAL pThis, const char *pszUrl, PCRTUTF16 pwszProxies)
+{
+ /*
+ * Fend off empty strings (very unlikely, but just in case).
+ */
+ if (!pwszProxies)
+ return VINF_NOT_SUPPORTED;
+
+ RTUTF16 wc;
+ while ( (wc = *pwszProxies) != '\0'
+ && ( RTUniCpIsSpace(wc)
+ || wc == ';') )
+ pwszProxies++;
+ if (wc == '\0')
+ return VINF_NOT_SUPPORTED;
+
+ /*
+ * We now need to parse the URL and extract the scheme.
+ */
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, false);
+ char *pszUrlScheme = RTUriParsedScheme(pszUrl, &Parsed);
+ AssertReturn(pszUrlScheme, VERR_NO_STR_MEMORY);
+ size_t const cchUrlScheme = strlen(pszUrlScheme);
+
+ int rcRet = VINF_NOT_SUPPORTED;
+ char *pszProxiesFree;
+ rc = RTUtf16ToUtf8(pwszProxies, &pszProxiesFree);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Walk the server list.
+ *
+ * According to https://msdn.microsoft.com/en-us/library/aa383912(v=vs.85).aspx
+ * this is also a semicolon delimited list. The entries are on the form:
+ * [<scheme>=][<scheme>"://"]<server>[":"<port>]
+ */
+ bool fBestEntryHasSameScheme = false;
+ const char *pszBestEntry = NULL;
+ char *pszEntry = pszProxiesFree;
+ while (*pszEntry != '\0')
+ {
+ /*
+ * Find end of entry. We include spaces here in addition to ';'.
+ */
+ char ch;
+ size_t cchEntry = 1;
+ while ( (ch = pszEntry[cchEntry]) != '\0'
+ && ch != ';'
+ && !RT_C_IS_SPACE(ch))
+ cchEntry++;
+
+ char const chSaved = pszEntry[cchEntry];
+ pszEntry[cchEntry] = '\0';
+
+ /* Parse the entry. */
+ const char *pszEndOfScheme = strstr(pszEntry, "://");
+ const char *pszEqual = (const char *)memchr(pszEntry, '=',
+ pszEndOfScheme ? pszEndOfScheme - pszEntry : cchEntry);
+ if (pszEqual)
+ {
+ if ( (uintptr_t)(pszEqual - pszEntry) == cchUrlScheme
+ && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0)
+ {
+ pszBestEntry = pszEqual + 1;
+ break;
+ }
+ }
+ else
+ {
+ bool fSchemeMatch = pszEndOfScheme
+ && (uintptr_t)(pszEndOfScheme - pszEntry) == cchUrlScheme
+ && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0;
+ if ( !pszBestEntry
+ || ( !fBestEntryHasSameScheme
+ && fSchemeMatch) )
+ {
+ pszBestEntry = pszEntry;
+ fBestEntryHasSameScheme = fSchemeMatch;
+ }
+ }
+
+ /*
+ * Next entry.
+ */
+ if (!chSaved)
+ break;
+ pszEntry += cchEntry + 1;
+ while ( (ch = *pszEntry) != '\0'
+ && ( ch == ';'
+ || RT_C_IS_SPACE(ch)) )
+ pszEntry++;
+ }
+
+ /*
+ * If we found something, try use it.
+ */
+ if (pszBestEntry)
+ rcRet = rtHttpConfigureProxyFromUrl(pThis, pszBestEntry);
+
+ RTStrFree(pszProxiesFree);
+ }
+
+ RTStrFree(pszUrlScheme);
+ return rc;
+}
+
+
+/**
+ * Reconfigures the cURL proxy settings for the given URL, Windows style.
+ *
+ * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
+ * @param pThis The HTTP client instance.
+ * @param pszUrl The URL.
+ */
+static int rtHttpWinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ int rcRet = VINF_NOT_SUPPORTED;
+
+ int rc = RTOnce(&g_WinResolveImportsOnce, rtHttpWinResolveImports, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try get some proxy info for the URL. We first try getting the IE
+ * config and seeing if we can use WinHttpGetIEProxyConfigForCurrentUser
+ * in some way, if we can we prepare ProxyOptions with a non-zero dwFlags.
+ */
+ WINHTTP_PROXY_INFO ProxyInfo;
+ WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions;
+ RT_ZERO(AutoProxyOptions);
+ RT_ZERO(ProxyInfo);
+
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IeProxyConfig;
+ if (g_pfnWinHttpGetIEProxyConfigForCurrentUser(&IeProxyConfig))
+ {
+ AutoProxyOptions.fAutoLogonIfChallenged = FALSE;
+ AutoProxyOptions.lpszAutoConfigUrl = IeProxyConfig.lpszAutoConfigUrl;
+ if (IeProxyConfig.fAutoDetect)
+ {
+ AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_RUN_INPROCESS;
+ AutoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ }
+ else if (AutoProxyOptions.lpszAutoConfigUrl)
+ AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+ else if (ProxyInfo.lpszProxy)
+ ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ ProxyInfo.lpszProxy = IeProxyConfig.lpszProxy;
+ ProxyInfo.lpszProxyBypass = IeProxyConfig.lpszProxyBypass;
+ }
+ else
+ {
+ AssertMsgFailed(("WinHttpGetIEProxyConfigForCurrentUser -> %u\n", GetLastError()));
+ if (!g_pfnWinHttpGetDefaultProxyConfiguration(&ProxyInfo))
+ {
+ AssertMsgFailed(("WinHttpGetDefaultProxyConfiguration -> %u\n", GetLastError()));
+ RT_ZERO(ProxyInfo);
+ }
+ }
+
+ /*
+ * Should we try WinHttGetProxyForUrl?
+ */
+ if (AutoProxyOptions.dwFlags != 0)
+ {
+ HINTERNET hSession = g_pfnWinHttpOpen(NULL /*pwszUserAgent*/, WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 /*dwFlags*/ );
+ if (hSession != NULL)
+ {
+ PRTUTF16 pwszUrl;
+ rc = RTStrToUtf16(pszUrl, &pwszUrl);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try autodetect first, then fall back on the config URL if there is one.
+ *
+ * Also, we first try without auto authentication, then with. This will according
+ * to http://msdn.microsoft.com/en-us/library/aa383153%28v=VS.85%29.aspx help with
+ * caching the result when it's processed out-of-process (seems default here on W10).
+ */
+ WINHTTP_PROXY_INFO TmpProxyInfo;
+ BOOL fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
+ if ( !fRc
+ && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE)
+ {
+ AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
+ fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
+ }
+
+ if ( !fRc
+ && AutoProxyOptions.dwFlags != WINHTTP_AUTOPROXY_CONFIG_URL
+ && AutoProxyOptions.lpszAutoConfigUrl)
+ {
+ AutoProxyOptions.fAutoLogonIfChallenged = FALSE;
+ AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+ AutoProxyOptions.dwAutoDetectFlags = 0;
+ fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
+ if ( !fRc
+ && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE)
+ {
+ AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
+ fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
+ }
+ }
+
+ if (fRc)
+ {
+ if (ProxyInfo.lpszProxy)
+ GlobalFree(ProxyInfo.lpszProxy);
+ if (ProxyInfo.lpszProxyBypass)
+ GlobalFree(ProxyInfo.lpszProxyBypass);
+ ProxyInfo = TmpProxyInfo;
+ }
+ /*
+ * If the autodetection failed, assume no proxy.
+ */
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if ( dwErr == ERROR_WINHTTP_AUTODETECTION_FAILED
+ || dwErr == ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT
+ || ( dwErr == ERROR_WINHTTP_UNRECOGNIZED_SCHEME
+ && ( RTStrNICmp(pszUrl, RT_STR_TUPLE("https://")) == 0
+ || RTStrNICmp(pszUrl, RT_STR_TUPLE("http://")) == 0) ) )
+ rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
+ else
+ AssertMsgFailed(("g_pfnWinHttpGetProxyForUrl(%s) -> %u; lpszAutoConfigUrl=%sx\n",
+ pszUrl, dwErr, AutoProxyOptions.lpszAutoConfigUrl));
+ }
+ RTUtf16Free(pwszUrl);
+ }
+ else
+ {
+ AssertMsgFailed(("RTStrToUtf16(%s,) -> %Rrc\n", pszUrl, rc));
+ rcRet = rc;
+ }
+ g_pfnWinHttpCloseHandle(hSession);
+ }
+ else
+ AssertMsgFailed(("g_pfnWinHttpOpen -> %u\n", GetLastError()));
+ }
+
+ /*
+ * Try use the proxy info we've found.
+ */
+ switch (ProxyInfo.dwAccessType)
+ {
+ case WINHTTP_ACCESS_TYPE_NO_PROXY:
+ rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
+ break;
+
+ case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
+ if (!rtHttpWinIsUrlInBypassList(pszUrl, ProxyInfo.lpszProxyBypass))
+ rcRet = rtHttpWinSelectProxyFromList(pThis, pszUrl, ProxyInfo.lpszProxy);
+ else
+ rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
+ break;
+
+ case 0:
+ break;
+
+ default:
+ AssertMsgFailed(("%#x\n", ProxyInfo.dwAccessType));
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (ProxyInfo.lpszProxy)
+ GlobalFree(ProxyInfo.lpszProxy);
+ if (ProxyInfo.lpszProxyBypass)
+ GlobalFree(ProxyInfo.lpszProxyBypass);
+ if (AutoProxyOptions.lpszAutoConfigUrl)
+ GlobalFree((PRTUTF16)AutoProxyOptions.lpszAutoConfigUrl);
+ }
+
+ return rcRet;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+
+static int rtHttpConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ if (pThis->fUseSystemProxySettings)
+ {
+#ifdef IPRT_USE_LIBPROXY
+ int rc = rtHttpLibProxyConfigureProxyForUrl(pThis, pszUrl);
+ if (rc == VINF_SUCCESS || RT_FAILURE(rc))
+ return rc;
+ Assert(rc == VINF_NOT_SUPPORTED);
+#endif
+#ifdef RT_OS_DARWIN
+ int rc = rtHttpDarwinConfigureProxyForUrl(pThis, pszUrl);
+ if (rc == VINF_SUCCESS || RT_FAILURE(rc))
+ return rc;
+ Assert(rc == VINF_NOT_SUPPORTED);
+#endif
+#ifdef RT_OS_WINDOWS
+ int rc = rtHttpWinConfigureProxyForUrl(pThis, pszUrl);
+ if (rc == VINF_SUCCESS || RT_FAILURE(rc))
+ return rc;
+ Assert(rc == VINF_NOT_SUPPORTED);
+#endif
+/** @todo system specific class here, fall back on env vars if necessary. */
+ return rtHttpConfigureProxyForUrlFromEnv(pThis, pszUrl);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpSetProxy(RTHTTP hHttp, const char *pcszProxy, uint32_t uPort,
+ const char *pcszProxyUser, const char *pcszProxyPwd)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Update the settings.
+ *
+ * Currently, we don't make alot of effort parsing or checking the input, we
+ * leave that to cURL. (A bit afraid of breaking user settings.)
+ */
+ pThis->fUseSystemProxySettings = false;
+ return rtHttpUpdateProxyConfig(pThis, CURLPROXY_HTTP, pcszProxy, uPort ? uPort : 1080, pcszProxyUser, pcszProxyPwd);
+}
+
+
+
+/*********************************************************************************************************************************
+* HTTP Headers *
+*********************************************************************************************************************************/
+
+/**
+ * Helper for RTHttpSetHeaders and RTHttpAddRawHeader that unsets the user agent
+ * if it is now in one of the headers.
+ */
+static int rtHttpUpdateUserAgentHeader(PRTHTTPINTERNAL pThis, PRTHTTPHEADER pNewHdr)
+{
+ static const char s_szUserAgent[] = "User-Agent";
+ if ( pNewHdr->cchName == sizeof(s_szUserAgent) - 1
+ && RTStrNICmpAscii(pNewHdr->szData, RT_STR_TUPLE(s_szUserAgent)) == 0)
+ {
+ pThis->fHaveUserAgentHeader = true;
+ if (pThis->fHaveSetUserAgent)
+ {
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_USERAGENT, (char *)NULL);
+ Assert(CURL_SUCCESS(rcCurl)); NOREF(rcCurl);
+ pThis->fHaveSetUserAgent = false;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Free the headers associated with the instance (w/o telling cURL about it).
+ *
+ * @param pThis The HTTP client instance.
+ */
+static void rtHttpFreeHeaders(PRTHTTPINTERNAL pThis)
+{
+ struct curl_slist *pHead = pThis->pHeaders;
+ pThis->pHeaders = NULL;
+ pThis->ppHeadersTail = &pThis->pHeaders;
+ pThis->fHaveUserAgentHeader = false;
+
+ while (pHead)
+ {
+ struct curl_slist *pFree = pHead;
+ pHead = pHead->next;
+ ASMCompilerBarrier(); /* paranoia */
+
+ pFree->next = NULL;
+ pFree->data = NULL;
+ RTMemFree(pFree);
+ }
+}
+
+
+/**
+ * Worker for RTHttpSetHeaders and RTHttpAddHeader.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP client instance.
+ * @param pchName The field name. Does not need to be terminated.
+ * @param cchName The field name length.
+ * @param pchValue The field value. Does not need to be terminated.
+ * @param cchValue The field value length.
+ * @param fFlags RTHTTPADDHDR_F_XXX.
+ */
+static int rtHttpAddHeaderWorker(PRTHTTPINTERNAL pThis, const char *pchName, size_t cchName,
+ const char *pchValue, size_t cchValue, uint32_t fFlags)
+{
+ /*
+ * Create the list entry.
+ */
+ size_t cbData = cchName + 2 + cchValue + 1;
+ PRTHTTPHEADER pHdr = (PRTHTTPHEADER)RTMemAlloc(RT_UOFFSETOF_DYN(RTHTTPHEADER, szData[cbData]));
+ if (pHdr)
+ {
+ pHdr->Core.next = NULL;
+ pHdr->Core.data = pHdr->szData;
+ pHdr->cchName = (uint32_t)cchName;
+ pHdr->offValue = (uint32_t)(cchName + 2);
+ char *psz = pHdr->szData;
+ memcpy(psz, pchName, cchName);
+ psz += cchName;
+ *psz++ = ':';
+ *psz++ = ' ';
+ memcpy(psz, pchValue, cchValue);
+ psz[cchValue] = '\0';
+
+ /*
+ * Appending to an existing list requires no cURL interaction.
+ */
+ AssertCompile(RTHTTPADDHDR_F_FRONT != 0);
+ if ( !(fFlags & RTHTTPADDHDR_F_FRONT)
+ && pThis->pHeaders != NULL)
+ {
+ *pThis->ppHeadersTail = &pHdr->Core;
+ pThis->ppHeadersTail = &pHdr->Core.next;
+ return rtHttpUpdateUserAgentHeader(pThis, pHdr);
+ }
+
+ /*
+ * When prepending or adding the first header we need to inform cURL
+ * about the new list head.
+ */
+ pHdr->Core.next = pThis->pHeaders;
+ if (!pThis->pHeaders)
+ pThis->ppHeadersTail = &pHdr->Core.next;
+ pThis->pHeaders = &pHdr->Core;
+
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pThis->pHeaders);
+ if (CURL_SUCCESS(rcCurl))
+ return rtHttpUpdateUserAgentHeader(pThis, pHdr);
+ return VERR_HTTP_CURL_ERROR;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTR3DECL(int) RTHttpSetHeaders(RTHTTP hHttp, size_t cHeaders, const char * const *papszHeaders)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * Drop old headers and reset state.
+ */
+ if (pThis->pHeaders)
+ {
+ rtHttpFreeHeaders(pThis);
+ curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, (struct curl_slist *)NULL);
+ }
+ pThis->ppHeadersTail = &pThis->pHeaders;
+ pThis->fHaveUserAgentHeader = false;
+
+ /*
+ * We're done if no headers specified.
+ */
+ if (!cHeaders)
+ return VINF_SUCCESS;
+
+ /*
+ * Add the headers, one by one.
+ */
+ int rc = VINF_SUCCESS;
+ for (size_t i = 0; i < cHeaders; i++)
+ {
+ const char *pszHeader = papszHeaders[i];
+ size_t cchHeader = strlen(pszHeader);
+ size_t cchName = (const char *)memchr(pszHeader, ':', cchHeader) - pszHeader;
+ AssertBreakStmt(cchName < cchHeader, rc = VERR_INVALID_PARAMETER);
+ size_t offValue = RT_C_IS_BLANK(pszHeader[cchName + 1]) ? cchName + 2 : cchName + 1;
+ rc = rtHttpAddHeaderWorker(pThis, pszHeader, cchName, &pszHeader[offValue], cchHeader - offValue, RTHTTPADDHDR_F_BACK);
+ AssertRCBreak(rc);
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+ rtHttpFreeHeaders(pThis);
+ curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, (struct curl_slist *)NULL);
+ return rc;
+}
+
+
+#if 0 /** @todo reimplement RTHttpAddRawHeader if ever actually needed. */
+RTR3DECL(int) RTHttpAddRawHeader(RTHTTP hHttp, const char *pszHeader, uint32_t fFlags)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!(fFlags & ~RTHTTPADDHDR_F_BACK), VERR_INVALID_FLAGS);
+/** @todo implement RTHTTPADDHDR_F_FRONT */
+
+ /*
+ * Append it to the header list, checking for User-Agent and such.
+ */
+ struct curl_slist *pHeaders = pThis->pHeaders;
+ struct curl_slist *pNewHeaders = curl_slist_append(pHeaders, pszHeader);
+ if (pNewHeaders)
+ pHeaders = pNewHeaders;
+ else
+ return VERR_NO_MEMORY;
+
+ if (strncmp(pszHeader, RT_STR_TUPLE("User-Agent:")) == 0)
+ pThis->fHaveUserAgentHeader = true;
+
+ /*
+ * If this is the first header, we need to tell curl.
+ */
+ if (!pThis->pHeaders)
+ {
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pHeaders);
+ if (CURL_FAILURE(rcCurl))
+ {
+ curl_slist_free_all(pHeaders);
+ return VERR_INVALID_PARAMETER;
+ }
+ pThis->pHeaders = pHeaders;
+ }
+ else
+ Assert(pThis->pHeaders == pHeaders);
+
+ rtHttpUpdateUserAgentHeader(pThis);
+
+ return VINF_SUCCESS;
+}
+#endif
+
+
+RTR3DECL(int) RTHttpAddHeader(RTHTTP hHttp, const char *pszField, const char *pszValue, size_t cchValue, uint32_t fFlags)
+{
+ /*
+ * Validate input and calc string lengths.
+ */
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!(fFlags & ~RTHTTPADDHDR_F_BACK), VERR_INVALID_FLAGS);
+ AssertPtr(pszField);
+ size_t const cchField = strlen(pszField);
+ AssertReturn(cchField > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(pszField[cchField - 1] != ':', VERR_INVALID_PARAMETER);
+ AssertReturn(!RT_C_IS_SPACE(pszField[cchField - 1]), VERR_INVALID_PARAMETER);
+#ifdef RT_STRICT
+ for (size_t i = 0; i < cchField; i++)
+ {
+ char const ch = pszField[i];
+ Assert(RT_C_IS_PRINT(ch) && ch != ':');
+ }
+#endif
+
+ AssertPtr(pszValue);
+ if (cchValue == RTSTR_MAX)
+ cchValue = strlen(pszValue);
+
+ /*
+ * Just pass it along to the worker.
+ */
+ return rtHttpAddHeaderWorker(pThis, pszField, cchField, pszValue, cchValue, fFlags);
+}
+
+
+RTR3DECL(const char *) RTHttpGetHeader(RTHTTP hHttp, const char *pszField, size_t cchField)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN_RC(pThis, NULL);
+
+ PRTHTTPHEADER pCur = (PRTHTTPHEADER)pThis->pHeaders;
+ if (pCur)
+ {
+ if (cchField == RTSTR_MAX)
+ cchField = strlen(pszField);
+ do
+ {
+ if ( pCur->cchName == cchField
+ && RTStrNICmpAscii(pCur->szData, pszField, cchField) == 0)
+ return &pCur->szData[pCur->offValue];
+
+ /* next field. */
+ pCur = (PRTHTTPHEADER)pCur->Core.next;
+ } while (pCur);
+ }
+ return NULL;
+}
+
+
+RTR3DECL(size_t) RTHttpGetHeaderCount(RTHTTP hHttp)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN_RC(pThis, 0);
+
+ /* Note! Only for test cases and debugging, so we don't care about performance. */
+ size_t cHeaders = 0;
+ for (PRTHTTPHEADER pCur = (PRTHTTPHEADER)pThis->pHeaders; pCur != NULL; pCur = (PRTHTTPHEADER)pCur->Core.next)
+ cHeaders++;
+ return cHeaders;
+}
+
+
+RTR3DECL(const char *) RTHttpGetByOrdinal(RTHTTP hHttp, size_t iOrdinal)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN_RC(pThis, NULL);
+
+ /* Note! Only for test cases and debugging, so we don't care about performance. */
+ for (PRTHTTPHEADER pCur = (PRTHTTPHEADER)pThis->pHeaders; pCur != NULL; pCur = (PRTHTTPHEADER)pCur->Core.next)
+ {
+ if (iOrdinal == 0)
+ return pCur->szData;
+ iOrdinal--;
+ }
+
+ return NULL;
+}
+
+
+
+RTR3DECL(int) RTHttpSignHeaders(RTHTTP hHttp, RTHTTPMETHOD enmMethod, const char *pszUrl,
+ RTCRKEY hKey, const char *pszKeyId, uint32_t fFlags)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(enmMethod > RTHTTPMETHOD_INVALID && enmMethod < RTHTTPMETHOD_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszUrl, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+ AssertPtrReturn(pszKeyId, VERR_INVALID_POINTER);
+
+ /*
+ * Do a little bit of preprocessing while we can easily return without
+ * needing clean anything up..
+ */
+ RTURIPARSED ParsedUrl;
+ int rc = RTUriParse(pszUrl, &ParsedUrl);
+ AssertRCReturn(rc, rc);
+ const char * const pszPath = pszUrl + ParsedUrl.offPath;
+
+ const char *pszMethodSp = NULL;
+ switch (enmMethod)
+ {
+ case RTHTTPMETHOD_GET: pszMethodSp = "get "; break;
+ case RTHTTPMETHOD_PUT: pszMethodSp = "put "; break;
+ case RTHTTPMETHOD_POST: pszMethodSp = "post "; break;
+ case RTHTTPMETHOD_PATCH: pszMethodSp = "patch "; break;
+ case RTHTTPMETHOD_DELETE: pszMethodSp = "delete "; break;
+ case RTHTTPMETHOD_HEAD: pszMethodSp = "head "; break;
+ case RTHTTPMETHOD_OPTIONS: pszMethodSp = "options "; break;
+ case RTHTTPMETHOD_TRACE: pszMethodSp = "trace "; break;
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ case RTHTTPMETHOD_PROPFIND: pszMethodSp = "propfind "; break;
+#endif
+ /* no default! */
+ case RTHTTPMETHOD_INVALID:
+ case RTHTTPMETHOD_END:
+ case RTHTTPMETHOD_32BIT_HACK:
+ break;
+ }
+ AssertReturn(pszMethodSp, VERR_INTERNAL_ERROR_4);
+
+ /*
+ * We work the authorization header entry directly here to avoid extra copying and stuff.
+ */
+
+ /* Estimate required string length first. */
+ static const char s_szSuffixFmt[] = "Authorization: Signature version=\"1\",keyId=\"%s\",algorithm=\"rsa-sha256\",headers=\"";
+ static const char s_szInfix[] = "\",signature=\"";
+ static const char s_szPostfix[] = "\"";
+ static const char s_szRequestField[] = "(request-target)";
+ size_t const cchKeyId = strlen(pszKeyId);
+ size_t const cbSigRaw = (RTCrKeyGetBitCount(hKey) + 7) / 8;
+ size_t const cbSigRawAligned = RT_ALIGN_Z(cbSigRaw, 8);
+ size_t const cchSigStr = RTBase64EncodedLengthEx(cbSigRaw, RTBASE64_FLAGS_NO_LINE_BREAKS);
+ size_t cbEstimated = sizeof(s_szSuffixFmt) + sizeof(s_szInfix) + sizeof(s_szPostfix)
+ + cchKeyId + sizeof(s_szRequestField) + cchSigStr;
+ for (PRTHTTPHEADER pCur = (PRTHTTPHEADER)pThis->pHeaders; pCur; pCur = (PRTHTTPHEADER)pCur->Core.next)
+ cbEstimated += pCur->cchName + 1;
+ cbEstimated += 32; /* safetype fudge */
+ /* Lazy bird: Put the raw signature at the end. */
+ cbEstimated = RT_ALIGN_Z(cbEstimated, 8) + cbSigRawAligned;
+
+ /* Allocate and initialize header entry. */
+ PRTHTTPHEADER const pHdr = (PRTHTTPHEADER)RTMemAllocZ(cbEstimated);
+ AssertPtrReturn(pHdr, VERR_NO_MEMORY);
+ uint8_t * const pbSigRaw = (uint8_t *)pHdr + cbEstimated - cbSigRawAligned;
+
+ pHdr->cchName = sizeof("Authorization") - 1;
+ pHdr->offValue = sizeof("Authorization") + 1;
+ pHdr->Core.next = NULL;
+ pHdr->Core.data = pHdr->szData;
+ char *pszLeft = pHdr->szData;
+ size_t cbLeft = cbEstimated - RT_UOFFSETOF(RTHTTPHEADER, szData) - cbSigRawAligned;
+
+ size_t cch = RTStrPrintf(pszLeft, cbLeft, s_szSuffixFmt, pszKeyId);
+ cbLeft -= cch;
+ pszLeft += cch;
+
+ /*
+ * Instantiate the digest.
+ */
+ RTCRDIGEST hDigest = NIL_RTCRDIGEST;
+ rc = RTCrDigestCreateByType(&hDigest, RTDIGESTTYPE_SHA256);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add the request-target pseudo header first.
+ */
+ Assert(cbLeft > sizeof(s_szRequestField));
+ memcpy(pszLeft, RT_STR_TUPLE(s_szRequestField));
+ pszLeft += sizeof(s_szRequestField) - 1;
+
+ rc = RTCrDigestUpdate(hDigest, RT_STR_TUPLE(s_szRequestField));
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestUpdate(hDigest, RT_STR_TUPLE(": "));
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestUpdate(hDigest, pszMethodSp, strlen(pszMethodSp));
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestUpdate(hDigest, pszPath, strlen(pszPath));
+
+ /*
+ * Add the header fields.
+ */
+ for (PRTHTTPHEADER pCur = (PRTHTTPHEADER)pThis->pHeaders; pCur && RT_SUCCESS(rc); pCur = (PRTHTTPHEADER)pCur->Core.next)
+ {
+ AssertBreakStmt(cbLeft > pCur->cchName, rc = VERR_INTERNAL_ERROR_3);
+ *pszLeft++ = ' ';
+ cbLeft--;
+ memcpy(pszLeft, pCur->szData, pCur->cchName);
+ pszLeft[pCur->cchName] = '\0';
+ RTStrToLower(pszLeft);
+
+ rc = RTCrDigestUpdate(hDigest, RT_STR_TUPLE("\n"));
+ AssertRCBreak(rc);
+ rc = RTCrDigestUpdate(hDigest, pszLeft, pCur->cchName);
+ AssertRCBreak(rc);
+ rc = RTCrDigestUpdate(hDigest, RT_STR_TUPLE(": "));
+ AssertRCBreak(rc);
+ const char *pszValue = &pCur->szData[pCur->offValue];
+ rc = RTCrDigestUpdate(hDigest, pszValue, strlen(pszValue));
+ AssertRCBreak(rc);
+
+ pszLeft += pCur->cchName;
+ cbLeft -= pCur->cchName;
+ }
+ if (RT_SUCCESS(rc))
+ AssertStmt(cbLeft > sizeof(s_szInfix) + cchSigStr + sizeof(s_szPostfix), rc = VERR_INTERNAL_ERROR_3);
+ if (RT_SUCCESS(rc))
+ {
+ /* Complete the header field part. */
+ memcpy(pszLeft, RT_STR_TUPLE(s_szInfix));
+ pszLeft += sizeof(s_szInfix) - 1;
+ cbLeft -= sizeof(s_szInfix) - 1;
+
+ /*
+ * Sign the digest.
+ */
+ RTCRPKIXSIGNATURE hSigner;
+ rc = RTCrPkixSignatureCreateByObjIdString(&hSigner, RTCR_PKCS1_SHA256_WITH_RSA_OID, hKey, NULL, true /*fSigning*/);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbActual = cbSigRawAligned;
+ rc = RTCrPkixSignatureSign(hSigner, hDigest, pbSigRaw, &cbActual);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbActual == cbSigRaw);
+ RTCrPkixSignatureRelease(hSigner);
+ hSigner = NIL_RTCRPKIXSIGNATURE;
+ RTCrDigestRelease(hDigest);
+ hDigest = NIL_RTCRDIGEST;
+
+ /*
+ * Convert the signature to Base64 and append it to the string.
+ */
+ size_t cchActual;
+ rc = RTBase64EncodeEx(pbSigRaw, cbActual, RTBASE64_FLAGS_NO_LINE_BREAKS, pszLeft, cbLeft, &cchActual);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cchActual == cchSigStr);
+ pszLeft += cchActual;
+ cbLeft -= cchActual;
+
+ /*
+ * Append the postfix and add the header to the front of the list.
+ */
+ AssertStmt(cbLeft >= sizeof(s_szPostfix), rc = VERR_INTERNAL_ERROR_3);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pszLeft, s_szPostfix, sizeof(s_szPostfix));
+
+ pHdr->Core.next = pThis->pHeaders;
+ if (!pThis->pHeaders)
+ pThis->ppHeadersTail = &pHdr->Core.next;
+ pThis->pHeaders = &pHdr->Core;
+
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pThis->pHeaders);
+ if (CURL_SUCCESS(rcCurl))
+ return VINF_SUCCESS;
+ rc = VERR_HTTP_CURL_ERROR;
+ }
+ }
+ }
+ RTCrPkixSignatureRelease(hSigner);
+ }
+ }
+ RTCrDigestRelease(hDigest);
+ }
+ RTMemFree(pHdr);
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* HTTPS and root certficates *
+*********************************************************************************************************************************/
+
+/**
+ * Set the CA file to NULL, deleting any temporary file if necessary.
+ *
+ * @param pThis The HTTP/HTTPS client instance.
+ */
+static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis)
+{
+ if (pThis->pszCaFile)
+ {
+ if (pThis->fDeleteCaFile)
+ {
+ int rc2 = RTFileDelete(pThis->pszCaFile); RT_NOREF_PV(rc2);
+ AssertMsg(RT_SUCCESS(rc2) || !RTFileExists(pThis->pszCaFile), ("rc=%Rrc '%s'\n", rc2, pThis->pszCaFile));
+ }
+ RTStrFree(pThis->pszCaFile);
+ pThis->pszCaFile = NULL;
+ }
+}
+
+
+RTR3DECL(int) RTHttpSetCAFile(RTHTTP hHttp, const char *pszCaFile)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rtHttpUnsetCaFile(pThis);
+
+ pThis->fDeleteCaFile = false;
+ if (pszCaFile)
+ return RTStrDupEx(&pThis->pszCaFile, pszCaFile);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpUseTemporaryCaFile(RTHTTP hHttp, PRTERRINFO pErrInfo)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * Create a temporary file.
+ */
+ int rc = VERR_NO_STR_MEMORY;
+ char *pszCaFile = RTStrAlloc(RTPATH_MAX);
+ if (pszCaFile)
+ {
+ RTFILE hFile;
+ rc = RTFileOpenTemp(&hFile, pszCaFile, RTPATH_MAX,
+ RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Gather certificates into a temporary store and export them to the temporary file.
+ */
+ RTCRSTORE hStore;
+ rc = RTCrStoreCreateInMem(&hStore, 256);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTHttpGatherCaCertsInStore(hStore, 0 /*fFlags*/, pErrInfo);
+ if (RT_SUCCESS(rc))
+ /** @todo Consider adding an API for exporting to a RTFILE... */
+ rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
+ RTCrStoreRelease(hStore);
+ }
+ RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the CA file for the instance.
+ */
+ rtHttpUnsetCaFile(pThis);
+
+ pThis->fDeleteCaFile = true;
+ pThis->pszCaFile = pszCaFile;
+ return VINF_SUCCESS;
+ }
+
+ int rc2 = RTFileDelete(pszCaFile);
+ AssertRC(rc2);
+ }
+ else
+ RTErrInfoAddF(pErrInfo, rc, "Error creating temorary file: %Rrc", rc);
+
+ RTStrFree(pszCaFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpGatherCaCertsInStore(RTCRSTORE hStore, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ uint32_t const cBefore = RTCrStoreCertCount(hStore);
+ AssertReturn(cBefore != UINT32_MAX, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Add the user store, quitely ignoring any errors.
+ */
+ RTCRSTORE hSrcStore;
+ int rcUser = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
+ if (RT_SUCCESS(rcUser))
+ {
+ rcUser = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ hSrcStore);
+ RTCrStoreRelease(hSrcStore);
+ }
+
+ /*
+ * Ditto for the system store.
+ */
+ int rcSystem = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
+ if (RT_SUCCESS(rcSystem))
+ {
+ rcSystem = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ hSrcStore);
+ RTCrStoreRelease(hSrcStore);
+ }
+
+ /*
+ * If the number of certificates increased, we consider it a success.
+ */
+ if (RTCrStoreCertCount(hStore) > cBefore)
+ {
+ if (RT_FAILURE(rcSystem))
+ return -rcSystem;
+ if (RT_FAILURE(rcUser))
+ return -rcUser;
+ return rcSystem != VINF_SUCCESS ? rcSystem : rcUser;
+ }
+
+ if (RT_FAILURE(rcSystem))
+ return rcSystem;
+ if (RT_FAILURE(rcUser))
+ return rcUser;
+ return VERR_NOT_FOUND;
+}
+
+
+RTR3DECL(int) RTHttpGatherCaCertsInFile(const char *pszCaFile, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ RTCRSTORE hStore;
+ int rc = RTCrStoreCreateInMem(&hStore, 256);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTHttpGatherCaCertsInStore(hStore, fFlags, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
+ RTCrStoreRelease(hStore);
+ }
+ return rc;
+}
+
+
+RTR3DECL(bool) RTHttpGetVerifyPeer(RTHTTP hHttp)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN_RC(pThis, false);
+ return pThis->fVerifyPeer;
+}
+
+
+RTR3DECL(int) RTHttpSetVerifyPeer(RTHTTP hHttp, bool fVerify)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
+
+ if (pThis->fVerifyPeer != fVerify)
+ {
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_SSL_VERIFYPEER, (long)fVerify);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("CURLOPT_SSL_VERIFYPEER=%RTbool: %d (%#x)\n", fVerify, rcCurl, rcCurl),
+ VERR_HTTP_CURL_ERROR);
+ pThis->fVerifyPeer = fVerify;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/*********************************************************************************************************************************
+* .......
+*********************************************************************************************************************************/
+
+
+/**
+ * Figures out the IPRT status code for a GET.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP/HTTPS client instance.
+ * @param rcCurl What curl returned.
+ * @param puHttpStatus Where to optionally return the HTTP status. If specified,
+ * the HTTP statuses are not translated to IPRT status codes.
+ */
+static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pThis, CURLcode rcCurl, uint32_t *puHttpStatus)
+{
+ int rc = VERR_HTTP_CURL_ERROR;
+
+ if (pThis->pszRedirLocation)
+ {
+ RTStrFree(pThis->pszRedirLocation);
+ pThis->pszRedirLocation = NULL;
+ }
+ if (CURL_SUCCESS(rcCurl))
+ {
+ curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, &pThis->lLastResp);
+ if (puHttpStatus)
+ {
+ *puHttpStatus = pThis->lLastResp;
+ rc = VINF_SUCCESS;
+ }
+
+ switch (pThis->lLastResp)
+ {
+ case 200:
+ /* OK, request was fulfilled */
+ case 204:
+ /* empty response */
+ rc = VINF_SUCCESS;
+ break;
+ case 301: /* Moved permantently. */
+ case 302: /* Found / Moved temporarily. */
+ case 303: /* See Other. */
+ case 307: /* Temporary redirect. */
+ case 308: /* Permanent redirect. */
+ {
+ const char *pszRedirect = NULL;
+ curl_easy_getinfo(pThis->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);
+ size_t cb = pszRedirect ? strlen(pszRedirect) : 0;
+ if (cb > 0 && cb < 2048)
+ pThis->pszRedirLocation = RTStrDup(pszRedirect);
+ if (!puHttpStatus)
+ rc = VERR_HTTP_REDIRECTED;
+ break;
+ }
+ case 400:
+ /* bad request */
+ if (!puHttpStatus)
+ rc = VERR_HTTP_BAD_REQUEST;
+ break;
+ case 403:
+ /* forbidden, authorization will not help */
+ if (!puHttpStatus)
+ rc = VERR_HTTP_ACCESS_DENIED;
+ break;
+ case 404:
+ /* URL not found */
+ if (!puHttpStatus)
+ rc = VERR_HTTP_NOT_FOUND;
+ break;
+ }
+
+ if (pThis->pszRedirLocation)
+ Log(("rtHttpGetCalcStatus: rc=%Rrc lastResp=%lu redir='%s'\n", rc, pThis->lLastResp, pThis->pszRedirLocation));
+ else
+ Log(("rtHttpGetCalcStatus: rc=%Rrc lastResp=%lu\n", rc, pThis->lLastResp));
+ }
+ else
+ {
+ switch (rcCurl)
+ {
+ case CURLE_URL_MALFORMAT:
+ case CURLE_COULDNT_RESOLVE_HOST:
+ rc = VERR_HTTP_HOST_NOT_FOUND;
+ break;
+ case CURLE_COULDNT_CONNECT:
+ rc = VERR_HTTP_COULDNT_CONNECT;
+ break;
+ case CURLE_SSL_CONNECT_ERROR:
+ rc = VERR_HTTP_SSL_CONNECT_ERROR;
+ break;
+ case CURLE_SSL_CACERT:
+ /* The peer certificate cannot be authenticated with the CA certificates
+ * set by RTHttpSetCAFile(). We need other or additional CA certificates. */
+ rc = VERR_HTTP_CACERT_CANNOT_AUTHENTICATE;
+ break;
+ case CURLE_SSL_CACERT_BADFILE:
+ /* CAcert file (see RTHttpSetCAFile()) has wrong format */
+ rc = VERR_HTTP_CACERT_WRONG_FORMAT;
+ break;
+ case CURLE_ABORTED_BY_CALLBACK:
+ /* forcefully aborted */
+ rc = VERR_HTTP_ABORTED;
+ break;
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ rc = VERR_HTTP_PROXY_NOT_FOUND;
+ break;
+ case CURLE_WRITE_ERROR:
+ rc = RT_FAILURE_NP(pThis->rcOutput) ? pThis->rcOutput : VERR_WRITE_ERROR;
+ break;
+ //case CURLE_READ_ERROR
+
+ default:
+ break;
+ }
+ Log(("%s: %Rrc: %u = %s%s%.*s\n",
+ __FUNCTION__,
+ rc, rcCurl, curl_easy_strerror((CURLcode)rcCurl),
+ pThis->szErrorBuffer[0] != '\0' ? ": " : "",
+ (int) sizeof(pThis->szErrorBuffer),
+ pThis->szErrorBuffer[0] != '\0' ? pThis->szErrorBuffer : ""));
+ }
+
+ return rc;
+}
+
+
+/**
+ * cURL callback for reporting progress, we use it for checking for abort.
+ */
+static int rtHttpProgress(void *pData, double rdTotalDownload, double rdDownloaded, double rdTotalUpload, double rdUploaded) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pData;
+ AssertReturn(pThis->u32Magic == RTHTTP_MAGIC, 1);
+ RT_NOREF_PV(rdTotalUpload);
+ RT_NOREF_PV(rdUploaded);
+
+ pThis->cbDownloadHint = (uint64_t)rdTotalDownload;
+
+ if (pThis->pfnDownloadProgress)
+ pThis->pfnDownloadProgress(pThis, pThis->pvDownloadProgressUser, (uint64_t)rdTotalDownload, (uint64_t)rdDownloaded);
+
+ return pThis->fAbort ? 1 : 0;
+}
+
+
+/**
+ * Whether we're likely to need SSL to handle the give URL.
+ *
+ * @returns true if we need, false if we probably don't.
+ * @param pszUrl The URL.
+ */
+static bool rtHttpNeedSsl(const char *pszUrl)
+{
+ return RTStrNICmp(pszUrl, RT_STR_TUPLE("https:")) == 0;
+}
+
+
+/**
+ * Applies recoded settings to the cURL instance before doing work.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP/HTTPS client instance.
+ * @param pszUrl The URL.
+ */
+static int rtHttpApplySettings(PRTHTTPINTERNAL pThis, const char *pszUrl)
+{
+ /*
+ * The URL.
+ */
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_URL, pszUrl);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Proxy config.
+ */
+ int rc = rtHttpConfigureProxyForUrl(pThis, pszUrl);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Setup SSL. Can be a bit of work.
+ */
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_INVALID_PARAMETER;
+
+ const char *pszCaFile = pThis->pszCaFile;
+ if ( !pszCaFile
+ && rtHttpNeedSsl(pszUrl))
+ {
+ rc = RTHttpUseTemporaryCaFile(pThis, NULL);
+ if (RT_SUCCESS(rc))
+ pszCaFile = pThis->pszCaFile;
+ else
+ return rc; /* Non-portable alternative: pszCaFile = "/etc/ssl/certs/ca-certificates.crt"; */
+ }
+ if (pszCaFile)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CAINFO, pszCaFile);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ }
+
+ /*
+ * Progress/abort.
+ */
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSDATA, (void *)pThis);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROGRESS, (long)0);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ /*
+ * Set default user agent string if necessary. Some websites take offence
+ * if we don't set it.
+ */
+ if ( !pThis->fHaveSetUserAgent
+ && !pThis->fHaveUserAgentHeader)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_USERAGENT, "Mozilla/5.0 (AgnosticOS; Blend) IPRT/64.42");
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ pThis->fHaveSetUserAgent = true;
+ }
+
+ /*
+ * Use GET by default.
+ */
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 0L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADER, 0L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Resets state.
+ *
+ * @param pThis HTTP client instance.
+ */
+static void rtHttpResetState(PRTHTTPINTERNAL pThis)
+{
+ pThis->fAbort = false;
+ pThis->rcOutput = VINF_SUCCESS;
+ pThis->uDownloadHttpStatus = UINT32_MAX;
+ pThis->cbDownloadContent = UINT64_MAX;
+ pThis->offDownloadContent = 0;
+ pThis->offUploadContent = 0;
+ pThis->rcOutput = VINF_SUCCESS;
+ pThis->cbDownloadHint = 0;
+ Assert(pThis->BodyOutput.pHttp == pThis);
+ Assert(pThis->HeadersOutput.pHttp == pThis);
+}
+
+
+/**
+ * Tries to determin uDownloadHttpStatus and cbDownloadContent.
+ *
+ * @param pThis HTTP client instance.
+ */
+static void rtHttpGetDownloadStatusAndLength(PRTHTTPINTERNAL pThis)
+{
+ long lHttpStatus = 0;
+ curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, &lHttpStatus);
+ pThis->uDownloadHttpStatus = (uint32_t)lHttpStatus;
+
+#ifdef CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
+ curl_off_t cbContent = -1;
+ curl_easy_getinfo(pThis->pCurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cbContent);
+ if (cbContent >= 0)
+ pThis->cbDownloadContent = (uint64_t)cbContent;
+#else
+ double rdContent = -1.0;
+ curl_easy_getinfo(pThis->pCurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &rdContent);
+ if (rdContent >= 0.0)
+ pThis->cbDownloadContent = (uint64_t)rdContent;
+#endif
+}
+
+
+/**
+ * Worker for rtHttpWriteHeaderData and rtHttpWriteBodyData.
+ */
+static size_t rtHttpWriteDataToMemOutput(PRTHTTPINTERNAL pThis, RTHTTPOUTPUTDATA *pOutput, char const *pchBuf, size_t cbToAppend)
+{
+ /*
+ * Do max size and overflow checks.
+ */
+ size_t const cbCurSize = pOutput->uData.Mem.cb;
+ size_t const cbNewSize = cbCurSize + cbToAppend;
+ if ( cbToAppend < RTHTTP_MAX_MEM_DOWNLOAD_SIZE
+ && cbNewSize < RTHTTP_MAX_MEM_DOWNLOAD_SIZE)
+ {
+ if (cbNewSize + 1 <= pOutput->uData.Mem.cbAllocated)
+ {
+ memcpy(&pOutput->uData.Mem.pb[cbCurSize], pchBuf, cbToAppend);
+ pOutput->uData.Mem.cb = cbNewSize;
+ pOutput->uData.Mem.pb[cbNewSize] = '\0';
+ return cbToAppend;
+ }
+
+ /*
+ * We need to reallocate the output buffer.
+ */
+ /** @todo this could do with a better strategy wrt growth. */
+ size_t cbAlloc = RT_ALIGN_Z(cbNewSize + 1, 64);
+ if ( cbAlloc <= pThis->cbDownloadHint
+ && pThis->cbDownloadHint < RTHTTP_MAX_MEM_DOWNLOAD_SIZE
+ && pOutput == &pThis->BodyOutput)
+ cbAlloc = RT_ALIGN_Z(pThis->cbDownloadHint + 1, 64);
+
+ uint8_t *pbNew = (uint8_t *)RTMemRealloc(pOutput->uData.Mem.pb, cbAlloc);
+ if (pbNew)
+ {
+ memcpy(&pbNew[cbCurSize], pchBuf, cbToAppend);
+ pbNew[cbNewSize] = '\0';
+
+ pOutput->uData.Mem.cbAllocated = cbAlloc;
+ pOutput->uData.Mem.pb = pbNew;
+ pOutput->uData.Mem.cb = cbNewSize;
+ return cbToAppend;
+ }
+
+ pThis->rcOutput = VERR_NO_MEMORY;
+ }
+ else
+ pThis->rcOutput = VERR_TOO_MUCH_DATA;
+
+ /*
+ * Failure - abort.
+ */
+ RTMemFree(pOutput->uData.Mem.pb);
+ pOutput->uData.Mem.pb = NULL;
+ pOutput->uData.Mem.cb = RTHTTP_MAX_MEM_DOWNLOAD_SIZE;
+ pThis->fAbort = true;
+ return 0;
+}
+
+
+/**
+ * cURL callback for writing body data.
+ */
+static size_t rtHttpWriteBodyData(char *pchBuf, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
+ size_t const cbToAppend = cbUnit * cUnits;
+
+ /*
+ * Check if this belongs to the body download callback.
+ */
+ if (pThis->pfnDownloadCallback)
+ {
+ if (pThis->offDownloadContent == 0)
+ rtHttpGetDownloadStatusAndLength(pThis);
+
+ if ( (pThis->fDownloadCallback & RTHTTPDOWNLOAD_F_ONLY_STATUS_MASK) == RTHTTPDOWNLOAD_F_ANY_STATUS
+ || (pThis->fDownloadCallback & RTHTTPDOWNLOAD_F_ONLY_STATUS_MASK) == pThis->uDownloadHttpStatus)
+ {
+ int rc = pThis->pfnDownloadCallback(pThis, pchBuf, cbToAppend, pThis->uDownloadHttpStatus, pThis->offDownloadContent,
+ pThis->cbDownloadContent, pThis->pvDownloadCallbackUser);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->offDownloadContent += cbToAppend;
+ return cbToAppend;
+ }
+ if (RT_SUCCESS(pThis->rcOutput))
+ pThis->rcOutput = rc;
+ pThis->fAbort = true;
+ return 0;
+ }
+ }
+
+ /*
+ * Otherwise, copy to memory output buffer.
+ */
+ return rtHttpWriteDataToMemOutput(pThis, &pThis->BodyOutput, pchBuf, cbToAppend);
+}
+
+
+/**
+ * cURL callback for writing header data.
+ */
+static size_t rtHttpWriteHeaderData(char *pchBuf, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
+ size_t const cbToAppend = cbUnit * cUnits;
+
+ /*
+ * Work the header callback, if one.
+ * ASSUMES cURL is giving us one header at a time.
+ */
+ if (pThis->pfnHeaderCallback)
+ {
+ /*
+ * Find the end of the field name first.
+ */
+ uint32_t uMatchWord;
+ size_t cchField;
+ const char *pchField = pchBuf;
+ size_t cchValue;
+ const char *pchValue = (const char *)memchr(pchBuf, ':', cbToAppend);
+ if (pchValue)
+ {
+ cchField = pchValue - pchField;
+ if (RT_LIKELY(cchField >= 3))
+ uMatchWord = RTHTTP_MAKE_HDR_MATCH_WORD(cchField, RT_C_TO_LOWER(pchBuf[0]),
+ RT_C_TO_LOWER(pchBuf[1]), RT_C_TO_LOWER(pchBuf[2]));
+ else
+ uMatchWord = RTHTTP_MAKE_HDR_MATCH_WORD(cchField,
+ cchField >= 1 ? RT_C_TO_LOWER(pchBuf[0]) : 0,
+ cchField >= 2 ? RT_C_TO_LOWER(pchBuf[1]) : 0,
+ 0);
+ pchValue++;
+ cchValue = cbToAppend - cchField - 1;
+ }
+ /* Since cURL gives us the "HTTP/{version} {code} {status}" line too,
+ we slap a fictitious field name ':http-status-line' in front of it. */
+ else if (cbToAppend > 5 && pchBuf[0] == 'H' && pchBuf[1] == 'T' && pchBuf[2] == 'T' && pchBuf[3] == 'P' && pchBuf[4] == '/')
+ {
+ pchField = ":http-status-line";
+ cchField = 17;
+ uMatchWord = RTHTTP_MAKE_HDR_MATCH_WORD(17, ':', 'h', 't');
+ pchValue = pchBuf;
+ cchValue = cbToAppend;
+ }
+ /* cURL also gives us the empty line before the body, so we slap another
+ fictitious field name ':end-of-headers' in front of it as well. */
+ else if (cbToAppend == 2 && pchBuf[0] == '\r' && pchBuf[1] == '\n')
+ {
+ pchField = ":end-of-headers";
+ cchField = 15;
+ uMatchWord = RTHTTP_MAKE_HDR_MATCH_WORD(15, ':', 'e', 'n');
+ pchValue = pchBuf;
+ cchValue = cbToAppend;
+ }
+ else
+ AssertMsgFailedReturn(("pchBuf=%.*s\n", cbToAppend, pchBuf), cbToAppend);
+
+ /*
+ * Determin the field value, stripping one leading blank and all
+ * trailing spaces.
+ */
+ if (cchValue > 0 && RT_C_IS_BLANK(*pchValue))
+ pchValue++, cchValue--;
+ while (cchValue > 0 && RT_C_IS_SPACE(pchValue[cchValue - 1]))
+ cchValue--;
+
+ /*
+ * Pass it to the callback.
+ */
+ Log6(("rtHttpWriteHeaderData: %.*s: %.*s\n", cchField, pchBuf, cchValue, pchValue));
+ int rc = pThis->pfnHeaderCallback(pThis, uMatchWord, pchBuf, cchField,
+ pchValue, cchValue, pThis->pvHeaderCallbackUser);
+ if (RT_SUCCESS(rc))
+ return cbToAppend;
+
+ /* Abort on error. */
+ if (RT_SUCCESS(pThis->rcOutput))
+ pThis->rcOutput = rc;
+ pThis->fAbort = true;
+ return 0;
+ }
+
+ return rtHttpWriteDataToMemOutput(pThis, &pThis->HeadersOutput, pchBuf, cbToAppend);
+}
+
+
+/**
+ * cURL callback for working the upload callback.
+ */
+static size_t rtHttpWriteDataToDownloadCallback(char *pchBuf, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
+ size_t const cbBuf = cbUnit * cUnits;
+
+ /* Get download info the first time we're called. */
+ if (pThis->offDownloadContent == 0)
+ rtHttpGetDownloadStatusAndLength(pThis);
+
+ /* Call the callback if the HTTP status code matches, otherwise let it go to /dev/null. */
+ if ( (pThis->fDownloadCallback & RTHTTPDOWNLOAD_F_ONLY_STATUS_MASK) == RTHTTPDOWNLOAD_F_ANY_STATUS
+ || (pThis->fDownloadCallback & RTHTTPDOWNLOAD_F_ONLY_STATUS_MASK) == pThis->uDownloadHttpStatus)
+ {
+ int rc = pThis->pfnDownloadCallback(pThis, pchBuf, cbBuf, pThis->uDownloadHttpStatus, pThis->offDownloadContent,
+ pThis->cbDownloadContent, pThis->pvDownloadCallbackUser);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ {
+ if (RT_SUCCESS(pThis->rcOutput))
+ pThis->rcOutput = rc;
+ pThis->fAbort = true;
+ return 0;
+ }
+ }
+ pThis->offDownloadContent += cbBuf;
+ return cbBuf;
+}
+
+
+/**
+ * Callback feeding cURL data from RTHTTPINTERNAL::ReadData::Mem.
+ */
+static size_t rtHttpReadData(void *pvDst, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
+ size_t const cbReq = cbUnit * cUnits;
+ size_t const offMem = pThis->ReadData.Mem.offMem;
+ size_t cbToCopy = pThis->ReadData.Mem.cbMem - offMem;
+ if (cbToCopy > cbReq)
+ cbToCopy = cbReq;
+ memcpy(pvDst, (uint8_t const *)pThis->ReadData.Mem.pvMem + offMem, cbToCopy);
+ pThis->ReadData.Mem.offMem = offMem + cbToCopy;
+ return cbToCopy;
+}
+
+
+/**
+ * Callback feeding cURL data via the user upload callback.
+ */
+static size_t rtHttpReadDataFromUploadCallback(void *pvDst, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
+ size_t const cbReq = cbUnit * cUnits;
+
+ size_t cbActual = 0;
+ int rc = pThis->pfnUploadCallback(pThis, pvDst, cbReq, pThis->offUploadContent, &cbActual, pThis->pvUploadCallbackUser);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->offUploadContent += cbActual;
+ return cbActual;
+ }
+
+ if (RT_SUCCESS(pThis->rcOutput))
+ pThis->rcOutput = rc;
+ pThis->fAbort = true;
+ return CURL_READFUNC_ABORT;
+}
+
+
+/**
+ * Helper for installing a (body) write callback function.
+ *
+ * @returns cURL status code.
+ * @param pThis The HTTP client instance.
+ * @param pfnWrite The callback.
+ * @param pvUser The callback user argument.
+ */
+static CURLcode rtHttpSetWriteCallback(PRTHTTPINTERNAL pThis, PFNRTHTTPWRITECALLBACKRAW pfnWrite, void *pvUser)
+{
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, pfnWrite);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, pvUser);
+ return rcCurl;
+}
+
+
+/**
+ * Helper for installing a header write callback function.
+ *
+ * @returns cURL status code.
+ * @param pThis The HTTP client instance.
+ * @param pfnWrite The callback.
+ * @param pvUser The callback user argument.
+ */
+static CURLcode rtHttpSetHeaderCallback(PRTHTTPINTERNAL pThis, PFNRTHTTPWRITECALLBACKRAW pfnWrite, void *pvUser)
+{
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERFUNCTION, pfnWrite);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERDATA, pvUser);
+ return rcCurl;
+}
+
+
+/**
+ * Helper for installing a (body) read callback function.
+ *
+ * @returns cURL status code.
+ * @param pThis The HTTP client instance.
+ * @param pfnRead The callback.
+ * @param pvUser The callback user argument.
+ */
+static CURLcode rtHttpSetReadCallback(PRTHTTPINTERNAL pThis, PFNRTHTTPREADCALLBACKRAW pfnRead, void *pvUser)
+{
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READFUNCTION, pfnRead);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READDATA, pvUser);
+ return rcCurl;
+}
+
+
+/**
+ * Internal worker that performs a HTTP GET.
+ *
+ * @returns IPRT status code.
+ * @param hHttp The HTTP/HTTPS client instance.
+ * @param pszUrl The URL.
+ * @param fNoBody Set to suppress the body.
+ * @param ppvResponse Where to return the pointer to the allocated
+ * response data (RTMemFree). There will always be
+ * an zero terminator char after the response, that
+ * is not part of the size returned via @a pcb.
+ * @param pcb The size of the response data.
+ *
+ * @remarks We ASSUME the API user doesn't do concurrent GETs in different
+ * threads, because that will probably blow up!
+ */
+static int rtHttpGetToMem(RTHTTP hHttp, const char *pszUrl, bool fNoBody, uint8_t **ppvResponse, size_t *pcb)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * Reset the return values in case of more "GUI programming" on the client
+ * side (i.e. a programming style not bothering checking return codes).
+ */
+ *ppvResponse = NULL;
+ *pcb = 0;
+
+ /*
+ * Set the busy flag (paranoia).
+ */
+ bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
+ AssertReturn(!fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Reset the state and apply settings.
+ */
+ rtHttpResetState(pThis);
+ int rc = rtHttpApplySettings(hHttp, pszUrl);
+ if (RT_SUCCESS(rc))
+ {
+ RT_ZERO(pThis->BodyOutput.uData.Mem);
+ CURLcode rcCurl = rtHttpSetWriteCallback(pThis, &rtHttpWriteBodyData, pThis);
+ if (fNoBody)
+ {
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 1L);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADER, 1L);
+ }
+ if (CURL_SUCCESS(rcCurl))
+ {
+ /*
+ * Perform the HTTP operation.
+ */
+ rcCurl = curl_easy_perform(pThis->pCurl);
+ rc = rtHttpGetCalcStatus(pThis, rcCurl, NULL);
+ if (RT_SUCCESS(rc))
+ rc = pThis->rcOutput;
+ if (RT_SUCCESS(rc))
+ {
+ *ppvResponse = pThis->BodyOutput.uData.Mem.pb;
+ *pcb = pThis->BodyOutput.uData.Mem.cb;
+ Log(("rtHttpGetToMem: %zx bytes (allocated %zx)\n",
+ pThis->BodyOutput.uData.Mem.cb, pThis->BodyOutput.uData.Mem.cbAllocated));
+ }
+ else if (pThis->BodyOutput.uData.Mem.pb)
+ RTMemFree(pThis->BodyOutput.uData.Mem.pb);
+ RT_ZERO(pThis->BodyOutput.uData.Mem);
+ }
+ else
+ rc = VERR_HTTP_CURL_ERROR;
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8)
+{
+ Log(("RTHttpGetText: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
+ uint8_t *pv;
+ size_t cb;
+ int rc = rtHttpGetToMem(hHttp, pszUrl, false /*fNoBody*/, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (pv) /* paranoia */
+ *ppszNotUtf8 = (char *)pv;
+ else
+ *ppszNotUtf8 = (char *)RTMemDup("", 1);
+ }
+ else
+ *ppszNotUtf8 = NULL;
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpGetHeaderText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8)
+{
+ Log(("RTHttpGetText: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
+ uint8_t *pv;
+ size_t cb;
+ int rc = rtHttpGetToMem(hHttp, pszUrl, true /*fNoBody*/, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (pv) /* paranoia */
+ *ppszNotUtf8 = (char *)pv;
+ else
+ *ppszNotUtf8 = (char *)RTMemDup("", 1);
+ }
+ else
+ *ppszNotUtf8 = NULL;
+ return rc;
+
+}
+
+
+RTR3DECL(void) RTHttpFreeResponseText(char *pszNotUtf8)
+{
+ RTMemFree(pszNotUtf8);
+}
+
+
+RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb)
+{
+ Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
+ return rtHttpGetToMem(hHttp, pszUrl, false /*fNoBody*/, (uint8_t **)ppvResponse, pcb);
+}
+
+
+RTR3DECL(int) RTHttpGetHeaderBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb)
+{
+ Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
+ return rtHttpGetToMem(hHttp, pszUrl, true /*fNoBody*/, (uint8_t **)ppvResponse, pcb);
+}
+
+
+RTR3DECL(void) RTHttpFreeResponse(void *pvResponse)
+{
+ RTMemFree(pvResponse);
+}
+
+
+/**
+ * cURL callback for writing data to a file.
+ */
+static size_t rtHttpWriteDataToFile(char *pchBuf, size_t cbUnit, size_t cUnits, void *pvUser) RT_NOTHROW_DEF
+{
+ RTHTTPOUTPUTDATA *pOutput = (RTHTTPOUTPUTDATA *)pvUser;
+ PRTHTTPINTERNAL pThis = pOutput->pHttp;
+
+ size_t cbWritten = 0;
+ int rc = RTFileWrite(pOutput->uData.hFile, pchBuf, cbUnit * cUnits, &cbWritten);
+ if (RT_SUCCESS(rc))
+ return cbWritten;
+
+ Log(("rtHttpWriteDataToFile: rc=%Rrc cbUnit=%zd cUnits=%zu\n", rc, cbUnit, cUnits));
+ pThis->rcOutput = rc;
+ return 0;
+}
+
+
+RTR3DECL(int) RTHttpGetFile(RTHTTP hHttp, const char *pszUrl, const char *pszDstFile)
+{
+ Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s pszDstFile=%s\n", hHttp, pszUrl, pszDstFile));
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * Set the busy flag (paranoia).
+ */
+ bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
+ AssertReturn(!fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Reset the state and apply settings.
+ */
+ rtHttpResetState(pThis);
+ int rc = rtHttpApplySettings(hHttp, pszUrl);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->BodyOutput.uData.hFile = NIL_RTFILE;
+ CURLcode rcCurl = rtHttpSetWriteCallback(pThis, &rtHttpWriteDataToFile, (void *)&pThis->BodyOutput);
+ if (CURL_SUCCESS(rcCurl))
+ {
+ /*
+ * Open the output file.
+ */
+ rc = RTFileOpen(&pThis->BodyOutput.uData.hFile, pszDstFile,
+ RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_READWRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform the HTTP operation.
+ */
+ rcCurl = curl_easy_perform(pThis->pCurl);
+ rc = rtHttpGetCalcStatus(pThis, rcCurl, NULL);
+ if (RT_SUCCESS(rc))
+ rc = pThis->rcOutput;
+
+ int rc2 = RTFileClose(pThis->BodyOutput.uData.hFile);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ pThis->BodyOutput.uData.hFile = NIL_RTFILE;
+ }
+ else
+ rc = VERR_HTTP_CURL_ERROR;
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpQueryProxyInfoForUrl(RTHTTP hHttp, const char *pszUrl, PRTHTTPPROXYINFO pProxy)
+{
+ /*
+ * Validate input and clear output.
+ */
+ Log(("RTHttpQueryProxyInfoForUrl: hHttp=%p pszUrl=%s pProxy=%s\n", hHttp, pszUrl, pProxy));
+ RT_ZERO(*pProxy);
+ pProxy->uProxyPort = UINT32_MAX;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * Set up the proxy for the URL.
+ */
+ rtHttpResetState(pThis);
+ /** @todo this does a bit too much (we don't need to set up SSL for instance). */
+ int rc = rtHttpApplySettings(pThis, pszUrl);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy out the result.
+ */
+ if (pThis->fNoProxy)
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_NOPROXY;
+ else
+ {
+ switch (pThis->enmProxyType)
+ {
+ case CURLPROXY_HTTP:
+#ifdef CURL_AT_LEAST_VERSION
+# if CURL_AT_LEAST_VERSION(7,19,4)
+ case CURLPROXY_HTTP_1_0:
+# endif
+#endif
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_HTTP;
+ break;
+#ifdef CURL_AT_LEAST_VERSION
+# if CURL_AT_LEAST_VERSION(7,52,0)
+ case CURLPROXY_HTTPS:
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_HTTPS;
+ break;
+# endif
+#endif
+ case CURLPROXY_SOCKS4:
+ case CURLPROXY_SOCKS4A:
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_SOCKS4;
+ break;
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_SOCKS5;
+ break;
+ default:
+ AssertFailed();
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_UNKNOWN;
+ break;
+ }
+ pProxy->uProxyPort = pThis->uProxyPort;
+ if (pThis->pszProxyHost != NULL)
+ {
+ rc = RTStrDupEx(&pProxy->pszProxyHost, pThis->pszProxyHost);
+ if (pThis->pszProxyUsername && RT_SUCCESS(rc))
+ rc = RTStrDupEx(&pProxy->pszProxyUsername, pThis->pszProxyUsername);
+ if (pThis->pszProxyPassword && RT_SUCCESS(rc))
+ rc = RTStrDupEx(&pProxy->pszProxyPassword, pThis->pszProxyPassword);
+ if (RT_FAILURE(rc))
+ RTHttpFreeProxyInfo(pProxy);
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpFreeProxyInfo(PRTHTTPPROXYINFO pProxy)
+{
+ if (pProxy)
+ {
+ RTStrFree(pProxy->pszProxyHost);
+ RTStrFree(pProxy->pszProxyUsername);
+ RTStrFree(pProxy->pszProxyPassword);
+ pProxy->pszProxyHost = NULL;
+ pProxy->pszProxyUsername = NULL;
+ pProxy->pszProxyPassword = NULL;
+ pProxy->enmProxyType = RTHTTPPROXYTYPE_INVALID;
+ pProxy->uProxyPort = UINT32_MAX;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpPerform(RTHTTP hHttp, const char *pszUrl, RTHTTPMETHOD enmMethod, void const *pvReqBody, size_t cbReqBody,
+ uint32_t *puHttpStatus, void **ppvHeaders, size_t *pcbHeaders, void **ppvBody, size_t *pcbBody)
+{
+ /*
+ * Set safe return values and validate input.
+ */
+ Log(("RTHttpPerform: hHttp=%p pszUrl=%s enmMethod=%d pvReqBody=%p cbReqBody=%zu puHttpStatus=%p ppvHeaders=%p ppvBody=%p\n",
+ hHttp, pszUrl, enmMethod, pvReqBody, cbReqBody, puHttpStatus, ppvHeaders, ppvBody));
+
+ if (ppvHeaders)
+ *ppvHeaders = NULL;
+ if (pcbHeaders)
+ *pcbHeaders = 0;
+ if (ppvBody)
+ *ppvBody = NULL;
+ if (pcbBody)
+ *pcbBody = 0;
+ if (puHttpStatus)
+ *puHttpStatus = UINT32_MAX;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(enmMethod > RTHTTPMETHOD_INVALID && enmMethod < RTHTTPMETHOD_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszUrl, VERR_INVALID_POINTER);
+
+#ifdef LOG_ENABLED
+ if (LogIs4Enabled() && pThis->pHeaders)
+ {
+ Log4(("RTHttpPerform: headers:\n"));
+ for (struct curl_slist const *pCur = pThis->pHeaders; pCur; pCur = pCur->next)
+ Log4(("%s\n", pCur->data));
+ }
+ if (pvReqBody && cbReqBody)
+ Log5(("RTHttpPerform: request body:\n%.*Rhxd\n", cbReqBody, pvReqBody));
+#endif
+
+ /*
+ * Set the busy flag (paranoia).
+ */
+ bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
+ AssertReturn(!fBusy, VERR_WRONG_ORDER);
+
+ /*
+ * Reset the state and apply settings.
+ */
+ rtHttpResetState(pThis);
+ int rc = rtHttpApplySettings(hHttp, pszUrl);
+ if (RT_SUCCESS(rc))
+ {
+ /* Set the HTTP method. */
+ CURLcode rcCurl = CURLE_BAD_FUNCTION_ARGUMENT;
+ switch (enmMethod)
+ {
+ case RTHTTPMETHOD_GET:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
+ break;
+ case RTHTTPMETHOD_PUT:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PUT, 1L);
+ break;
+ case RTHTTPMETHOD_POST:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POST, 1L);
+ break;
+ case RTHTTPMETHOD_PATCH:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, "PATCH");
+ break;
+ case RTHTTPMETHOD_DELETE:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ break;
+ case RTHTTPMETHOD_HEAD:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 1L);
+ break;
+ case RTHTTPMETHOD_OPTIONS:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
+ break;
+ case RTHTTPMETHOD_TRACE:
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, "TRACE");
+ break;
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ case RTHTTPMETHOD_PROPFIND:
+ RT_FALL_THROUGH();
+#endif
+ case RTHTTPMETHOD_END:
+ case RTHTTPMETHOD_INVALID:
+ case RTHTTPMETHOD_32BIT_HACK:
+ AssertFailed();
+ }
+
+ /* Request body. POST requests should always have a body. */
+ if ( pvReqBody
+ && CURL_SUCCESS(rcCurl)
+ && ( cbReqBody > 0
+ || enmMethod == RTHTTPMETHOD_POST) )
+ {
+ if (enmMethod == RTHTTPMETHOD_POST)
+ {
+ /** @todo ??? */
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDSIZE, cbReqBody);
+ if (CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDS, pvReqBody);
+ }
+ else
+ {
+ pThis->ReadData.Mem.pvMem = pvReqBody;
+ pThis->ReadData.Mem.cbMem = cbReqBody;
+ pThis->ReadData.Mem.offMem = 0;
+ rcCurl = rtHttpSetReadCallback(pThis, rtHttpReadData, pThis);
+ /* curl will use chunked transfer is it doesn't know the body size */
+ if (enmMethod == RTHTTPMETHOD_PUT && CURL_SUCCESS(rcCurl))
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_INFILESIZE_LARGE, cbReqBody);
+ }
+ }
+ else if (pThis->pfnUploadCallback && CURL_SUCCESS(rcCurl))
+ rcCurl = rtHttpSetReadCallback(pThis, rtHttpReadDataFromUploadCallback, pThis);
+
+ /* Headers. */
+ if (CURL_SUCCESS(rcCurl))
+ {
+ RT_ZERO(pThis->HeadersOutput.uData.Mem);
+ rcCurl = rtHttpSetHeaderCallback(pThis, rtHttpWriteHeaderData, pThis);
+ }
+
+ /* Body */
+ if (ppvBody && CURL_SUCCESS(rcCurl))
+ {
+ RT_ZERO(pThis->BodyOutput.uData.Mem);
+ rcCurl = rtHttpSetWriteCallback(pThis, rtHttpWriteBodyData, pThis);
+ }
+ else if (pThis->pfnDownloadCallback && CURL_SUCCESS(rcCurl))
+ rcCurl = rtHttpSetWriteCallback(pThis, rtHttpWriteDataToDownloadCallback, pThis);
+
+ if (CURL_SUCCESS(rcCurl))
+ {
+ /*
+ * Perform the HTTP operation.
+ */
+ rcCurl = curl_easy_perform(pThis->pCurl);
+ rc = rtHttpGetCalcStatus(pThis, rcCurl, puHttpStatus);
+ if (RT_SUCCESS(rc))
+ rc = pThis->rcOutput;
+ if (RT_SUCCESS(rc))
+ {
+ if (ppvHeaders)
+ {
+ Log(("RTHttpPerform: headers: %zx bytes (allocated %zx)\n",
+ pThis->HeadersOutput.uData.Mem.cb, pThis->HeadersOutput.uData.Mem.cbAllocated));
+ Log6(("RTHttpPerform: headers blob:\n%.*Rhxd\n", pThis->HeadersOutput.uData.Mem.cb, pThis->HeadersOutput.uData.Mem.pb));
+ *ppvHeaders = pThis->HeadersOutput.uData.Mem.pb;
+ *pcbHeaders = pThis->HeadersOutput.uData.Mem.cb;
+ pThis->HeadersOutput.uData.Mem.pb = NULL;
+ }
+ if (ppvBody)
+ {
+ Log(("RTHttpPerform: body: %zx bytes (allocated %zx)\n",
+ pThis->BodyOutput.uData.Mem.cb, pThis->BodyOutput.uData.Mem.cbAllocated));
+ Log7(("RTHttpPerform: body blob:\n%.*Rhxd\n", pThis->BodyOutput.uData.Mem.cb, pThis->BodyOutput.uData.Mem.pb));
+ *ppvBody = pThis->BodyOutput.uData.Mem.pb;
+ *pcbBody = pThis->BodyOutput.uData.Mem.cb;
+ pThis->BodyOutput.uData.Mem.pb = NULL;
+ }
+ }
+ }
+ else
+ rc = VERR_HTTP_CURL_ERROR;
+
+ /* Ensure we've freed all unused output and dropped references to input memory.*/
+ if (pThis->HeadersOutput.uData.Mem.pb)
+ RTMemFree(pThis->HeadersOutput.uData.Mem.pb);
+ if (pThis->BodyOutput.uData.Mem.pb)
+ RTMemFree(pThis->BodyOutput.uData.Mem.pb);
+ RT_ZERO(pThis->HeadersOutput.uData.Mem);
+ RT_ZERO(pThis->BodyOutput.uData.Mem);
+ RT_ZERO(pThis->ReadData);
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Callback APIs. *
+*********************************************************************************************************************************/
+
+RTR3DECL(int) RTHttpSetUploadCallback(RTHTTP hHttp, uint64_t cbContent, PFNRTHTTPUPLOADCALLBACK pfnCallback, void *pvUser)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ pThis->pfnUploadCallback = pfnCallback;
+ pThis->pvUploadCallbackUser = pvUser;
+ pThis->cbUploadContent = cbContent;
+ pThis->offUploadContent = 0;
+
+ if (cbContent != UINT64_MAX)
+ {
+ AssertCompile(sizeof(curl_off_t) == sizeof(uint64_t));
+ CURLcode rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_INFILESIZE_LARGE, cbContent);
+ AssertMsgReturn(CURL_SUCCESS(rcCurl), ("%d (%#x)\n", rcCurl, rcCurl), VERR_HTTP_CURL_ERROR);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpSetDownloadCallback(RTHTTP hHttp, uint32_t fFlags, PFNRTHTTPDOWNLOADCALLBACK pfnCallback, void *pvUser)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertReturn(!pfnCallback || (fFlags & RTHTTPDOWNLOAD_F_ONLY_STATUS_MASK) != 0, VERR_INVALID_FLAGS);
+
+ pThis->pfnDownloadCallback = pfnCallback;
+ pThis->pvDownloadCallbackUser = pvUser;
+ pThis->fDownloadCallback = fFlags;
+ pThis->uDownloadHttpStatus = UINT32_MAX;
+ pThis->cbDownloadContent = UINT64_MAX;
+ pThis->offDownloadContent = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpSetDownloadProgressCallback(RTHTTP hHttp, PFNRTHTTPDOWNLDPROGRCALLBACK pfnCallback, void *pvUser)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ pThis->pfnDownloadProgress = pfnCallback;
+ pThis->pvDownloadProgressUser = pvUser;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpSetHeaderCallback(RTHTTP hHttp, PFNRTHTTPHEADERCALLBACK pfnCallback, void *pvUser)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ pThis->pfnHeaderCallback = pfnCallback;
+ pThis->pvHeaderCallbackUser = pvUser;
+ return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+* Temporary raw cURL stuff. Will be gone before 6.0 is out! *
+*********************************************************************************************************************************/
+
+RTR3DECL(int) RTHttpRawSetUrl(RTHTTP hHttp, const char *pszUrl)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ int rc = rtHttpConfigureProxyForUrl(pThis, pszUrl);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_URL, pszUrl);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetGet(RTHTTP hHttp)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetHead(RTHTTP hHttp)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 1L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetPost(RTHTTP hHttp)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POST, 1L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetPut(RTHTTP hHttp)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PUT, 1L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetDelete(RTHTTP hHttp)
+{
+ /* curl doesn't provide an option for this */
+ return RTHttpRawSetCustomRequest(hHttp, "DELETE");
+}
+
+
+RTR3DECL(int) RTHttpRawSetCustomRequest(RTHTTP hHttp, const char *pszVerb)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, pszVerb);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetPostFields(RTHTTP hHttp, const void *pv, size_t cb)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDSIZE, cb);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDS, pv);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTHttpRawSetInfileSize(RTHTTP hHttp, RTFOFF cb)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_INFILESIZE_LARGE, cb);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetVerbose(RTHTTP hHttp, bool fValue)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_VERBOSE, fValue ? 1L : 0L);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetTimeout(RTHTTP hHttp, long sec)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_TIMEOUT, sec);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawPerform(RTHTTP hHttp)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ /*
+ * XXX: Do this here for now as a stop-gap measure as
+ * RTHttpReset() resets this (and proxy settings).
+ */
+ if (pThis->pszCaFile)
+ {
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CAINFO, pThis->pszCaFile);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+ }
+
+ rcCurl = curl_easy_perform(pThis->pCurl);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawGetResponseCode(RTHTTP hHttp, long *plCode)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+ AssertPtrReturn(plCode, VERR_INVALID_PARAMETER);
+
+ rcCurl = curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, plCode);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetReadCallback(RTHTTP hHttp, PFNRTHTTPREADCALLBACKRAW pfnRead, void *pvUser)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READFUNCTION, pfnRead);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READDATA, pvUser);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetWriteCallback(RTHTTP hHttp, PFNRTHTTPWRITECALLBACKRAW pfnWrite, void *pvUser)
+{
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ CURLcode rcCurl = rtHttpSetWriteCallback(pThis, pfnWrite, pvUser);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTHttpRawSetWriteHeaderCallback(RTHTTP hHttp, PFNRTHTTPWRITECALLBACKRAW pfnWrite, void *pvUser)
+{
+ CURLcode rcCurl;
+
+ PRTHTTPINTERNAL pThis = hHttp;
+ RTHTTP_VALID_RETURN(pThis);
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERFUNCTION, pfnWrite);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERDATA, pvUser);
+ if (CURL_FAILURE(rcCurl))
+ return VERR_HTTP_CURL_ERROR;
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/generic/http.cpp b/src/VBox/Runtime/generic/http.cpp
new file mode 100644
index 00000000..aecc4af7
--- /dev/null
+++ b/src/VBox/Runtime/generic/http.cpp
@@ -0,0 +1,409 @@
+/* $Id: http.cpp $ */
+/** @file
+ * IPRT - HTTP common API.
+ */
+
+/*
+ * Copyright (C) 2012-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_HTTP
+#include <iprt/http-common.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTPHEADERLIST_VALID_RETURN_RC(hList, a_rc) \
+ do { \
+ AssertPtrReturn((hList), (a_rc)); \
+ AssertReturn((hList)->u32Magic == RTHTTPHEADERLIST_MAGIC, (a_rc)); \
+ } while (0)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTPHEADERLIST_VALID_RETURN(hList) RTHTTPHEADERLIST_VALID_RETURN_RC((hList), VERR_INVALID_HANDLE)
+
+/** Validates a handle and returns (void) if not valid. */
+#define RTHTTPHEADERLIST_VALID_RETURN_VOID(hList) \
+ do { \
+ AssertPtrReturnVoid(hList); \
+ AssertReturnVoid((hList)->u32Magic == RTHTTPHEADERLIST_MAGIC); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Internal structs. *
+*********************************************************************************************************************************/
+/**
+ * HTTP header list, internal definition.
+ */
+typedef struct RTHTTPHEADERLISTINTERNAL
+{
+ /** The list node. */
+ RTLISTANCHOR List;
+ /** Magic value. */
+ uint32_t u32Magic;
+} RTHTTPHEADERLISTINTERNAL;
+/** Pointer to an internal HTTP header list. */
+typedef RTHTTPHEADERLISTINTERNAL *PRTHTTPHEADERLISTINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Prototypes. *
+*********************************************************************************************************************************/
+static void rtHttpHeaderListRemoveAll(PRTHTTPHEADERLISTINTERNAL pThis);
+
+
+/*********************************************************************************************************************************
+* Lookup / conversion functions *
+*********************************************************************************************************************************/
+
+RTR3DECL(const char *) RTHttpMethodToStr(RTHTTPMETHOD enmMethod)
+{
+ switch (enmMethod)
+ {
+ case RTHTTPMETHOD_INVALID: return "invalid";
+ case RTHTTPMETHOD_GET: return "GET";
+ case RTHTTPMETHOD_PUT: return "PUT";
+ case RTHTTPMETHOD_POST: return "POST";
+ case RTHTTPMETHOD_PATCH: return "PATCH";
+ case RTHTTPMETHOD_DELETE: return "DELETE";
+ case RTHTTPMETHOD_HEAD: return "HEAD";
+ case RTHTTPMETHOD_OPTIONS: return "OPTIONS";
+ case RTHTTPMETHOD_TRACE: return "TRACE";
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ case RTHTTPMETHOD_PROPFIND: return "PROPFIND";
+#endif
+ case RTHTTPMETHOD_END:
+ RT_FALL_THROUGH();
+ case RTHTTPMETHOD_32BIT_HACK:
+ break;
+ }
+ return "unknown";
+}
+
+RTR3DECL(const char *) RTHttpStatusToStr(RTHTTPSTATUS enmSts)
+{
+ switch (enmSts)
+ {
+ case RTHTTPSTATUS_OK : return "OK";
+ case RTHTTPSTATUS_CREATED : return "Created";
+ case RTHTTPSTATUS_ACCEPTED : return "Accepted";
+ case RTHTTPSTATUS_NONAUTHORITATIVEINFORMATION : return "Non-Authoritative Information";
+ case RTHTTPSTATUS_NOCONTENT : return "No Content";
+ case RTHTTPSTATUS_RESETCONTENT : return "Reset Content";
+ case RTHTTPSTATUS_PARTIALCONTENT : return "Partial Content";
+ case RTHTTPSTATUS_MULTISTATUS : return "Multi-Status";
+ case RTHTTPSTATUS_ALREADYREPORTED : return "Already Reported";
+ case RTHTTPSTATUS_IMUSED : return "IM Used";
+
+ case RTHTTPSTATUS_BADREQUEST : return "Bad Request";
+ case RTHTTPSTATUS_UNAUTHORIZED : return "Unauthorized";
+ case RTHTTPSTATUS_PAYMENTREQUIRED : return "Payment Required";
+ case RTHTTPSTATUS_FORBIDDEN : return "Forbidden";
+ case RTHTTPSTATUS_NOTFOUND : return "Not Found";
+ case RTHTTPSTATUS_METHODNOTALLOWED : return "Method Not Allowed";
+ case RTHTTPSTATUS_NOTACCEPTABLE : return "Not Acceptable";
+ case RTHTTPSTATUS_PROXYAUTHENTICATIONREQUIRED : return "Proxy Authentication Required";
+ case RTHTTPSTATUS_REQUESTTIMEOUT : return "Request Timeout";
+ case RTHTTPSTATUS_CONFLICT : return "Conflict";
+ case RTHTTPSTATUS_GONE : return "Gone";
+ case RTHTTPSTATUS_LENGTHREQUIRED : return "Length Required";
+ case RTHTTPSTATUS_PRECONDITIONFAILED : return "Precondition Failed";
+ case RTHTTPSTATUS_PAYLOADTOOLARGE : return "Payload Too Large";
+ case RTHTTPSTATUS_URITOOLONG : return "URI Too Long";
+ case RTHTTPSTATUS_UNSUPPORTEDMEDIATYPE : return "Unsupported Media Type";
+ case RTHTTPSTATUS_RANGENOTSATISFIABLE : return "Range Not Satisfiable";
+ case RTHTTPSTATUS_EXPECTATIONFAILED : return "Expectation Failed";
+ case RTHTTPSTATUS_IMATEAPOT : return "I'm a teapot";
+ case RTHTTPSTATUS_UNPROCESSABLEENTITY : return "Unprocessable Entity";
+ case RTHTTPSTATUS_LOCKED : return "Locked";
+ case RTHTTPSTATUS_FAILEDDEPENDENCY : return "Failed Dependency";
+ case RTHTTPSTATUS_UPGRADEREQUIRED : return "Upgrade Required";
+ case RTHTTPSTATUS_PRECONDITIONREQUIRED : return "Precondition Required";
+ case RTHTTPSTATUS_TOOMANYREQUESTS : return "Too Many Requests";
+ case RTHTTPSTATUS_REQUESTHEADERFIELDSTOOLARGE : return "Request Header Fields Too Large";
+ case RTHTTPSTATUS_UNAVAILABLEFORLEGALREASONS : return "Unavailable For Legal Reasons";
+
+ case RTHTTPSTATUS_INTERNALSERVERERROR : return "Internal Server Error";
+ case RTHTTPSTATUS_NOTIMPLEMENTED : return "Not Implemented";
+ case RTHTTPSTATUS_BADGATEWAY : return "Bad Gateway";
+ case RTHTTPSTATUS_SERVICEUNAVAILABLE : return "Service Unavailable";
+ case RTHTTPSTATUS_GATEWAYTIMEOUT : return "Gateway Time-out";
+ case RTHTTPSTATUS_HTTPVERSIONNOTSUPPORTED : return "HTTP Version Not Supported";
+ case RTHTTPSTATUS_VARIANTALSONEGOTIATES : return "Variant Also Negotiates";
+ case RTHTTPSTATUS_INSUFFICIENTSTORAGE : return "Insufficient Storage";
+ case RTHTTPSTATUS_LOOPDETECTED : return "Loop Detected";
+ case RTHTTPSTATUS_NOTEXTENDED : return "Not Extended";
+ case RTHTTPSTATUS_NETWORKAUTHENTICATIONREQUIRED: return "Network Authentication Required";
+
+ default: break;
+ }
+
+ AssertFailed();
+ return "<Not implemented>";
+}
+
+
+/*********************************************************************************************************************************
+* HTTP Header List *
+*********************************************************************************************************************************/
+
+RTR3DECL(int) RTHttpHeaderListInit(PRTHTTPHEADERLIST hHdrLst)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = (PRTHTTPHEADERLISTINTERNAL)RTMemAllocZ(sizeof(RTHTTPHEADERLISTINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTHTTPHEADERLIST_MAGIC;
+
+ RTListInit(&pThis->List);
+
+ *hHdrLst = (RTHTTPHEADERLIST)pThis;
+
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Destroys the headers associated with this list (w/o telling cURL about it).
+ *
+ * @param hHdrLst The HTTP header list instance.
+ */
+RTR3DECL(void) RTHttpHeaderListDestroy(RTHTTPHEADERLIST hHdrLst)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN_VOID(pThis);
+
+ rtHttpHeaderListRemoveAll(pThis);
+}
+
+
+static void rtHttpHeaderListRemoveAll(PRTHTTPHEADERLISTINTERNAL pThis)
+{
+ PRTHTTPHEADERENTRY pEntry, pNext;
+ RTListForEachSafe(&pThis->List, pEntry, pNext, RTHTTPHEADERENTRY, Node)
+ {
+ RTListNodeRemove(&pEntry->Node);
+ RTMemFree(pEntry);
+ }
+}
+
+/**
+ * Worker for RTHttpHeaderListSet and RTHttpHeaderListAdd.
+ *
+ * @returns IPRT status code.
+ * @param pThis The HTTP header list instance.
+ * @param pchName The field name. Does not need to be terminated.
+ * @param cchName The field name length.
+ * @param pchValue The field value. Does not need to be terminated.
+ * @param cchValue The field value length.
+ * @param fFlags RTHTTPADDHDR_F_XXX.
+ */
+static int rtHttpHeaderListAddWorker(PRTHTTPHEADERLISTINTERNAL pThis,
+ const char *pchName, size_t cchName, const char *pchValue, size_t cchValue, uint32_t fFlags)
+{
+ /*
+ * Create the list entry.
+ */
+ size_t cbData = cchName + 2 + cchValue + 1;
+ PRTHTTPHEADERENTRY pHdr = (PRTHTTPHEADERENTRY)RTMemAlloc(RT_UOFFSETOF_DYN(RTHTTPHEADERENTRY, szData[cbData]));
+ if (pHdr)
+ {
+ pHdr->cchName = (uint32_t)cchName;
+ pHdr->offValue = (uint32_t)(cchName + 2);
+ char *psz = pHdr->szData;
+ memcpy(psz, pchName, cchName);
+ psz += cchName;
+ *psz++ = ':';
+ *psz++ = ' ';
+ memcpy(psz, pchValue, cchValue);
+ psz[cchValue] = '\0';
+
+ /*
+ * Appending to an existing list requires no cURL interaction.
+ */
+ AssertCompile(RTHTTPHEADERLISTADD_F_FRONT != 0);
+ if (!(fFlags & RTHTTPHEADERLISTADD_F_FRONT))
+ {
+ RTListAppend(&pThis->List, &pHdr->Node);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * When prepending or adding the first header we need to inform cURL
+ * about the new list head.
+ */
+ RTListPrepend(&pThis->List, &pHdr->Node);
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTR3DECL(int) RTHttpHeaderListSet(RTHTTPHEADERLIST hHdrLst,
+ size_t cHeaders, const char * const *papszHeaders)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN(pThis);
+
+ /*
+ * Drop old headers and reset state.
+ */
+ rtHttpHeaderListRemoveAll(pThis);
+
+ /*
+ * We're done if no headers specified.
+ */
+ if (!cHeaders)
+ return VINF_SUCCESS;
+
+ /*
+ * Add the headers, one by one.
+ */
+ int rc = VINF_SUCCESS;
+ for (size_t i = 0; i < cHeaders; i++)
+ {
+ const char *pszHeader = papszHeaders[i];
+ size_t cchHeader = strlen(pszHeader);
+ size_t cchName = (const char *)memchr(pszHeader, ':', cchHeader) - pszHeader;
+ AssertBreakStmt(cchName < cchHeader, rc = VERR_INVALID_PARAMETER);
+ size_t offValue = RT_C_IS_BLANK(pszHeader[cchName + 1]) ? cchName + 2 : cchName + 1;
+ rc = rtHttpHeaderListAddWorker(pThis, pszHeader, cchName, &pszHeader[offValue], cchHeader - offValue,
+ RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCBreak(rc);
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+ rtHttpHeaderListRemoveAll(pThis);
+ return rc;
+}
+
+
+RTR3DECL(int) RTHttpHeaderListAdd(RTHTTPHEADERLIST hHdrLst,
+ const char *pszField, const char *pszValue, size_t cchValue, uint32_t fFlags)
+{
+ /*
+ * Validate input and calc string lengths.
+ */
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN(pThis);
+ AssertReturn(!(fFlags & ~RTHTTPHEADERLISTADD_F_BACK), VERR_INVALID_FLAGS);
+ AssertPtr(pszField);
+ size_t const cchField = strlen(pszField);
+ AssertReturn(cchField > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(pszField[cchField - 1] != ':', VERR_INVALID_PARAMETER);
+ AssertReturn(!RT_C_IS_SPACE(pszField[cchField - 1]), VERR_INVALID_PARAMETER);
+#ifdef RT_STRICT
+ for (size_t i = 0; i < cchField; i++)
+ {
+ char const ch = pszField[i];
+ Assert(RT_C_IS_PRINT(ch) && ch != ':');
+ }
+#endif
+
+ AssertPtr(pszValue);
+ if (cchValue == RTSTR_MAX)
+ cchValue = strlen(pszValue);
+
+ /*
+ * Just pass it along to the worker.
+ */
+ return rtHttpHeaderListAddWorker(pThis, pszField, cchField, pszValue, cchValue, fFlags);
+}
+
+
+RTR3DECL(const char *) RTHttpHeaderListGet(RTHTTPHEADERLIST hHdrLst, const char *pszField, size_t cchField)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
+
+ if (cchField == RTSTR_MAX)
+ cchField = strlen(pszField);
+
+ PRTHTTPHEADERENTRY pEntry;
+ RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
+ {
+ if ( pEntry->cchName == cchField
+ && RTStrNICmpAscii(pEntry->szData, pszField, cchField) == 0)
+ return &pEntry->szData[pEntry->offValue];
+ }
+ return NULL;
+}
+
+
+RTR3DECL(size_t) RTHttpHeaderListGetCount(RTHTTPHEADERLIST hHdrLst)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, 0);
+
+ /* Note! Only for test cases and debugging, so we don't care about performance. */
+ size_t cHeaders = 0;
+ PRTHTTPHEADERENTRY pEntry;
+ RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
+ cHeaders++;
+ return cHeaders;
+}
+
+
+RTR3DECL(const char *) RTHttpHeaderListGetByOrdinal(RTHTTPHEADERLIST hHdrLst, size_t iOrdinal)
+{
+ PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
+ RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
+
+ /* Note! Only for test cases and debugging, so we don't care about performance. */
+ PRTHTTPHEADERENTRY pEntry;
+ RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
+ {
+ if (iOrdinal == 0)
+ return pEntry->szData;
+ iOrdinal--;
+ }
+
+ return NULL;
+}
+
diff --git a/src/VBox/Runtime/generic/mempool-generic.cpp b/src/VBox/Runtime/generic/mempool-generic.cpp
new file mode 100644
index 00000000..370f2e11
--- /dev/null
+++ b/src/VBox/Runtime/generic/mempool-generic.cpp
@@ -0,0 +1,420 @@
+/* $Id: mempool-generic.cpp $ */
+/** @file
+ * IPRT - Memory Allocation Pool.
+ */
+
+/*
+ * 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/mempool.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a memory pool instance. */
+typedef struct RTMEMPOOLINT *PRTMEMPOOLINT;
+/** Pointer to a memory pool entry. */
+typedef struct RTMEMPOOLENTRY *PRTMEMPOOLENTRY;
+
+/**
+ * Memory pool entry.
+ */
+typedef struct RTMEMPOOLENTRY
+{
+ /** Pointer to the pool */
+ PRTMEMPOOLINT pMemPool;
+ /** Pointer to the next entry. */
+ PRTMEMPOOLENTRY volatile pNext;
+ /** Pointer to the previous entry. */
+ PRTMEMPOOLENTRY volatile pPrev;
+ /** The number of references to the pool entry. */
+ uint32_t volatile cRefs;
+} RTMEMPOOLENTRY;
+
+
+/**
+ * Memory pool instance data.
+ */
+typedef struct RTMEMPOOLINT
+{
+ /** Magic number (RTMEMPOOL_MAGIC). */
+ uint32_t u32Magic;
+ /** Spinlock protecting the pool entry list updates. */
+ RTSPINLOCK hSpinLock;
+ /** Head entry pointer. */
+ PRTMEMPOOLENTRY volatile pHead;
+ /** The number of entries in the pool (for statistical purposes). */
+ uint32_t volatile cEntries;
+ /** User data associated with the pool. */
+ void *pvUser;
+ /** The pool name. (variable length) */
+ char szName[8];
+} RTMEMPOOLINT;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a memory pool handle, translating RTMEMPOOL_DEFAULT when found,
+ * and returns rc if not valid. */
+#define RTMEMPOOL_VALID_RETURN_RC(pMemPool, rc) \
+ do { \
+ if (pMemPool == RTMEMPOOL_DEFAULT) \
+ pMemPool = &g_rtMemPoolDefault; \
+ else \
+ { \
+ AssertPtrReturn((pMemPool), (rc)); \
+ AssertReturn((pMemPool)->u32Magic == RTMEMPOOL_MAGIC, (rc)); \
+ } \
+ } while (0)
+
+/** Validates a memory pool entry and returns rc if not valid. */
+#define RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, rc) \
+ do { \
+ AssertPtrReturn(pEntry, (rc)); \
+ AssertPtrNullReturn((pEntry)->pMemPool, (rc)); \
+ Assert((pEntry)->cRefs < UINT32_MAX / 2); \
+ AssertReturn((pEntry)->pMemPool->u32Magic == RTMEMPOOL_MAGIC, (rc)); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The */
+static RTMEMPOOLINT g_rtMemPoolDefault =
+{
+ /* .u32Magic = */ RTMEMPOOL_MAGIC,
+ /* .hSpinLock = */ NIL_RTSPINLOCK,
+ /* .pHead = */ NULL,
+ /* .cEntries = */ 0,
+ /* .pvUser = */ NULL,
+ /* .szName = */ "default"
+};
+
+
+
+RTDECL(int) RTMemPoolCreate(PRTMEMPOOL phMemPool, const char *pszName)
+{
+ AssertPtr(phMemPool);
+ AssertPtr(pszName);
+ Assert(*pszName);
+
+ size_t cchName = strlen(pszName);
+ PRTMEMPOOLINT pMemPool = (PRTMEMPOOLINT)RTMemAlloc(RT_UOFFSETOF_DYN(RTMEMPOOLINT, szName[cchName + 1]));
+ if (!pMemPool)
+ return VERR_NO_MEMORY;
+ int rc = RTSpinlockCreate(&pMemPool->hSpinLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "RTMemPoolCreate");
+ if (RT_SUCCESS(rc))
+ {
+ pMemPool->u32Magic = RTMEMPOOL_MAGIC;
+ pMemPool->pHead = NULL;
+ pMemPool->cEntries = 0;
+ pMemPool->pvUser = NULL;
+ memcpy(pMemPool->szName, pszName, cchName);
+ *phMemPool = pMemPool;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pMemPool);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMemPoolCreate);
+
+
+RTDECL(int) RTMemPoolDestroy(RTMEMPOOL hMemPool)
+{
+ if (hMemPool == NIL_RTMEMPOOL)
+ return VINF_SUCCESS;
+ PRTMEMPOOLINT pMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pMemPool, VERR_INVALID_HANDLE);
+ if (pMemPool == &g_rtMemPoolDefault)
+ return VINF_SUCCESS;
+
+ /*
+ * Invalidate the handle and free all associated resources.
+ */
+ ASMAtomicWriteU32(&pMemPool->u32Magic, RTMEMPOOL_MAGIC_DEAD);
+
+ int rc = RTSpinlockDestroy(pMemPool->hSpinLock); AssertRC(rc);
+ pMemPool->hSpinLock = NIL_RTSPINLOCK;
+
+ PRTMEMPOOLENTRY pEntry = pMemPool->pHead;
+ pMemPool->pHead = NULL;
+ while (pEntry)
+ {
+ PRTMEMPOOLENTRY pFree = pEntry;
+ Assert(pFree->cRefs > 0 && pFree->cRefs < UINT32_MAX / 2);
+ pEntry = pEntry->pNext;
+
+ pFree->pMemPool = NULL;
+ pFree->pNext = NULL;
+ pFree->pPrev = NULL;
+ pFree->cRefs = UINT32_MAX - 3;
+ RTMemFree(pFree);
+ }
+
+ RTMemFree(pMemPool);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemPoolDestroy);
+
+
+DECLINLINE(void) rtMemPoolInitAndLink(PRTMEMPOOLINT pMemPool, PRTMEMPOOLENTRY pEntry)
+{
+ pEntry->pMemPool = pMemPool;
+ pEntry->pNext = NULL;
+ pEntry->pPrev = NULL;
+ pEntry->cRefs = 1;
+
+ if (pMemPool->hSpinLock != NIL_RTSPINLOCK)
+ {
+ RTSpinlockAcquire(pMemPool->hSpinLock);
+
+ PRTMEMPOOLENTRY pHead = pMemPool->pHead;
+ pEntry->pNext = pHead;
+ if (pHead)
+ pHead->pPrev = pEntry;
+ pMemPool->pHead = pEntry;
+
+ RTSpinlockRelease(pMemPool->hSpinLock);
+ }
+
+ ASMAtomicIncU32(&pMemPool->cEntries);
+}
+
+
+DECLINLINE(void) rtMemPoolUnlink(PRTMEMPOOLENTRY pEntry)
+{
+ PRTMEMPOOLINT pMemPool = pEntry->pMemPool;
+ if (pMemPool->hSpinLock != NIL_RTSPINLOCK)
+ {
+ RTSpinlockAcquire(pMemPool->hSpinLock);
+
+ PRTMEMPOOLENTRY pNext = pEntry->pNext;
+ PRTMEMPOOLENTRY pPrev = pEntry->pPrev;
+ if (pNext)
+ pNext->pPrev = pPrev;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pMemPool->pHead = pNext;
+ pEntry->pMemPool = NULL;
+
+ RTSpinlockRelease(pMemPool->hSpinLock);
+ }
+ else
+ pEntry->pMemPool = NULL;
+
+ ASMAtomicDecU32(&pMemPool->cEntries);
+}
+
+
+RTDECL(void *) RTMemPoolAlloc(RTMEMPOOL hMemPool, size_t cb) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLINT pMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL);
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cb + sizeof(*pEntry));
+ if (!pEntry)
+ return NULL;
+ rtMemPoolInitAndLink(pMemPool, pEntry);
+
+ return pEntry + 1;
+}
+RT_EXPORT_SYMBOL(RTMemPoolAlloc);
+
+
+RTDECL(void *) RTMemPoolAllocZ(RTMEMPOOL hMemPool, size_t cb) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLINT pMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL);
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAllocZ(cb + sizeof(*pEntry));
+ if (!pEntry)
+ return NULL;
+ rtMemPoolInitAndLink(pMemPool, pEntry);
+
+ return pEntry + 1;
+}
+RT_EXPORT_SYMBOL(RTMemPoolAllocZ);
+
+
+RTDECL(void *) RTMemPoolDup(RTMEMPOOL hMemPool, const void *pvSrc, size_t cb) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLINT pMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL);
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cb + sizeof(*pEntry));
+ if (!pEntry)
+ return NULL;
+ memcpy(pEntry + 1, pvSrc, cb);
+ rtMemPoolInitAndLink(pMemPool, pEntry);
+
+ return pEntry + 1;
+}
+RT_EXPORT_SYMBOL(RTMemPoolDup);
+
+
+RTDECL(void *) RTMemPoolDupEx(RTMEMPOOL hMemPool, const void *pvSrc, size_t cbSrc, size_t cbExtra) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLINT pMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL);
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cbSrc + cbExtra + sizeof(*pEntry));
+ if (!pEntry)
+ return NULL;
+ memcpy(pEntry + 1, pvSrc, cbSrc);
+ memset((uint8_t *)(pEntry + 1) + cbSrc, '\0', cbExtra);
+ rtMemPoolInitAndLink(pMemPool, pEntry);
+
+ return pEntry + 1;
+}
+RT_EXPORT_SYMBOL(RTMemPoolDupEx);
+
+
+
+RTDECL(void *) RTMemPoolRealloc(RTMEMPOOL hMemPool, void *pvOld, size_t cbNew) RT_NO_THROW_DEF
+{
+ /*
+ * Fend off the odd cases.
+ */
+ if (!cbNew)
+ {
+ RTMemPoolRelease(hMemPool, pvOld);
+ return NULL;
+ }
+
+ if (!pvOld)
+ return RTMemPoolAlloc(hMemPool, cbNew);
+
+ /*
+ * Real realloc.
+ */
+ PRTMEMPOOLINT pNewMemPool = hMemPool;
+ RTMEMPOOL_VALID_RETURN_RC(pNewMemPool, NULL);
+
+ PRTMEMPOOLENTRY pOldEntry = (PRTMEMPOOLENTRY)pvOld - 1;
+ RTMEMPOOL_VALID_ENTRY_RETURN_RC(pOldEntry, NULL);
+ PRTMEMPOOLINT pOldMemPool = pOldEntry->pMemPool;
+ AssertReturn(pOldEntry->cRefs == 1, NULL);
+
+ /*
+ * Unlink it from the current pool and try reallocate it.
+ */
+ rtMemPoolUnlink(pOldEntry);
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemRealloc(pOldEntry, cbNew + sizeof(*pEntry));
+ if (!pEntry)
+ {
+ rtMemPoolInitAndLink(pOldMemPool, pOldEntry);
+ return NULL;
+ }
+ rtMemPoolInitAndLink(pNewMemPool, pEntry);
+
+ return pEntry + 1;
+}
+RT_EXPORT_SYMBOL(RTMemPoolRealloc);
+
+
+RTDECL(void) RTMemPoolFree(RTMEMPOOL hMemPool, void *pv) RT_NO_THROW_DEF
+{
+ RTMemPoolRelease(hMemPool, pv);
+}
+RT_EXPORT_SYMBOL(RTMemPoolFree);
+
+
+RTDECL(uint32_t) RTMemPoolRetain(void *pv) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1;
+ RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pEntry->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTMemPoolRetain);
+
+
+RTDECL(uint32_t) RTMemPoolRelease(RTMEMPOOL hMemPool, void *pv) RT_NO_THROW_DEF
+{
+ if (!pv)
+ return 0;
+
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1;
+ RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX);
+ Assert( hMemPool == NIL_RTMEMPOOL
+ || hMemPool == pEntry->pMemPool
+ || (hMemPool == RTMEMPOOL_DEFAULT && pEntry->pMemPool == &g_rtMemPoolDefault)); RT_NOREF_PV(hMemPool);
+ AssertReturn(pEntry->cRefs > 0, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pEntry->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ {
+ rtMemPoolUnlink(pEntry);
+ pEntry->cRefs = UINT32_MAX - 2;
+ RTMemFree(pEntry);
+ }
+
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTMemPoolRelease);
+
+
+RTDECL(uint32_t) RTMemPoolRefCount(void *pv) RT_NO_THROW_DEF
+{
+ PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1;
+ RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicReadU32(&pEntry->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTMemPoolRefCount);
+
diff --git a/src/VBox/Runtime/generic/memsafer-generic.cpp b/src/VBox/Runtime/generic/memsafer-generic.cpp
new file mode 100644
index 00000000..0b4a6a7d
--- /dev/null
+++ b/src/VBox/Runtime/generic/memsafer-generic.cpp
@@ -0,0 +1,238 @@
+/* $Id: memsafer-generic.cpp $ */
+/** @file
+ * IPRT - Memory Allocate for Sensitive Data, generic heap-based implementation.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+#include <iprt/memsafer.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Allocation size alignment. */
+#define RTMEMSAFER_ALIGN 16
+/** Padding after the block to avoid small overruns. */
+#define RTMEMSAFER_PAD_BEFORE 96
+/** Padding after the block to avoid small underruns. */
+#define RTMEMSAFER_PAD_AFTER 32
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** XOR scrabler value.
+ * @todo determine this at runtime */
+#if ARCH_BITS == 32
+static uintptr_t g_uScramblerXor = UINT32_C(0x867af88d);
+#elif ARCH_BITS == 64
+static uintptr_t g_uScramblerXor = UINT64_C(0xed95ecc99416d312);
+#else
+# error "Bad ARCH_BITS value"
+#endif
+
+
+
+RTDECL(int) RTMemSaferScramble(void *pv, size_t cb)
+{
+
+ AssertMsg(*(size_t *)((char *)pv - RTMEMSAFER_PAD_BEFORE) == cb,
+ ("*pvStart=%#zx cb=%#zx\n", *(size_t *)((char *)pv- RTMEMSAFER_PAD_BEFORE), cb));
+
+ /* Note! This isn't supposed to be safe, just less obvious. */
+ uintptr_t *pu = (uintptr_t *)pv;
+ cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
+ while (cb > 0)
+ {
+ *pu ^= g_uScramblerXor;
+ pu++;
+ cb -= sizeof(*pu);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemSaferScramble);
+
+
+RTDECL(int) RTMemSaferUnscramble(void *pv, size_t cb)
+{
+ AssertMsg(*(size_t *)((char *)pv - RTMEMSAFER_PAD_BEFORE) == cb,
+ ("*pvStart=%#zx cb=%#zx\n", *(size_t *)((char *)pv - RTMEMSAFER_PAD_BEFORE), cb));
+
+ /* Note! This isn't supposed to be safe, just less obvious. */
+ uintptr_t *pu = (uintptr_t *)pv;
+ cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
+ while (cb > 0)
+ {
+ *pu ^= g_uScramblerXor;
+ pu++;
+ cb -= sizeof(*pu);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemSaferUnscramble);
+
+
+RTDECL(int) RTMemSaferAllocZExTag(void **ppvNew, size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ AssertPtrReturn(ppvNew, VERR_INVALID_PARAMETER);
+ *ppvNew = NULL;
+ AssertReturn(cb, VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(pszTag);
+
+ /*
+ * We support none of the hard requirements passed thru flags.
+ */
+ if (fFlags == 0)
+ {
+ /*
+ * Don't request zeroed memory. We want random heap garbage in the
+ * padding zones, nothing that makes our allocations easier to find.
+ */
+ size_t cbUser = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
+ void *pvNew = RTMemAlloc(cbUser + RTMEMSAFER_PAD_BEFORE + RTMEMSAFER_PAD_AFTER);
+ if (pvNew)
+ {
+#ifdef RT_STRICT /* For checking input in string builds. */
+ memset(pvNew, 0xad, RTMEMSAFER_PAD_BEFORE);
+ memset((char *)pvNew + RTMEMSAFER_PAD_BEFORE + cb, 0xda, RTMEMSAFER_PAD_AFTER + (cbUser - cb));
+ *(size_t *)pvNew = cb;
+#endif
+
+ void *pvUser = (char *)pvNew + RTMEMSAFER_PAD_BEFORE;
+ *ppvNew = pvUser;
+
+ /* You don't use this API for performance, so we always clean memory. */
+ RT_BZERO(pvUser, cb);
+
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+ }
+ AssertReturn(!(fFlags & ~RTMEMSAFER_F_VALID_MASK), VERR_INVALID_FLAGS);
+ return VWRN_UNABLE_TO_SATISFY_REQUIREMENTS;
+}
+RT_EXPORT_SYMBOL(RTMemSaferAllocZExTag);
+
+
+RTDECL(void) RTMemSaferFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ {
+ Assert(cb); /* does not support openssl. */
+ void *pvStart = (char *)pv - RTMEMSAFER_PAD_BEFORE;
+ AssertMsg(*(size_t *)pvStart == cb, ("*pvStart=%#zx cb=%#zx\n", *(size_t *)pvStart, cb));
+ RTMemWipeThoroughly(pv, RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN), 3);
+ RTMemFree(pvStart);
+ }
+ else
+ Assert(cb == 0);
+}
+RT_EXPORT_SYMBOL(RTMemSaferFree);
+
+
+RTDECL(int) RTMemSaferReallocZExTag(size_t cbOld, void *pvOld, size_t cbNew, void **ppvNew, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ /*
+ * We cannot let the heap move us around because we will be failing in our
+ * duty to clean things up. So, allocate a new block, copy over the old
+ * content, and free the old one.
+ */
+ int rc;
+ /* Real realloc. */
+ if (cbNew && cbOld)
+ {
+ AssertPtr(pvOld);
+ AssertMsg(*(size_t *)((char *)pvOld - RTMEMSAFER_PAD_BEFORE) == cbOld,
+ ("*pvStart=%#zx cbOld=%#zx\n", *(size_t *)((char *)pvOld - RTMEMSAFER_PAD_BEFORE), cbOld));
+
+ /*
+ * We support none of the hard requirements passed thru flags.
+ */
+ void *pvNew;
+ rc = RTMemSaferAllocZExTag(&pvNew, cbNew, fFlags, pszTag);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pvNew, pvOld, RT_MIN(cbNew, cbOld));
+ RTMemSaferFree(pvOld, cbOld);
+ *ppvNew = pvNew;
+ }
+ }
+ /* First allocation. */
+ else if (!cbOld)
+ {
+ Assert(pvOld == NULL);
+ rc = RTMemSaferAllocZExTag(ppvNew, cbNew, fFlags, pszTag);
+ }
+ /* Free operation*/
+ else
+ {
+ RTMemSaferFree(pvOld, cbOld);
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMemSaferReallocZExTag);
+
+
+RTDECL(void *) RTMemSaferAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvNew = NULL;
+ int rc = RTMemSaferAllocZExTag(&pvNew, cb, 0 /*fFlags*/, pszTag);
+ if (RT_SUCCESS(rc))
+ return pvNew;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemSaferAllocZTag);
+
+
+RTDECL(void *) RTMemSaferReallocZTag(size_t cbOld, void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvNew = NULL;
+ int rc = RTMemSaferReallocZExTag(cbOld, pvOld, cbNew, &pvNew, 0 /*fFlags*/, pszTag);
+ if (RT_SUCCESS(rc))
+ return pvNew;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemSaferReallocZTag);
+
diff --git a/src/VBox/Runtime/generic/mppresent-generic-online.cpp b/src/VBox/Runtime/generic/mppresent-generic-online.cpp
new file mode 100644
index 00000000..df843d55
--- /dev/null
+++ b/src/VBox/Runtime/generic/mppresent-generic-online.cpp
@@ -0,0 +1,71 @@
+/* $Id: mppresent-generic-online.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Stubs for the RTMp*Present* API mapping to RTMp*Online.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet)
+{
+ return RTMpGetOnlineSet(pSet);
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentSet);
+
+
+RTDECL(RTCPUID) RTMpGetPresentCount(void)
+{
+ return RTMpGetOnlineCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentCount);
+
+
+RTDECL(RTCPUID) RTMpGetPresentCoreCount(void)
+{
+ return RTMpGetOnlineCoreCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentCoreCount);
+
+
+RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu)
+{
+ return RTMpIsCpuOnline(idCpu);
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuPresent);
+
diff --git a/src/VBox/Runtime/generic/mppresent-generic.cpp b/src/VBox/Runtime/generic/mppresent-generic.cpp
new file mode 100644
index 00000000..035a963b
--- /dev/null
+++ b/src/VBox/Runtime/generic/mppresent-generic.cpp
@@ -0,0 +1,71 @@
+/* $Id: mppresent-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Stubs for the RTMp*Present* API.
+ */
+
+/*
+ * Copyright (C) 2008-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/mp.h>
+#include "internal/iprt.h"
+
+
+RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet)
+{
+ return RTMpGetSet(pSet);
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentSet);
+
+
+RTDECL(RTCPUID) RTMpGetPresentCount(void)
+{
+ return RTMpGetCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentCount);
+
+
+RTDECL(RTCPUID) RTMpGetPresentCoreCount(void)
+{
+ return RTMpGetCoreCount();
+}
+RT_EXPORT_SYMBOL(RTMpGetPresentCoreCount);
+
+
+RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu)
+{
+ return RTMpIsCpuPossible(idCpu);
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuPresent);
+
diff --git a/src/VBox/Runtime/generic/new-delete-generic.cpp b/src/VBox/Runtime/generic/new-delete-generic.cpp
new file mode 100644
index 00000000..3ba61819
--- /dev/null
+++ b/src/VBox/Runtime/generic/new-delete-generic.cpp
@@ -0,0 +1,144 @@
+/* $Id: new-delete-generic.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, C++ electric fence.
+ */
+
+/*
+ * 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/nocrt/new>
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+
+
+
+void *RT_NEW_DELETE_CDECL operator new(RT_NEW_DELETE_SIZE_T cb) RT_NEW_DELETE_THROWS_BAD_ALLOC
+{
+ void *pv = RTMemAlloc(cb);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (!pv)
+ throw std::bad_alloc();
+#endif
+ return pv;
+}
+
+
+void *RT_NEW_DELETE_CDECL operator new(RT_NEW_DELETE_SIZE_T cb, const std::nothrow_t &) RT_NEW_DELETE_NOTHROW
+{
+ return RTMemAlloc(cb);
+}
+
+
+void *RT_NEW_DELETE_CDECL operator new(RT_NEW_DELETE_SIZE_T cb, void *pvPlacement) RT_NEW_DELETE_NOTHROW
+{
+ RT_NOREF(cb);
+ return pvPlacement;
+}
+
+
+void RT_NEW_DELETE_CDECL operator delete(void *pv) RT_NEW_DELETE_NOTHROW
+{
+ RTMemFree(pv);
+}
+
+
+#ifdef __cpp_sized_deallocation
+void RT_NEW_DELETE_CDECL operator delete(void *pv, RT_NEW_DELETE_SIZE_T cb) RT_NEW_DELETE_NOTHROW
+{
+ RT_NOREF_PV(cb);
+ RTMemFree(pv);
+}
+#endif
+
+
+void RT_NEW_DELETE_CDECL operator delete(void *pv, const std::nothrow_t &) RT_NEW_DELETE_NOTHROW
+{
+ RTMemFree(pv);
+}
+
+
+/*
+ *
+ * Array object form.
+ * Array object form.
+ * Array object form.
+ *
+ */
+
+void *RT_NEW_DELETE_CDECL operator new[](RT_NEW_DELETE_SIZE_T cb) RT_NEW_DELETE_THROWS_BAD_ALLOC
+{
+ void *pv = RTMemAlloc(cb);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (!pv)
+ throw std::bad_alloc();
+#endif
+ return pv;
+}
+
+
+void *RT_NEW_DELETE_CDECL operator new[](RT_NEW_DELETE_SIZE_T cb, const std::nothrow_t &) RT_NEW_DELETE_NOTHROW
+{
+ return RTMemAlloc(cb);
+}
+
+
+void *RT_NEW_DELETE_CDECL operator new[](RT_NEW_DELETE_SIZE_T cb, void *pvPlacement) RT_NEW_DELETE_NOTHROW
+{
+ RT_NOREF_PV(cb);
+ return pvPlacement;
+}
+
+
+void RT_NEW_DELETE_CDECL operator delete[](void * pv) RT_NEW_DELETE_NOTHROW
+{
+ RTMemFree(pv);
+}
+
+
+#ifdef __cpp_sized_deallocation
+void RT_NEW_DELETE_CDECL operator delete[](void * pv, RT_NEW_DELETE_SIZE_T cb) RT_NEW_DELETE_NOTHROW
+{
+ RT_NOREF_PV(cb);
+ RTMemFree(pv);
+}
+#endif
+
+
+void RT_NEW_DELETE_CDECL operator delete[](void *pv, const std::nothrow_t &) RT_NEW_DELETE_NOTHROW
+{
+ RTMemFree(pv);
+}
+
diff --git a/src/VBox/Runtime/generic/pathhost-generic.cpp b/src/VBox/Runtime/generic/pathhost-generic.cpp
new file mode 100644
index 00000000..59ffa66e
--- /dev/null
+++ b/src/VBox/Runtime/generic/pathhost-generic.cpp
@@ -0,0 +1,101 @@
+/* $Id: pathhost-generic.cpp $ */
+/** @file
+ * IPRT - Path Conversions, generic pass through.
+ */
+
+/*
+ * 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_PATH
+#include "internal/iprt.h"
+#include "internal/path.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+int rtPathToNative(char const **ppszNativePath, const char *pszPath, const char *pszBasePath)
+{
+ *ppszNativePath = pszPath;
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return VINF_SUCCESS;
+}
+
+
+void rtPathFreeNative(char const *pszNativePath, const char *pszPath)
+{
+ Assert(pszNativePath == pszPath || !pszNativePath);
+ NOREF(pszNativePath); NOREF(pszPath);
+}
+
+
+int rtPathFromNative(const char **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ *ppszPath = pszNativePath;
+ else
+ *ppszPath = NULL;
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+void rtPathFreeIprt(const char *pszPath, const char *pszNativePath)
+{
+ Assert(pszPath == pszNativePath || !pszPath);
+ NOREF(pszPath); NOREF(pszNativePath);
+}
+
+
+int rtPathFromNativeCopy(char *pszPath, size_t cbPath, const char *pszNativePath, const char *pszBasePath)
+{
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ rc = RTStrCopy(pszPath, cbPath, pszNativePath);
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+int rtPathFromNativeDup(char **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ rc = RTStrDupEx(ppszPath, pszNativePath);
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp b/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp
new file mode 100644
index 00000000..501e9e84
--- /dev/null
+++ b/src/VBox/Runtime/generic/rtStrFormatKernelAddress-generic.cpp
@@ -0,0 +1,84 @@
+/* $Id: rtStrFormatKernelAddress-generic.cpp $ */
+/** @file
+ * IPRT - IPRT String Formatter, ring-0 addresses.
+ */
+
+/*
+ * 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_STRING
+#include <iprt/string.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "internal/string.h"
+
+
+
+DECLHIDDEN(size_t) rtStrFormatKernelAddress(char *pszBuf, size_t cbBuf, RTR0INTPTR uPtr, signed int cchWidth,
+ signed int cchPrecision, unsigned int fFlags)
+{
+#ifndef DEBUG
+ RT_NOREF(uPtr, cchWidth, cchPrecision);
+# if R0_ARCH_BITS == 64
+ static const char s_szObfuscated[] = "0xXXXXXXXXXXXXXXXX";
+# else
+ static const char s_szObfuscated[] = "0xXXXXXXXX";
+# endif
+ size_t cbSrc = sizeof(s_szObfuscated);
+ const char *pszSrc = s_szObfuscated;
+ if (!(fFlags & RTSTR_F_SPECIAL))
+ {
+ pszSrc += 2;
+ cbSrc -= 2;
+ }
+ if (cbSrc <= cbBuf)
+ {
+ memcpy(pszBuf, pszSrc, cbSrc);
+ return cbSrc - 1;
+ }
+ AssertFailed();
+ memcpy(pszBuf, pszSrc, cbBuf);
+ pszBuf[cbBuf - 1] = '\0';
+ return cbBuf - 1;
+
+#else /* DEBUG */
+ Assert(cbBuf >= 64); RT_NOREF(cbBuf);
+ return RTStrFormatNumber(pszBuf, uPtr, 16, cchWidth, cchPrecision, fFlags);
+#endif /* DEBUG */
+}
+
diff --git a/src/VBox/Runtime/generic/sched-generic.cpp b/src/VBox/Runtime/generic/sched-generic.cpp
new file mode 100644
index 00000000..896c2f55
--- /dev/null
+++ b/src/VBox/Runtime/generic/sched-generic.cpp
@@ -0,0 +1,77 @@
+/* $Id: sched-generic.cpp $ */
+/** @file
+ * IPRT - Scheduling, generic stubs.
+ */
+
+/*
+ * 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_THREAD
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/generic/semfastmutex-generic.cpp b/src/VBox/Runtime/generic/semfastmutex-generic.cpp
new file mode 100644
index 00000000..28366ab5
--- /dev/null
+++ b/src/VBox/Runtime/generic/semfastmutex-generic.cpp
@@ -0,0 +1,92 @@
+/* $Id: semfastmutex-generic.cpp $ */
+/** @file
+ * IPRT - Fast Mutex, Generic.
+ */
+
+/*
+ * 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/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/errcore.h>
+#include <iprt/critsect.h>
+
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(RTCRITSECT));
+ if (!pCritSect)
+ return VERR_NO_MEMORY;
+
+ int rc = RTCritSectInitEx(pCritSect, RTCRITSECT_FLAGS_NO_NESTING,
+ NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+ if (RT_SUCCESS(rc))
+ *phFastMtx = (RTSEMFASTMUTEX)pCritSect;
+ else
+ RTMemFree(pCritSect);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexCreate);
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ if (hFastMtx == NIL_RTSEMFASTMUTEX)
+ return VERR_INVALID_PARAMETER;
+ PRTCRITSECT pCritSect = (PRTCRITSECT)hFastMtx;
+ int rc = RTCritSectDelete(pCritSect);
+ if (RT_SUCCESS(rc))
+ RTMemFree(pCritSect);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexDestroy);
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ return RTCritSectEnter((PRTCRITSECT)hFastMtx);
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexRequest);
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ return RTCritSectLeave((PRTCRITSECT)hFastMtx);
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexRelease);
+
diff --git a/src/VBox/Runtime/generic/semrw-generic.cpp b/src/VBox/Runtime/generic/semrw-generic.cpp
new file mode 100644
index 00000000..3cde84bf
--- /dev/null
+++ b/src/VBox/Runtime/generic/semrw-generic.cpp
@@ -0,0 +1,988 @@
+/* $Id: semrw-generic.cpp $ */
+/** @file
+ * IPRT - Read-Write Semaphore, Generic.
+ *
+ * This is a generic implementation for OSes which don't have
+ * native RW semaphores.
+ */
+
+/*
+ * 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 RTSEMRW_WITHOUT_REMAPPING
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Internal representation of a Read-Write semaphore for the
+ * Generic implementation. */
+struct RTSEMRWINTERNAL
+{
+ /** The usual magic. (RTSEMRW_MAGIC) */
+ uint32_t u32Magic;
+ /* Alignment padding. */
+ uint32_t u32Padding;
+ /** This critical section serializes the access to and updating of the structure members. */
+ RTCRITSECT CritSect;
+ /** The current number of reads. (pure read recursion counts too) */
+ uint32_t cReads;
+ /** The current number of writes. (recursion counts too) */
+ uint32_t cWrites;
+ /** Number of read recursions by the writer. */
+ uint32_t cWriterReads;
+ /** Number of writers waiting. */
+ uint32_t cWritesWaiting;
+ /** The write owner of the lock. */
+ RTNATIVETHREAD hWriter;
+ /** The handle of the event object on which the waiting readers block. (manual reset). */
+ RTSEMEVENTMULTI ReadEvent;
+ /** The handle of the event object on which the waiting writers block. (automatic reset). */
+ RTSEMEVENT WriteEvent;
+ /** Need to reset ReadEvent. */
+ bool fNeedResetReadEvent;
+#ifdef RTSEMRW_STRICT
+ /** The validator record for the writer. */
+ RTLOCKVALRECEXCL ValidatorWrite;
+ /** The validator record for the readers. */
+ RTLOCKVALRECSHRD ValidatorRead;
+#endif
+};
+
+
+
+RTDECL(int) RTSemRWCreate(PRTSEMRW phRWSem)
+{
+ return RTSemRWCreateEx(phRWSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemRW");
+}
+RT_EXPORT_SYMBOL(RTSemRWCreate);
+
+
+RTDECL(int) RTSemRWCreateEx(PRTSEMRW phRWSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMRW_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate memory.
+ */
+ int rc;
+ struct RTSEMRWINTERNAL *pThis = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
+ if (pThis)
+ {
+ /*
+ * Create the semaphores.
+ */
+ rc = RTSemEventCreateEx(&pThis->WriteEvent, RTSEMEVENT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventMultiCreateEx(&pThis->ReadEvent, RTSEMEVENT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInitEx(&pThis->CritSect, RTCRITSECT_FLAGS_NO_LOCK_VAL,
+ NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Signal the read semaphore and initialize other variables.
+ */
+ rc = RTSemEventMultiSignal(pThis->ReadEvent);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Padding = UINT32_C(0xa5a55a5a);
+ pThis->cReads = 0;
+ pThis->cWrites = 0;
+ pThis->cWriterReads = 0;
+ pThis->cWritesWaiting = 0;
+ pThis->hWriter = NIL_RTNATIVETHREAD;
+ pThis->fNeedResetReadEvent = true;
+ pThis->u32Magic = RTSEMRW_MAGIC;
+#ifdef RTSEMRW_STRICT
+ bool const fLVEnabled = !(fFlags & RTSEMRW_FLAGS_NO_LOCK_VAL);
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemRWAnon = 0;
+ uint32_t i = ASMAtomicIncU32(&s_iSemRWAnon) - 1;
+ RTLockValidatorRecExclInit(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, "RTSemRW-%u", i);
+ RTLockValidatorRecSharedInit(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, "RTSemRW-%u", i);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ }
+ RTLockValidatorRecMakeSiblings(&pThis->ValidatorWrite.Core, &pThis->ValidatorRead.Core);
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+#endif
+ *phRWSem = pThis;
+ return VINF_SUCCESS;
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTSemEventMultiDestroy(pThis->ReadEvent);
+ }
+ RTSemEventDestroy(pThis->WriteEvent);
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemRWCreate);
+
+
+RTDECL(int) RTSemRWDestroy(RTSEMRW hRWSem)
+{
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+
+ /*
+ * Validate handle.
+ */
+ if (pThis == NIL_RTSEMRW)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check if busy.
+ */
+ int rc = RTCritSectTryEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pThis->cReads && !pThis->cWrites)
+ {
+ /*
+ * Make it invalid and unusable.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMRW_MAGIC);
+ pThis->cReads = UINT32_MAX;
+
+ /*
+ * Do actual cleanup. None of these can now fail.
+ */
+ rc = RTSemEventMultiDestroy(pThis->ReadEvent);
+ AssertMsgRC(rc, ("RTSemEventMultiDestroy failed! rc=%Rrc\n", rc));
+ pThis->ReadEvent = NIL_RTSEMEVENTMULTI;
+
+ rc = RTSemEventDestroy(pThis->WriteEvent);
+ AssertMsgRC(rc, ("RTSemEventDestroy failed! rc=%Rrc\n", rc));
+ pThis->WriteEvent = NIL_RTSEMEVENT;
+
+ RTCritSectLeave(&pThis->CritSect);
+ rc = RTCritSectDelete(&pThis->CritSect);
+ AssertMsgRC(rc, ("RTCritSectDelete failed! rc=%Rrc\n", rc));
+
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->ValidatorRead);
+ RTLockValidatorRecExclDelete(&pThis->ValidatorWrite);
+#endif
+ RTMemFree(pThis);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_SEM_BUSY;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ }
+ else
+ {
+ AssertMsgRC(rc, ("RTCritSectTryEnter failed! rc=%Rrc\n", rc));
+ rc = VERR_SEM_BUSY;
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemRWDestroy);
+
+
+RTDECL(uint32_t) RTSemRWSetSubClass(RTSEMRW hRWSem, uint32_t uSubClass)
+{
+#ifdef RTSEMRW_STRICT
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ RTLockValidatorRecSharedSetSubClass(&pThis->ValidatorRead, uSubClass);
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorWrite, uSubClass);
+#else
+ RT_NOREF_PV(hRWSem); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWSetSubClass);
+
+
+DECL_FORCE_INLINE(int) rtSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies, bool fInterruptible, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ RTMSINTERVAL cMilliesInitial = cMillies;
+ uint64_t tsStart = 0;
+ if (cMillies != RT_INDEFINITE_WAIT && cMillies != 0)
+ tsStart = RTTimeNanoTS();
+
+#ifdef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (cMillies > 0)
+ {
+ int rc9;
+ if (pThis->hWriter != NIL_RTTHREAD && pThis->hWriter == RTThreadNativeSelf())
+ rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, cMillies);
+ else
+ rc9 = RTLockValidatorRecSharedCheckOrder(&pThis->ValidatorRead, hThreadSelf, pSrcPos, cMillies);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Take critsect.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ return rc;
+ }
+
+ /*
+ * Check if the state of affairs allows read access.
+ * Do not block further readers if there is a writer waiting, as
+ * that will break/deadlock reader recursion.
+ */
+ if ( pThis->hWriter == NIL_RTNATIVETHREAD
+#if 0
+ && ( !pThis->cWritesWaiting
+ || pThis->cReads)
+#endif
+ )
+ {
+ pThis->cReads++;
+ Assert(pThis->cReads > 0);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
+ if (pThis->hWriter == hNativeSelf)
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursionMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core, pSrcPos);
+ if (RT_FAILURE(rc9))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ return rc9;
+ }
+#endif
+
+ pThis->cWriterReads++;
+ Assert(pThis->cWriterReads > 0);
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Wait till it's ready for reading.
+ */
+ if (cMillies == 0)
+ return VERR_TIMEOUT;
+
+#ifndef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ for (;;)
+ {
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ int64_t tsDelta = RTTimeNanoTS() - tsStart;
+ if (tsDelta >= 1000000)
+ {
+ tsDelta /= 1000000;
+ if ((uint64_t)tsDelta < cMilliesInitial)
+ cMilliesInitial = (RTMSINTERVAL)tsDelta;
+ else
+ cMilliesInitial = 1;
+ }
+ }
+#ifdef RTSEMRW_STRICT
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->ValidatorRead, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_READ, false);
+ if (RT_FAILURE(rc))
+ break;
+#else
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_READ, false);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ int rcWait;
+ if (fInterruptible)
+ rcWait = rc = RTSemEventMultiWaitNoResume(pThis->ReadEvent, cMillies);
+ else
+ rcWait = rc = RTSemEventMultiWait(pThis->ReadEvent, cMillies);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_READ);
+ if (RT_FAILURE(rc) && rc != VERR_TIMEOUT) /* handle timeout below */
+ {
+ AssertMsgRC(rc, ("RTSemEventMultiWait failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ break;
+ }
+
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ {
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+
+ /*
+ * Re-take critsect and repeat the check we did before the loop.
+ */
+ rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ break;
+ }
+
+ if ( pThis->hWriter == NIL_RTNATIVETHREAD
+#if 0
+ && ( !pThis->cWritesWaiting
+ || pThis->cReads)
+#endif
+ )
+ {
+ pThis->cReads++;
+ Assert(pThis->cReads > 0);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Quit if the wait already timed out.
+ */
+ if (rcWait == VERR_TIMEOUT)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+
+ /* failed */
+ return rc;
+}
+
+
+RTDECL(int) RTSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, false, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, false, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestRead);
+
+
+RTDECL(int) RTSemRWRequestReadDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, false, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadDebug);
+
+
+RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, true, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, true, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadNoResume);
+
+
+RTDECL(int) RTSemRWRequestReadNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, true, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadNoResumeDebug);
+
+
+RTDECL(int) RTSemRWReleaseRead(RTSEMRW hRWSem)
+{
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+
+ /*
+ * Validate handle.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Take critsect.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hWriter == NIL_RTNATIVETHREAD)
+ {
+#ifdef RTSEMRW_STRICT
+ rc = RTLockValidatorRecSharedCheckAndRelease(&pThis->ValidatorRead, NIL_RTTHREAD);
+ if (RT_SUCCESS(rc))
+#endif
+ {
+ if (RT_LIKELY(pThis->cReads > 0))
+ {
+ pThis->cReads--;
+
+ /* Kick off a writer if appropriate. */
+ if ( pThis->cWritesWaiting > 0
+ && !pThis->cReads)
+ {
+ rc = RTSemEventSignal(pThis->WriteEvent);
+ AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_NOT_OWNER;
+ }
+ }
+ }
+ else
+ {
+ RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
+ if (pThis->hWriter == hNativeSelf)
+ {
+ if (pThis->cWriterReads > 0)
+ {
+#ifdef RTSEMRW_STRICT
+ rc = RTLockValidatorRecExclUnwindMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core);
+ if (RT_SUCCESS(rc))
+#endif
+ {
+ pThis->cWriterReads--;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_NOT_OWNER;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_NOT_OWNER;
+ }
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ else
+ AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemRWReleaseRead);
+
+
+DECL_FORCE_INLINE(int) rtSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies, bool fInterruptible, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ RTMSINTERVAL cMilliesInitial = cMillies;
+ uint64_t tsStart = 0;
+ if (cMillies != RT_INDEFINITE_WAIT && cMillies != 0)
+ tsStart = RTTimeNanoTS();
+
+#ifdef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies)
+ {
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, cMillies);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Take critsect.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ return rc;
+ }
+
+ /*
+ * Check if the state of affairs allows write access.
+ */
+ RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
+ if ( !pThis->cReads
+ && ( ( !pThis->cWrites
+ && ( !pThis->cWritesWaiting /* play fair if we can wait */
+ || !cMillies)
+ )
+ || pThis->hWriter == hNativeSelf
+ )
+ )
+ {
+ /*
+ * Reset the reader event semaphore if necessary.
+ */
+ if (pThis->fNeedResetReadEvent)
+ {
+ pThis->fNeedResetReadEvent = false;
+ rc = RTSemEventMultiReset(pThis->ReadEvent);
+ AssertMsgRC(rc, ("Failed to reset readers, rwsem %p, rc=%Rrc.\n", hRWSem, rc));
+ }
+
+ pThis->cWrites++;
+ pThis->hWriter = hNativeSelf;
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, pThis->cWrites == 1);
+#endif
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Signal writer presence.
+ */
+ if (cMillies != 0)
+ pThis->cWritesWaiting++;
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Wait till it's ready for writing.
+ */
+ if (cMillies == 0)
+ return VERR_TIMEOUT;
+
+#ifndef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ for (;;)
+ {
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ int64_t tsDelta = RTTimeNanoTS() - tsStart;
+ if (tsDelta >= 1000000)
+ {
+ tsDelta /= 1000000;
+ if ((uint64_t)tsDelta < cMilliesInitial)
+ cMilliesInitial = (RTMSINTERVAL)tsDelta;
+ else
+ cMilliesInitial = 1;
+ }
+ }
+
+#ifdef RTSEMRW_STRICT
+ rc = RTLockValidatorRecExclCheckBlocking(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_WRITE, false);
+ if (RT_FAILURE(rc))
+ break;
+#else
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_WRITE, false);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ int rcWait;
+ if (fInterruptible)
+ rcWait = rc = RTSemEventWaitNoResume(pThis->WriteEvent, cMillies);
+ else
+ rcWait = rc = RTSemEventWait(pThis->WriteEvent, cMillies);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_WRITE);
+ if (RT_UNLIKELY(RT_FAILURE_NP(rc) && rc != VERR_TIMEOUT)) /* timeouts are handled below */
+ {
+ AssertMsgRC(rc, ("RTSemEventWait failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ break;
+ }
+
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMRW_MAGIC))
+ {
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+
+ /*
+ * Re-take critsect and repeat the check we did prior to this loop.
+ */
+ rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("RTCritSectEnter failed on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ break;
+ }
+
+ if (!pThis->cReads && (!pThis->cWrites || pThis->hWriter == hNativeSelf))
+ {
+ /*
+ * Reset the reader event semaphore if necessary.
+ */
+ if (pThis->fNeedResetReadEvent)
+ {
+ pThis->fNeedResetReadEvent = false;
+ rc = RTSemEventMultiReset(pThis->ReadEvent);
+ AssertMsgRC(rc, ("Failed to reset readers, rwsem %p, rc=%Rrc.\n", hRWSem, rc));
+ }
+
+ pThis->cWrites++;
+ pThis->hWriter = hNativeSelf;
+ pThis->cWritesWaiting--;
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true);
+#endif
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Quit if the wait already timed out.
+ */
+ if (rcWait == VERR_TIMEOUT)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+
+ /*
+ * Timeout/error case, clean up.
+ */
+ if (pThis->u32Magic == RTSEMRW_MAGIC)
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ /* Adjust this counter, whether we got the critsect or not. */
+ pThis->cWritesWaiting--;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWrite);
+
+
+RTDECL(int) RTSemRWRequestWriteDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteDebug);
+
+
+RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteNoResume);
+
+
+RTDECL(int) RTSemRWRequestWriteNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteNoResumeDebug);
+
+
+RTDECL(int) RTSemRWReleaseWrite(RTSEMRW hRWSem)
+{
+
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Take critsect.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Check if owner.
+ */
+ RTNATIVETHREAD hNativeSelf = pThis->CritSect.NativeThreadOwner;
+ if (pThis->hWriter != hNativeSelf)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ AssertMsgFailed(("Not read-write owner of rwsem %p.\n", hRWSem));
+ return VERR_NOT_OWNER;
+ }
+
+#ifdef RTSEMRW_STRICT
+ if (pThis->cWrites > 1 || !pThis->cWriterReads) /* don't check+release if VERR_WRONG_ORDER */
+ {
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorWrite, pThis->cWrites == 1);
+ if (RT_FAILURE(rc9))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ return rc9;
+ }
+ }
+#endif
+
+ /*
+ * Release ownership and remove ourselves from the writers count.
+ */
+ Assert(pThis->cWrites > 0);
+ pThis->cWrites--;
+ if (!pThis->cWrites)
+ {
+ if (RT_UNLIKELY(pThis->cWriterReads > 0))
+ {
+ pThis->cWrites++;
+ RTCritSectLeave(&pThis->CritSect);
+ AssertMsgFailed(("All recursive read locks need to be released prior to the final write lock! (%p)n\n", pThis));
+ return VERR_WRONG_ORDER;
+ }
+
+ pThis->hWriter = NIL_RTNATIVETHREAD;
+ }
+
+ /*
+ * Release the readers if no more writers waiting, otherwise the writers.
+ */
+ if (!pThis->cWritesWaiting)
+ {
+ rc = RTSemEventMultiSignal(pThis->ReadEvent);
+ AssertMsgRC(rc, ("RTSemEventMultiSignal failed for rwsem %p, rc=%Rrc.\n", hRWSem, rc));
+ pThis->fNeedResetReadEvent = true;
+ }
+ else
+ {
+ rc = RTSemEventSignal(pThis->WriteEvent);
+ AssertMsgRC(rc, ("Failed to signal writers on rwsem %p, rc=%Rrc\n", hRWSem, rc));
+ }
+ RTCritSectLeave(&pThis->CritSect);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemRWReleaseWrite);
+
+
+RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW hRWSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, false);
+
+ /*
+ * Check ownership.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hWriter;
+ ASMAtomicUoReadHandle(&pThis->hWriter, &hWriter);
+ return hWriter == hNativeSelf;
+}
+RT_EXPORT_SYMBOL(RTSemRWIsWriteOwner);
+
+
+RTDECL(bool) RTSemRWIsReadOwner(RTSEMRW hRWSem, bool fWannaHear)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, false);
+
+ /*
+ * Check write ownership. The writer is also a valid reader.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hWriter;
+ ASMAtomicUoReadHandle(&pThis->hWriter, &hWriter);
+ if (hWriter == hNativeSelf)
+ return true;
+ if (hWriter != NIL_RTNATIVETHREAD)
+ return false;
+
+#ifdef RTSEMRW_STRICT
+ /*
+ * Ask the lock validator.
+ */
+ NOREF(fWannaHear);
+ return RTLockValidatorRecSharedIsOwner(&pThis->ValidatorRead, NIL_RTTHREAD);
+#else
+ /*
+ * If there are no reads we cannot be one of them... But if there are we
+ * cannot know and can only return what the caller want to hear.
+ */
+ if (pThis->cReads == 0)
+ return false;
+ return fWannaHear;
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWIsReadOwner);
+
+
+RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW hRWSem)
+{
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+
+ /*
+ * Validate handle.
+ */
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWrites;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetWriteRecursion);
+
+
+RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW hRWSem)
+{
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+
+ /*
+ * Validate handle.
+ */
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriterReads;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetWriterReadRecursion);
+
+
+RTDECL(uint32_t) RTSemRWGetReadCount(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cReads;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetReadCount);
+
diff --git a/src/VBox/Runtime/generic/semrw-lockless-generic.cpp b/src/VBox/Runtime/generic/semrw-lockless-generic.cpp
new file mode 100644
index 00000000..b956ac09
--- /dev/null
+++ b/src/VBox/Runtime/generic/semrw-lockless-generic.cpp
@@ -0,0 +1,981 @@
+/* $Id: semrw-lockless-generic.cpp $ */
+/** @file
+ * IPRT - Read-Write Semaphore, Generic, lockless variant.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define RTSEMRW_WITHOUT_REMAPPING
+#define RTASSERT_QUIET
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTSEMRWINTERNAL
+{
+ /** Magic value (RTSEMRW_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Indicates whether hEvtRead needs resetting. */
+ bool volatile fNeedReset;
+
+ /** The state variable.
+ * All accesses are atomic and it bits are defined like this:
+ * Bits 0..14 - cReads.
+ * Bit 15 - Unused.
+ * Bits 16..31 - cWrites. - doesn't make sense here
+ * Bit 31 - fDirection; 0=Read, 1=Write.
+ * Bits 32..46 - cWaitingReads
+ * Bit 47 - Unused.
+ * Bits 48..62 - cWaitingWrites
+ * Bit 63 - Unused.
+ */
+ uint64_t volatile u64State;
+ /** The write owner. */
+ RTNATIVETHREAD volatile hNativeWriter;
+ /** The number of reads made by the current writer. */
+ uint32_t volatile cWriterReads;
+ /** The number of recursions made by the current writer. (The initial grabbing
+ * of the lock counts as the first one.) */
+ uint32_t volatile cWriteRecursions;
+
+ /** What the writer threads are blocking on. */
+ RTSEMEVENT hEvtWrite;
+ /** What the read threads are blocking on when waiting for the writer to
+ * finish. */
+ RTSEMEVENTMULTI hEvtRead;
+
+#ifdef RTSEMRW_STRICT
+ /** The validator record for the writer. */
+ RTLOCKVALRECEXCL ValidatorWrite;
+ /** The validator record for the readers. */
+ RTLOCKVALRECSHRD ValidatorRead;
+#endif
+} RTSEMRWINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define RTSEMRW_CNT_BITS 15
+#define RTSEMRW_CNT_MASK UINT64_C(0x00007fff)
+
+#define RTSEMRW_CNT_RD_SHIFT 0
+#define RTSEMRW_CNT_RD_MASK (RTSEMRW_CNT_MASK << RTSEMRW_CNT_RD_SHIFT)
+#define RTSEMRW_CNT_WR_SHIFT 16
+#define RTSEMRW_CNT_WR_MASK (RTSEMRW_CNT_MASK << RTSEMRW_CNT_WR_SHIFT)
+#define RTSEMRW_DIR_SHIFT 31
+#define RTSEMRW_DIR_MASK RT_BIT_64(RTSEMRW_DIR_SHIFT)
+#define RTSEMRW_DIR_READ UINT64_C(0)
+#define RTSEMRW_DIR_WRITE UINT64_C(1)
+
+#define RTSEMRW_WAIT_CNT_RD_SHIFT 32
+#define RTSEMRW_WAIT_CNT_RD_MASK (RTSEMRW_CNT_MASK << RTSEMRW_WAIT_CNT_RD_SHIFT)
+//#define RTSEMRW_WAIT_CNT_WR_SHIFT 48
+//#define RTSEMRW_WAIT_CNT_WR_MASK (RTSEMRW_CNT_MASK << RTSEMRW_WAIT_CNT_WR_SHIFT)
+
+
+RTDECL(int) RTSemRWCreate(PRTSEMRW phRWSem)
+{
+ return RTSemRWCreateEx(phRWSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemRW");
+}
+RT_EXPORT_SYMBOL(RTSemRWCreate);
+
+
+RTDECL(int) RTSemRWCreateEx(PRTSEMRW phRWSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMRW_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ RTSEMRWINTERNAL *pThis = (RTSEMRWINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ int rc = RTSemEventMultiCreate(&pThis->hEvtRead);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pThis->hEvtWrite);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTSEMRW_MAGIC;
+ pThis->u32Padding = 0;
+ pThis->u64State = 0;
+ pThis->hNativeWriter = NIL_RTNATIVETHREAD;
+ pThis->cWriterReads = 0;
+ pThis->cWriteRecursions = 0;
+ pThis->fNeedReset = false;
+#ifdef RTSEMRW_STRICT
+ bool const fLVEnabled = !(fFlags & RTSEMRW_FLAGS_NO_LOCK_VAL);
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemRWAnon = 0;
+ uint32_t i = ASMAtomicIncU32(&s_iSemRWAnon) - 1;
+ RTLockValidatorRecExclInit(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, "RTSemRW-%u", i);
+ RTLockValidatorRecSharedInit(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, "RTSemRW-%u", i);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ }
+ RTLockValidatorRecMakeSiblings(&pThis->ValidatorWrite.Core, &pThis->ValidatorRead.Core);
+#endif
+
+ *phRWSem = pThis;
+ return VINF_SUCCESS;
+ }
+ RTSemEventMultiDestroy(pThis->hEvtRead);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemRWCreateEx);
+
+
+RTDECL(int) RTSemRWDestroy(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMRWINTERNAL *pThis = hRWSem;
+ if (pThis == NIL_RTSEMRW)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+ Assert(!(ASMAtomicReadU64(&pThis->u64State) & (RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK)));
+
+ /*
+ * Invalidate the object and free up the resources.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMRW_MAGIC, RTSEMRW_MAGIC), VERR_INVALID_HANDLE);
+
+ RTSEMEVENTMULTI hEvtRead;
+ ASMAtomicXchgHandle(&pThis->hEvtRead, NIL_RTSEMEVENTMULTI, &hEvtRead);
+ int rc = RTSemEventMultiDestroy(hEvtRead);
+ AssertRC(rc);
+
+ RTSEMEVENT hEvtWrite;
+ ASMAtomicXchgHandle(&pThis->hEvtWrite, NIL_RTSEMEVENT, &hEvtWrite);
+ rc = RTSemEventDestroy(hEvtWrite);
+ AssertRC(rc);
+
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->ValidatorRead);
+ RTLockValidatorRecExclDelete(&pThis->ValidatorWrite);
+#endif
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemRWDestroy);
+
+
+RTDECL(uint32_t) RTSemRWSetSubClass(RTSEMRW hRWSem, uint32_t uSubClass)
+{
+#ifdef RTSEMRW_STRICT
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ RTLockValidatorRecSharedSetSubClass(&pThis->ValidatorRead, uSubClass);
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorWrite, uSubClass);
+#else
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWSetSubClass);
+
+
+static int rtSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies, bool fInterruptible, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMRWINTERNAL *pThis = hRWSem;
+ if (pThis == NIL_RTSEMRW)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (cMillies > 0)
+ {
+ int rc9;
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ if (hNativeWriter != NIL_RTTHREAD && hNativeWriter == RTThreadNativeSelf())
+ rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, cMillies);
+ else
+ rc9 = RTLockValidatorRecSharedCheckOrder(&pThis->ValidatorRead, hThreadSelf, pSrcPos, cMillies);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Get cracking...
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ uint64_t u64OldState = u64State;
+
+ for (;;)
+ {
+ if ((u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT))
+ {
+ /* It flows in the right direction, try follow it before it changes. */
+ uint64_t c = (u64State & RTSEMRW_CNT_RD_MASK) >> RTSEMRW_CNT_RD_SHIFT;
+ c++;
+ Assert(c < RTSEMRW_CNT_MASK / 2);
+ u64State &= ~RTSEMRW_CNT_RD_MASK;
+ u64State |= c << RTSEMRW_CNT_RD_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+ else if ((u64State & (RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK)) == 0)
+ {
+ /* Wrong direction, but we're alone here and can simply try switch the direction. */
+ u64State &= ~(RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK | RTSEMRW_DIR_MASK);
+ u64State |= (UINT64_C(1) << RTSEMRW_CNT_RD_SHIFT) | (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ Assert(!pThis->fNeedReset);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+ else
+ {
+ /* Is the writer perhaps doing a read recursion? */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ if (hNativeSelf == hNativeWriter)
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursionMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWriterReads < UINT32_MAX / 2);
+ ASMAtomicIncU32(&pThis->cWriterReads);
+ return VINF_SUCCESS; /* don't break! */
+ }
+
+ /* If the timeout is 0, return already. */
+ if (!cMillies)
+ return VERR_TIMEOUT;
+
+ /* Add ourselves to the queue and wait for the direction to change. */
+ uint64_t c = (u64State & RTSEMRW_CNT_RD_MASK) >> RTSEMRW_CNT_RD_SHIFT;
+ c++;
+ Assert(c < RTSEMRW_CNT_MASK / 2);
+
+ uint64_t cWait = (u64State & RTSEMRW_WAIT_CNT_RD_MASK) >> RTSEMRW_WAIT_CNT_RD_SHIFT;
+ cWait++;
+ Assert(cWait <= c);
+ Assert(cWait < RTSEMRW_CNT_MASK / 2);
+
+ u64State &= ~(RTSEMRW_CNT_RD_MASK | RTSEMRW_WAIT_CNT_RD_MASK);
+ u64State |= (c << RTSEMRW_CNT_RD_SHIFT) | (cWait << RTSEMRW_WAIT_CNT_RD_SHIFT);
+
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ for (uint32_t iLoop = 0; ; iLoop++)
+ {
+ int rc;
+#ifdef RTSEMRW_STRICT
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->ValidatorRead, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_READ, false);
+ if (RT_SUCCESS(rc))
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_READ, false);
+#endif
+ {
+ if (fInterruptible)
+ rc = RTSemEventMultiWaitNoResume(pThis->hEvtRead, cMillies);
+ else
+ rc = RTSemEventMultiWait(pThis->hEvtRead, cMillies);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_READ);
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* Decrement the counts and return the error. */
+ for (;;)
+ {
+ u64OldState = u64State = ASMAtomicReadU64(&pThis->u64State);
+ c = (u64State & RTSEMRW_CNT_RD_MASK) >> RTSEMRW_CNT_RD_SHIFT; Assert(c > 0);
+ c--;
+ cWait = (u64State & RTSEMRW_WAIT_CNT_RD_MASK) >> RTSEMRW_WAIT_CNT_RD_SHIFT; Assert(cWait > 0);
+ cWait--;
+ u64State &= ~(RTSEMRW_CNT_RD_MASK | RTSEMRW_WAIT_CNT_RD_MASK);
+ u64State |= (c << RTSEMRW_CNT_RD_SHIFT) | (cWait << RTSEMRW_WAIT_CNT_RD_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+ return rc;
+ }
+
+ Assert(pThis->fNeedReset);
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ if ((u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT))
+ break;
+ AssertMsg(iLoop < 1, ("%u\n", iLoop));
+ }
+
+ /* Decrement the wait count and maybe reset the semaphore (if we're last). */
+ for (;;)
+ {
+ u64OldState = u64State;
+
+ cWait = (u64State & RTSEMRW_WAIT_CNT_RD_MASK) >> RTSEMRW_WAIT_CNT_RD_SHIFT;
+ Assert(cWait > 0);
+ cWait--;
+ u64State &= ~RTSEMRW_WAIT_CNT_RD_MASK;
+ u64State |= cWait << RTSEMRW_WAIT_CNT_RD_SHIFT;
+
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ if (cWait == 0)
+ {
+ if (ASMAtomicXchgBool(&pThis->fNeedReset, false))
+ {
+ int rc = RTSemEventMultiReset(pThis->hEvtRead);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ break;
+ }
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ }
+
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ break;
+ }
+ }
+
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+ }
+
+ /* got it! */
+ Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT));
+ return VINF_SUCCESS;
+
+}
+
+
+RTDECL(int) RTSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, false, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, false, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestRead);
+
+
+RTDECL(int) RTSemRWRequestReadDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, false, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadDebug);
+
+
+RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, true, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, true, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadNoResume);
+
+
+RTDECL(int) RTSemRWRequestReadNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, true, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestReadNoResumeDebug);
+
+
+
+RTDECL(int) RTSemRWReleaseRead(RTSEMRW hRWSem)
+{
+ /*
+ * Validate handle.
+ */
+ RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check the direction and take action accordingly.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ uint64_t u64OldState = u64State;
+ if ((u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT))
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecSharedCheckAndRelease(&pThis->ValidatorRead, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ for (;;)
+ {
+ uint64_t c = (u64State & RTSEMRW_CNT_RD_MASK) >> RTSEMRW_CNT_RD_SHIFT;
+ AssertReturn(c > 0, VERR_NOT_OWNER);
+ c--;
+
+ if ( c > 0
+ || (u64State & RTSEMRW_CNT_WD_MASK) == 0)
+ {
+ /* Don't change the direction. */
+ u64State &= ~RTSEMRW_CNT_RD_MASK;
+ u64State |= c << RTSEMRW_CNT_RD_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+ else
+ {
+ /* Reverse the direction and signal the reader threads. */
+ u64State &= ~(RTSEMRW_CNT_RD_MASK | RTSEMRW_DIR_MASK);
+ u64State |= RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ int rc = RTSemEventSignal(pThis->hEvtWrite);
+ AssertRC(rc);
+ break;
+ }
+ }
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+ }
+ }
+ else
+ {
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ AssertReturn(hNativeSelf == hNativeWriter, VERR_NOT_OWNER);
+ AssertReturn(pThis->cWriterReads > 0, VERR_NOT_OWNER);
+#ifdef RTSEMRW_STRICT
+ int rc = RTLockValidatorRecExclUnwindMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+ ASMAtomicDecU32(&pThis->cWriterReads);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemRWReleaseRead);
+
+
+DECL_FORCE_INLINE(int) rtSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies, bool fInterruptible, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMRWINTERNAL *pThis = hRWSem;
+ if (pThis == NIL_RTSEMRW)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMRW_STRICT
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies)
+ {
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, cMillies);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Check if we're already the owner and just recursing.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ if (hNativeSelf == hNativeWriter)
+ {
+ Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT));
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorWrite, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWriteRecursions < UINT32_MAX / 2);
+ ASMAtomicIncU32(&pThis->cWriteRecursions);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Get cracking.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ uint64_t u64OldState = u64State;
+
+ for (;;)
+ {
+ if ( (u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT)
+ || (u64State & (RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK)) != 0)
+ {
+ /* It flows in the right direction, try follow it before it changes. */
+ uint64_t c = (u64State & RTSEMRW_CNT_WR_MASK) >> RTSEMRW_CNT_WR_SHIFT;
+ c++;
+ Assert(c < RTSEMRW_CNT_MASK / 2);
+ u64State &= ~RTSEMRW_CNT_WR_MASK;
+ u64State |= c << RTSEMRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+ else if ((u64State & (RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK)) == 0)
+ {
+ /* Wrong direction, but we're alone here and can simply try switch the direction. */
+ u64State &= ~(RTSEMRW_CNT_RD_MASK | RTSEMRW_CNT_WR_MASK | RTSEMRW_DIR_MASK);
+ u64State |= (UINT64_C(1) << RTSEMRW_CNT_WR_SHIFT) | (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+ else if (!cMillies)
+ /* Wrong direction and we're not supposed to wait, just return. */
+ return VERR_TIMEOUT;
+ else
+ {
+ /* Add ourselves to the write count and break out to do the wait. */
+ uint64_t c = (u64State & RTSEMRW_CNT_WR_MASK) >> RTSEMRW_CNT_WR_SHIFT;
+ c++;
+ Assert(c < RTSEMRW_CNT_MASK / 2);
+ u64State &= ~RTSEMRW_CNT_WR_MASK;
+ u64State |= c << RTSEMRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+ }
+
+ /*
+ * If we're in write mode now try grab the ownership. Play fair if there
+ * are threads already waiting.
+ */
+ bool fDone = (u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT)
+ && ( ((u64State & RTSEMRW_CNT_WR_MASK) >> RTSEMRW_CNT_WR_SHIFT) == 1
+ || cMillies == 0);
+ if (fDone)
+ ASMAtomicCmpXchgHandle(&pThis->hNativeWriter, hNativeSelf, NIL_RTNATIVETHREAD, fDone);
+ if (!fDone)
+ {
+ /*
+ * Wait for our turn.
+ */
+ for (uint32_t iLoop = 0; ; iLoop++)
+ {
+ int rc;
+#ifdef RTSEMRW_STRICT
+ if (cMillies)
+ {
+ if (hThreadSelf == NIL_RTTHREAD)
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ rc = RTLockValidatorRecExclCheckBlocking(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_WRITE, false);
+ }
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_WRITE, false);
+#endif
+ {
+ if (fInterruptible)
+ rc = RTSemEventWaitNoResume(pThis->hEvtWrite, cMillies);
+ else
+ rc = RTSemEventWait(pThis->hEvtWrite, cMillies);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_WRITE);
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+ if (RT_FAILURE(rc))
+ {
+ /* Decrement the counts and return the error. */
+ for (;;)
+ {
+ u64OldState = u64State = ASMAtomicReadU64(&pThis->u64State);
+ uint64_t c = (u64State & RTSEMRW_CNT_WR_MASK) >> RTSEMRW_CNT_WR_SHIFT; Assert(c > 0);
+ c--;
+ u64State &= ~RTSEMRW_CNT_WR_MASK;
+ u64State |= c << RTSEMRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ break;
+ }
+ return rc;
+ }
+
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ if ((u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT))
+ {
+ ASMAtomicCmpXchgHandle(&pThis->hNativeWriter, hNativeSelf, NIL_RTNATIVETHREAD, fDone);
+ if (fDone)
+ break;
+ }
+ AssertMsg(iLoop < 1000, ("%u\n", iLoop)); /* may loop a few times here... */
+ }
+ }
+
+ /*
+ * Got it!
+ */
+ Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT));
+ ASMAtomicWriteU32(&pThis->cWriteRecursions, 1);
+ Assert(pThis->cWriterReads == 0);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWrite);
+
+
+RTDECL(int) RTSemRWRequestWriteDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, false, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteDebug);
+
+
+RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteNoResume);
+
+
+RTDECL(int) RTSemRWRequestWriteNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, true, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemRWRequestWriteNoResumeDebug);
+
+
+RTDECL(int) RTSemRWReleaseWrite(RTSEMRW hRWSem)
+{
+
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, VERR_INVALID_HANDLE);
+
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ AssertReturn(hNativeSelf == hNativeWriter, VERR_NOT_OWNER);
+
+ /*
+ * Unwind a recursion.
+ */
+ if (pThis->cWriteRecursions == 1)
+ {
+ AssertReturn(pThis->cWriterReads == 0, VERR_WRONG_ORDER); /* (must release all read recursions before the final write.) */
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorWrite, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ /*
+ * Update the state.
+ */
+ ASMAtomicWriteU32(&pThis->cWriteRecursions, 0);
+ ASMAtomicWriteHandle(&pThis->hNativeWriter, NIL_RTNATIVETHREAD);
+
+ for (;;)
+ {
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ uint64_t u64OldState = u64State;
+
+ uint64_t c = (u64State & RTSEMRW_CNT_WR_MASK) >> RTSEMRW_CNT_WR_SHIFT;
+ Assert(c > 0);
+ c--;
+
+ if ( c > 0
+ || (u64State & RTSEMRW_CNT_RD_MASK) == 0)
+ {
+ /* Don't change the direction, wait up the next writer if any. */
+ u64State &= ~RTSEMRW_CNT_WR_MASK;
+ u64State |= c << RTSEMRW_CNT_WR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ if (c > 0)
+ {
+ int rc = RTSemEventSignal(pThis->hEvtWrite);
+ AssertRC(rc);
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* Reverse the direction and signal the reader threads. */
+ u64State &= ~(RTSEMRW_CNT_WR_MASK | RTSEMRW_DIR_MASK);
+ u64State |= RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ Assert(!pThis->fNeedReset);
+ ASMAtomicWriteBool(&pThis->fNeedReset, true);
+ int rc = RTSemEventMultiSignal(pThis->hEvtRead);
+ AssertRC(rc);
+ break;
+ }
+ }
+
+ ASMNopPause();
+ if (pThis->u32Magic != RTSEMRW_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+ }
+ else
+ {
+ Assert(pThis->cWriteRecursions != 0);
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclUnwind(&pThis->ValidatorWrite);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicDecU32(&pThis->cWriteRecursions);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemRWReleaseWrite);
+
+
+RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW hRWSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, false);
+
+ /*
+ * Check ownership.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hNativeWriter);
+ return hNativeWriter == hNativeSelf;
+}
+RT_EXPORT_SYMBOL(RTSemRWIsWriteOwner);
+
+
+RTDECL(bool) RTSemRWIsReadOwner(RTSEMRW hRWSem, bool fWannaHear)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, false);
+
+ /*
+ * Inspect the state.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ if ((u64State & RTSEMRW_DIR_MASK) == (RTSEMRW_DIR_WRITE << RTSEMRW_DIR_SHIFT))
+ {
+ /*
+ * It's in write mode, so we can only be a reader if we're also the
+ * current writer.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hWriter;
+ ASMAtomicUoReadHandle(&pThis->hNativeWriter, &hWriter);
+ return hWriter == hNativeSelf;
+ }
+
+ /*
+ * Read mode. If there are no current readers, then we cannot be a reader.
+ */
+ if (!(u64State & RTSEMRW_CNT_RD_MASK))
+ return false;
+
+#ifdef RTSEMRW_STRICT
+ /*
+ * Ask the lock validator.
+ */
+ return RTLockValidatorRecSharedIsOwner(&pThis->ValidatorRead, NIL_RTTHREAD);
+#else
+ /*
+ * Ok, we don't know, just tell the caller what he want to hear.
+ */
+ return fWannaHear;
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWIsReadOwner);
+
+
+RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW hRWSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriteRecursions;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetWriteRecursion);
+
+
+RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW hRWSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriterReads;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetWriterReadRecursion);
+
+
+RTDECL(uint32_t) RTSemRWGetReadCount(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ 0);
+
+ /*
+ * Return the requested data.
+ */
+ uint64_t u64State = ASMAtomicReadU64(&pThis->u64State);
+ if ((u64State & RTSEMRW_DIR_MASK) != (RTSEMRW_DIR_READ << RTSEMRW_DIR_SHIFT))
+ return 0;
+ return (u64State & RTSEMRW_CNT_RD_MASK) >> RTSEMRW_CNT_RD_SHIFT;
+}
+RT_EXPORT_SYMBOL(RTSemRWGetReadCount);
+
diff --git a/src/VBox/Runtime/generic/semxroads-generic.cpp b/src/VBox/Runtime/generic/semxroads-generic.cpp
new file mode 100644
index 00000000..e0e4c977
--- /dev/null
+++ b/src/VBox/Runtime/generic/semxroads-generic.cpp
@@ -0,0 +1,451 @@
+/* $Id: semxroads-generic.cpp $ */
+/** @file
+ * IPRT Testcase - RTSemXRoads, generic implementation.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define RTASSERT_QUIET
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTSEMXROADSINTERNAL
+{
+ /** Magic value (RTSEMXROADS_MAGIC). */
+ uint32_t volatile u32Magic;
+ uint32_t u32Padding; /**< alignment padding.*/
+ /* The state variable.
+ * All accesses are atomic and it bits are defined like this:
+ * Bits 0..14 - cNorthSouth.
+ * Bit 15 - Unused.
+ * Bits 16..31 - cEastWest.
+ * Bit 31 - fDirection; 0=NS, 1=EW.
+ * Bits 32..46 - cWaitingNS
+ * Bit 47 - Unused.
+ * Bits 48..62 - cWaitingEW
+ * Bit 63 - Unused.
+ */
+ uint64_t volatile u64State;
+ /** Per-direction data. */
+ struct
+ {
+ /** What the north/south bound threads are blocking on when waiting for
+ * east/west traffic to stop. */
+ RTSEMEVENTMULTI hEvt;
+ /** Indicates whether the semaphore needs resetting. */
+ bool volatile fNeedReset;
+ } aDirs[2];
+} RTSEMXROADSINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define RTSEMXROADS_CNT_BITS 15
+#define RTSEMXROADS_CNT_MASK UINT64_C(0x00007fff)
+
+#define RTSEMXROADS_CNT_NS_SHIFT 0
+#define RTSEMXROADS_CNT_NS_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_CNT_NS_SHIFT)
+#define RTSEMXROADS_CNT_EW_SHIFT 16
+#define RTSEMXROADS_CNT_EW_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_CNT_EW_SHIFT)
+#define RTSEMXROADS_DIR_SHIFT 31
+#define RTSEMXROADS_DIR_MASK RT_BIT_64(RTSEMXROADS_DIR_SHIFT)
+
+#define RTSEMXROADS_WAIT_CNT_NS_SHIFT 32
+#define RTSEMXROADS_WAIT_CNT_NS_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_WAIT_CNT_NS_SHIFT)
+#define RTSEMXROADS_WAIT_CNT_EW_SHIFT 48
+#define RTSEMXROADS_WAIT_CNT_EW_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_WAIT_CNT_EW_SHIFT)
+
+
+#if 0 /* debugging aid */
+static uint32_t volatile g_iHist = 0;
+static struct
+{
+ void *tsc;
+ RTTHREAD hThread;
+ uint32_t line;
+ bool fDir;
+ void *u64State;
+ void *u64OldState;
+ bool fNeedResetNS;
+ bool fNeedResetEW;
+ const char *psz;
+} g_aHist[256];
+
+# define add_hist(ns, os, dir, what) \
+ do \
+ { \
+ uint32_t i = (ASMAtomicIncU32(&g_iHist) - 1) % RT_ELEMENTS(g_aHist);\
+ g_aHist[i].line = __LINE__; \
+ g_aHist[i].u64OldState = (void *)(os); \
+ g_aHist[i].u64State = (void *)(ns); \
+ g_aHist[i].fDir = (dir); \
+ g_aHist[i].psz = (what); \
+ g_aHist[i].fNeedResetNS = pThis->aDirs[0].fNeedReset; \
+ g_aHist[i].fNeedResetEW = pThis->aDirs[1].fNeedReset; \
+ g_aHist[i].hThread = RTThreadSelf(); \
+ g_aHist[i].tsc = (void *)ASMReadTSC(); \
+ } while (0)
+
+# undef DECL_FORCE_INLINE
+# define DECL_FORCE_INLINE(type) static type
+#else
+# define add_hist(ns, os, dir, what) do { } while (0)
+#endif
+
+
+RTDECL(int) RTSemXRoadsCreate(PRTSEMXROADS phXRoads)
+{
+ RTSEMXROADSINTERNAL *pThis = (RTSEMXROADSINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ int rc = RTSemEventMultiCreate(&pThis->aDirs[0].hEvt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventMultiCreate(&pThis->aDirs[1].hEvt);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTSEMXROADS_MAGIC;
+ pThis->u32Padding = 0;
+ pThis->u64State = 0;
+ pThis->aDirs[0].fNeedReset = false;
+ pThis->aDirs[1].fNeedReset = false;
+ *phXRoads = pThis;
+ return VINF_SUCCESS;
+ }
+ RTSemEventMultiDestroy(pThis->aDirs[0].hEvt);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSemXRoadsDestroy(RTSEMXROADS hXRoads)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMXROADSINTERNAL *pThis = hXRoads;
+ if (pThis == NIL_RTSEMXROADS)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
+ Assert(!(ASMAtomicReadU64(&pThis->u64State) & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)));
+
+ /*
+ * Invalidate the object and free up the resources.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMXROADS_MAGIC_DEAD, RTSEMXROADS_MAGIC), VERR_INVALID_HANDLE);
+
+ RTSEMEVENTMULTI hEvt;
+ ASMAtomicXchgHandle(&pThis->aDirs[0].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
+ int rc = RTSemEventMultiDestroy(hEvt);
+ AssertRC(rc);
+
+ ASMAtomicXchgHandle(&pThis->aDirs[1].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
+ rc = RTSemEventMultiDestroy(hEvt);
+ AssertRC(rc);
+
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for RTSemXRoadsNSEnter and RTSemXRoadsEWEnter.
+ *
+ * @returns IPRT status code.
+ * @param pThis The semaphore instance.
+ * @param fDir The direction.
+ * @param uCountShift The shift count for getting the count.
+ * @param fCountMask The mask for getting the count.
+ * @param uWaitCountShift The shift count for getting the wait count.
+ * @param fWaitCountMask The mask for getting the wait count.
+ */
+DECL_FORCE_INLINE(int) rtSemXRoadsEnter(RTSEMXROADSINTERNAL *pThis, uint64_t fDir,
+ uint64_t uCountShift, uint64_t fCountMask,
+ uint64_t uWaitCountShift, uint64_t fWaitCountMask)
+{
+ uint64_t u64OldState;
+ uint64_t u64State;
+
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+ add_hist(u64State, u64OldState, fDir, "enter");
+
+ for (;;)
+ {
+ if ((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT))
+ {
+ /* It flows in the right direction, try follow it before it changes. */
+ uint64_t c = (u64State & fCountMask) >> uCountShift;
+ c++;
+ Assert(c < 8*_1K);
+ u64State &= ~fCountMask;
+ u64State |= c << uCountShift;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ add_hist(u64State, u64OldState, fDir, "enter-simple");
+ break;
+ }
+ }
+ else if ((u64State & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)) == 0)
+ {
+ /* Wrong direction, but we're alone here and can simply try switch the direction. */
+ u64State &= ~(RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK | RTSEMXROADS_DIR_MASK);
+ u64State |= (UINT64_C(1) << uCountShift) | (fDir << RTSEMXROADS_DIR_SHIFT);
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ Assert(!pThis->aDirs[fDir].fNeedReset);
+ add_hist(u64State, u64OldState, fDir, "enter-switch");
+ break;
+ }
+ }
+ else
+ {
+ /* Add ourselves to the queue and wait for the direction to change. */
+ uint64_t c = (u64State & fCountMask) >> uCountShift;
+ c++;
+ Assert(c < RTSEMXROADS_CNT_MASK / 2);
+
+ uint64_t cWait = (u64State & fWaitCountMask) >> uWaitCountShift;
+ cWait++;
+ Assert(cWait <= c);
+ Assert(cWait < RTSEMXROADS_CNT_MASK / 2);
+
+ u64State &= ~(fCountMask | fWaitCountMask);
+ u64State |= (c << uCountShift) | (cWait << uWaitCountShift);
+
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ add_hist(u64State, u64OldState, fDir, "enter-wait");
+ for (uint32_t iLoop = 0; ; iLoop++)
+ {
+ int rc = RTSemEventMultiWait(pThis->aDirs[fDir].hEvt, RT_INDEFINITE_WAIT);
+ AssertRCReturn(rc, rc);
+
+ if (pThis->u32Magic != RTSEMXROADS_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ Assert(pThis->aDirs[fDir].fNeedReset);
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ add_hist(u64State, u64OldState, fDir, "enter-wakeup");
+ if ((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT))
+ break;
+ AssertMsg(iLoop < 1, ("%u\n", iLoop));
+ }
+
+ /* Decrement the wait count and maybe reset the semaphore (if we're last). */
+ for (;;)
+ {
+ u64OldState = u64State;
+
+ cWait = (u64State & fWaitCountMask) >> uWaitCountShift;
+ Assert(cWait > 0);
+ cWait--;
+ u64State &= ~fWaitCountMask;
+ u64State |= cWait << uWaitCountShift;
+
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ if (cWait == 0)
+ {
+ if (ASMAtomicXchgBool(&pThis->aDirs[fDir].fNeedReset, false))
+ {
+ add_hist(u64State, u64OldState, fDir, fDir ? "enter-reset-EW" : "enter-reset-NS");
+ int rc = RTSemEventMultiReset(pThis->aDirs[fDir].hEvt);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ add_hist(u64State, u64OldState, fDir, "enter-dec-no-need");
+ }
+ break;
+ }
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ }
+ break;
+ }
+
+ add_hist(u64State, u64OldState, fDir, "enter-wait-failed");
+ }
+
+ if (pThis->u32Magic != RTSEMXROADS_MAGIC)
+ return VERR_SEM_DESTROYED;
+
+ ASMNopPause();
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+ }
+
+ /* got it! */
+ Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for RTSemXRoadsNSLeave and RTSemXRoadsEWLeave.
+ *
+ * @returns IPRT status code.
+ * @param pThis The semaphore instance.
+ * @param fDir The direction.
+ * @param uCountShift The shift count for getting the count.
+ * @param fCountMask The mask for getting the count.
+ */
+DECL_FORCE_INLINE(int) rtSemXRoadsLeave(RTSEMXROADSINTERNAL *pThis, uint64_t fDir, uint64_t uCountShift, uint64_t fCountMask)
+{
+ for (;;)
+ {
+ uint64_t u64OldState;
+ uint64_t u64State;
+ uint64_t c;
+
+ u64State = ASMAtomicReadU64(&pThis->u64State);
+ u64OldState = u64State;
+
+ /* The direction cannot change until we've left or we'll crash. */
+ Assert((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
+
+ c = (u64State & fCountMask) >> uCountShift;
+ Assert(c > 0);
+ c--;
+
+ if ( c > 0
+ || (u64State & ((RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK) & ~fCountMask)) == 0)
+ {
+ /* We're not the last one across or there aren't any one waiting in the other direction. */
+ u64State &= ~fCountMask;
+ u64State |= c << uCountShift;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ add_hist(u64State, u64OldState, fDir, "leave-simple");
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ /* Reverse the direction and signal the threads in the other direction. */
+ u64State &= ~(fCountMask | RTSEMXROADS_DIR_MASK);
+ u64State |= (uint64_t)!fDir << RTSEMXROADS_DIR_SHIFT;
+ if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
+ {
+ add_hist(u64State, u64OldState, fDir, fDir ? "leave-signal-NS" : "leave-signal-EW");
+ Assert(!pThis->aDirs[!fDir].fNeedReset);
+ ASMAtomicWriteBool(&pThis->aDirs[!fDir].fNeedReset, true);
+ int rc = RTSemEventMultiSignal(pThis->aDirs[!fDir].hEvt);
+ AssertRC(rc);
+ return VINF_SUCCESS;
+ }
+ }
+
+ ASMNopPause();
+ if (pThis->u32Magic != RTSEMXROADS_MAGIC)
+ return VERR_SEM_DESTROYED;
+ }
+}
+
+
+RTDECL(int) RTSemXRoadsNSEnter(RTSEMXROADS hXRoads)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMXROADSINTERNAL *pThis = hXRoads;
+ if (pThis == NIL_RTSEMXROADS)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSemXRoadsEnter(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK, RTSEMXROADS_WAIT_CNT_NS_SHIFT, RTSEMXROADS_WAIT_CNT_NS_MASK);
+}
+
+
+RTDECL(int) RTSemXRoadsNSLeave(RTSEMXROADS hXRoads)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMXROADSINTERNAL *pThis = hXRoads;
+ if (pThis == NIL_RTSEMXROADS)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSemXRoadsLeave(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK);
+}
+
+
+RTDECL(int) RTSemXRoadsEWEnter(RTSEMXROADS hXRoads)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMXROADSINTERNAL *pThis = hXRoads;
+ if (pThis == NIL_RTSEMXROADS)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSemXRoadsEnter(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK, RTSEMXROADS_WAIT_CNT_EW_SHIFT, RTSEMXROADS_WAIT_CNT_EW_MASK);
+}
+
+
+RTDECL(int) RTSemXRoadsEWLeave(RTSEMXROADS hXRoads)
+{
+ /*
+ * Validate input.
+ */
+ RTSEMXROADSINTERNAL *pThis = hXRoads;
+ if (pThis == NIL_RTSEMXROADS)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSemXRoadsLeave(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK);
+}
+
diff --git a/src/VBox/Runtime/generic/spinlock-generic.cpp b/src/VBox/Runtime/generic/spinlock-generic.cpp
new file mode 100644
index 00000000..dde62b6d
--- /dev/null
+++ b/src/VBox/Runtime/generic/spinlock-generic.cpp
@@ -0,0 +1,237 @@
+/* $Id: spinlock-generic.cpp $ */
+/** @file
+ * IPRT - Spinlock, generic implementation.
+ */
+
+/*
+ * 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
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RT_CFG_SPINLOCK_GENERIC_DO_SLEEP
+ * Force cpu yields after spinning the number of times indicated by the define.
+ * If 0 we will spin forever. */
+#define RT_CFG_SPINLOCK_GENERIC_DO_SLEEP 100000
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/spinlock.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#if RT_CFG_SPINLOCK_GENERIC_DO_SLEEP
+# include <iprt/thread.h>
+#endif
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Generic spinlock structure.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_GEN_MAGIC). */
+ uint32_t u32Magic;
+ /** The spinlock creation flags. */
+ uint32_t fFlags;
+ /** The spinlock. */
+ uint32_t volatile fLocked;
+ /** The saved CPU interrupt. */
+ uint32_t volatile fIntSaved;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ PRTSPINLOCKINTERNAL pThis;
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(pszName);
+
+ /*
+ * Allocate.
+ */
+ pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize and return.
+ */
+ pThis->u32Magic = RTSPINLOCK_GEN_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->fIntSaved = 0;
+ ASMAtomicWriteU32(&pThis->fLocked, 0);
+
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSpinlockCreate);
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ if (pThis->u32Magic != RTSPINLOCK_GEN_MAGIC)
+ {
+ AssertMsgFailed(("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ ASMAtomicIncU32(&pThis->u32Magic);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSpinlockDestroy);
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_GEN_MAGIC,
+ ("pThis=%p u32Magic=%08x\n", pThis, pThis ? (int)pThis->u32Magic : 0));
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t fIntSaved = ASMGetFlags();
+#endif
+
+#if RT_CFG_SPINLOCK_GENERIC_DO_SLEEP
+ for (;;)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMIntDisable();
+#endif
+ for (int c = RT_CFG_SPINLOCK_GENERIC_DO_SLEEP; c > 0; c--)
+ {
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ {
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ pThis->fIntSaved = fIntSaved;
+# endif
+ return;
+ }
+ ASMNopPause();
+ }
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMSetFlags(fIntSaved);
+#endif
+ RTThreadYield();
+ }
+#else
+ for (;;)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMIntDisable();
+#endif
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ {
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ pThis->fIntSaved = fIntSaved;
+# endif
+ return;
+ }
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMSetFlags(fIntSaved);
+#endif
+ ASMNopPause();
+ }
+#endif
+ }
+ else
+ {
+#if RT_CFG_SPINLOCK_GENERIC_DO_SLEEP
+ for (;;)
+ {
+ for (int c = RT_CFG_SPINLOCK_GENERIC_DO_SLEEP; c > 0; c--)
+ {
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ return;
+ ASMNopPause();
+ }
+ RTThreadYield();
+ }
+#else
+ while (!ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ ASMNopPause();
+#endif
+ }
+}
+RT_EXPORT_SYMBOL(RTSpinlockAcquire);
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_GEN_MAGIC,
+ ("pThis=%p u32Magic=%08x\n", pThis, pThis ? (int)pThis->u32Magic : 0));
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+#endif
+
+ if (!ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1))
+ AssertMsgFailed(("Spinlock %p was not locked!\n", pThis));
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMSetFlags(fIntSaved);
+#endif
+ }
+ else
+ {
+ if (!ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1))
+ AssertMsgFailed(("Spinlock %p was not locked!\n", pThis));
+ }
+}
+RT_EXPORT_SYMBOL(RTSpinlockRelease);
+
diff --git a/src/VBox/Runtime/generic/strcache-stubs-generic.cpp b/src/VBox/Runtime/generic/strcache-stubs-generic.cpp
new file mode 100644
index 00000000..3497db50
--- /dev/null
+++ b/src/VBox/Runtime/generic/strcache-stubs-generic.cpp
@@ -0,0 +1,151 @@
+/* $Id: strcache-stubs-generic.cpp $ */
+/** @file
+ * IPRT - String Cache, stub implementation.
+ */
+
+/*
+ * 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/strcache.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mempool.h>
+#include <iprt/string.h>
+
+
+
+RTDECL(int) RTStrCacheCreate(PRTSTRCACHE phStrCache, const char *pszName)
+{
+ AssertCompile(sizeof(RTSTRCACHE) == sizeof(RTMEMPOOL));
+ AssertCompileNS(NIL_RTSTRCACHE == (RTSTRCACHE)NIL_RTMEMPOOL);
+ AssertCompileNS(RTSTRCACHE_DEFAULT == (RTSTRCACHE)RTMEMPOOL_DEFAULT);
+ return RTMemPoolCreate((PRTMEMPOOL)phStrCache, pszName);
+}
+RT_EXPORT_SYMBOL(RTStrCacheCreate);
+
+
+RTDECL(int) RTStrCacheDestroy(RTSTRCACHE hStrCache)
+{
+ if ( hStrCache == NIL_RTSTRCACHE
+ || hStrCache == RTSTRCACHE_DEFAULT)
+ return VINF_SUCCESS;
+ return RTMemPoolDestroy((RTMEMPOOL)hStrCache);
+}
+RT_EXPORT_SYMBOL(RTStrCacheDestroy);
+
+
+RTDECL(const char *) RTStrCacheEnterN(RTSTRCACHE hStrCache, const char *pchString, size_t cchString)
+{
+ AssertPtr(pchString);
+ AssertReturn(cchString < _1G, NULL);
+ Assert(!RTStrEnd(pchString, cchString));
+
+ return (const char *)RTMemPoolDupEx((RTMEMPOOL)hStrCache, pchString, cchString, 1);
+}
+RT_EXPORT_SYMBOL(RTStrCacheEnterN);
+
+
+RTDECL(const char *) RTStrCacheEnter(RTSTRCACHE hStrCache, const char *psz)
+{
+ return RTStrCacheEnterN(hStrCache, psz, strlen(psz));
+}
+RT_EXPORT_SYMBOL(RTStrCacheEnter);
+
+
+RTDECL(const char *) RTStrCacheEnterLowerN(RTSTRCACHE hStrCache, const char *pchString, size_t cchString)
+{
+ AssertPtr(pchString);
+ AssertReturn(cchString < _1G, NULL);
+ Assert(!RTStrEnd(pchString, cchString));
+
+ char *pszRet = (char *)RTMemPoolDupEx((RTMEMPOOL)hStrCache, pchString, cchString, 1);
+ if (pszRet)
+ RTStrToLower(pszRet);
+ return pszRet;
+}
+RT_EXPORT_SYMBOL(RTStrCacheEnterLowerN);
+
+
+RTDECL(const char *) RTStrCacheEnterLower(RTSTRCACHE hStrCache, const char *psz)
+{
+ return RTStrCacheEnterLowerN(hStrCache, psz, strlen(psz));
+}
+RT_EXPORT_SYMBOL(RTStrCacheEnterLower);
+
+
+RTDECL(uint32_t) RTStrCacheRetain(const char *psz)
+{
+ AssertPtr(psz);
+ return RTMemPoolRetain((void *)psz);
+}
+RT_EXPORT_SYMBOL(RTStrCacheRetain);
+
+
+RTDECL(uint32_t) RTStrCacheRelease(RTSTRCACHE hStrCache, const char *psz)
+{
+ if (!psz)
+ return 0;
+ return RTMemPoolRelease((RTMEMPOOL)hStrCache, (void *)psz);
+}
+RT_EXPORT_SYMBOL(RTStrCacheRelease);
+
+
+RTDECL(size_t) RTStrCacheLength(const char *psz)
+{
+ if (!psz)
+ return 0;
+ return strlen(psz);
+}
+RT_EXPORT_SYMBOL(RTStrCacheLength);
+
+
+RTDECL(bool) RTStrCacheIsRealImpl(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTStrCacheIsRealImpl);
+
+
+RTDECL(uint32_t) RTStrCacheGetStats(RTSTRCACHE hStrCache, size_t *pcbStrings, size_t *pcbChunks, size_t *pcbBigEntries,
+ uint32_t *pcHashCollisions, uint32_t *pcHashCollisions2, uint32_t *pcHashInserts,
+ uint32_t *pcRehashes)
+{
+ return UINT32_MAX;
+}
+RT_EXPORT_SYMBOL(RTStrCacheGetStats);
+
diff --git a/src/VBox/Runtime/generic/timer-generic.cpp b/src/VBox/Runtime/generic/timer-generic.cpp
new file mode 100644
index 00000000..3501ac75
--- /dev/null
+++ b/src/VBox/Runtime/generic/timer-generic.cpp
@@ -0,0 +1,347 @@
+/* $Id: timer-generic.cpp $ */
+/** @file
+ * IPRT - Timers, Generic.
+ */
+
+/*
+ * 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/timer.h>
+#include "internal/iprt.h"
+
+#include <iprt/thread.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/log.h>
+#include "internal/magics.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating the timer is suspended. */
+ uint8_t volatile fSuspended;
+ /** Flag indicating that the timer has been destroyed. */
+ uint8_t volatile fDestroyed;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** User argument. */
+ void *pvUser;
+ /** The timer thread. */
+ RTTHREAD Thread;
+ /** Event semaphore on which the thread is blocked. */
+ RTSEMEVENT Event;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+ /** The start of the current run (ns).
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64StartTS;
+ /** The start of the current run (ns).
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64NextTS;
+ /** The current tick number (since u64StartTS). */
+ uint64_t volatile iTick;
+} RTTIMER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtTimerThread(RTTHREAD Thread, void *pvUser);
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ *ppTimer = NULL;
+
+ /*
+ * We don't support the fancy MP features.
+ */
+ if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Allocate and initialize the timer handle.
+ */
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (!pTimer)
+ return VERR_NO_MEMORY;
+
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fDestroyed = false;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->Thread = NIL_RTTHREAD;
+ pTimer->Event = NIL_RTSEMEVENT;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->u64StartTS = 0;
+
+ int rc = RTSemEventCreate(&pTimer->Event);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pTimer->Thread, rtTimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
+ if (RT_SUCCESS(rc))
+ {
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+
+ pTimer->u32Magic = 0;
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+ }
+ RTMemFree(pTimer);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerCreateEx);
+
+
+/**
+ * Validates the timer handle.
+ *
+ * @returns true if valid, false if invalid.
+ * @param pTimer The handle.
+ */
+DECLINLINE(bool) rtTimerIsValid(PRTTIMER pTimer)
+{
+ AssertPtrReturn(pTimer, false);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, false);
+ AssertReturn(!pTimer->fDestroyed, false);
+ return true;
+}
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* It's ok to pass NULL pointer. */
+ if (pTimer == /*NIL_RTTIMER*/ NULL)
+ return VINF_SUCCESS;
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * If the timer is active, we stop and destruct it in one go, to avoid
+ * unnecessary waiting for the next tick. If it's suspended we can safely
+ * set the destroy flag and signal it.
+ */
+ RTTHREAD Thread = pTimer->Thread;
+ if (!pTimer->fSuspended)
+ ASMAtomicXchgU8(&pTimer->fSuspended, true);
+ ASMAtomicXchgU8(&pTimer->fDestroyed, true);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+
+ RTThreadWait(Thread, 250, NULL);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerDestroy);
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (!pTimer->fSuspended)
+ return VERR_TIMER_ACTIVE;
+
+ /*
+ * Calc when it should start firing and give the thread a kick so it get going.
+ */
+ u64First += RTTimeNanoTS();
+ ASMAtomicXchgU64(&pTimer->iTick, 0);
+ ASMAtomicXchgU64(&pTimer->u64StartTS, u64First);
+ ASMAtomicXchgU64(&pTimer->u64NextTS, u64First);
+ ASMAtomicXchgU8(&pTimer->fSuspended, false);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerStart);
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (pTimer->fSuspended)
+ return VERR_TIMER_SUSPENDED;
+
+ /*
+ * Mark it as suspended and kick the thread.
+ */
+ ASMAtomicXchgU8(&pTimer->fSuspended, true);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerStop);
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ NOREF(u64NanoInterval);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTTimerChangeInterval);
+
+
+static DECLCALLBACK(int) rtTimerThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvUser;
+ NOREF(hThreadSelf);
+
+ /*
+ * The loop.
+ */
+ while (!pTimer->fDestroyed)
+ {
+ if (pTimer->fSuspended)
+ {
+ int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ }
+ else
+ {
+ const uint64_t u64NanoTS = RTTimeNanoTS();
+ if (u64NanoTS >= pTimer->u64NextTS)
+ {
+ pTimer->iTick++;
+
+ /* one shot? */
+ if (!pTimer->u64NanoInterval)
+ ASMAtomicXchgU8(&pTimer->fSuspended, true);
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, pTimer->iTick);
+
+ /* status changed? */
+ if (pTimer->fSuspended || pTimer->fDestroyed)
+ continue;
+
+ /* calc the next time we should fire. */
+ pTimer->u64NextTS = pTimer->u64StartTS + pTimer->iTick * pTimer->u64NanoInterval;
+ if (pTimer->u64NextTS < u64NanoTS)
+#ifdef IN_RING3 /* In ring-3 we'll catch up lost ticks immediately. */
+ pTimer->u64NextTS = u64NanoTS + 1;
+#else
+ pTimer->u64NextTS = u64NanoTS + RTTimerGetSystemGranularity() / 2;
+#endif
+ }
+
+ /* block. */
+ uint64_t cNanoSeconds = pTimer->u64NextTS - u64NanoTS;
+#ifdef IN_RING3 /* In ring-3 we'll catch up lost ticks immediately. */
+ if (cNanoSeconds > 10)
+#endif
+ {
+ int rc = RTSemEventWait(pTimer->Event, cNanoSeconds < 1000000 ? 1 : cNanoSeconds / 1000000);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
+ {
+ AssertRC(rc);
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ }
+ }
+ }
+
+ /*
+ * Release the timer resources.
+ */
+ ASMAtomicIncU32(&pTimer->u32Magic); /* make the handle invalid. */
+ int rc = RTSemEventDestroy(pTimer->Event); AssertRC(rc);
+ pTimer->Event = NIL_RTSEMEVENT;
+ pTimer->Thread = NIL_RTTHREAD;
+ RTMemFree(pTimer);
+
+ return VINF_SUCCESS;
+}
+
+
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+ return 10000000; /* 10ms */
+}
+RT_EXPORT_SYMBOL(RTTimerGetSystemGranularity);
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ NOREF(u32Request); NOREF(pu32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTTimerRequestSystemGranularity);
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ NOREF(u32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTTimerReleaseSystemGranularity);
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTTimerCanDoHighResolution);
diff --git a/src/VBox/Runtime/generic/timerlr-generic.cpp b/src/VBox/Runtime/generic/timerlr-generic.cpp
new file mode 100644
index 00000000..36bbe982
--- /dev/null
+++ b/src/VBox/Runtime/generic/timerlr-generic.cpp
@@ -0,0 +1,452 @@
+/* $Id: timerlr-generic.cpp $ */
+/** @file
+ * IPRT - Low Resolution Timers, Generic.
+ *
+ * This code is more or less identical to timer-generic.cpp, so
+ * bugfixes goes into both files.
+ */
+
+/*
+ * 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/timer.h>
+#include "internal/iprt.h"
+
+#include <iprt/thread.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/log.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The smallest interval for low resolution timers. */
+#define RTTIMERLR_MIN_INTERVAL RT_NS_100MS
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a timer handle.
+ */
+typedef struct RTTIMERLRINT
+{
+ /** Magic.
+ * This is RTTIMERRT_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating the timer is suspended. */
+ bool volatile fSuspended;
+ /** Flag indicating that the timer has been destroyed. */
+ bool volatile fDestroyed;
+ /** Set when the thread is blocked. */
+ bool volatile fBlocked;
+ bool fPadding;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t volatile u64NanoInterval;
+ /** The start of the current run (ns).
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64StartTS;
+ /** The start of the current run (ns).
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64NextTS;
+ /** The current tick number (since u64StartTS). */
+ uint64_t volatile iTick;
+
+ /** Callback. */
+ PFNRTTIMERLR pfnTimer;
+ /** User argument. */
+ void *pvUser;
+ /** The timer thread. */
+ RTTHREAD hThread;
+ /** Event semaphore on which the thread is blocked. */
+ RTSEMEVENT hEvent;
+} RTTIMERLRINT;
+typedef RTTIMERLRINT *PRTTIMERLRINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtTimerLRThread(RTTHREAD hThread, void *pvUser);
+
+
+RTDECL(int) RTTimerLRCreateEx(RTTIMERLR *phTimerLR, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMERLR pfnTimer, void *pvUser)
+{
+ AssertPtr(phTimerLR);
+ *phTimerLR = NIL_RTTIMERLR;
+
+ /*
+ * We don't support the fancy MP features, nor intervals lower than 100 ms.
+ */
+ AssertReturn(!(fFlags & RTTIMER_FLAGS_CPU_SPECIFIC), VERR_NOT_SUPPORTED);
+ AssertReturn(!u64NanoInterval || u64NanoInterval >= RTTIMERLR_MIN_INTERVAL, VERR_OUT_OF_RANGE);
+
+ /*
+ * Allocate and initialize the timer handle.
+ */
+ PRTTIMERLRINT pThis = (PRTTIMERLRINT)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTTIMERLR_MAGIC;
+ pThis->fSuspended = true;
+ pThis->fDestroyed = false;
+ pThis->fBlocked = false;
+ pThis->fPadding = false;
+ pThis->pfnTimer = pfnTimer;
+ pThis->pvUser = pvUser;
+ pThis->hThread = NIL_RTTHREAD;
+ pThis->hEvent = NIL_RTSEMEVENT;
+ pThis->u64NanoInterval = u64NanoInterval;
+ pThis->u64StartTS = 0;
+
+ int rc = RTSemEventCreate(&pThis->hEvent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pThis->hThread, rtTimerLRThread, pThis, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "TimerLR");
+ if (RT_SUCCESS(rc))
+ {
+ *phTimerLR = pThis;
+ return VINF_SUCCESS;
+ }
+
+ pThis->u32Magic = 0;
+ RTSemEventDestroy(pThis->hEvent);
+ pThis->hEvent = NIL_RTSEMEVENT;
+ }
+ RTMemFree(pThis);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerLRCreateEx);
+
+
+RTDECL(int) RTTimerLRDestroy(RTTIMERLR hTimerLR)
+{
+ /*
+ * Validate input, NIL is fine though.
+ */
+ if (hTimerLR == NIL_RTTIMERLR)
+ return VINF_SUCCESS;
+ PRTTIMERLRINT pThis = hTimerLR;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTTIMERLR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fDestroyed, VERR_INVALID_HANDLE);
+
+ /*
+ * If the timer is active, we stop and destruct it in one go, to avoid
+ * unnecessary waiting for the next tick. If it's suspended we can safely
+ * set the destroy flag and signal it.
+ */
+ RTTHREAD hThread = pThis->hThread;
+ if (!pThis->fSuspended)
+ ASMAtomicWriteBool(&pThis->fSuspended, true);
+ ASMAtomicWriteBool(&pThis->fDestroyed, true);
+ int rc = RTSemEventSignal(pThis->hEvent);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+
+ RTThreadWait(hThread, 250, NULL);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerLRDestroy);
+
+
+/**
+ * Internal worker fro RTTimerLRStart and RTTiemrLRChangeInterval.
+ */
+static int rtTimerLRStart(PRTTIMERLRINT pThis, uint64_t u64First)
+{
+ if (!pThis->fSuspended)
+ return VERR_TIMER_ACTIVE;
+
+ /*
+ * Calc when it should start firing and give the thread a kick so it get going.
+ */
+ u64First += RTTimeNanoTS();
+ ASMAtomicWriteU64(&pThis->iTick, 0);
+ ASMAtomicWriteU64(&pThis->u64StartTS, u64First);
+ ASMAtomicWriteU64(&pThis->u64NextTS, u64First);
+ ASMAtomicWriteBool(&pThis->fSuspended, false);
+ int rc = RTSemEventSignal(pThis->hEvent);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerLRStart(RTTIMERLR hTimerLR, uint64_t u64First)
+{
+ /*
+ * Validate input.
+ */
+ PRTTIMERLRINT pThis = hTimerLR;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTTIMERLR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fDestroyed, VERR_INVALID_HANDLE);
+ AssertReturn(!u64First || u64First >= RTTIMERLR_MIN_INTERVAL, VERR_OUT_OF_RANGE);
+
+ /*
+ * Do the job.
+ */
+ return rtTimerLRStart(pThis, u64First);
+}
+RT_EXPORT_SYMBOL(RTTimerLRStart);
+
+
+/**
+ * Internal worker for RTTimerLRStop and RTTimerLRChangeInterval
+ */
+static int rtTimerLRStop(PRTTIMERLRINT pThis, bool fSynchronous)
+{
+ /*
+ * Fail if already suspended.
+ */
+ if (pThis->fSuspended)
+ return VERR_TIMER_SUSPENDED;
+
+ /*
+ * Mark it as suspended and kick the thread.
+ * It's simpler to always reset the thread user semaphore, so we do that first.
+ */
+ int rc = RTThreadUserReset(pThis->hThread);
+ AssertRC(rc);
+
+ ASMAtomicWriteBool(&pThis->fSuspended, true);
+ rc = RTSemEventSignal(pThis->hEvent);
+ if (rc == VERR_ALREADY_POSTED)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+
+ /*
+ * Wait for the thread to stop running if synchronous.
+ */
+ if (fSynchronous && RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserWait(pThis->hThread, RT_MS_1MIN);
+ AssertRC(rc);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTTimerLRStop(RTTIMERLR hTimerLR)
+{
+ /*
+ * Validate input.
+ */
+ PRTTIMERLRINT pThis = hTimerLR;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTTIMERLR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fDestroyed, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ return rtTimerLRStop(pThis, false);
+}
+RT_EXPORT_SYMBOL(RTTimerLRStop);
+
+
+RTDECL(int) RTTimerLRChangeInterval(RTTIMERLR hTimerLR, uint64_t u64NanoInterval)
+{
+ /*
+ * Validate input.
+ */
+ PRTTIMERLRINT pThis = hTimerLR;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTTIMERLR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fDestroyed, VERR_INVALID_HANDLE);
+ AssertReturn(!u64NanoInterval || u64NanoInterval >= RTTIMERLR_MIN_INTERVAL, VERR_OUT_OF_RANGE);
+
+ /*
+ * Do the job accoring to state and caller.
+ */
+ int rc;
+ if (pThis->fSuspended)
+ {
+ /* Stopped: Just update the interval. */
+ ASMAtomicWriteU64(&pThis->u64NanoInterval, u64NanoInterval);
+ rc = VINF_SUCCESS;
+ }
+ else if (RTThreadSelf() == pThis->hThread)
+ {
+ /* Running: Updating interval from the callback. */
+ uint64_t u64Now = RTTimeNanoTS();
+ pThis->iTick = 0;
+ pThis->u64StartTS = u64Now;
+ pThis->u64NextTS = u64Now;
+ ASMAtomicWriteU64(&pThis->u64NanoInterval, u64NanoInterval);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /* Running: Stopping */
+ rc = rtTimerLRStop(pThis, true);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteU64(&pThis->u64NanoInterval, u64NanoInterval);
+ rc = rtTimerLRStart(pThis, 0);
+ }
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTTimerLRChangeInterval);
+
+
+static DECLCALLBACK(int) rtTimerLRThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ PRTTIMERLRINT pThis = (PRTTIMERLRINT)pvUser;
+ NOREF(hThreadSelf);
+
+ /*
+ * The loop.
+ */
+ while (!ASMAtomicUoReadBool(&pThis->fDestroyed))
+ {
+ if (ASMAtomicUoReadBool(&pThis->fSuspended))
+ {
+ /* Signal rtTimerLRStop thread. */
+ int rc = RTThreadUserSignal(hThreadSelf);
+ AssertRC(rc);
+
+ ASMAtomicWriteBool(&pThis->fBlocked, true);
+ rc = RTSemEventWait(pThis->hEvent, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ ASMAtomicWriteBool(&pThis->fBlocked, false);
+ }
+ else
+ {
+ uint64_t cNanoSeconds;
+ const uint64_t u64NanoTS = RTTimeNanoTS();
+ uint64_t u64NextTS = pThis->u64NextTS;
+ if (u64NanoTS >= u64NextTS)
+ {
+ uint64_t iTick = ++pThis->iTick;
+ pThis->pfnTimer(pThis, pThis->pvUser, iTick);
+
+ /* status changed? */
+ if ( ASMAtomicUoReadBool(&pThis->fSuspended)
+ || ASMAtomicUoReadBool(&pThis->fDestroyed))
+ continue;
+
+ /*
+ * Read timer data (it's all volatile and better if we read it all at once):
+ */
+ iTick = pThis->iTick;
+ uint64_t const u64StartTS = pThis->u64StartTS;
+ uint64_t const u64NanoInterval = pThis->u64NanoInterval;
+ ASMCompilerBarrier();
+
+ /*
+ * Suspend if one shot.
+ */
+ if (!u64NanoInterval)
+ {
+ ASMAtomicWriteBool(&pThis->fSuspended, true);
+ continue;
+ }
+
+ /*
+ * Calc the next time we should fire.
+ *
+ * If we're more than 60 intervals behind, just skip ahead. We
+ * don't want the timer thread running wild just because the
+ * clock changed in an unexpected way. As seen in @bugref{3611} this
+ * does happen during suspend/resume, but it may also happen
+ * if we're using a non-monotonic clock as time source.
+ */
+ u64NextTS = u64StartTS + iTick * u64NanoInterval;
+ if (RT_LIKELY(u64NextTS > u64NanoTS))
+ cNanoSeconds = u64NextTS - u64NanoTS;
+ else
+ {
+ uint64_t iActualTick = (u64NanoTS - u64StartTS) / u64NanoInterval;
+ if (iActualTick - iTick > 60)
+ pThis->iTick = iActualTick - 1;
+#ifdef IN_RING0
+ cNanoSeconds = RTTimerGetSystemGranularity() / 2;
+#else
+ cNanoSeconds = RT_NS_1MS;
+#endif
+ u64NextTS = u64NanoTS + cNanoSeconds;
+ }
+
+ pThis->u64NextTS = u64NextTS;
+ }
+ else
+ cNanoSeconds = u64NextTS - u64NanoTS;
+
+ /* block. */
+ ASMAtomicWriteBool(&pThis->fBlocked, true);
+ int rc = RTSemEventWait(pThis->hEvent,
+ (RTMSINTERVAL)(cNanoSeconds < 1000000 ? 1 : cNanoSeconds / 1000000));
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
+ {
+ AssertRC(rc);
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ ASMAtomicWriteBool(&pThis->fBlocked, false);
+ }
+ }
+
+ /*
+ * Release the timer resources.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTTIMERLR_MAGIC); /* make the handle invalid. */
+ int rc = RTSemEventDestroy(pThis->hEvent); AssertRC(rc);
+ pThis->hEvent = NIL_RTSEMEVENT;
+ pThis->hThread = NIL_RTTHREAD;
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/generic/tls-generic.cpp b/src/VBox/Runtime/generic/tls-generic.cpp
new file mode 100644
index 00000000..52898a1b
--- /dev/null
+++ b/src/VBox/Runtime/generic/tls-generic.cpp
@@ -0,0 +1,169 @@
+/* $Id: tls-generic.cpp $ */
+/** @file
+ * IPRT - Thread Local Storage (TSL), Generic Implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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_THREAD
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Allocation bitmap. Set bits indicates allocated entries. */
+static uint32_t volatile g_au32AllocatedBitmap[(RTTHREAD_TLS_ENTRIES + 31) / 32];
+/** Destructors for each of the TLS entries. */
+static PFNRTTLSDTOR g_apfnDestructors[RTTHREAD_TLS_ENTRIES];
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ RTTLS iTls;
+ int rc = RTTlsAllocEx(&iTls, NULL);
+ return RT_SUCCESS(rc) ? iTls : NIL_RTTLS;
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ for (unsigned i = 0; i < 128; i++)
+ {
+ int iTls = ASMBitFirstClear(&g_au32AllocatedBitmap[0], RTTHREAD_TLS_ENTRIES);
+ if (iTls < 0)
+ {
+ *piTls = NIL_RTTLS;
+ return VERR_NO_MEMORY;
+ }
+ if (!ASMAtomicBitTestAndSet(&g_au32AllocatedBitmap[0], iTls))
+ {
+ g_apfnDestructors[iTls] = pfnDestructor;
+ *piTls = iTls;
+ return VINF_SUCCESS;
+ }
+ }
+
+ AssertFailed();
+ return VERR_NO_MEMORY;
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ if ( iTls < 0
+ || iTls >= RTTHREAD_TLS_ENTRIES
+ || !ASMBitTest(&g_au32AllocatedBitmap[0], iTls))
+ return VERR_INVALID_PARAMETER;
+
+ ASMAtomicWriteNullPtr(&g_apfnDestructors[iTls]);
+ rtThreadClearTlsEntry(iTls);
+ ASMAtomicBitClear(&g_au32AllocatedBitmap[0], iTls);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ void *pv;
+ int rc = RTTlsGetEx(iTls, &pv);
+ return RT_SUCCESS(rc) ? pv : NULL;
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ if (RT_UNLIKELY( iTls < 0
+ || iTls >= RTTHREAD_TLS_ENTRIES
+ || !ASMBitTest(&g_au32AllocatedBitmap[0], iTls)))
+ return VERR_INVALID_PARAMETER;
+
+ PRTTHREADINT pThread = rtThreadGet(RTThreadSelf());
+ AssertReturn(pThread, VERR_NOT_SUPPORTED);
+ void *pv = pThread->apvTlsEntries[iTls];
+ rtThreadRelease(pThread);
+ *ppvValue = pv;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ if (RT_UNLIKELY( iTls < 0
+ || iTls >= RTTHREAD_TLS_ENTRIES
+ || !ASMBitTest(&g_au32AllocatedBitmap[0], iTls)))
+ return VERR_INVALID_PARAMETER;
+
+ PRTTHREADINT pThread = rtThreadGet(RTThreadSelf());
+ AssertReturn(pThread, VERR_NOT_SUPPORTED);
+ pThread->apvTlsEntries[iTls] = pvValue;
+ rtThreadRelease(pThread);
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Called at thread termination to invoke TLS destructors.
+ *
+ * @param pThread The current thread.
+ */
+DECLHIDDEN(void) rtThreadTlsDestruction(PRTTHREADINT pThread)
+{
+ for (RTTLS iTls = 0; iTls < RTTHREAD_TLS_ENTRIES; iTls++)
+ {
+ void *pv = pThread->apvTlsEntries[iTls];
+ if (pv)
+ {
+ PFNRTTLSDTOR pfnDestructor = (PFNRTTLSDTOR)(uintptr_t)ASMAtomicUoReadPtr((void * volatile *)(uintptr_t)&g_apfnDestructors[iTls]);
+ if (pfnDestructor)
+ {
+ pThread->apvTlsEntries[iTls] = NULL;
+ pfnDestructor(pv);
+ }
+ }
+ }
+}
+
diff --git a/src/VBox/Runtime/generic/utf16locale-generic.cpp b/src/VBox/Runtime/generic/utf16locale-generic.cpp
new file mode 100644
index 00000000..9acb679c
--- /dev/null
+++ b/src/VBox/Runtime/generic/utf16locale-generic.cpp
@@ -0,0 +1,51 @@
+/* $Id: utf16locale-generic.cpp $ */
+/** @file
+ * IPRT - UTF-16 Locale Specific Manipulation, Generic.
+ */
+
+/*
+ * 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_UTF16
+#include <iprt/utf16.h>
+#include "internal/iprt.h"
+
+
+RTDECL(int) RTUtf16LocaleICmp(PCRTUTF16 pusz1, PCRTUTF16 pusz2)
+{
+ return RTUtf16ICmp(pusz1, pusz2);
+}
+RT_EXPORT_SYMBOL(RTUtf16LocaleICmp);
+
diff --git a/src/VBox/Runtime/generic/uuid-generic.cpp b/src/VBox/Runtime/generic/uuid-generic.cpp
new file mode 100644
index 00000000..3451731e
--- /dev/null
+++ b/src/VBox/Runtime/generic/uuid-generic.cpp
@@ -0,0 +1,532 @@
+/* $Id: uuid-generic.cpp $ */
+/** @file
+ * IPRT - UUID, Generic.
+ */
+
+/*
+ * 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/uuid.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Conversion table used by the conversion functions.
+ * 0xff if not a hex number, otherwise the value. */
+static const uint8_t g_au8Digits[256] =
+{
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 0..0f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 10..1f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 20..2f */
+ 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0xff,0xff, 0xff,0xff,0xff,0xff, /* 30..3f */
+ 0xff,0x0a,0x0b,0x0c, 0x0d,0x0e,0x0f,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 40..4f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 50..5f */
+ 0xff,0x0a,0x0b,0x0c, 0x0d,0x0e,0x0f,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 60..6f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 70..7f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 80..8f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* 90..9f */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* a0..af */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* b0..bf */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* c0..cf */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* d0..df */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* e0..ef */
+ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, /* f0..ff */
+};
+/** Conversion to string. */
+static const char g_achDigits[17] = "0123456789abcdef";
+
+
+
+/* WARNING: This implementation ASSUMES little endian. Does not work on big endian! */
+
+/* Remember, the time fields in the UUID must be little endian. */
+
+
+RTDECL(int) RTUuidClear(PRTUUID pUuid)
+{
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+ pUuid->au64[0] = 0;
+ pUuid->au64[1] = 0;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidClear);
+
+
+RTDECL(bool) RTUuidIsNull(PCRTUUID pUuid)
+{
+ AssertPtrReturn(pUuid, true);
+ return !pUuid->au64[0]
+ && !pUuid->au64[1];
+}
+RT_EXPORT_SYMBOL(RTUuidIsNull);
+
+
+RTDECL(int) RTUuidCompare(PCRTUUID pUuid1, PCRTUUID pUuid2)
+{
+ /*
+ * Special cases.
+ */
+ if (pUuid1 == pUuid2)
+ return 0;
+ if (!pUuid1)
+ return RTUuidIsNull(pUuid2) ? 0 : -1;
+ if (!pUuid2)
+ return RTUuidIsNull(pUuid1) ? 0 : 1;
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pUuid2, 1);
+
+ /*
+ * Standard cases.
+ */
+ if (pUuid1->Gen.u32TimeLow != pUuid2->Gen.u32TimeLow)
+ return pUuid1->Gen.u32TimeLow < pUuid2->Gen.u32TimeLow ? -1 : 1;
+ if (pUuid1->Gen.u16TimeMid != pUuid2->Gen.u16TimeMid)
+ return pUuid1->Gen.u16TimeMid < pUuid2->Gen.u16TimeMid ? -1 : 1;
+ if (pUuid1->Gen.u16TimeHiAndVersion != pUuid2->Gen.u16TimeHiAndVersion)
+ return pUuid1->Gen.u16TimeHiAndVersion < pUuid2->Gen.u16TimeHiAndVersion ? -1 : 1;
+ if (pUuid1->Gen.u8ClockSeqHiAndReserved != pUuid2->Gen.u8ClockSeqHiAndReserved)
+ return pUuid1->Gen.u8ClockSeqHiAndReserved < pUuid2->Gen.u8ClockSeqHiAndReserved ? -1 : 1;
+ if (pUuid1->Gen.u8ClockSeqLow != pUuid2->Gen.u8ClockSeqLow)
+ return pUuid1->Gen.u8ClockSeqLow < pUuid2->Gen.u8ClockSeqLow ? -1 : 1;
+ if (pUuid1->Gen.au8Node[0] != pUuid2->Gen.au8Node[0])
+ return pUuid1->Gen.au8Node[0] < pUuid2->Gen.au8Node[0] ? -1 : 1;
+ if (pUuid1->Gen.au8Node[1] != pUuid2->Gen.au8Node[1])
+ return pUuid1->Gen.au8Node[1] < pUuid2->Gen.au8Node[1] ? -1 : 1;
+ if (pUuid1->Gen.au8Node[2] != pUuid2->Gen.au8Node[2])
+ return pUuid1->Gen.au8Node[2] < pUuid2->Gen.au8Node[2] ? -1 : 1;
+ if (pUuid1->Gen.au8Node[3] != pUuid2->Gen.au8Node[3])
+ return pUuid1->Gen.au8Node[3] < pUuid2->Gen.au8Node[3] ? -1 : 1;
+ if (pUuid1->Gen.au8Node[4] != pUuid2->Gen.au8Node[4])
+ return pUuid1->Gen.au8Node[4] < pUuid2->Gen.au8Node[4] ? -1 : 1;
+ if (pUuid1->Gen.au8Node[5] != pUuid2->Gen.au8Node[5])
+ return pUuid1->Gen.au8Node[5] < pUuid2->Gen.au8Node[5] ? -1 : 1;
+ return 0;
+}
+RT_EXPORT_SYMBOL(RTUuidCompare);
+
+
+RTDECL(int) RTUuidCompareStr(PCRTUUID pUuid1, const char *pszString2)
+{
+ RTUUID Uuid2;
+ int rc;
+
+ /* check params */
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the string to a UUID and then compare the two.
+ */
+ rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(pUuid1, &Uuid2);
+}
+RT_EXPORT_SYMBOL(RTUuidCompareStr);
+
+
+RTDECL(int) RTUuidCompare2Strs(const char *pszString1, const char *pszString2)
+{
+ RTUUID Uuid1;
+ RTUUID Uuid2;
+ int rc;
+
+ /* check params */
+ AssertPtrReturn(pszString1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the strings to UUIDs and then compare them.
+ */
+ rc = RTUuidFromStr(&Uuid1, pszString1);
+ AssertRCReturn(rc, -1);
+
+ rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(&Uuid1, &Uuid2);
+}
+RT_EXPORT_SYMBOL(RTUuidCompare2Strs);
+
+
+RTDECL(int) RTUuidToStr(PCRTUUID pUuid, char *pszString, size_t cchString)
+{
+ uint32_t u32TimeLow;
+ unsigned u;
+
+ /* validate parameters */
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszString, VERR_INVALID_PARAMETER);
+ AssertReturn(cchString >= RTUUID_STR_LENGTH, VERR_INVALID_PARAMETER);
+
+ /*
+ * RTStrPrintf(,,"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ * pUuid->Gen.u32TimeLow,
+ * pUuid->Gen.u16TimeMin,
+ * pUuid->Gen.u16TimeHiAndVersion,
+ * pUuid->Gen.u16ClockSeq & 0xff,
+ * pUuid->Gen.u16ClockSeq >> 8,
+ * pUuid->Gen.au8Node[0],
+ * pUuid->Gen.au8Node[1],
+ * pUuid->Gen.au8Node[2],
+ * pUuid->Gen.au8Node[3],
+ * pUuid->Gen.au8Node[4],
+ * pUuid->Gen.au8Node[5]);
+ */
+ u32TimeLow = RT_H2LE_U32(pUuid->Gen.u32TimeLow);
+ pszString[ 0] = g_achDigits[(u32TimeLow >> 28)/*& 0xf*/];
+ pszString[ 1] = g_achDigits[(u32TimeLow >> 24) & 0xf];
+ pszString[ 2] = g_achDigits[(u32TimeLow >> 20) & 0xf];
+ pszString[ 3] = g_achDigits[(u32TimeLow >> 16) & 0xf];
+ pszString[ 4] = g_achDigits[(u32TimeLow >> 12) & 0xf];
+ pszString[ 5] = g_achDigits[(u32TimeLow >> 8) & 0xf];
+ pszString[ 6] = g_achDigits[(u32TimeLow >> 4) & 0xf];
+ pszString[ 7] = g_achDigits[(u32TimeLow/*>>0*/)& 0xf];
+ pszString[ 8] = '-';
+ u = RT_H2LE_U16(pUuid->Gen.u16TimeMid);
+ pszString[ 9] = g_achDigits[(u >> 12)/*& 0xf*/];
+ pszString[10] = g_achDigits[(u >> 8) & 0xf];
+ pszString[11] = g_achDigits[(u >> 4) & 0xf];
+ pszString[12] = g_achDigits[(u/*>>0*/)& 0xf];
+ pszString[13] = '-';
+ u = RT_H2LE_U16(pUuid->Gen.u16TimeHiAndVersion);
+ pszString[14] = g_achDigits[(u >> 12)/*& 0xf*/];
+ pszString[15] = g_achDigits[(u >> 8) & 0xf];
+ pszString[16] = g_achDigits[(u >> 4) & 0xf];
+ pszString[17] = g_achDigits[(u/*>>0*/)& 0xf];
+ pszString[18] = '-';
+ pszString[19] = g_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved >> 4];
+ pszString[20] = g_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved & 0xf];
+ pszString[21] = g_achDigits[pUuid->Gen.u8ClockSeqLow >> 4];
+ pszString[22] = g_achDigits[pUuid->Gen.u8ClockSeqLow & 0xf];
+ pszString[23] = '-';
+ pszString[24] = g_achDigits[pUuid->Gen.au8Node[0] >> 4];
+ pszString[25] = g_achDigits[pUuid->Gen.au8Node[0] & 0xf];
+ pszString[26] = g_achDigits[pUuid->Gen.au8Node[1] >> 4];
+ pszString[27] = g_achDigits[pUuid->Gen.au8Node[1] & 0xf];
+ pszString[28] = g_achDigits[pUuid->Gen.au8Node[2] >> 4];
+ pszString[29] = g_achDigits[pUuid->Gen.au8Node[2] & 0xf];
+ pszString[30] = g_achDigits[pUuid->Gen.au8Node[3] >> 4];
+ pszString[31] = g_achDigits[pUuid->Gen.au8Node[3] & 0xf];
+ pszString[32] = g_achDigits[pUuid->Gen.au8Node[4] >> 4];
+ pszString[33] = g_achDigits[pUuid->Gen.au8Node[4] & 0xf];
+ pszString[34] = g_achDigits[pUuid->Gen.au8Node[5] >> 4];
+ pszString[35] = g_achDigits[pUuid->Gen.au8Node[5] & 0xf];
+ pszString[36] = '\0';
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidToStr);
+
+
+RTDECL(int) RTUuidFromStr(PRTUUID pUuid, const char *pszString)
+{
+ bool fHaveBraces;
+
+ /*
+ * Validate parameters.
+ */
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszString, VERR_INVALID_PARAMETER);
+
+ fHaveBraces = pszString[0] == '{';
+ pszString += fHaveBraces;
+
+#define MY_CHECK(expr) do { if (RT_UNLIKELY(!(expr))) return VERR_INVALID_UUID_FORMAT; } while (0)
+#define MY_ISXDIGIT(ch) (g_au8Digits[(ch) & 0xff] != 0xff)
+ MY_CHECK(MY_ISXDIGIT(pszString[ 0]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 1]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 2]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 3]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 4]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 5]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 6]));
+ MY_CHECK(MY_ISXDIGIT(pszString[ 7]));
+ MY_CHECK(pszString[ 8] == '-');
+ MY_CHECK(MY_ISXDIGIT(pszString[ 9]));
+ MY_CHECK(MY_ISXDIGIT(pszString[10]));
+ MY_CHECK(MY_ISXDIGIT(pszString[11]));
+ MY_CHECK(MY_ISXDIGIT(pszString[12]));
+ MY_CHECK(pszString[13] == '-');
+ MY_CHECK(MY_ISXDIGIT(pszString[14]));
+ MY_CHECK(MY_ISXDIGIT(pszString[15]));
+ MY_CHECK(MY_ISXDIGIT(pszString[16]));
+ MY_CHECK(MY_ISXDIGIT(pszString[17]));
+ MY_CHECK(pszString[18] == '-');
+ MY_CHECK(MY_ISXDIGIT(pszString[19]));
+ MY_CHECK(MY_ISXDIGIT(pszString[20]));
+ MY_CHECK(MY_ISXDIGIT(pszString[21]));
+ MY_CHECK(MY_ISXDIGIT(pszString[22]));
+ MY_CHECK(pszString[23] == '-');
+ MY_CHECK(MY_ISXDIGIT(pszString[24]));
+ MY_CHECK(MY_ISXDIGIT(pszString[25]));
+ MY_CHECK(MY_ISXDIGIT(pszString[26]));
+ MY_CHECK(MY_ISXDIGIT(pszString[27]));
+ MY_CHECK(MY_ISXDIGIT(pszString[28]));
+ MY_CHECK(MY_ISXDIGIT(pszString[29]));
+ MY_CHECK(MY_ISXDIGIT(pszString[30]));
+ MY_CHECK(MY_ISXDIGIT(pszString[31]));
+ MY_CHECK(MY_ISXDIGIT(pszString[32]));
+ MY_CHECK(MY_ISXDIGIT(pszString[33]));
+ MY_CHECK(MY_ISXDIGIT(pszString[34]));
+ MY_CHECK(MY_ISXDIGIT(pszString[35]));
+ if (fHaveBraces)
+ MY_CHECK(pszString[36] == '}');
+ MY_CHECK(!pszString[36 + fHaveBraces]);
+#undef MY_ISXDIGIT
+#undef MY_CHECK
+
+ /*
+ * Inverse of RTUuidToStr (see above).
+ */
+#define MY_TONUM(ch) (g_au8Digits[(ch) & 0xff])
+ pUuid->Gen.u32TimeLow = RT_LE2H_U32((uint32_t)MY_TONUM(pszString[ 0]) << 28
+ | (uint32_t)MY_TONUM(pszString[ 1]) << 24
+ | (uint32_t)MY_TONUM(pszString[ 2]) << 20
+ | (uint32_t)MY_TONUM(pszString[ 3]) << 16
+ | (uint32_t)MY_TONUM(pszString[ 4]) << 12
+ | (uint32_t)MY_TONUM(pszString[ 5]) << 8
+ | (uint32_t)MY_TONUM(pszString[ 6]) << 4
+ | (uint32_t)MY_TONUM(pszString[ 7]));
+ pUuid->Gen.u16TimeMid = RT_LE2H_U16((uint16_t)MY_TONUM(pszString[ 9]) << 12
+ | (uint16_t)MY_TONUM(pszString[10]) << 8
+ | (uint16_t)MY_TONUM(pszString[11]) << 4
+ | (uint16_t)MY_TONUM(pszString[12]));
+ pUuid->Gen.u16TimeHiAndVersion = RT_LE2H_U16(
+ (uint16_t)MY_TONUM(pszString[14]) << 12
+ | (uint16_t)MY_TONUM(pszString[15]) << 8
+ | (uint16_t)MY_TONUM(pszString[16]) << 4
+ | (uint16_t)MY_TONUM(pszString[17]));
+ pUuid->Gen.u8ClockSeqHiAndReserved =
+ (uint16_t)MY_TONUM(pszString[19]) << 4
+ | (uint16_t)MY_TONUM(pszString[20]);
+ pUuid->Gen.u8ClockSeqLow =
+ (uint16_t)MY_TONUM(pszString[21]) << 4
+ | (uint16_t)MY_TONUM(pszString[22]);
+ pUuid->Gen.au8Node[0] = (uint8_t)MY_TONUM(pszString[24]) << 4
+ | (uint8_t)MY_TONUM(pszString[25]);
+ pUuid->Gen.au8Node[1] = (uint8_t)MY_TONUM(pszString[26]) << 4
+ | (uint8_t)MY_TONUM(pszString[27]);
+ pUuid->Gen.au8Node[2] = (uint8_t)MY_TONUM(pszString[28]) << 4
+ | (uint8_t)MY_TONUM(pszString[29]);
+ pUuid->Gen.au8Node[3] = (uint8_t)MY_TONUM(pszString[30]) << 4
+ | (uint8_t)MY_TONUM(pszString[31]);
+ pUuid->Gen.au8Node[4] = (uint8_t)MY_TONUM(pszString[32]) << 4
+ | (uint8_t)MY_TONUM(pszString[33]);
+ pUuid->Gen.au8Node[5] = (uint8_t)MY_TONUM(pszString[34]) << 4
+ | (uint8_t)MY_TONUM(pszString[35]);
+#undef MY_TONUM
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidFromStr);
+
+
+RTDECL(int) RTUuidToUtf16(PCRTUUID pUuid, PRTUTF16 pwszString, size_t cwcString)
+{
+ uint32_t u32TimeLow;
+ unsigned u;
+
+ /* validate parameters */
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pwszString, VERR_INVALID_PARAMETER);
+ AssertReturn(cwcString >= RTUUID_STR_LENGTH, VERR_INVALID_PARAMETER);
+
+ /*
+ * RTStrPrintf(,,"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ * pUuid->Gen.u32TimeLow,
+ * pUuid->Gen.u16TimeMin,
+ * pUuid->Gen.u16TimeHiAndVersion,
+ * pUuid->Gen.u16ClockSeq & 0xff,
+ * pUuid->Gen.u16ClockSeq >> 8,
+ * pUuid->Gen.au8Node[0],
+ * pUuid->Gen.au8Node[1],
+ * pUuid->Gen.au8Node[2],
+ * pUuid->Gen.au8Node[3],
+ * pUuid->Gen.au8Node[4],
+ * pUuid->Gen.au8Node[5]);
+ */
+ u32TimeLow = RT_H2LE_U32(pUuid->Gen.u32TimeLow);
+ pwszString[ 0] = g_achDigits[(u32TimeLow >> 28)/*& 0xf*/];
+ pwszString[ 1] = g_achDigits[(u32TimeLow >> 24) & 0xf];
+ pwszString[ 2] = g_achDigits[(u32TimeLow >> 20) & 0xf];
+ pwszString[ 3] = g_achDigits[(u32TimeLow >> 16) & 0xf];
+ pwszString[ 4] = g_achDigits[(u32TimeLow >> 12) & 0xf];
+ pwszString[ 5] = g_achDigits[(u32TimeLow >> 8) & 0xf];
+ pwszString[ 6] = g_achDigits[(u32TimeLow >> 4) & 0xf];
+ pwszString[ 7] = g_achDigits[(u32TimeLow/*>>0*/)& 0xf];
+ pwszString[ 8] = '-';
+ u = RT_H2LE_U16(pUuid->Gen.u16TimeMid);
+ pwszString[ 9] = g_achDigits[(u >> 12)/*& 0xf*/];
+ pwszString[10] = g_achDigits[(u >> 8) & 0xf];
+ pwszString[11] = g_achDigits[(u >> 4) & 0xf];
+ pwszString[12] = g_achDigits[(u/*>>0*/)& 0xf];
+ pwszString[13] = '-';
+ u = RT_H2LE_U16(pUuid->Gen.u16TimeHiAndVersion);
+ pwszString[14] = g_achDigits[(u >> 12)/*& 0xf*/];
+ pwszString[15] = g_achDigits[(u >> 8) & 0xf];
+ pwszString[16] = g_achDigits[(u >> 4) & 0xf];
+ pwszString[17] = g_achDigits[(u/*>>0*/)& 0xf];
+ pwszString[18] = '-';
+ pwszString[19] = g_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved >> 4];
+ pwszString[20] = g_achDigits[pUuid->Gen.u8ClockSeqHiAndReserved & 0xf];
+ pwszString[21] = g_achDigits[pUuid->Gen.u8ClockSeqLow >> 4];
+ pwszString[22] = g_achDigits[pUuid->Gen.u8ClockSeqLow & 0xf];
+ pwszString[23] = '-';
+ pwszString[24] = g_achDigits[pUuid->Gen.au8Node[0] >> 4];
+ pwszString[25] = g_achDigits[pUuid->Gen.au8Node[0] & 0xf];
+ pwszString[26] = g_achDigits[pUuid->Gen.au8Node[1] >> 4];
+ pwszString[27] = g_achDigits[pUuid->Gen.au8Node[1] & 0xf];
+ pwszString[28] = g_achDigits[pUuid->Gen.au8Node[2] >> 4];
+ pwszString[29] = g_achDigits[pUuid->Gen.au8Node[2] & 0xf];
+ pwszString[30] = g_achDigits[pUuid->Gen.au8Node[3] >> 4];
+ pwszString[31] = g_achDigits[pUuid->Gen.au8Node[3] & 0xf];
+ pwszString[32] = g_achDigits[pUuid->Gen.au8Node[4] >> 4];
+ pwszString[33] = g_achDigits[pUuid->Gen.au8Node[4] & 0xf];
+ pwszString[34] = g_achDigits[pUuid->Gen.au8Node[5] >> 4];
+ pwszString[35] = g_achDigits[pUuid->Gen.au8Node[5] & 0xf];
+ pwszString[36] = '\0';
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidToUtf16);
+
+
+RTDECL(int) RTUuidFromUtf16(PRTUUID pUuid, PCRTUTF16 pwszString)
+{
+ bool fHaveBraces;
+
+ /*
+ * Validate parameters.
+ */
+ AssertPtrReturn(pUuid, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pwszString, VERR_INVALID_PARAMETER);
+
+ fHaveBraces = pwszString[0] == '{';
+ pwszString += fHaveBraces;
+
+#define MY_CHECK(expr) do { if (RT_UNLIKELY(!(expr))) return VERR_INVALID_UUID_FORMAT; } while (0)
+#define MY_ISXDIGIT(ch) (!((ch) & 0xff00) && g_au8Digits[(ch) & 0xff] != 0xff)
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 0]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 1]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 2]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 3]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 4]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 5]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 6]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 7]));
+ MY_CHECK(pwszString[ 8] == '-');
+ MY_CHECK(MY_ISXDIGIT(pwszString[ 9]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[10]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[11]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[12]));
+ MY_CHECK(pwszString[13] == '-');
+ MY_CHECK(MY_ISXDIGIT(pwszString[14]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[15]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[16]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[17]));
+ MY_CHECK(pwszString[18] == '-');
+ MY_CHECK(MY_ISXDIGIT(pwszString[19]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[20]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[21]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[22]));
+ MY_CHECK(pwszString[23] == '-');
+ MY_CHECK(MY_ISXDIGIT(pwszString[24]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[25]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[26]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[27]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[28]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[29]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[30]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[31]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[32]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[33]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[34]));
+ MY_CHECK(MY_ISXDIGIT(pwszString[35]));
+ if (fHaveBraces)
+ MY_CHECK(pwszString[36] == '}');
+ MY_CHECK(!pwszString[36 + fHaveBraces]);
+#undef MY_ISXDIGIT
+#undef MY_CHECK
+
+ /*
+ * Inverse of RTUuidToUtf8 (see above).
+ */
+#define MY_TONUM(ch) (g_au8Digits[(ch) & 0xff])
+ pUuid->Gen.u32TimeLow = RT_LE2H_U32((uint32_t)MY_TONUM(pwszString[ 0]) << 28
+ | (uint32_t)MY_TONUM(pwszString[ 1]) << 24
+ | (uint32_t)MY_TONUM(pwszString[ 2]) << 20
+ | (uint32_t)MY_TONUM(pwszString[ 3]) << 16
+ | (uint32_t)MY_TONUM(pwszString[ 4]) << 12
+ | (uint32_t)MY_TONUM(pwszString[ 5]) << 8
+ | (uint32_t)MY_TONUM(pwszString[ 6]) << 4
+ | (uint32_t)MY_TONUM(pwszString[ 7]));
+ pUuid->Gen.u16TimeMid = RT_LE2H_U16((uint16_t)MY_TONUM(pwszString[ 9]) << 12
+ | (uint16_t)MY_TONUM(pwszString[10]) << 8
+ | (uint16_t)MY_TONUM(pwszString[11]) << 4
+ | (uint16_t)MY_TONUM(pwszString[12]));
+ pUuid->Gen.u16TimeHiAndVersion = RT_LE2H_U16(
+ (uint16_t)MY_TONUM(pwszString[14]) << 12
+ | (uint16_t)MY_TONUM(pwszString[15]) << 8
+ | (uint16_t)MY_TONUM(pwszString[16]) << 4
+ | (uint16_t)MY_TONUM(pwszString[17]));
+ pUuid->Gen.u8ClockSeqHiAndReserved =
+ (uint16_t)MY_TONUM(pwszString[19]) << 4
+ | (uint16_t)MY_TONUM(pwszString[20]);
+ pUuid->Gen.u8ClockSeqLow =
+ (uint16_t)MY_TONUM(pwszString[21]) << 4
+ | (uint16_t)MY_TONUM(pwszString[22]);
+ pUuid->Gen.au8Node[0] = (uint8_t)MY_TONUM(pwszString[24]) << 4
+ | (uint8_t)MY_TONUM(pwszString[25]);
+ pUuid->Gen.au8Node[1] = (uint8_t)MY_TONUM(pwszString[26]) << 4
+ | (uint8_t)MY_TONUM(pwszString[27]);
+ pUuid->Gen.au8Node[2] = (uint8_t)MY_TONUM(pwszString[28]) << 4
+ | (uint8_t)MY_TONUM(pwszString[29]);
+ pUuid->Gen.au8Node[3] = (uint8_t)MY_TONUM(pwszString[30]) << 4
+ | (uint8_t)MY_TONUM(pwszString[31]);
+ pUuid->Gen.au8Node[4] = (uint8_t)MY_TONUM(pwszString[32]) << 4
+ | (uint8_t)MY_TONUM(pwszString[33]);
+ pUuid->Gen.au8Node[5] = (uint8_t)MY_TONUM(pwszString[34]) << 4
+ | (uint8_t)MY_TONUM(pwszString[35]);
+#undef MY_TONUM
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTUuidFromUtf16);
+