diff options
Diffstat (limited to 'src/VBox/Runtime/generic')
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); + |