diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/r3/win | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
84 files changed, 29472 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/Makefile.kup b/src/VBox/Runtime/r3/win/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/r3/win/Makefile.kup diff --git a/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp new file mode 100644 index 00000000..1eead2b1 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp @@ -0,0 +1,194 @@ +/* $Id: RTCrStoreCreateSnapshotById-win.cpp $ */ +/** @file + * IPRT - RTCrStoreCreateSnapshotById, Windows. + */ + +/* + * 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/once.h> +#include <iprt/ldr.h> + +#include <iprt/win/windows.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv, + DWORD dwFlags, const void *pvParam); +typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags); +typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext); + + + +static int rtCrStoreAddCertsFromNative(RTCRSTORE hStore, DWORD fStore, PCRTUTF16 pwszStoreName, + PFNCERTOPENSTORE pfnOpenStore, PFNCERTCLOSESTORE pfnCloseStore, + PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts, int rc, PRTERRINFO pErrInfo) +{ + DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG; + HCERTSTORE hNativeStore = pfnOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + NULL /* hCryptProv = default */, fStore | fOpenStore, pwszStoreName); + if (hNativeStore) + { + PCCERT_CONTEXT pCurCtx = NULL; + while ((pCurCtx = pfnEnumCerts(hNativeStore, pCurCtx)) != NULL) + { + if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING) + { + RTERRINFOSTATIC StaticErrInfo; + RTASN1CURSORPRIMARY PrimaryCursor; + RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, + RTErrInfoInitStatic(&StaticErrInfo), + &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx"); + RTCRX509CERTIFICATE MyCert; + int rc2 = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert"); + if (RT_SUCCESS(rc2)) + { + rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND, + pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded, + RTErrInfoInitStatic(&StaticErrInfo)); + RTCrX509Certificate_Delete(&MyCert); + } + if (RT_FAILURE(rc2)) + { + if (RTErrInfoIsSet(&StaticErrInfo.Core)) + RTErrInfoAddF(pErrInfo, -rc2, " %s", StaticErrInfo.Core.pszMsg); + else + RTErrInfoAddF(pErrInfo, -rc2, " %Rrc adding cert", rc2); + rc = -rc2; + } + } + } + pfnCloseStore(hNativeStore, CERT_CLOSE_STORE_CHECK_FLAG); + } + else + { + DWORD uLastErr = GetLastError(); + if (uLastErr != ERROR_FILE_NOT_FOUND) + rc = RTErrInfoAddF(pErrInfo, -RTErrConvertFromWin32(uLastErr), + " CertOpenStore(%#x,'%ls') failed: %u", fStore, pwszStoreName); + } + return rc; +} + + + +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; + int rc = RTCrStoreCreateInMem(&hStore, 128); + if (RT_SUCCESS(rc)) + { + *phStore = hStore; + + /* + * Resolve the APIs we need to do this job. + */ + RTLDRMOD hLdrMod; + int rc2 = RTLdrLoadSystem("crypt32.dll", false /*NoUnload*/, &hLdrMod); + if (RT_SUCCESS(rc2)) + { + PFNCERTOPENSTORE pfnOpenStore = NULL; + rc2 = RTLdrGetSymbol(hLdrMod, "CertOpenStore", (void **)&pfnOpenStore); + + PFNCERTCLOSESTORE pfnCloseStore = NULL; + if (RT_SUCCESS(rc2)) + rc2 = RTLdrGetSymbol(hLdrMod, "CertCloseStore", (void **)&pfnCloseStore); + + PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts = NULL; + if (RT_SUCCESS(rc2)) + rc2 = RTLdrGetSymbol(hLdrMod, "CertEnumCertificatesInStore", (void **)&pfnEnumCerts); + if (RT_SUCCESS(rc2)) + { + /* + * Do the work. + */ + DWORD fStore = CERT_SYSTEM_STORE_CURRENT_USER; + switch (enmStoreId) + { + case RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES: + case RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS: + fStore = CERT_SYSTEM_STORE_LOCAL_MACHINE; + RT_FALL_THRU(); + case RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES: + case RTCRSTOREID_USER_INTERMEDIATE_CAS: + { + /** @todo CA and MY in s_apwszRootStores are _very_ questionable!!! However, + * curl may need them to work correct and it doesn't seem to have any + * intermediate ca file. :/ */ + static PCRTUTF16 const s_apwszRootStores[] = { L"AuthRoot", L"CA", L"MY", L"Root" }; + static PCRTUTF16 const s_apwszIntermediateStores[] = { L"CA", L"MY" }; + PCRTUTF16 const *papwszStores = s_apwszRootStores; + uint32_t cStores = RT_ELEMENTS(s_apwszRootStores); + if (enmStoreId == RTCRSTOREID_USER_INTERMEDIATE_CAS || enmStoreId == RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS) + { + papwszStores = s_apwszIntermediateStores; + cStores = RT_ELEMENTS(s_apwszIntermediateStores); + } + + for (uint32_t i = 0; i < cStores; i++) + rc = rtCrStoreAddCertsFromNative(hStore, fStore, papwszStores[i], pfnOpenStore, pfnCloseStore, + pfnEnumCerts, rc, pErrInfo); + break; + } + + default: + AssertFailed(); /* implement me */ + } + } + else + rc = RTErrInfoSetF(pErrInfo, -rc2, "Error resolving crypt32.dll APIs"); + RTLdrClose(hLdrMod); + } + else + rc = RTErrInfoSetF(pErrInfo, -rc2, "Error loading crypt32.dll"); + } + else + RTErrInfoSet(pErrInfo, rc, "RTCrStoreCreateInMem failed"); + return rc; +} +RT_EXPORT_SYMBOL(RTCrStoreCreateSnapshotById); + diff --git a/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp new file mode 100644 index 00000000..97a38271 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp @@ -0,0 +1,71 @@ +/* $Id: RTFileQuerySectorSize-win.cpp $ */ +/** @file + * IPRT - RTFileQuerySectorSize, Windows. + */ + +/* + * Copyright (C) 2017-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> +#include <iprt/string.h> + +#include <iprt/win/windows.h> + + + +RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector) +{ + AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER); + + DISK_GEOMETRY DriveGeo; + RT_ZERO(DriveGeo); + DWORD cbDriveGeo = 0; + if (DeviceIoControl((HANDLE)RTFileToNative(hFile), + IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL)) + { + AssertReturn(DriveGeo.BytesPerSector > 0, VERR_INVALID_PARAMETER); + *pcbSector = DriveGeo.BytesPerSector; + return VINF_SUCCESS; + } + int rc = RTErrConvertFromWin32(GetLastError()); + AssertMsg(rc == VERR_IO_NOT_READY, ("%d / %Rrc\n", GetLastError(), rc)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp new file mode 100644 index 00000000..c0b7f780 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp @@ -0,0 +1,143 @@ +/* $Id: RTHandleGetStandard-win.cpp $ */ +/** @file + * IPRT - RTHandleGetStandard, Windows. + */ + +/* + * 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 "internal/iprt.h" +#include <iprt/handle.h> + +#include <iprt/file.h> +#include <iprt/pipe.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/log.h> + +#include <iprt/win/windows.h> + +#include "internal/socket.h" /* (Needs Windows.h.) */ +#include "internal-r3-win.h" /* (Needs Windows.h.) */ + + +RTDECL(int) RTHandleGetStandard(RTHANDLESTD enmStdHandle, bool fLeaveOpen, PRTHANDLE ph) +{ + /* + * Validate and convert input. + */ + AssertPtrReturn(ph, VERR_INVALID_POINTER); + DWORD dwStdHandle; + switch (enmStdHandle) + { + case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break; + case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break; + case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break; + default: + AssertFailedReturn(VERR_INVALID_PARAMETER); + } + + /* + * Is the requested descriptor valid and which IPRT handle type does it + * best map on to? + */ + HANDLE hNative = GetStdHandle(dwStdHandle); + if (hNative == INVALID_HANDLE_VALUE) + return RTErrConvertFromWin32(GetLastError()); + + DWORD dwInfo = 0; + if (g_pfnGetHandleInformation && !g_pfnGetHandleInformation(hNative, &dwInfo)) + return RTErrConvertFromWin32(GetLastError()); + bool const fInherit = RT_BOOL(dwInfo & HANDLE_FLAG_INHERIT); + + SetLastError(NO_ERROR); + RTHANDLE h; + DWORD dwType = GetFileType(hNative); + switch (dwType & ~FILE_TYPE_REMOTE) + { + case FILE_TYPE_UNKNOWN: + if (GetLastError() != NO_ERROR) + return RTErrConvertFromWin32(GetLastError()); + RT_FALL_THROUGH(); + default: + case FILE_TYPE_CHAR: + case FILE_TYPE_DISK: + h.enmType = RTHANDLETYPE_FILE; + break; + + case FILE_TYPE_PIPE: + { + DWORD cMaxInstances; + DWORD fInfo; + if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances)) + h.enmType = RTHANDLETYPE_SOCKET; + else + h.enmType = RTHANDLETYPE_PIPE; + break; + } + } + + /* + * Create the IPRT handle. + */ + int rc; + switch (h.enmType) + { + case RTHANDLETYPE_FILE: + /** @todo fLeaveOpen */ + rc = RTFileFromNative(&h.u.hFile, (RTHCUINTPTR)hNative); + break; + + case RTHANDLETYPE_PIPE: + rc = RTPipeFromNative(&h.u.hPipe, (RTHCUINTPTR)hNative, + (enmStdHandle == RTHANDLESTD_INPUT ? RTPIPE_N_READ : RTPIPE_N_WRITE) + | (fInherit ? RTPIPE_N_INHERIT : 0) + | (fLeaveOpen ? RTPIPE_N_LEAVE_OPEN : 0)); + break; + + case RTHANDLETYPE_SOCKET: + rc = rtSocketCreateForNative(&h.u.hSocket, (RTHCUINTPTR)hNative, fLeaveOpen); + break; + + default: /* shut up gcc */ + return VERR_INTERNAL_ERROR; + } + + if (RT_SUCCESS(rc)) + *ph = h; + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp new file mode 100644 index 00000000..10efd342 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp @@ -0,0 +1,116 @@ +/* $Id: RTLocaleQueryNormalizedBaseLocaleName-win.cpp $ */ +/** @file + * IPRT - RTLocaleQueryNormalizedBaseLocaleName, ring-3, Windows. + */ + +/* + * Copyright (C) 2017-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/win/windows.h> + +#include <iprt/locale.h> +#include "internal/iprt.h" + +#include <iprt/ctype.h> +#include <iprt/errcore.h> +#include <iprt/string.h> + + +RTDECL(int) RTLocaleQueryNormalizedBaseLocaleName(char *pszName, size_t cbName) +{ + /* + * Note! This part is duplicate of r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp! + */ + char szLocale[_1K]; + int rc = RTLocaleQueryLocaleName(szLocale, sizeof(szLocale)); + if (RT_SUCCESS(rc)) + { + /* + * May return some complicated "LC_XXX=yyy;LC.." sequence if + * partially set (like IPRT does). Try get xx_YY sequence first + * because 'C' or 'POSIX' may be LC_xxx variants that haven't been + * set yet. + * + * ASSUMES complicated locale mangling is done in a certain way... + */ + const char *pszLocale = strchr(szLocale, '='); + if (!pszLocale) + pszLocale = szLocale; + else + pszLocale++; + bool fSeenC = false; + bool fSeenPOSIX = false; + do + { + const char *pszEnd = strchr(pszLocale, ';'); + + if ( RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(pszLocale) + && ( pszLocale[5] == '\0' + || RT_C_IS_PUNCT(pszLocale[5])) ) + return RTStrCopyEx(pszName, cbName, pszLocale, 5); + + if ( pszLocale[0] == 'C' + && ( pszLocale[1] == '\0' + || RT_C_IS_PUNCT(pszLocale[1])) ) + fSeenC = true; + else if ( strncmp(pszLocale, "POSIX", 5) == 0 + && ( pszLocale[5] == '\0' + || RT_C_IS_PUNCT(pszLocale[5])) ) + fSeenPOSIX = true; + + /* advance */ + pszLocale = pszEnd ? strchr(pszEnd + 1, '=') : NULL; + } while (pszLocale++); + + if (fSeenC || fSeenPOSIX) + return RTStrCopy(pszName, cbName, "C"); /* C and POSIX should be identical IIRC, so keep it simple. */ + + rc = VERR_NOT_AVAILABLE; + } + + /* + * Fallback. + */ + if ( GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO639LANGNAME, szLocale, sizeof(szLocale)) == 3 + && GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO3166CTRYNAME, &szLocale[3], sizeof(szLocale) - 4) == 3) + { + szLocale[2] = '_'; + Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szLocale)); + return RTStrCopy(pszName, cbName, szLocale); + } + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp new file mode 100644 index 00000000..a95b1c22 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp @@ -0,0 +1,129 @@ +/* $Id: RTLocaleQueryUserCountryCode-win.cpp $ */ +/** @file + * IPRT - RTLocaleQueryUserCountryCode, ring-3, Windows. + */ + +/* + * Copyright (C) 2017-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/win/windows.h> + +#include <iprt/locale.h> +#include "internal/iprt.h" + +#include <iprt/ctype.h> +#include <iprt/errcore.h> +#include <iprt/string.h> + +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef GEOID (WINAPI *PFNGETUSERGEOID)(GEOCLASS); +typedef INT (WINAPI *PFNGETGEOINFOW)(GEOID,GEOTYPE,LPWSTR,INT,LANGID); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Pointer to GetUserGeoID. */ +static PFNGETUSERGEOID g_pfnGetUserGeoID = NULL; +/** Pointer to GetGeoInfoW. */ +static PFNGETGEOINFOW g_pfnGetGeoInfoW = NULL; +/** Set if we've tried to resolve the APIs. */ +static bool volatile g_fResolvedApis = false; + + +RTDECL(int) RTLocaleQueryUserCountryCode(char pszCountryCode[3]) +{ + /* + * Get API pointers. + */ + PFNGETUSERGEOID pfnGetUserGeoID; + PFNGETGEOINFOW pfnGetGeoInfoW; + if (g_fResolvedApis) + { + pfnGetUserGeoID = g_pfnGetUserGeoID; + pfnGetGeoInfoW = g_pfnGetGeoInfoW; + } + else + { + pfnGetUserGeoID = (PFNGETUSERGEOID)GetProcAddress(g_hModKernel32, "GetUserGeoID"); + pfnGetGeoInfoW = (PFNGETGEOINFOW)GetProcAddress(g_hModKernel32, "GetGeoInfoW"); + g_pfnGetUserGeoID = pfnGetUserGeoID; + g_pfnGetGeoInfoW = pfnGetGeoInfoW; + g_fResolvedApis = true; + } + + int rc; + if ( pfnGetGeoInfoW + && pfnGetUserGeoID) + { + /* + * Call the API and retrieve the two letter ISO country code. + */ + GEOID idGeo = pfnGetUserGeoID(GEOCLASS_NATION); + if (idGeo != GEOID_NOT_AVAILABLE) + { + RTUTF16 wszName[16]; + RT_ZERO(wszName); + DWORD cwcReturned = pfnGetGeoInfoW(idGeo, GEO_ISO2, wszName, RT_ELEMENTS(wszName), LOCALE_NEUTRAL); + if ( cwcReturned >= 2 + && cwcReturned <= 3 + && wszName[2] == '\0' + && wszName[1] != '\0' + && RT_C_IS_ALPHA(wszName[1]) + && wszName[0] != '\0' + && RT_C_IS_ALPHA(wszName[0]) ) + { + pszCountryCode[0] = RT_C_TO_UPPER(wszName[0]); + pszCountryCode[1] = RT_C_TO_UPPER(wszName[1]); + pszCountryCode[2] = '\0'; + return VINF_SUCCESS; + } + AssertMsgFailed(("cwcReturned=%d err=%u wszName='%.16ls'\n", cwcReturned, GetLastError(), wszName)); + } + rc = VERR_NOT_AVAILABLE; + } + else + rc = VERR_NOT_SUPPORTED; + pszCountryCode[0] = 'Z'; + pszCountryCode[1] = 'Z'; + pszCountryCode[2] = '\0'; + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp new file mode 100644 index 00000000..27d3bc05 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp @@ -0,0 +1,54 @@ +/* $Id: RTLogWriteDebugger-win.cpp $ */ +/** @file + * IPRT - Log To Debugger, Win32. + */ + +/* + * 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/win/windows.h> + +#include <iprt/log.h> +#include <iprt/assert.h> + + +RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb) +{ + if (pch[cb] != '\0') + AssertBreakpoint(); + OutputDebugStringA(pch); + return; +} + diff --git a/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp new file mode 100644 index 00000000..d00dacb1 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp @@ -0,0 +1,223 @@ +/* $Id: RTSystemFirmware-win.cpp $ */ +/** @file + * IPRT - System firmware information, Win32. + */ + +/* + * 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/nt/nt-and-windows.h> +#include <WinSDKVer.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/ldr.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +#if _WIN32_MAXVER < 0x0602 /* Windows 7 or older, supply missing GetFirmwareType bits. */ +typedef enum _FIRMWARE_TYPE +{ + FirmwareTypeUnknown, + FirmwareTypeBios, + FirmwareTypeUefi, + FirmwareTypeMax +} FIRMWARE_TYPE; +typedef FIRMWARE_TYPE *PFIRMWARE_TYPE; +WINBASEAPI BOOL WINAPI GetFirmwareType(PFIRMWARE_TYPE); +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Defines the UEFI Globals UUID. */ +#define VBOX_UEFI_UUID_GLOBALS L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}" +/** Defines an UEFI dummy UUID, see MSDN docs of the API. */ +#define VBOX_UEFI_UUID_DUMMY L"{00000000-0000-0000-0000-000000000000}" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static volatile bool g_fResolvedApis = false; +static decltype(GetFirmwareType) *g_pfnGetFirmwareType; +static decltype(GetFirmwareEnvironmentVariableW) *g_pfnGetFirmwareEnvironmentVariableW; + + +static void rtSystemFirmwareResolveApis(void) +{ + FARPROC pfnTmp1 = GetProcAddress(g_hModKernel32, "GetFirmwareType"); + FARPROC pfnTmp2 = GetProcAddress(g_hModKernel32, "GetFirmwareEnvironmentVariableW"); + ASMCompilerBarrier(); /* paranoia^2 */ + + g_pfnGetFirmwareType = (decltype(GetFirmwareType) *)pfnTmp1; + g_pfnGetFirmwareEnvironmentVariableW = (decltype(GetFirmwareEnvironmentVariableW) *)pfnTmp2; + ASMAtomicWriteBool(&g_fResolvedApis, true); +} + + +static int rtSystemFirmwareGetPrivileges(LPCTSTR pcszPrivilege) +{ + HANDLE hToken; + BOOL fRc = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); + if (!fRc) + return RTErrConvertFromWin32(GetLastError()); + + int rc = VINF_SUCCESS; + + TOKEN_PRIVILEGES tokenPriv; + fRc = LookupPrivilegeValue(NULL, pcszPrivilege, &tokenPriv.Privileges[0].Luid); + if (fRc) + { + tokenPriv.PrivilegeCount = 1; + tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + fRc = AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, (PTOKEN_PRIVILEGES)NULL, 0); + if (!fRc) + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + CloseHandle(hToken); + + return rc; +} + + +RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType) +{ + AssertPtrReturn(penmFirmwareType, VERR_INVALID_POINTER); + + if (!g_fResolvedApis) + rtSystemFirmwareResolveApis(); + + *penmFirmwareType = RTSYSFWTYPE_INVALID; + int rc = VERR_NOT_SUPPORTED; + + /* GetFirmwareType is Windows 8 and later. */ + if (g_pfnGetFirmwareType) + { + FIRMWARE_TYPE enmWinFwType; + if (g_pfnGetFirmwareType(&enmWinFwType)) + { + switch (enmWinFwType) + { + case FirmwareTypeBios: + *penmFirmwareType = RTSYSFWTYPE_BIOS; + break; + case FirmwareTypeUefi: + *penmFirmwareType = RTSYSFWTYPE_UEFI; + break; + default: + *penmFirmwareType = RTSYSFWTYPE_UNKNOWN; + AssertMsgFailed(("%d\n", enmWinFwType)); + break; + } + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + /* GetFirmwareEnvironmentVariableW is XP and later. */ + else if (g_pfnGetFirmwareEnvironmentVariableW) + { + rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME); + + /* On a non-UEFI system (or such a system in legacy boot mode), we will get + back ERROR_INVALID_FUNCTION when querying any firmware variable. While on a + UEFI system we'll typically get ERROR_ACCESS_DENIED or similar as the dummy + is a non-exising dummy namespace. See the API docs. */ + SetLastError(0); + uint8_t abWhatever[64]; + DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(L"", VBOX_UEFI_UUID_DUMMY, abWhatever, sizeof(abWhatever)); + DWORD dwErr = GetLastError(); + *penmFirmwareType = cbRet != 0 || dwErr != ERROR_INVALID_FUNCTION ? RTSYSFWTYPE_UEFI : RTSYSFWTYPE_BIOS; + rc = VINF_SUCCESS; + } + return rc; +} + + +RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue) +{ + *pfValue = false; + + /* + * Translate the enmBoolean to a name: + */ + const wchar_t *pwszName = NULL; + switch (enmBoolean) + { + case RTSYSFWBOOL_SECURE_BOOT: + pwszName = L"SecureBoot"; + break; + + default: + AssertReturn(enmBoolean > RTSYSFWBOOL_INVALID && enmBoolean < RTSYSFWBOOL_END, VERR_INVALID_PARAMETER); + return VERR_SYS_UNSUPPORTED_FIRMWARE_PROPERTY; + } + + /* + * Do the query. + * Note! This will typically fail with access denied unless we're in an elevated process. + */ + if (!g_pfnGetFirmwareEnvironmentVariableW) + return VERR_NOT_SUPPORTED; + rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME); + + uint8_t bValue = 0; + DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(pwszName, VBOX_UEFI_UUID_GLOBALS, &bValue, sizeof(bValue)); + *pfValue = cbRet != 0 && bValue != 0; + if (cbRet != 0) + return VINF_SUCCESS; + DWORD dwErr = GetLastError(); + if ( dwErr == ERROR_INVALID_FUNCTION + || dwErr == ERROR_ENVVAR_NOT_FOUND) + return VINF_SUCCESS; + return RTErrConvertFromWin32(dwErr); +} + diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp new file mode 100644 index 00000000..bd9c97e5 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp @@ -0,0 +1,269 @@ +/* $Id: RTSystemQueryDmiString-win.cpp $ */ +/** @file + * IPRT - RTSystemQueryDmiString, windows ring-3. + */ + +/* + * 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 _WIN32_DCOM +#include <iprt/win/windows.h> +#include <WbemCli.h> + +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + + +/** + * Initialize COM. + * + * @returns COM status code. + */ +static HRESULT rtSystemDmiWinInitialize(void) +{ + HRESULT hrc = CoInitializeEx(0, COINIT_MULTITHREADED); + if (SUCCEEDED(hrc)) + { + hrc = CoInitializeSecurity(NULL, + -1, /* COM authentication. */ + NULL, /* Which authentication services. */ + NULL, /* Reserved. */ + RPC_C_AUTHN_LEVEL_DEFAULT, /* Default authentication. */ + RPC_C_IMP_LEVEL_IMPERSONATE, /* Default impersonation. */ + NULL, /* Authentication info. */ + EOAC_NONE, /* Additional capabilities. */ + NULL); /* Reserved. */ + if (hrc == RPC_E_TOO_LATE) + hrc = S_OK; + else if (FAILED(hrc)) + CoUninitialize(); + } + return hrc; +} + + +/** + * Undo what rtSystemDmiWinInitialize did. + */ +static void rtSystemDmiWinTerminate(void) +{ + CoUninitialize(); +} + + +/** + * Convert a UTF-8 string to a BSTR. + * + * @returns BSTR pointer. + * @param psz The UTF-8 string. + */ +static BSTR rtSystemWinBstrFromUtf8(const char *psz) +{ + PRTUTF16 pwsz = NULL; + int rc = RTStrToUtf16(psz, &pwsz); + if (RT_FAILURE(rc)) + return NULL; + BSTR pBStr = SysAllocString((const OLECHAR *)pwsz); + RTUtf16Free(pwsz); + return pBStr; +} + + +/** + * Connect to the DMI server. + * + * @returns COM status code. + * @param pLocator The locator. + * @param pszServer The server name. + * @param ppServices Where to return the services interface. + */ +static HRESULT rtSystemDmiWinConnectToServer(IWbemLocator *pLocator, const char *pszServer, IWbemServices **ppServices) +{ + AssertPtr(pLocator); + AssertPtrNull(pszServer); + AssertPtr(ppServices); + + BSTR pBStrServer = rtSystemWinBstrFromUtf8(pszServer); + if (!pBStrServer) + return E_OUTOFMEMORY; + + HRESULT hrc = pLocator->ConnectServer(pBStrServer, + NULL, + NULL, + 0, + NULL, + 0, + 0, + ppServices); + if (SUCCEEDED(hrc)) + { + hrc = CoSetProxyBlanket(*ppServices, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE); + if (FAILED(hrc)) + (*ppServices)->Release(); + } + SysFreeString(pBStrServer); + return hrc; +} + + +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); + + /* + * Figure the property name before we start. + */ + const char *pszPropName; + switch (enmString) + { + case RTSYSDMISTR_PRODUCT_NAME: pszPropName = "Name"; break; + case RTSYSDMISTR_PRODUCT_VERSION: pszPropName = "Version"; break; + case RTSYSDMISTR_PRODUCT_UUID: pszPropName = "UUID"; break; + case RTSYSDMISTR_PRODUCT_SERIAL: pszPropName = "IdentifyingNumber"; break; + case RTSYSDMISTR_MANUFACTURER: pszPropName = "Vendor"; break; + + default: + return VERR_NOT_SUPPORTED; + } + + /* + * Before we do anything with COM, we have to initialize it. + */ + bool fUninit = true; + HRESULT hrc = rtSystemDmiWinInitialize(); + if (hrc == RPC_E_CHANGED_MODE) + fUninit = false; /* don't fail if already initialized */ + else if (FAILED(hrc)) + return VERR_NOT_SUPPORTED; + + int rc = VERR_NOT_SUPPORTED; + BSTR pBstrPropName = rtSystemWinBstrFromUtf8(pszPropName); + if (pBstrPropName) + { + /* + * Instantiate the IWbemLocator, whatever that is and connect to the + * DMI serve. + */ + IWbemLocator *pLoc; + hrc = CoCreateInstance(CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID *)&pLoc); + if (SUCCEEDED(hrc)) + { + IWbemServices *pServices; + hrc = rtSystemDmiWinConnectToServer(pLoc, "ROOT\\CIMV2", &pServices); + if (SUCCEEDED(hrc)) + { + /* + * Enumerate whatever it is we're looking at and try get + * the desired property. + */ + BSTR pBstrFilter = rtSystemWinBstrFromUtf8("Win32_ComputerSystemProduct"); + if (pBstrFilter) + { + IEnumWbemClassObject *pEnum; + hrc = pServices->CreateInstanceEnum(pBstrFilter, 0, NULL, &pEnum); + if (SUCCEEDED(hrc)) + { + do + { + IWbemClassObject *pObj; + ULONG cObjRet; + hrc = pEnum->Next(WBEM_INFINITE, 1, &pObj, &cObjRet); + if ( SUCCEEDED(hrc) + && cObjRet >= 1) + { + VARIANT Var; + VariantInit(&Var); + hrc = pObj->Get(pBstrPropName, 0, &Var, 0, 0); + if ( SUCCEEDED(hrc) + && V_VT(&Var) == VT_BSTR) + { + /* + * Convert the BSTR to UTF-8 and copy it + * into the return buffer. + */ + char *pszValue; + rc = RTUtf16ToUtf8(Var.bstrVal, &pszValue); + if (RT_SUCCESS(rc)) + { + rc = RTStrCopy(pszBuf, cbBuf, pszValue); + RTStrFree(pszValue); + hrc = WBEM_S_FALSE; + } + } + VariantClear(&Var); + pObj->Release(); + } + } while (hrc != WBEM_S_FALSE); + + pEnum->Release(); + } + SysFreeString(pBstrFilter); + } + else + hrc = E_OUTOFMEMORY; + pServices->Release(); + } + pLoc->Release(); + } + SysFreeString(pBstrPropName); + } + else + hrc = E_OUTOFMEMORY; + if (fUninit) + rtSystemDmiWinTerminate(); + if (FAILED(hrc) && rc == VERR_NOT_SUPPORTED) + rc = VERR_NOT_SUPPORTED; + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp new file mode 100644 index 00000000..56688e42 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp @@ -0,0 +1,358 @@ +/* $Id: RTSystemQueryOSInfo-win.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 "internal/iprt.h" +#include <iprt/win/windows.h> +#include <WinUser.h> + +#include "internal-r3-win.h" +#include <iprt/system.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/errcore.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * These are the PRODUCT_* defines found in the Vista Platform SDK and returned + * by GetProductInfo(). + * + * We define them ourselves because we don't necessarily have any Vista PSDK around. + */ +typedef enum RTWINPRODTYPE +{ + kRTWinProdType_UNDEFINED = 0x00000000, ///< An unknown product + kRTWinProdType_BUSINESS = 0x00000006, ///< Business Edition + kRTWinProdType_BUSINESS_N = 0x00000010, ///< Business Edition + kRTWinProdType_CLUSTER_SERVER = 0x00000012, ///< Cluster Server Edition + kRTWinProdType_DATACENTER_SERVER = 0x00000008, ///< Server Datacenter Edition (full installation) + kRTWinProdType_DATACENTER_SERVER_CORE = 0x0000000C, ///< Server Datacenter Edition (core installation) + kRTWinProdType_ENTERPRISE = 0x00000004, ///< Enterprise Edition + kRTWinProdType_ENTERPRISE_N = 0x0000001B, ///< Enterprise Edition + kRTWinProdType_ENTERPRISE_SERVER = 0x0000000A, ///< Server Enterprise Edition (full installation) + kRTWinProdType_ENTERPRISE_SERVER_CORE = 0x0000000E, ///< Server Enterprise Edition (core installation) + kRTWinProdType_ENTERPRISE_SERVER_IA64 = 0x0000000F, ///< Server Enterprise Edition for Itanium-based Systems + kRTWinProdType_HOME_BASIC = 0x00000002, ///< Home Basic Edition + kRTWinProdType_HOME_BASIC_N = 0x00000005, ///< Home Basic Edition + kRTWinProdType_HOME_PREMIUM = 0x00000003, ///< Home Premium Edition + kRTWinProdType_HOME_PREMIUM_N = 0x0000001A, ///< Home Premium Edition + kRTWinProdType_HOME_SERVER = 0x00000013, ///< Home Server Edition + kRTWinProdType_SERVER_FOR_SMALLBUSINESS = 0x00000018, ///< Server for Small Business Edition + kRTWinProdType_SMALLBUSINESS_SERVER = 0x00000009, ///< Small Business Server + kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, ///< Small Business Server Premium Edition + kRTWinProdType_STANDARD_SERVER = 0x00000007, ///< Server Standard Edition (full installation) + kRTWinProdType_STANDARD_SERVER_CORE = 0x0000000D, ///< Server Standard Edition (core installation) + kRTWinProdType_STARTER = 0x0000000B, ///< Starter Edition + kRTWinProdType_STORAGE_ENTERPRISE_SERVER = 0x00000017, ///< Storage Server Enterprise Edition + kRTWinProdType_STORAGE_EXPRESS_SERVER = 0x00000014, ///< Storage Server Express Edition + kRTWinProdType_STORAGE_STANDARD_SERVER = 0x00000015, ///< Storage Server Standard Edition + kRTWinProdType_STORAGE_WORKGROUP_SERVER = 0x00000016, ///< Storage Server Workgroup Edition + kRTWinProdType_ULTIMATE = 0x00000001, ///< Ultimate Edition + kRTWinProdType_ULTIMATE_N = 0x0000001C, ///< Ultimate Edition + kRTWinProdType_WEB_SERVER = 0x00000011, ///< Web Server Edition (full) + kRTWinProdType_WEB_SERVER_CORE = 0x0000001D ///< Web Server Edition (core) +} RTWINPRODTYPE; + + +/** + * Wrapper around the GetProductInfo API. + * + * @returns The vista type. + */ +static RTWINPRODTYPE rtSystemWinGetProductInfo(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion) +{ + BOOL (WINAPI *pfnGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD); + pfnGetProductInfo = (BOOL (WINAPI *)(DWORD, DWORD, DWORD, DWORD, PDWORD))GetProcAddress(GetModuleHandle("kernel32.dll"), + "GetProductInfo"); + if (pfnGetProductInfo) + { + DWORD dwProductType = kRTWinProdType_UNDEFINED; + if (pfnGetProductInfo(dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion, dwSpMinorVersion, &dwProductType)) + return (RTWINPRODTYPE)dwProductType; + } + return kRTWinProdType_UNDEFINED; +} + + + +/** + * Appends the product type if available (Vista & 2003 only for some reason). + * + * @param pszTmp The buffer. + * @param cbTmp The buffer size. + */ +static void rtSystemWinAppendProductType(char *pszTmp, size_t cbTmp) +{ + RTWINPRODTYPE enmVistaType = rtSystemWinGetProductInfo(6, 0, 0, 0); + switch (enmVistaType) + { + case kRTWinProdType_BUSINESS: RTStrCat(pszTmp, cbTmp, " Business Edition"); break; + case kRTWinProdType_BUSINESS_N: RTStrCat(pszTmp, cbTmp, " Business Edition"); break; + case kRTWinProdType_CLUSTER_SERVER: RTStrCat(pszTmp, cbTmp, " Cluster Server Edition"); break; + case kRTWinProdType_DATACENTER_SERVER: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (full installation)"); break; + case kRTWinProdType_DATACENTER_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (core installation)"); break; + case kRTWinProdType_ENTERPRISE: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break; + case kRTWinProdType_ENTERPRISE_N: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break; + case kRTWinProdType_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (full installation)"); break; + case kRTWinProdType_ENTERPRISE_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (core installation)"); break; + case kRTWinProdType_ENTERPRISE_SERVER_IA64: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition for Itanium-based Systems"); break; + case kRTWinProdType_HOME_BASIC: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break; + case kRTWinProdType_HOME_BASIC_N: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break; + case kRTWinProdType_HOME_PREMIUM: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break; + case kRTWinProdType_HOME_PREMIUM_N: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break; + case kRTWinProdType_HOME_SERVER: RTStrCat(pszTmp, cbTmp, " Home Server Edition"); break; + case kRTWinProdType_SERVER_FOR_SMALLBUSINESS: RTStrCat(pszTmp, cbTmp, " Server for Small Business Edition"); break; + case kRTWinProdType_SMALLBUSINESS_SERVER: RTStrCat(pszTmp, cbTmp, " Small Business Server"); break; + case kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM: RTStrCat(pszTmp, cbTmp, " Small Business Server Premium Edition"); break; + case kRTWinProdType_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (full installation)"); break; + case kRTWinProdType_STANDARD_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (core installation)"); break; + case kRTWinProdType_STARTER: RTStrCat(pszTmp, cbTmp, " Starter Edition"); break; + case kRTWinProdType_STORAGE_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Enterprise Edition"); break; + case kRTWinProdType_STORAGE_EXPRESS_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Express Edition"); break; + case kRTWinProdType_STORAGE_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Standard Edition"); break; + case kRTWinProdType_STORAGE_WORKGROUP_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Workgroup Edition"); break; + case kRTWinProdType_ULTIMATE: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break; + case kRTWinProdType_ULTIMATE_N: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break; + case kRTWinProdType_WEB_SERVER: RTStrCat(pszTmp, cbTmp, " Web Server Edition (full installation)"); break; + case kRTWinProdType_WEB_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Web Server Edition (core installation)"); break; + case kRTWinProdType_UNDEFINED: break; + } +} + + +/** + * Services the RTSYSOSINFO_PRODUCT, RTSYSOSINFO_RELEASE + * and RTSYSOSINFO_SERVICE_PACK requests. + * + * @returns See RTSystemQueryOSInfo. + * @param enmInfo See RTSystemQueryOSInfo. + * @param pszInfo See RTSystemQueryOSInfo. + * @param cchInfo See RTSystemQueryOSInfo. + */ +static int rtSystemWinQueryOSVersion(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo) +{ + /* + * Make sure it's terminated correctly in case of error. + */ + *pszInfo = '\0'; + + /* + * Check that we got the windows version at init time. + */ + AssertReturn(g_WinOsInfoEx.dwOSVersionInfoSize, VERR_WRONG_ORDER); + + /* + * Service the request. + */ + char szTmp[512]; + szTmp[0] = '\0'; + switch (enmInfo) + { + /* + * The product name. + */ + case RTSYSOSINFO_PRODUCT: + { + switch (g_enmWinVer) + { + case kRTWinOSType_95: strcpy(szTmp, "Windows 95"); break; + case kRTWinOSType_95SP1: strcpy(szTmp, "Windows 95 (Service Pack 1)"); break; + case kRTWinOSType_95OSR2: strcpy(szTmp, "Windows 95 (OSR 2)"); break; + case kRTWinOSType_98: strcpy(szTmp, "Windows 98"); break; + case kRTWinOSType_98SP1: strcpy(szTmp, "Windows 98 (Service Pack 1)"); break; + case kRTWinOSType_98SE: strcpy(szTmp, "Windows 98 (Second Edition)"); break; + case kRTWinOSType_ME: strcpy(szTmp, "Windows Me"); break; + case kRTWinOSType_NT310: strcpy(szTmp, "Windows NT 3.10"); break; + case kRTWinOSType_NT350: strcpy(szTmp, "Windows NT 3.50"); break; + case kRTWinOSType_NT351: strcpy(szTmp, "Windows NT 3.51"); break; + case kRTWinOSType_NT4: strcpy(szTmp, "Windows NT 4.0"); break; + case kRTWinOSType_2K: strcpy(szTmp, "Windows 2000"); break; + case kRTWinOSType_XP: + strcpy(szTmp, "Windows XP"); + if (g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL) + RTStrCat(szTmp, sizeof(szTmp), " Home"); + if ( g_WinOsInfoEx.wProductType == VER_NT_WORKSTATION + && !(g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL)) + RTStrCat(szTmp, sizeof(szTmp), " Professional"); +#if 0 /** @todo fixme */ + if (GetSystemMetrics(SM_MEDIACENTER)) + RTStrCat(szTmp, sizeof(szTmp), " Media Center"); +#endif + break; + + case kRTWinOSType_2003: strcpy(szTmp, "Windows 2003"); break; + case kRTWinOSType_VISTA: + { + strcpy(szTmp, "Windows Vista"); + rtSystemWinAppendProductType(szTmp, sizeof(szTmp)); + break; + } + case kRTWinOSType_2008: strcpy(szTmp, "Windows 2008"); break; + case kRTWinOSType_7: strcpy(szTmp, "Windows 7"); break; + case kRTWinOSType_2008R2: strcpy(szTmp, "Windows 2008 R2"); break; + case kRTWinOSType_8: strcpy(szTmp, "Windows 8"); break; + case kRTWinOSType_2012: strcpy(szTmp, "Windows 2012"); break; + case kRTWinOSType_81: strcpy(szTmp, "Windows 8.1"); break; + case kRTWinOSType_2012R2: strcpy(szTmp, "Windows 2012 R2"); break; + case kRTWinOSType_10: strcpy(szTmp, "Windows 10"); break; + case kRTWinOSType_2016: strcpy(szTmp, "Windows 2016"); break; + case kRTWinOSType_2019: strcpy(szTmp, "Windows 2019"); break; + case kRTWinOSType_2022: strcpy(szTmp, "Windows 2022"); break; + case kRTWinOSType_11: strcpy(szTmp, "Windows 11"); break; + + case kRTWinOSType_NT_UNKNOWN: + RTStrPrintf(szTmp, sizeof(szTmp), "Unknown NT v%u.%u", + g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion); + break; + + default: + AssertFailed(); + case kRTWinOSType_UNKNOWN: + RTStrPrintf(szTmp, sizeof(szTmp), "Unknown %d v%u.%u", + g_WinOsInfoEx.dwPlatformId, g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion); + break; + } + break; + } + + /* + * The release. + */ + case RTSYSOSINFO_RELEASE: + { + RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u.%u", + g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber); + break; + } + + + /* + * Get the service pack. + */ + case RTSYSOSINFO_SERVICE_PACK: + { + if (g_WinOsInfoEx.wServicePackMajor) + { + if (g_WinOsInfoEx.wServicePackMinor) + RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u", + (unsigned)g_WinOsInfoEx.wServicePackMajor, (unsigned)g_WinOsInfoEx.wServicePackMinor); + else + RTStrPrintf(szTmp, sizeof(szTmp), "%u", + (unsigned)g_WinOsInfoEx.wServicePackMajor); + } + else if (g_WinOsInfoEx.szCSDVersion[0]) + { + /* just copy the entire string. */ + char *pszTmp = szTmp; + int rc = RTUtf16ToUtf8Ex(g_WinOsInfoEx.szCSDVersion, RT_ELEMENTS(g_WinOsInfoEx.szCSDVersion), + &pszTmp, sizeof(szTmp), NULL); + if (RT_SUCCESS(rc)) + RTStrStripR(szTmp); + else + szTmp[0] = '\0'; + AssertCompile(sizeof(szTmp) > sizeof(g_WinOsInfoEx.szCSDVersion)); + } + else + { + switch (g_enmWinVer) + { + case kRTWinOSType_95SP1: strcpy(szTmp, "1"); break; + case kRTWinOSType_98SP1: strcpy(szTmp, "1"); break; + default: + break; + } + } + break; + } + + default: + AssertFatalFailed(); + } + + /* + * Copy the result to the return buffer. + */ + size_t cchTmp = strlen(szTmp); + Assert(cchTmp < sizeof(szTmp)); + if (cchTmp < cchInfo) + { + memcpy(pszInfo, szTmp, cchTmp + 1); + return VINF_SUCCESS; + } + memcpy(pszInfo, szTmp, cchInfo - 1); + pszInfo[cchInfo - 1] = '\0'; + return VERR_BUFFER_OVERFLOW; +} + + +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); + if (!cchInfo) + return VERR_BUFFER_OVERFLOW; + + + /* + * Handle the request. + */ + switch (enmInfo) + { + case RTSYSOSINFO_PRODUCT: + case RTSYSOSINFO_RELEASE: + case RTSYSOSINFO_SERVICE_PACK: + return rtSystemWinQueryOSVersion(enmInfo, pszInfo, cchInfo); + + case RTSYSOSINFO_VERSION: + default: + *pszInfo = '\0'; + } + + return VERR_NOT_SUPPORTED; +} + diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp new file mode 100644 index 00000000..49465166 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp @@ -0,0 +1,131 @@ +/* $Id: RTSystemQueryTotalRam-win.cpp $ */ +/** @file + * IPRT - RTSystemQueryTotalRam, windows ring-3. + */ + +/* + * 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/win/windows.h> +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/string.h> + +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static bool volatile g_fInitialized = false; +typedef BOOL (WINAPI *PFNGLOBALMEMORYSTATUSEX)(LPMEMORYSTATUSEX); +static PFNGLOBALMEMORYSTATUSEX g_pfnGlobalMemoryStatusEx = NULL; + + +/** + * The GlobalMemoryStatusEx API is not available on older Windows version. + * + * @returns Pointer to GlobalMemoryStatusEx or NULL if not available. + */ +DECLINLINE(PFNGLOBALMEMORYSTATUSEX) rtSystemWinGetExApi(void) +{ + PFNGLOBALMEMORYSTATUSEX pfnEx; + if (g_fInitialized) + pfnEx = g_pfnGlobalMemoryStatusEx; + else + { + pfnEx = (PFNGLOBALMEMORYSTATUSEX)GetProcAddress(g_hModKernel32, "GlobalMemoryStatusEx"); + g_pfnGlobalMemoryStatusEx = pfnEx; + g_fInitialized = true; + } + return pfnEx; +} + + +RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi(); + if (pfnGlobalMemoryStatusEx) + { + MEMORYSTATUSEX MemStatus; + MemStatus.dwLength = sizeof(MemStatus); + if (pfnGlobalMemoryStatusEx(&MemStatus)) + *pcb = MemStatus.ullTotalPhys; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + MEMORYSTATUS MemStatus; + RT_ZERO(MemStatus); + MemStatus.dwLength = sizeof(MemStatus); + GlobalMemoryStatus(&MemStatus); + *pcb = MemStatus.dwTotalPhys; + } + return rc; +} + + +RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi(); + if (pfnGlobalMemoryStatusEx) + { + MEMORYSTATUSEX MemStatus; + MemStatus.dwLength = sizeof(MemStatus); + if (pfnGlobalMemoryStatusEx(&MemStatus)) + *pcb = MemStatus.ullAvailPhys; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + MEMORYSTATUS MemStatus; + RT_ZERO(MemStatus); + MemStatus.dwLength = sizeof(MemStatus); + GlobalMemoryStatus(&MemStatus); + *pcb = MemStatus.dwAvailPhys; + } + return rc; +} diff --git a/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp new file mode 100644 index 00000000..9a19ef3c --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp @@ -0,0 +1,183 @@ +/* $Id: RTSystemShutdown-win.cpp $ */ +/** @file + * IPRT - RTSystemShutdown, Windows. + */ + +/* + * 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/assert.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include <iprt/win/windows.h> + + +RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg) +{ + AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Before we start, try grant the necessary privileges. + */ + DWORD dwErr; + HANDLE hToken = NULL; + if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE /*OpenAsSelf*/, &hToken)) + dwErr = NO_ERROR; + else + { + dwErr = GetLastError(); + if (dwErr == ERROR_NO_TOKEN) + { + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + dwErr = NO_ERROR; + else + dwErr = GetLastError(); + } + } + if (dwErr == NO_ERROR) + { + union + { + TOKEN_PRIVILEGES TokenPriv; + char ab[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; + } u; + u.TokenPriv.PrivilegeCount = 1; + u.TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (LookupPrivilegeValue(NULL /*localhost*/, SE_SHUTDOWN_NAME, &u.TokenPriv.Privileges[0].Luid)) + { + if (!AdjustTokenPrivileges(hToken, + FALSE /*DisableAllPrivileges*/, + &u.TokenPriv, + RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]), + NULL, + NULL) ) + dwErr = GetLastError(); + } + else + dwErr = GetLastError(); + CloseHandle(hToken); + } + + /* + * Do some parameter conversion. + */ + PRTUTF16 pwszLogMsg; + int rc = RTStrToUtf16(pszLogMsg, &pwszLogMsg); + if (RT_FAILURE(rc)) + return rc; + DWORD cSecsTimeout = (cMsDelay + 499) / 1000; + + /* + * If we're told to power off the system, we should try use InitiateShutdownW (6.0+) + * or ExitWindowsEx (3.50) rather than InitiateSystemShutdownW, because these other + * APIs allows us to explicitly specify that we want to power off. + * + * Note! For NT version 4, 3.51, and 3.50 the system may instaed reboot since the + * x86 HALs typically didn't know how to perform a power off. + */ + bool fDone = false; + if ( (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF + || (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF_HALT) + { + /* This API has the grace period thing. */ + decltype(InitiateShutdownW) *pfnInitiateShutdownW; + pfnInitiateShutdownW = (decltype(InitiateShutdownW) *)GetProcAddress(GetModuleHandleW(L"ADVAPI32.DLL"), "InitiateShutdownW"); + if (pfnInitiateShutdownW) + { + DWORD fShutdownFlags = SHUTDOWN_POWEROFF; + if (fFlags & RTSYSTEM_SHUTDOWN_FORCE) + fShutdownFlags |= SHUTDOWN_FORCE_OTHERS | SHUTDOWN_FORCE_SELF; + DWORD fReason = SHTDN_REASON_MAJOR_OTHER | (fFlags & RTSYSTEM_SHUTDOWN_PLANNED ? SHTDN_REASON_FLAG_PLANNED : 0); + dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason); + if (dwErr == ERROR_INVALID_PARAMETER) + { + fReason &= ~SHTDN_REASON_FLAG_PLANNED; /* just in case... */ + dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason); + } + if (dwErr == ERROR_SUCCESS) + { + rc = VINF_SUCCESS; + fDone = true; + } + } + + if (!fDone) + { + /* No grace period here, too bad. */ + decltype(ExitWindowsEx) *pfnExitWindowsEx; + pfnExitWindowsEx = (decltype(ExitWindowsEx) *)GetProcAddress(GetModuleHandleW(L"USER32.DLL"), "ExitWindowsEx"); + if (pfnExitWindowsEx) + { + DWORD fExitWindows = EWX_POWEROFF | EWX_SHUTDOWN; + if (fFlags & RTSYSTEM_SHUTDOWN_FORCE) + fExitWindows |= EWX_FORCE | EWX_FORCEIFHUNG; + + if (pfnExitWindowsEx(fExitWindows, SHTDN_REASON_MAJOR_OTHER)) + fDone = true; + else if (pfnExitWindowsEx(fExitWindows & ~EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_OTHER)) + fDone = true; + } + } + } + + /* + * Fall back on the oldest API. + */ + if (!fDone) + { + BOOL fRebootAfterShutdown = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_REBOOT + ? TRUE : FALSE; + BOOL fForceAppsClosed = fFlags & RTSYSTEM_SHUTDOWN_FORCE ? TRUE : FALSE; + if (InitiateSystemShutdownW(NULL /*pwszMachineName = NULL = localhost*/, + pwszLogMsg, + cSecsTimeout, + fForceAppsClosed, + fRebootAfterShutdown)) + rc = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_HALT ? VINF_SYS_MAY_POWER_OFF : VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(dwErr); + } + + RTUtf16Free(pwszLogMsg); + return rc; +} +RT_EXPORT_SYMBOL(RTSystemShutdown); + diff --git a/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp new file mode 100644 index 00000000..310d8507 --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp @@ -0,0 +1,117 @@ +/* $Id: RTTimeZoneGetCurrent-win.cpp $ */ +/** @file + * IPRT - RTTimeZoneGetCurrent, generic. + */ + +/* + * Copyright (C) 2017-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" + +#include <iprt/errcore.h> +#include <iprt/env.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include <iprt/win/windows.h> +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef DWORD (WINAPI *PFNGETDYNAMICTIMEZONEINFORMATION)(PDYNAMIC_TIME_ZONE_INFORMATION); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Pointer to the GetDynamicTimeZoneInformation API if present. */ +static PFNGETDYNAMICTIMEZONEINFORMATION g_pfnGetDynamicTimeZoneInformation = NULL; +/** Flipped after we've tried to resolve g_pfnGetDynamicTimeZoneInformation. */ +static bool volatile g_fResolvedApi = false; + + +RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(cbName > 0, VERR_BUFFER_OVERFLOW); + + /* + * Resolve API. + */ + PFNGETDYNAMICTIMEZONEINFORMATION pfnApi; + if (g_fResolvedApi) + pfnApi = g_pfnGetDynamicTimeZoneInformation; + else + { + pfnApi = (PFNGETDYNAMICTIMEZONEINFORMATION)GetProcAddress(g_hModKernel32, "GetDynamicTimeZoneInformation"); + g_pfnGetDynamicTimeZoneInformation = pfnApi; + g_fResolvedApi = true; + } + + /* + * Call the API and convert the name we get. + */ + union + { + TIME_ZONE_INFORMATION Tzi; + DYNAMIC_TIME_ZONE_INFORMATION DynTzi; + } uBuf; + RT_ZERO(uBuf); + DWORD dwRc; + PCRTUTF16 pwszSrcName; + size_t cwcSrcName; + if (pfnApi) + { + dwRc = pfnApi(&uBuf.DynTzi); + pwszSrcName = uBuf.DynTzi.TimeZoneKeyName; + cwcSrcName = RT_ELEMENTS(uBuf.DynTzi.TimeZoneKeyName); + } + else + { + /* Not sure how helpful this fallback really is... */ + dwRc = GetTimeZoneInformation(&uBuf.Tzi); + pwszSrcName = uBuf.Tzi.StandardName; + cwcSrcName = RT_ELEMENTS(uBuf.Tzi.StandardName); + } + if (dwRc != TIME_ZONE_ID_INVALID) + { + Assert(*pwszSrcName != '\0'); + return RTUtf16ToUtf8Ex(pwszSrcName, cwcSrcName, &pszName, cbName, &cbName); + } + return RTErrConvertFromWin32(GetLastError()); +} + diff --git a/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp new file mode 100644 index 00000000..99a5692f --- /dev/null +++ b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp @@ -0,0 +1,82 @@ +/* $Id: RTUuidCreate-win.cpp $ */ +/** @file + * IPRT - UUID, Windows 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 * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_UUID +#include <iprt/win/windows.h> + +#include <iprt/uuid.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/rand.h> + +#include "internal-r3-win.h" + + +RTDECL(int) RTUuidCreate(PRTUUID pUuid) +{ + /* + * Input validation. + */ + AssertPtrReturn(pUuid, VERR_INVALID_POINTER); + + /* + * When using the UuidCreate API shortly after boot on NT 3.1 it typcially + * hangs for a long long time while polling for some service to start. + * What then usually happens next is a failure because it couldn't figure + * out the MAC address of the NIC. So, on NT 3.1 we always use the fallback. + */ + if (g_enmWinVer != kRTWinOSType_NT310) + { + RPC_STATUS rc = UuidCreate((UUID *)pUuid); + if ( rc == RPC_S_OK + || rc == RPC_S_UUID_LOCAL_ONLY) + return VINF_SUCCESS; + AssertMsg(rc == RPC_S_UUID_NO_ADDRESS, ("UuidCreate -> %u (%#x)\n", rc, rc)); + } + + /* + * Use generic implementation as fallback (copy of RTUuidCreate-generic.cpp). + */ + RTRandBytes(pUuid, sizeof(*pUuid)); + pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80; + pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def new file mode 100644 index 00000000..5395dc50 --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def @@ -0,0 +1,1678 @@ + + ??0?$_Yarn@D@std@@QAE@ABV01@@Z + ??0?$_Yarn@D@std@@QAE@PBD@Z + ??0?$_Yarn@D@std@@QAE@XZ + ??0?$basic_ios@DU?$char_traits@D@std@@@std@@IAE@XZ + ??0?$basic_ios@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z + ??0?$basic_ios@GU?$char_traits@G@std@@@std@@IAE@XZ + ??0?$basic_ios@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z + ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAE@XZ + ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z + ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z + ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z + ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z + ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N1@Z + ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z + ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@@Z + ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N1@Z + ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z + ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@@Z + ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N1@Z + ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z + ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@@Z + ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z + ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@_N@Z + ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z + ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@_N@Z + ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z + ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z + ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@_N@Z + ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@ABV01@@Z + ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@W4_Uninitialized@1@@Z + ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@XZ + ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@ABV01@@Z + ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@W4_Uninitialized@1@@Z + ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@XZ + ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@ABV01@@Z + ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@W4_Uninitialized@1@@Z + ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@XZ + ??0?$codecvt@DDH@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$codecvt@DDH@std@@QAE@I@Z + ??0?$codecvt@GDH@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$codecvt@GDH@std@@QAE@I@Z + ??0?$codecvt@_WDH@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$codecvt@_WDH@std@@QAE@I@Z + ??0?$ctype@D@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$ctype@D@std@@QAE@PBF_NI@Z + ??0?$ctype@G@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$ctype@G@std@@QAE@I@Z + ??0?$ctype@_W@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$ctype@_W@std@@QAE@I@Z + ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z + ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z + ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z + ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z + ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z + ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z + ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAE@PBDI@Z + ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z + ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z + ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z + ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z + ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z + ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z + ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z + ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z + ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z + ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z + ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z + ??0Init@ios_base@std@@QAE@XZ + ??0_Concurrent_queue_base_v4@details@Concurrency@@IAE@I@Z + ??0_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@ABV_Concurrent_queue_base_v4@12@@Z + ??0_Container_base12@std@@QAE@ABU01@@Z + ??0_Container_base12@std@@QAE@XZ + ??0_Init_locks@std@@QAE@XZ + ??0_Locimp@locale@std@@AAE@ABV012@@Z + ??0_Locimp@locale@std@@AAE@_N@Z + ??0_Locinfo@std@@QAE@HPBD@Z + ??0_Locinfo@std@@QAE@PBD@Z + ??0_Lockit@std@@QAE@H@Z + ??0_Lockit@std@@QAE@XZ + ??0_Mutex@std@@QAE@W4_Uninitialized@1@@Z + ??0_Mutex@std@@QAE@XZ + ??0_Runtime_object@details@Concurrency@@QAE@H@Z + ??0_Runtime_object@details@Concurrency@@QAE@XZ + ??0_Timevec@std@@QAE@ABV01@@Z + ??0_Timevec@std@@QAE@PAX@Z + ??0_UShinit@std@@QAE@XZ + ??0_Winit@std@@QAE@XZ + ??0agent@Concurrency@@QAE@AAVScheduleGroup@1@@Z + ??0agent@Concurrency@@QAE@AAVScheduler@1@@Z + ??0agent@Concurrency@@QAE@XZ + ??0codecvt_base@std@@QAE@I@Z + ??0ctype_base@std@@QAE@I@Z + ??0facet@locale@std@@IAE@I@Z + ??0id@locale@std@@QAE@I@Z + ??0ios_base@std@@IAE@XZ + ??0ios_base@std@@QAE@ABV01@@Z + ??0time_base@std@@QAE@I@Z + ??1?$_Yarn@D@std@@QAE@XZ + ??1?$basic_ios@DU?$char_traits@D@std@@@std@@UAE@XZ + ??1?$basic_ios@GU?$char_traits@G@std@@@std@@UAE@XZ + ??1?$basic_ios@_WU?$char_traits@_W@std@@@std@@UAE@XZ + ??1?$basic_iostream@DU?$char_traits@D@std@@@std@@UAE@XZ + ??1?$basic_iostream@GU?$char_traits@G@std@@@std@@UAE@XZ + ??1?$basic_iostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ + ??1?$basic_istream@DU?$char_traits@D@std@@@std@@UAE@XZ + ??1?$basic_istream@GU?$char_traits@G@std@@@std@@UAE@XZ + ??1?$basic_istream@_WU?$char_traits@_W@std@@@std@@UAE@XZ + ??1?$basic_ostream@DU?$char_traits@D@std@@@std@@UAE@XZ + ??1?$basic_ostream@GU?$char_traits@G@std@@@std@@UAE@XZ + ??1?$basic_ostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ + ??1?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAE@XZ + ??1?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAE@XZ + ??1?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAE@XZ + ??1?$codecvt@DDH@std@@MAE@XZ + ??1?$codecvt@GDH@std@@MAE@XZ + ??1?$codecvt@_WDH@std@@MAE@XZ + ??1?$ctype@D@std@@MAE@XZ + ??1?$ctype@G@std@@MAE@XZ + ??1?$ctype@_W@std@@MAE@XZ + ??1?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ + ??1?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ + ??1?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ + ??1?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ + ??1?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ + ??1?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ + ??1?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ + ??1?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ + ??1?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ + ??1?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ + ??1?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ + ??1?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ + ??1Init@ios_base@std@@QAE@XZ + ??1_Concurrent_queue_base_v4@details@Concurrency@@MAE@XZ + ??1_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@XZ + ??1_Concurrent_vector_base_v4@details@Concurrency@@IAE@XZ + ??1_Container_base12@std@@QAE@XZ + ??1_Init_locks@std@@QAE@XZ + ??1_Locimp@locale@std@@MAE@XZ + ??1_Locinfo@std@@QAE@XZ + ??1_Lockit@std@@QAE@XZ + ??1_Mutex@std@@QAE@XZ + ??1_Timevec@std@@QAE@XZ + ??1_UShinit@std@@QAE@XZ + ??1_Winit@std@@QAE@XZ + ??1agent@Concurrency@@UAE@XZ + ??1codecvt_base@std@@UAE@XZ + ??1ctype_base@std@@UAE@XZ + ??1facet@locale@std@@UAE@XZ + ??1ios_base@std@@UAE@XZ + ??1time_base@std@@UAE@XZ + ??4?$_Iosb@H@std@@QAEAAV01@ABV01@@Z + ??4?$_Yarn@D@std@@QAEAAV01@ABV01@@Z + ??4?$_Yarn@D@std@@QAEAAV01@PBD@Z + ??4?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z + ??4?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEAAV01@ABV01@@Z + ??4?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEAAV01@ABV01@@Z + ??4?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEAAV01@ABV01@@Z + ??4?$numeric_limits@C@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@D@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@E@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@F@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@G@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@H@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@I@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@J@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@K@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@M@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@N@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@O@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@_J@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@_K@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@_N@std@@QAEAAV01@ABV01@@Z + ??4?$numeric_limits@_W@std@@QAEAAV01@ABV01@@Z + ??4Init@ios_base@std@@QAEAAV012@ABV012@@Z + ??4_Container_base0@std@@QAEAAU01@ABU01@@Z + ??4_Container_base12@std@@QAEAAU01@ABU01@@Z + ??4_Init_locks@std@@QAEAAV01@ABV01@@Z + ??4_Num_base@std@@QAEAAU01@ABU01@@Z + ??4_Num_float_base@std@@QAEAAU01@ABU01@@Z + ??4_Num_int_base@std@@QAEAAU01@ABU01@@Z + ??4_Timevec@std@@QAEAAV01@ABV01@@Z + ??4_UShinit@std@@QAEAAV01@ABV01@@Z + ??4_Winit@std@@QAEAAV01@ABV01@@Z + ??4ios_base@std@@QAEAAV01@ABV01@@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAF@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAG@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAH@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAI@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAJ@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAK@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAM@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAN@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAO@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAPAX@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_J@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_K@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_N@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAF@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAG@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAH@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAI@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAJ@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAK@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAM@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAN@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAO@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAPAX@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_J@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_K@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_N@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAF@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAG@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAH@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAI@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAJ@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAK@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAM@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAN@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAO@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAPAX@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_J@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_K@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_N@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@F@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@G@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@I@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@J@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@K@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@N@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@O@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PBX@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_J@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_K@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_N@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@F@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@G@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@H@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@I@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@J@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@K@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@M@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@N@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@O@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PBX@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_J@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_K@Z + ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_N@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@F@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@G@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@H@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@I@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@J@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@K@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@M@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@N@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@O@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PBX@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_J@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_K@Z + ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_N@Z + ??7ios_base@std@@QBE_NXZ + ??Bid@locale@std@@QAEIXZ + ??Bios_base@std@@QBEPAXXZ + ??_7?$basic_ios@DU?$char_traits@D@std@@@std@@6B@ + ??_7?$basic_ios@GU?$char_traits@G@std@@@std@@6B@ + ??_7?$basic_ios@_WU?$char_traits@_W@std@@@std@@6B@ + ??_7?$basic_iostream@DU?$char_traits@D@std@@@std@@6B@ + ??_7?$basic_iostream@GU?$char_traits@G@std@@@std@@6B@ + ??_7?$basic_iostream@_WU?$char_traits@_W@std@@@std@@6B@ + ??_7?$basic_istream@DU?$char_traits@D@std@@@std@@6B@ + ??_7?$basic_istream@GU?$char_traits@G@std@@@std@@6B@ + ??_7?$basic_istream@_WU?$char_traits@_W@std@@@std@@6B@ + ??_7?$basic_ostream@DU?$char_traits@D@std@@@std@@6B@ + ??_7?$basic_ostream@GU?$char_traits@G@std@@@std@@6B@ + ??_7?$basic_ostream@_WU?$char_traits@_W@std@@@std@@6B@ + ??_7?$basic_streambuf@DU?$char_traits@D@std@@@std@@6B@ + ??_7?$basic_streambuf@GU?$char_traits@G@std@@@std@@6B@ + ??_7?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@6B@ + ??_7?$codecvt@DDH@std@@6B@ + ??_7?$codecvt@GDH@std@@6B@ + ??_7?$codecvt@_WDH@std@@6B@ + ??_7?$ctype@D@std@@6B@ + ??_7?$ctype@G@std@@6B@ + ??_7?$ctype@_W@std@@6B@ + ??_7?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@ + ??_7?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@ + ??_7?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@ + ??_7?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@ + ??_7?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@ + ??_7?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@ + ??_7?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@ + ??_7?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@ + ??_7?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@ + ??_7?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@ + ??_7?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@ + ??_7?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@ + ??_7_Locimp@locale@std@@6B@ + ??_7codecvt_base@std@@6B@ + ??_7ctype_base@std@@6B@ + ??_7ios_base@std@@6B@ + ??_7time_base@std@@6B@ + ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_istream@DU?$char_traits@D@std@@@1@@ + ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_ostream@DU?$char_traits@D@std@@@1@@ + ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_istream@GU?$char_traits@G@std@@@1@@ + ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_ostream@GU?$char_traits@G@std@@@1@@ + ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_istream@_WU?$char_traits@_W@std@@@1@@ + ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_ostream@_WU?$char_traits@_W@std@@@1@@ + ??_8?$basic_istream@DU?$char_traits@D@std@@@std@@7B@ + ??_8?$basic_istream@GU?$char_traits@G@std@@@std@@7B@ + ??_8?$basic_istream@_WU?$char_traits@_W@std@@@std@@7B@ + ??_8?$basic_ostream@DU?$char_traits@D@std@@@std@@7B@ + ??_8?$basic_ostream@GU?$char_traits@G@std@@@std@@7B@ + ??_8?$basic_ostream@_WU?$char_traits@_W@std@@@std@@7B@ + ??_D?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXXZ + ??_D?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXXZ + ??_D?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ??_D?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ + ??_D?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ + ??_D?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ??_D?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ + ??_D?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ + ??_D?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ??_F?$codecvt@DDH@std@@QAEXXZ + ??_F?$codecvt@GDH@std@@QAEXXZ + ??_F?$codecvt@_WDH@std@@QAEXXZ + ??_F?$ctype@D@std@@QAEXXZ + ??_F?$ctype@G@std@@QAEXXZ + ??_F?$ctype@_W@std@@QAEXXZ + ??_F?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ + ??_F?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ + ??_F?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ + ??_F?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ + ??_F?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ + ??_F?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ + ??_F?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ + ??_F?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ + ??_F?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ + ??_F?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ + ??_F?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ + ??_F?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ + ??_F_Locimp@locale@std@@QAEXXZ + ??_F_Locinfo@std@@QAEXXZ + ??_F_Timevec@std@@QAEXXZ + ??_Fcodecvt_base@std@@QAEXXZ + ??_Fctype_base@std@@QAEXXZ + ??_Ffacet@locale@std@@QAEXXZ + ??_Fid@locale@std@@QAEXXZ + ??_Ftime_base@std@@QAEXXZ + ?NFS_Allocate@details@Concurrency@@YAPAXIIPAX@Z + ?NFS_Free@details@Concurrency@@YAXPAX@Z + ?NFS_GetLineSize@details@Concurrency@@YAIXZ + ?_Addcats@_Locinfo@std@@QAEAAV12@HPBD@Z + ?_Addfac@_Locimp@locale@std@@AAEXPAVfacet@23@I@Z + ?_Addstd@ios_base@std@@SAXPAV12@@Z + ?_Advance@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXXZ + ?_Assign@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXABV123@@Z + ?_Atexit@@YAXP6AXXZ@Z + ?_BADOFF@std@@3_JB + ?_C_str@?$_Yarn@D@std@@QBEPBDXZ + ?_Callfns@ios_base@std@@AAEXW4event@12@@Z + ?_Clocptr@_Locimp@locale@std@@0PAV123@A + ?_Decref@facet@locale@std@@QAEPAV123@XZ + ?_Donarrow@?$ctype@G@std@@IBEDGD@Z + ?_Donarrow@?$ctype@_W@std@@IBED_WD@Z + ?_Dowiden@?$ctype@G@std@@IBEGD@Z + ?_Dowiden@?$ctype@_W@std@@IBE_WD@Z + ?_Empty@?$_Yarn@D@std@@QBE_NXZ + ?_Ffmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADDH@Z + ?_Ffmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADDH@Z + ?_Ffmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADDH@Z + ?_Findarr@ios_base@std@@AAEAAU_Iosarray@12@H@Z + ?_Fiopen@std@@YAPAU_iobuf@@PBDHH@Z + ?_Fiopen@std@@YAPAU_iobuf@@PBGHH@Z + ?_Fiopen@std@@YAPAU_iobuf@@PB_WHH@Z + ?_Fput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBDIIII@Z + ?_Fput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBDIIII@Z + ?_Fput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBDIIII@Z + ?_GetCombinableSize@details@Concurrency@@YAIXZ + ?_GetCurrentThreadId@details@Concurrency@@YAKXZ + ?_Getcat@?$codecvt@DDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$codecvt@GDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$codecvt@_WDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$ctype@D@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$ctype@G@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$ctype@_W@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z + ?_Getcat@facet@locale@std@@SAIPAPBV123@PBV23@@Z + ?_Getcoll@_Locinfo@std@@QBE?AU_Collvec@@XZ + ?_Getctype@_Locinfo@std@@QBE?AU_Ctypevec@@XZ + ?_Getcvt@_Locinfo@std@@QBE?AU_Cvtvec@@XZ + ?_Getdateorder@_Locinfo@std@@QBEHXZ + ?_Getdays@_Locinfo@std@@QBEPBDXZ + ?_Getfalse@_Locinfo@std@@QBEPBDXZ + ?_Getffld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z + ?_Getffld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z + ?_Getffld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z + ?_Getffldx@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z + ?_Getffldx@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z + ?_Getffldx@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z + ?_Getfmt@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z + ?_Getfmt@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z + ?_Getfmt@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z + ?_Getgloballocale@locale@std@@CAPAV_Locimp@12@XZ + ?_Getifld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1HABVlocale@2@@Z + ?_Getifld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1HABVlocale@2@@Z + ?_Getifld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1HABVlocale@2@@Z + ?_Getint@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@0HHAAH@Z + ?_Getint@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@0HHAAH@Z + ?_Getint@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@0HHAAH@Z + ?_Getlconv@_Locinfo@std@@QBEPBUlconv@@XZ + ?_Getmonths@_Locinfo@std@@QBEPBDXZ + ?_Getname@_Locinfo@std@@QBEPBDXZ + ?_Getpfirst@_Container_base12@std@@QBEPAPAU_Iterator_base12@2@XZ + ?_Getptr@_Timevec@std@@QBEPAXXZ + ?_Gettnames@_Locinfo@std@@QBE?AV_Timevec@2@XZ + ?_Gettrue@_Locinfo@std@@QBEPBDXZ + ?_Gnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ + ?_Gnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ + ?_Gnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ + ?_Gndec@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ + ?_Gndec@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ + ?_Gndec@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ + ?_Gninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ + ?_Gninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ + ?_Gninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ + ?_Gnpreinc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ + ?_Gnpreinc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ + ?_Gnpreinc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ + ?_Hexdig@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHDDDD@Z + ?_Hexdig@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHGGGG@Z + ?_Hexdig@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAH_W000@Z + ?_Id_cnt@id@locale@std@@0HA + ?_Ifmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADPBDH@Z + ?_Ifmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADPBDH@Z + ?_Ifmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADPBDH@Z + ?_Incref@facet@locale@std@@QAEXXZ + ?_Index@ios_base@std@@0HA + ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAPAD0PAH001@Z + ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXXZ + ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAPAG0PAH001@Z + ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXXZ + ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPAPA_W0PAH001@Z + ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXXZ + ?_Init@?$codecvt@DDH@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$codecvt@GDH@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$codecvt@_WDH@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$ctype@D@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$ctype@G@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$ctype@_W@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z + ?_Init@ios_base@std@@IAEXXZ + ?_Init@locale@std@@CAPAV_Locimp@12@XZ + ?_Init_cnt@Init@ios_base@std@@0HA + ?_Init_cnt@_UShinit@std@@0HA + ?_Init_cnt@_Winit@std@@0HA + ?_Init_cnt_func@Init@ios_base@std@@CAAAHXZ + ?_Init_ctor@Init@ios_base@std@@CAXPAV123@@Z + ?_Init_dtor@Init@ios_base@std@@CAXPAV123@@Z + ?_Init_locks_ctor@_Init_locks@std@@CAXPAV12@@Z + ?_Init_locks_dtor@_Init_locks@std@@CAXPAV12@@Z + ?_Internal_assign@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXI@ZP6AX1PBXI@Z4@Z + ?_Internal_capacity@_Concurrent_vector_base_v4@details@Concurrency@@IBEIXZ + ?_Internal_clear@_Concurrent_vector_base_v4@details@Concurrency@@IAEIP6AXPAXI@Z@Z + ?_Internal_compact@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIPAXP6AX0I@ZP6AX0PBXI@Z@Z + ?_Internal_copy@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXPBXI@Z@Z + ?_Internal_empty@_Concurrent_queue_base_v4@details@Concurrency@@IBE_NXZ + ?_Internal_finish_clear@_Concurrent_queue_base_v4@details@Concurrency@@IAEXXZ + ?_Internal_grow_by@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z + ?_Internal_grow_to_at_least_with_result@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z + ?_Internal_pop_if_present@_Concurrent_queue_base_v4@details@Concurrency@@IAE_NPAX@Z + ?_Internal_push@_Concurrent_queue_base_v4@details@Concurrency@@IAEXPBX@Z + ?_Internal_push_back@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIAAI@Z + ?_Internal_reserve@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIII@Z + ?_Internal_resize@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIIIP6AXPAXI@ZP6AX0PBXI@Z2@Z + ?_Internal_size@_Concurrent_queue_base_v4@details@Concurrency@@IBEIXZ + ?_Internal_swap@_Concurrent_vector_base_v4@details@Concurrency@@IAEXAAV123@@Z + ?_Internal_throw_exception@_Concurrent_queue_base_v4@details@Concurrency@@IBEXXZ + ?_Internal_throw_exception@_Concurrent_vector_base_v4@details@Concurrency@@IBEXI@Z + ?_Ios_base_dtor@ios_base@std@@CAXPAV12@@Z + ?_Ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z + ?_Ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z + ?_Ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z + ?_Iput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPADI@Z + ?_Iput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPADI@Z + ?_Iput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPADI@Z + ?_Locimp_Addfac@_Locimp@locale@std@@CAXPAV123@PAVfacet@23@I@Z + ?_Locimp_ctor@_Locimp@locale@std@@CAXPAV123@ABV123@@Z + ?_Locimp_dtor@_Locimp@locale@std@@CAXPAV123@@Z + ?_Locinfo_Addcats@_Locinfo@std@@SAAAV12@PAV12@HPBD@Z + ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@HPBD@Z + ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@PBD@Z + ?_Locinfo_dtor@_Locinfo@std@@SAXPAV12@@Z + ?_Lock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ + ?_Lock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ + ?_Lock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ + ?_Lock@_Mutex@std@@QAEXXZ + ?_Lockit_ctor@_Lockit@std@@CAXPAV12@@Z + ?_Lockit_ctor@_Lockit@std@@CAXPAV12@H@Z + ?_Lockit_ctor@_Lockit@std@@SAXH@Z + ?_Lockit_dtor@_Lockit@std@@CAXPAV12@@Z + ?_Lockit_dtor@_Lockit@std@@SAXH@Z + ?_MP_Add@tr1@std@@YAXQA_K_K@Z + ?_MP_Get@tr1@std@@YA_KQA_K@Z + ?_MP_Mul@tr1@std@@YAXQA_K_K1@Z + ?_MP_Rem@tr1@std@@YAXQA_K_K@Z + ?_Makeloc@_Locimp@locale@std@@CAPAV123@ABV_Locinfo@3@HPAV123@PBV23@@Z + ?_Makeushloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z + ?_Makewloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z + ?_Makexloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z + ?_Mtx_delete@threads@stdext@@YAXPAX@Z + ?_Mtx_lock@threads@stdext@@YAXPAX@Z + ?_Mtx_new@threads@stdext@@YAXAAPAX@Z + ?_Mtx_unlock@threads@stdext@@YAXPAX@Z + ?_Mutex_Lock@_Mutex@std@@CAXPAV12@@Z + ?_Mutex_Unlock@_Mutex@std@@CAXPAV12@@Z + ?_Mutex_ctor@_Mutex@std@@CAXPAV12@@Z + ?_Mutex_dtor@_Mutex@std@@CAXPAV12@@Z + ?_Nomemory@std@@YAXXZ + ?_Orphan_all@_Container_base0@std@@QAEXXZ + ?_Orphan_all@_Container_base12@std@@QAEXXZ + ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ + ?_Osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ + ?_Osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ?_Pnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ + ?_Pnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ + ?_Pnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ + ?_Pninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ + ?_Pninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ + ?_Pninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ + ?_Ptr_cerr@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?_Ptr_cin@std@@3PAV?$basic_istream@DU?$char_traits@D@std@@@1@A + ?_Ptr_clog@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?_Ptr_cout@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?_Ptr_wcerr@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?_Ptr_wcerr@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?_Ptr_wcin@std@@3PAV?$basic_istream@GU?$char_traits@G@std@@@1@A + ?_Ptr_wcin@std@@3PAV?$basic_istream@_WU?$char_traits@_W@std@@@1@A + ?_Ptr_wclog@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?_Ptr_wclog@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?_Ptr_wcout@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?_Ptr_wcout@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?_Put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z + ?_Put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBGI@Z + ?_Put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PB_WI@Z + ?_Putc@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z + ?_Putc@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDI@Z + ?_Putc@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI@Z + ?_Putgrouped@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDID@Z + ?_Putgrouped@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDIG@Z + ?_Putgrouped@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI_W@Z + ?_Raise_handler@std@@3P6AXABVexception@stdext@@@ZA + ?_Random_device@tr1@std@@YAIXZ + ?_Rep@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@DI@Z + ?_Rep@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@GI@Z + ?_Rep@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@_WI@Z + ?_Rng_abort@tr1@std@@YAXPBD@Z + ?_Segment_index_of@_Concurrent_vector_base_v4@details@Concurrency@@KAII@Z + ?_Setgloballocale@locale@std@@CAXPAX@Z + ?_Swap_all@_Container_base0@std@@QAEXAAU12@@Z + ?_Swap_all@_Container_base12@std@@QAEXAAU12@@Z + ?_Sync@ios_base@std@@0_NA + ?_Tidy@?$_Yarn@D@std@@AAEXXZ + ?_Tidy@?$ctype@D@std@@IAEXXZ + ?_Tidy@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@AAEXXZ + ?_Tidy@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@AAEXXZ + ?_Tidy@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@AAEXXZ + ?_Tidy@ios_base@std@@AAEXXZ + ?_Unlock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ + ?_Unlock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ + ?_Unlock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ + ?_Unlock@_Mutex@std@@QAEXXZ + ?_XLgamma@tr1@std@@YAMM@Z + ?_XLgamma@tr1@std@@YANN@Z + ?_XLgamma@tr1@std@@YAOO@Z + ?_Xbad@tr1@std@@YAXW4error_type@regex_constants@12@@Z + ?_Xfunc@tr1@std@@YAXXZ + ?_Xinvalid_argument@std@@YAXPBD@Z + ?_Xlength_error@std@@YAXPBD@Z + ?_Xmem@tr1@std@@YAXXZ + ?_Xout_of_range@std@@YAXPBD@Z + ?_Xoverflow_error@std@@YAXPBD@Z + ?_Xruntime_error@std@@YAXPBD@Z + ?always_noconv@codecvt_base@std@@QBE_NXZ + ?bad@ios_base@std@@QBE_NXZ + ?c_str@?$_Yarn@D@std@@QBEPBDXZ + ?cancel@agent@Concurrency@@QAE_NXZ + ?cerr@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A + ?classic@locale@std@@SAABV12@XZ + ?classic_table@?$ctype@D@std@@SAPBFXZ + ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z + ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z + ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z + ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z + ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z + ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z + ?clear@ios_base@std@@QAEXH@Z + ?clear@ios_base@std@@QAEXH_N@Z + ?clear@ios_base@std@@QAEXI@Z + ?clog@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?copyfmt@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEAAV12@ABV12@@Z + ?copyfmt@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEAAV12@ABV12@@Z + ?copyfmt@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEAAV12@ABV12@@Z + ?copyfmt@ios_base@std@@QAEAAV12@ABV12@@Z + ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A + ?date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ + ?date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ + ?date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ + ?denorm_min@?$numeric_limits@C@std@@SACXZ + ?denorm_min@?$numeric_limits@D@std@@SADXZ + ?denorm_min@?$numeric_limits@E@std@@SAEXZ + ?denorm_min@?$numeric_limits@F@std@@SAFXZ + ?denorm_min@?$numeric_limits@G@std@@SAGXZ + ?denorm_min@?$numeric_limits@H@std@@SAHXZ + ?denorm_min@?$numeric_limits@I@std@@SAIXZ + ?denorm_min@?$numeric_limits@J@std@@SAJXZ + ?denorm_min@?$numeric_limits@K@std@@SAKXZ + ?denorm_min@?$numeric_limits@M@std@@SAMXZ + ?denorm_min@?$numeric_limits@N@std@@SANXZ + ?denorm_min@?$numeric_limits@O@std@@SAOXZ + ?denorm_min@?$numeric_limits@_J@std@@SA_JXZ + ?denorm_min@?$numeric_limits@_K@std@@SA_KXZ + ?denorm_min@?$numeric_limits@_N@std@@SA_NXZ + ?denorm_min@?$numeric_limits@_W@std@@SA_WXZ + ?digits10@?$numeric_limits@C@std@@2HB + ?digits10@?$numeric_limits@D@std@@2HB + ?digits10@?$numeric_limits@E@std@@2HB + ?digits10@?$numeric_limits@F@std@@2HB + ?digits10@?$numeric_limits@G@std@@2HB + ?digits10@?$numeric_limits@H@std@@2HB + ?digits10@?$numeric_limits@I@std@@2HB + ?digits10@?$numeric_limits@J@std@@2HB + ?digits10@?$numeric_limits@K@std@@2HB + ?digits10@?$numeric_limits@M@std@@2HB + ?digits10@?$numeric_limits@N@std@@2HB + ?digits10@?$numeric_limits@O@std@@2HB + ?digits10@?$numeric_limits@_J@std@@2HB + ?digits10@?$numeric_limits@_K@std@@2HB + ?digits10@?$numeric_limits@_N@std@@2HB + ?digits10@?$numeric_limits@_W@std@@2HB + ?digits10@_Num_base@std@@2HB + ?digits@?$numeric_limits@C@std@@2HB + ?digits@?$numeric_limits@D@std@@2HB + ?digits@?$numeric_limits@E@std@@2HB + ?digits@?$numeric_limits@F@std@@2HB + ?digits@?$numeric_limits@G@std@@2HB + ?digits@?$numeric_limits@H@std@@2HB + ?digits@?$numeric_limits@I@std@@2HB + ?digits@?$numeric_limits@J@std@@2HB + ?digits@?$numeric_limits@K@std@@2HB + ?digits@?$numeric_limits@M@std@@2HB + ?digits@?$numeric_limits@N@std@@2HB + ?digits@?$numeric_limits@O@std@@2HB + ?digits@?$numeric_limits@_J@std@@2HB + ?digits@?$numeric_limits@_K@std@@2HB + ?digits@?$numeric_limits@_N@std@@2HB + ?digits@?$numeric_limits@_W@std@@2HB + ?digits@_Num_base@std@@2HB + ?do_always_noconv@?$codecvt@GDH@std@@MBE_NXZ + ?do_always_noconv@?$codecvt@_WDH@std@@MBE_NXZ + ?do_always_noconv@codecvt_base@std@@MBE_NXZ + ?do_date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ + ?do_date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ + ?do_date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ + ?do_encoding@codecvt_base@std@@MBEHXZ + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?do_get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?do_get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?do_get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?do_get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?do_in@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z + ?do_in@?$codecvt@GDH@std@@MBEHAAHPBD1AAPBDPAG3AAPAG@Z + ?do_in@?$codecvt@_WDH@std@@MBEHAAHPBD1AAPBDPA_W3AAPA_W@Z + ?do_is@?$ctype@G@std@@MBEPBGPBG0PAF@Z + ?do_is@?$ctype@G@std@@MBE_NFG@Z + ?do_is@?$ctype@_W@std@@MBEPB_WPB_W0PAF@Z + ?do_is@?$ctype@_W@std@@MBE_NF_W@Z + ?do_length@?$codecvt@DDH@std@@MBEHABHPBD1I@Z + ?do_length@?$codecvt@GDH@std@@MBEHABHPBD1I@Z + ?do_length@?$codecvt@_WDH@std@@MBEHABHPBD1I@Z + ?do_max_length@?$codecvt@GDH@std@@MBEHXZ + ?do_max_length@?$codecvt@_WDH@std@@MBEHXZ + ?do_max_length@codecvt_base@std@@MBEHXZ + ?do_narrow@?$ctype@D@std@@MBEDDD@Z + ?do_narrow@?$ctype@D@std@@MBEPBDPBD0DPAD@Z + ?do_narrow@?$ctype@G@std@@MBEDGD@Z + ?do_narrow@?$ctype@G@std@@MBEPBGPBG0DPAD@Z + ?do_narrow@?$ctype@_W@std@@MBED_WD@Z + ?do_narrow@?$ctype@_W@std@@MBEPB_WPB_W0DPAD@Z + ?do_out@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z + ?do_out@?$codecvt@GDH@std@@MBEHAAHPBG1AAPBGPAD3AAPAD@Z + ?do_out@?$codecvt@_WDH@std@@MBEHAAHPB_W1AAPB_WPAD3AAPAD@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z + ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z + ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z + ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z + ?do_put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z + ?do_put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z + ?do_put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z + ?do_scan_is@?$ctype@G@std@@MBEPBGFPBG0@Z + ?do_scan_is@?$ctype@_W@std@@MBEPB_WFPB_W0@Z + ?do_scan_not@?$ctype@G@std@@MBEPBGFPBG0@Z + ?do_scan_not@?$ctype@_W@std@@MBEPB_WFPB_W0@Z + ?do_tolower@?$ctype@D@std@@MBEDD@Z + ?do_tolower@?$ctype@D@std@@MBEPBDPADPBD@Z + ?do_tolower@?$ctype@G@std@@MBEGG@Z + ?do_tolower@?$ctype@G@std@@MBEPBGPAGPBG@Z + ?do_tolower@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z + ?do_tolower@?$ctype@_W@std@@MBE_W_W@Z + ?do_toupper@?$ctype@D@std@@MBEDD@Z + ?do_toupper@?$ctype@D@std@@MBEPBDPADPBD@Z + ?do_toupper@?$ctype@G@std@@MBEGG@Z + ?do_toupper@?$ctype@G@std@@MBEPBGPAGPBG@Z + ?do_toupper@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z + ?do_toupper@?$ctype@_W@std@@MBE_W_W@Z + ?do_unshift@?$codecvt@DDH@std@@MBEHAAHPAD1AAPAD@Z + ?do_unshift@?$codecvt@GDH@std@@MBEHAAHPAD1AAPAD@Z + ?do_unshift@?$codecvt@_WDH@std@@MBEHAAHPAD1AAPAD@Z + ?do_widen@?$ctype@D@std@@MBEDD@Z + ?do_widen@?$ctype@D@std@@MBEPBDPBD0PAD@Z + ?do_widen@?$ctype@G@std@@MBEGD@Z + ?do_widen@?$ctype@G@std@@MBEPBDPBD0PAG@Z + ?do_widen@?$ctype@_W@std@@MBEPBDPBD0PA_W@Z + ?do_widen@?$ctype@_W@std@@MBE_WD@Z + ?done@agent@Concurrency@@IAE_NXZ + ?eback@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?eback@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?eback@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?egptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?egptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?egptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?empty@?$_Yarn@D@std@@QBE_NXZ + ?empty@locale@std@@SA?AV12@XZ + ?encoding@codecvt_base@std@@QBEHXZ + ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z + ?endl@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z + ?endl@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z + ?ends@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z + ?ends@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z + ?ends@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z + ?eof@ios_base@std@@QBE_NXZ + ?epptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?epptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?epptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?epsilon@?$numeric_limits@C@std@@SACXZ + ?epsilon@?$numeric_limits@D@std@@SADXZ + ?epsilon@?$numeric_limits@E@std@@SAEXZ + ?epsilon@?$numeric_limits@F@std@@SAFXZ + ?epsilon@?$numeric_limits@G@std@@SAGXZ + ?epsilon@?$numeric_limits@H@std@@SAHXZ + ?epsilon@?$numeric_limits@I@std@@SAIXZ + ?epsilon@?$numeric_limits@J@std@@SAJXZ + ?epsilon@?$numeric_limits@K@std@@SAKXZ + ?epsilon@?$numeric_limits@M@std@@SAMXZ + ?epsilon@?$numeric_limits@N@std@@SANXZ + ?epsilon@?$numeric_limits@O@std@@SAOXZ + ?epsilon@?$numeric_limits@_J@std@@SA_JXZ + ?epsilon@?$numeric_limits@_K@std@@SA_KXZ + ?epsilon@?$numeric_limits@_N@std@@SA_NXZ + ?epsilon@?$numeric_limits@_W@std@@SA_WXZ + ?exceptions@ios_base@std@@QAEXH@Z + ?exceptions@ios_base@std@@QAEXI@Z + ?exceptions@ios_base@std@@QBEHXZ + ?fail@ios_base@std@@QBE_NXZ + ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEDD@Z + ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDXZ + ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEGG@Z + ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGXZ + ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE_W_W@Z + ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WXZ + ?flags@ios_base@std@@QAEHH@Z + ?flags@ios_base@std@@QBEHXZ + ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ + ?flush@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ + ?flush@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ + ?flush@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z + ?flush@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z + ?flush@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z + ?gbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z + ?gbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z + ?gbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z + ?gcount@?$basic_istream@DU?$char_traits@D@std@@@std@@QBE_JXZ + ?gcount@?$basic_istream@GU?$char_traits@G@std@@@std@@QBE_JXZ + ?gcount@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QBE_JXZ + ?generic_category@std@@YAABVerror_category@1@XZ + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAD@Z + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@D@Z + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z + ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAG@Z + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@G@Z + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z + ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_W@Z + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AA_W@Z + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z + ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z + ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z + ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD4@Z + ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBG4@Z + ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z + ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PB_W4@Z + ?get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z + ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z + ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z + ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z + ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z + ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z + ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z + ?getloc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QBE?AVlocale@2@XZ + ?getloc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QBE?AVlocale@2@XZ + ?getloc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QBE?AVlocale@2@XZ + ?getloc@ios_base@std@@QBE?AVlocale@2@XZ + ?global@locale@std@@SA?AV12@ABV12@@Z + ?good@ios_base@std@@QBE_NXZ + ?gptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?gptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?gptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?has_denorm@_Num_base@std@@2W4float_denorm_style@2@B + ?has_denorm@_Num_float_base@std@@2W4float_denorm_style@2@B + ?has_denorm_loss@_Num_base@std@@2_NB + ?has_denorm_loss@_Num_float_base@std@@2_NB + ?has_infinity@_Num_base@std@@2_NB + ?has_infinity@_Num_float_base@std@@2_NB + ?has_quiet_NaN@_Num_base@std@@2_NB + ?has_quiet_NaN@_Num_float_base@std@@2_NB + ?has_signaling_NaN@_Num_base@std@@2_NB + ?has_signaling_NaN@_Num_float_base@std@@2_NB + ?id@?$codecvt@DDH@std@@2V0locale@2@A + ?id@?$codecvt@GDH@std@@2V0locale@2@A + ?id@?$codecvt@_WDH@std@@2V0locale@2@A + ?id@?$collate@D@std@@2V0locale@2@A + ?id@?$collate@G@std@@2V0locale@2@A + ?id@?$collate@_W@std@@2V0locale@2@A + ?id@?$ctype@D@std@@2V0locale@2@A + ?id@?$ctype@G@std@@2V0locale@2@A + ?id@?$ctype@_W@std@@2V0locale@2@A + ?id@?$messages@D@std@@2V0locale@2@A + ?id@?$messages@G@std@@2V0locale@2@A + ?id@?$messages@_W@std@@2V0locale@2@A + ?id@?$money_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$money_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$money_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?id@?$money_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$money_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$money_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?id@?$moneypunct@D$00@std@@2V0locale@2@A + ?id@?$moneypunct@D$0A@@std@@2V0locale@2@A + ?id@?$moneypunct@G$00@std@@2V0locale@2@A + ?id@?$moneypunct@G$0A@@std@@2V0locale@2@A + ?id@?$moneypunct@_W$00@std@@2V0locale@2@A + ?id@?$moneypunct@_W$0A@@std@@2V0locale@2@A + ?id@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?id@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?id@?$numpunct@D@std@@2V0locale@2@A + ?id@?$numpunct@G@std@@2V0locale@2@A + ?id@?$numpunct@_W@std@@2V0locale@2@A + ?id@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?id@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A + ?id@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A + ?id@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A + ?ignore@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z + ?ignore@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JG@Z + ?ignore@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JG@Z + ?imbue@?$basic_ios@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?imbue@?$basic_ios@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?imbue@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?imbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEXABVlocale@2@@Z + ?imbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEXABVlocale@2@@Z + ?imbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEXABVlocale@2@@Z + ?imbue@ios_base@std@@QAE?AVlocale@2@ABV32@@Z + ?in@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z + ?in@?$codecvt@GDH@std@@QBEHAAHPBD1AAPBDPAG3AAPAG@Z + ?in@?$codecvt@_WDH@std@@QBEHAAHPBD1AAPBDPA_W3AAPA_W@Z + ?in_avail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JXZ + ?in_avail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JXZ + ?in_avail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JXZ + ?infinity@?$numeric_limits@C@std@@SACXZ + ?infinity@?$numeric_limits@D@std@@SADXZ + ?infinity@?$numeric_limits@E@std@@SAEXZ + ?infinity@?$numeric_limits@F@std@@SAFXZ + ?infinity@?$numeric_limits@G@std@@SAGXZ + ?infinity@?$numeric_limits@H@std@@SAHXZ + ?infinity@?$numeric_limits@I@std@@SAIXZ + ?infinity@?$numeric_limits@J@std@@SAJXZ + ?infinity@?$numeric_limits@K@std@@SAKXZ + ?infinity@?$numeric_limits@M@std@@SAMXZ + ?infinity@?$numeric_limits@N@std@@SANXZ + ?infinity@?$numeric_limits@O@std@@SAOXZ + ?infinity@?$numeric_limits@_J@std@@SA_JXZ + ?infinity@?$numeric_limits@_K@std@@SA_KXZ + ?infinity@?$numeric_limits@_N@std@@SA_NXZ + ?infinity@?$numeric_limits@_W@std@@SA_WXZ + ?init@?$basic_ios@DU?$char_traits@D@std@@@std@@IAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@_N@Z + ?init@?$basic_ios@GU?$char_traits@G@std@@@std@@IAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@_N@Z + ?init@?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_N@Z + ?intl@?$moneypunct@D$00@std@@2_NB + ?intl@?$moneypunct@D$0A@@std@@2_NB + ?intl@?$moneypunct@G$00@std@@2_NB + ?intl@?$moneypunct@G$0A@@std@@2_NB + ?intl@?$moneypunct@_W$00@std@@2_NB + ?intl@?$moneypunct@_W$0A@@std@@2_NB + ?iostream_category@std@@YAABVerror_category@1@XZ + ?ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z + ?ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z + ?ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z + ?is@?$ctype@D@std@@QBEPBDPBD0PAF@Z + ?is@?$ctype@D@std@@QBE_NFD@Z + ?is@?$ctype@G@std@@QBEPBGPBG0PAF@Z + ?is@?$ctype@G@std@@QBE_NFG@Z + ?is@?$ctype@_W@std@@QBEPB_WPB_W0PAF@Z + ?is@?$ctype@_W@std@@QBE_NF_W@Z + ?is_bounded@_Num_base@std@@2_NB + ?is_bounded@_Num_float_base@std@@2_NB + ?is_bounded@_Num_int_base@std@@2_NB + ?is_current_task_group_canceling@Concurrency@@YA_NXZ + ?is_exact@_Num_base@std@@2_NB + ?is_exact@_Num_float_base@std@@2_NB + ?is_exact@_Num_int_base@std@@2_NB + ?is_iec559@_Num_base@std@@2_NB + ?is_iec559@_Num_float_base@std@@2_NB + ?is_integer@_Num_base@std@@2_NB + ?is_integer@_Num_float_base@std@@2_NB + ?is_integer@_Num_int_base@std@@2_NB + ?is_modulo@?$numeric_limits@_N@std@@2_NB + ?is_modulo@_Num_base@std@@2_NB + ?is_modulo@_Num_float_base@std@@2_NB + ?is_modulo@_Num_int_base@std@@2_NB + ?is_signed@?$numeric_limits@C@std@@2_NB + ?is_signed@?$numeric_limits@D@std@@2_NB + ?is_signed@?$numeric_limits@E@std@@2_NB + ?is_signed@?$numeric_limits@F@std@@2_NB + ?is_signed@?$numeric_limits@G@std@@2_NB + ?is_signed@?$numeric_limits@H@std@@2_NB + ?is_signed@?$numeric_limits@I@std@@2_NB + ?is_signed@?$numeric_limits@J@std@@2_NB + ?is_signed@?$numeric_limits@K@std@@2_NB + ?is_signed@?$numeric_limits@_J@std@@2_NB + ?is_signed@?$numeric_limits@_K@std@@2_NB + ?is_signed@?$numeric_limits@_N@std@@2_NB + ?is_signed@?$numeric_limits@_W@std@@2_NB + ?is_signed@_Num_base@std@@2_NB + ?is_signed@_Num_float_base@std@@2_NB + ?is_specialized@_Num_base@std@@2_NB + ?is_specialized@_Num_float_base@std@@2_NB + ?is_specialized@_Num_int_base@std@@2_NB + ?isfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ + ?isfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ + ?isfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ?iword@ios_base@std@@QAEAAJH@Z + ?length@?$codecvt@DDH@std@@QBEHABHPBD1I@Z + ?length@?$codecvt@GDH@std@@QBEHABHPBD1I@Z + ?length@?$codecvt@_WDH@std@@QBEHABHPBD1I@Z + ?lowest@?$numeric_limits@C@std@@SACXZ + ?lowest@?$numeric_limits@D@std@@SADXZ + ?lowest@?$numeric_limits@E@std@@SAEXZ + ?lowest@?$numeric_limits@F@std@@SAFXZ + ?lowest@?$numeric_limits@G@std@@SAGXZ + ?lowest@?$numeric_limits@H@std@@SAHXZ + ?lowest@?$numeric_limits@I@std@@SAIXZ + ?lowest@?$numeric_limits@J@std@@SAJXZ + ?lowest@?$numeric_limits@K@std@@SAKXZ + ?lowest@?$numeric_limits@M@std@@SAMXZ + ?lowest@?$numeric_limits@N@std@@SANXZ + ?lowest@?$numeric_limits@O@std@@SAOXZ + ?lowest@?$numeric_limits@_J@std@@SA_JXZ + ?lowest@?$numeric_limits@_K@std@@SA_KXZ + ?lowest@?$numeric_limits@_N@std@@SA_NXZ + ?lowest@?$numeric_limits@_W@std@@SA_WXZ + ?max@?$numeric_limits@C@std@@SACXZ + ?max@?$numeric_limits@D@std@@SADXZ + ?max@?$numeric_limits@E@std@@SAEXZ + ?max@?$numeric_limits@F@std@@SAFXZ + ?max@?$numeric_limits@G@std@@SAGXZ + ?max@?$numeric_limits@H@std@@SAHXZ + ?max@?$numeric_limits@I@std@@SAIXZ + ?max@?$numeric_limits@J@std@@SAJXZ + ?max@?$numeric_limits@K@std@@SAKXZ + ?max@?$numeric_limits@M@std@@SAMXZ + ?max@?$numeric_limits@N@std@@SANXZ + ?max@?$numeric_limits@O@std@@SAOXZ + ?max@?$numeric_limits@_J@std@@SA_JXZ + ?max@?$numeric_limits@_K@std@@SA_KXZ + ?max@?$numeric_limits@_N@std@@SA_NXZ + ?max@?$numeric_limits@_W@std@@SA_WXZ + ?max_digits10@?$numeric_limits@C@std@@2HB + ?max_digits10@?$numeric_limits@D@std@@2HB + ?max_digits10@?$numeric_limits@E@std@@2HB + ?max_digits10@?$numeric_limits@F@std@@2HB + ?max_digits10@?$numeric_limits@G@std@@2HB + ?max_digits10@?$numeric_limits@H@std@@2HB + ?max_digits10@?$numeric_limits@I@std@@2HB + ?max_digits10@?$numeric_limits@J@std@@2HB + ?max_digits10@?$numeric_limits@K@std@@2HB + ?max_digits10@?$numeric_limits@M@std@@2HB + ?max_digits10@?$numeric_limits@N@std@@2HB + ?max_digits10@?$numeric_limits@O@std@@2HB + ?max_digits10@?$numeric_limits@_J@std@@2HB + ?max_digits10@?$numeric_limits@_K@std@@2HB + ?max_digits10@?$numeric_limits@_N@std@@2HB + ?max_digits10@?$numeric_limits@_W@std@@2HB + ?max_digits10@_Num_base@std@@2HB + ?max_exponent10@?$numeric_limits@M@std@@2HB + ?max_exponent10@?$numeric_limits@N@std@@2HB + ?max_exponent10@?$numeric_limits@O@std@@2HB + ?max_exponent10@_Num_base@std@@2HB + ?max_exponent@?$numeric_limits@M@std@@2HB + ?max_exponent@?$numeric_limits@N@std@@2HB + ?max_exponent@?$numeric_limits@O@std@@2HB + ?max_exponent@_Num_base@std@@2HB + ?max_length@codecvt_base@std@@QBEHXZ + ?min@?$numeric_limits@C@std@@SACXZ + ?min@?$numeric_limits@D@std@@SADXZ + ?min@?$numeric_limits@E@std@@SAEXZ + ?min@?$numeric_limits@F@std@@SAFXZ + ?min@?$numeric_limits@G@std@@SAGXZ + ?min@?$numeric_limits@H@std@@SAHXZ + ?min@?$numeric_limits@I@std@@SAIXZ + ?min@?$numeric_limits@J@std@@SAJXZ + ?min@?$numeric_limits@K@std@@SAKXZ + ?min@?$numeric_limits@M@std@@SAMXZ + ?min@?$numeric_limits@N@std@@SANXZ + ?min@?$numeric_limits@O@std@@SAOXZ + ?min@?$numeric_limits@_J@std@@SA_JXZ + ?min@?$numeric_limits@_K@std@@SA_KXZ + ?min@?$numeric_limits@_N@std@@SA_NXZ + ?min@?$numeric_limits@_W@std@@SA_WXZ + ?min_exponent10@?$numeric_limits@M@std@@2HB + ?min_exponent10@?$numeric_limits@N@std@@2HB + ?min_exponent10@?$numeric_limits@O@std@@2HB + ?min_exponent10@_Num_base@std@@2HB + ?min_exponent@?$numeric_limits@M@std@@2HB + ?min_exponent@?$numeric_limits@N@std@@2HB + ?min_exponent@?$numeric_limits@O@std@@2HB + ?min_exponent@_Num_base@std@@2HB + ?move@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEX$$QAV12@@Z + ?move@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEX$$QAV12@@Z + ?move@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEX$$QAV12@@Z + ?narrow@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDDD@Z + ?narrow@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEDGD@Z + ?narrow@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBED_WD@Z + ?narrow@?$ctype@D@std@@QBEDDD@Z + ?narrow@?$ctype@D@std@@QBEPBDPBD0DPAD@Z + ?narrow@?$ctype@G@std@@QBEDGD@Z + ?narrow@?$ctype@G@std@@QBEPBGPBG0DPAD@Z + ?narrow@?$ctype@_W@std@@QBED_WD@Z + ?narrow@?$ctype@_W@std@@QBEPB_WPB_W0DPAD@Z + ?opfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE_NXZ + ?opfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE_NXZ + ?opfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE_NXZ + ?osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ + ?osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ + ?osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ?out@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z + ?out@?$codecvt@GDH@std@@QBEHAAHPBG1AAPBGPAD3AAPAD@Z + ?out@?$codecvt@_WDH@std@@QBEHAAHPB_W1AAPB_WPAD3AAPAD@Z + ?overflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z + ?overflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z + ?overflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z + ?pbackfail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z + ?pbackfail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z + ?pbackfail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z + ?pbase@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?pbase@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?pbase@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?pbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z + ?pbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z + ?pbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z + ?peek@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ + ?peek@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ + ?peek@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?pptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ + ?pptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ + ?pptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ + ?precision@ios_base@std@@QAE_J_J@Z + ?precision@ios_base@std@@QBE_JXZ + ?pubimbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?pubimbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?pubimbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z + ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z + ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z + ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z + ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z + ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z + ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z + ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z + ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z + ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z + ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z + ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z + ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z + ?pubsetbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEPAV12@PAD_J@Z + ?pubsetbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEPAV12@PAG_J@Z + ?pubsetbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEPAV12@PA_W_J@Z + ?pubsync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ + ?pubsync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEHXZ + ?pubsync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEHXZ + ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z + ?put@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z + ?put@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z + ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z + ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z + ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z + ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z + ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@PBD3@Z + ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z + ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@PBG3@Z + ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z + ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@PB_W4@Z + ?putback@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z + ?putback@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z + ?putback@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z + ?pword@ios_base@std@@QAEAAPAXH@Z + ?quiet_NaN@?$numeric_limits@C@std@@SACXZ + ?quiet_NaN@?$numeric_limits@D@std@@SADXZ + ?quiet_NaN@?$numeric_limits@E@std@@SAEXZ + ?quiet_NaN@?$numeric_limits@F@std@@SAFXZ + ?quiet_NaN@?$numeric_limits@G@std@@SAGXZ + ?quiet_NaN@?$numeric_limits@H@std@@SAHXZ + ?quiet_NaN@?$numeric_limits@I@std@@SAIXZ + ?quiet_NaN@?$numeric_limits@J@std@@SAJXZ + ?quiet_NaN@?$numeric_limits@K@std@@SAKXZ + ?quiet_NaN@?$numeric_limits@M@std@@SAMXZ + ?quiet_NaN@?$numeric_limits@N@std@@SANXZ + ?quiet_NaN@?$numeric_limits@O@std@@SAOXZ + ?quiet_NaN@?$numeric_limits@_J@std@@SA_JXZ + ?quiet_NaN@?$numeric_limits@_K@std@@SA_KXZ + ?quiet_NaN@?$numeric_limits@_N@std@@SA_NXZ + ?quiet_NaN@?$numeric_limits@_W@std@@SA_WXZ + ?radix@_Num_base@std@@2HB + ?radix@_Num_float_base@std@@2HB + ?radix@_Num_int_base@std@@2HB + ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@PAV32@@Z + ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ + ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@PAV32@@Z + ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@XZ + ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@PAV32@@Z + ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@XZ + ?rdstate@ios_base@std@@QBEHXZ + ?read@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z + ?read@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z + ?read@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z + ?readsome@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z + ?readsome@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z + ?readsome@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z + ?register_callback@ios_base@std@@QAEXP6AXW4event@12@AAV12@H@ZH@Z + ?resetiosflags@std@@YA?AU?$_Smanip@H@1@H@Z + ?round_error@?$numeric_limits@C@std@@SACXZ + ?round_error@?$numeric_limits@D@std@@SADXZ + ?round_error@?$numeric_limits@E@std@@SAEXZ + ?round_error@?$numeric_limits@F@std@@SAFXZ + ?round_error@?$numeric_limits@G@std@@SAGXZ + ?round_error@?$numeric_limits@H@std@@SAHXZ + ?round_error@?$numeric_limits@I@std@@SAIXZ + ?round_error@?$numeric_limits@J@std@@SAJXZ + ?round_error@?$numeric_limits@K@std@@SAKXZ + ?round_error@?$numeric_limits@M@std@@SAMXZ + ?round_error@?$numeric_limits@N@std@@SANXZ + ?round_error@?$numeric_limits@O@std@@SAOXZ + ?round_error@?$numeric_limits@_J@std@@SA_JXZ + ?round_error@?$numeric_limits@_K@std@@SA_KXZ + ?round_error@?$numeric_limits@_N@std@@SA_NXZ + ?round_error@?$numeric_limits@_W@std@@SA_WXZ + ?round_style@_Num_base@std@@2W4float_round_style@2@B + ?round_style@_Num_float_base@std@@2W4float_round_style@2@B + ?sbumpc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ + ?sbumpc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ + ?sbumpc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?scan_is@?$ctype@D@std@@QBEPBDFPBD0@Z + ?scan_is@?$ctype@G@std@@QBEPBGFPBG0@Z + ?scan_is@?$ctype@_W@std@@QBEPB_WFPB_W0@Z + ?scan_not@?$ctype@D@std@@QBEPBDFPBD0@Z + ?scan_not@?$ctype@G@std@@QBEPBGFPBG0@Z + ?scan_not@?$ctype@_W@std@@QBEPB_WFPB_W0@Z + ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z + ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z + ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z + ?seekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z + ?seekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z + ?seekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z + ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z + ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z + ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z + ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z + ?seekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z + ?seekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z + ?seekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z + ?set_new_handler@std@@YAP6AXXZH@Z + ?set_new_handler@std@@YAP6AXXZP6AXXZ@Z + ?set_rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z + ?set_rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z + ?set_rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z + ?setbase@std@@YA?AU?$_Smanip@H@1@H@Z + ?setbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEPAV12@PAD_J@Z + ?setbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEPAV12@PAG_J@Z + ?setbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEPAV12@PA_W_J@Z + ?setf@ios_base@std@@QAEHH@Z + ?setf@ios_base@std@@QAEHHH@Z + ?setg@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z + ?setg@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z + ?setg@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z + ?setiosflags@std@@YA?AU?$_Smanip@H@1@H@Z + ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z + ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD0@Z + ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z + ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG0@Z + ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z + ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W0@Z + ?setprecision@std@@YA?AU?$_Smanip@_J@1@_J@Z + ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z + ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z + ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z + ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z + ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z + ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z + ?setstate@ios_base@std@@QAEXH@Z + ?setstate@ios_base@std@@QAEXH_N@Z + ?setstate@ios_base@std@@QAEXI@Z + ?setw@std@@YA?AU?$_Smanip@_J@1@_J@Z + ?sgetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ + ?sgetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ + ?sgetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?sgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z + ?sgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z + ?sgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z + ?showmanyc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JXZ + ?showmanyc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JXZ + ?showmanyc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JXZ + ?signaling_NaN@?$numeric_limits@C@std@@SACXZ + ?signaling_NaN@?$numeric_limits@D@std@@SADXZ + ?signaling_NaN@?$numeric_limits@E@std@@SAEXZ + ?signaling_NaN@?$numeric_limits@F@std@@SAFXZ + ?signaling_NaN@?$numeric_limits@G@std@@SAGXZ + ?signaling_NaN@?$numeric_limits@H@std@@SAHXZ + ?signaling_NaN@?$numeric_limits@I@std@@SAIXZ + ?signaling_NaN@?$numeric_limits@J@std@@SAJXZ + ?signaling_NaN@?$numeric_limits@K@std@@SAKXZ + ?signaling_NaN@?$numeric_limits@M@std@@SAMXZ + ?signaling_NaN@?$numeric_limits@N@std@@SANXZ + ?signaling_NaN@?$numeric_limits@O@std@@SAOXZ + ?signaling_NaN@?$numeric_limits@_J@std@@SA_JXZ + ?signaling_NaN@?$numeric_limits@_K@std@@SA_KXZ + ?signaling_NaN@?$numeric_limits@_N@std@@SA_NXZ + ?signaling_NaN@?$numeric_limits@_W@std@@SA_WXZ + ?snextc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ + ?snextc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ + ?snextc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?sputbackc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z + ?sputbackc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z + ?sputbackc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z + ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z + ?sputc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z + ?sputc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z + ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPBD_J@Z + ?sputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPBG_J@Z + ?sputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPB_W_J@Z + ?start@agent@Concurrency@@QAE_NXZ + ?status@agent@Concurrency@@QAE?AW4agent_status@2@XZ + ?status_port@agent@Concurrency@@QAEPAV?$ISource@W4agent_status@Concurrency@@@2@XZ + ?stossc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ + ?stossc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEXXZ + ?stossc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEXXZ + ?sungetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ + ?sungetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ + ?sungetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ + ?swap@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z + ?swap@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXAAV12@@Z + ?swap@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXAAV12@@Z + ?swap@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXAAV12@@Z + ?swap@ios_base@std@@QAEXAAV12@@Z + ?sync@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ + ?sync@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEHXZ + ?sync@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEHXZ + ?sync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ + ?sync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEHXZ + ?sync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHXZ + ?sync_with_stdio@ios_base@std@@SA_N_N@Z + ?system_category@std@@YAABVerror_category@1@XZ + ?table@?$ctype@D@std@@QBEPBFXZ + ?table_size@?$ctype@D@std@@2IB + ?tellg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tellg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tellg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tellp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tellp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tellp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ + ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@PAV32@@Z + ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ + ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@PAV32@@Z + ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@XZ + ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@PAV32@@Z + ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@XZ + ?tinyness_before@_Num_base@std@@2_NB + ?tinyness_before@_Num_float_base@std@@2_NB + ?tolower@?$ctype@D@std@@QBEDD@Z + ?tolower@?$ctype@D@std@@QBEPBDPADPBD@Z + ?tolower@?$ctype@G@std@@QBEGG@Z + ?tolower@?$ctype@G@std@@QBEPBGPAGPBG@Z + ?tolower@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z + ?tolower@?$ctype@_W@std@@QBE_W_W@Z + ?toupper@?$ctype@D@std@@QBEDD@Z + ?toupper@?$ctype@D@std@@QBEPBDPADPBD@Z + ?toupper@?$ctype@G@std@@QBEGG@Z + ?toupper@?$ctype@G@std@@QBEPBGPAGPBG@Z + ?toupper@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z + ?toupper@?$ctype@_W@std@@QBE_W_W@Z + ?traps@_Num_base@std@@2_NB + ?traps@_Num_float_base@std@@2_NB + ?uflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ + ?uflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ + ?uflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ + ?uncaught_exception@std@@YA_NXZ + ?underflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ + ?underflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ + ?underflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ + ?unget@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ + ?unget@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ + ?unget@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ + ?unsetf@ios_base@std@@QAEXH@Z + ?unshift@?$codecvt@DDH@std@@QBEHAAHPAD1AAPAD@Z + ?unshift@?$codecvt@GDH@std@@QBEHAAHPAD1AAPAD@Z + ?unshift@?$codecvt@_WDH@std@@QBEHAAHPAD1AAPAD@Z + ?wait@agent@Concurrency@@SA?AW4agent_status@2@PAV12@I@Z + ?wait_for_all@agent@Concurrency@@SAXIPAPAV12@PAW4agent_status@2@I@Z + ?wait_for_one@agent@Concurrency@@SAXIPAPAV12@AAW4agent_status@2@AAII@Z + ?wcerr@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?wcerr@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?wcin@std@@3V?$basic_istream@GU?$char_traits@G@std@@@1@A + ?wcin@std@@3V?$basic_istream@_WU?$char_traits@_W@std@@@1@A + ?wclog@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?wclog@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?wcout@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A + ?wcout@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A + ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDD@Z + ?widen@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGD@Z + ?widen@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WD@Z + ?widen@?$ctype@D@std@@QBEDD@Z + ?widen@?$ctype@D@std@@QBEPBDPBD0PAD@Z + ?widen@?$ctype@G@std@@QBEGD@Z + ?widen@?$ctype@G@std@@QBEPBDPBD0PAG@Z + ?widen@?$ctype@_W@std@@QBEPBDPBD0PA_W@Z + ?widen@?$ctype@_W@std@@QBE_WD@Z + ?width@ios_base@std@@QAE_J_J@Z + ?width@ios_base@std@@QBE_JXZ + ?write@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@PBD_J@Z + ?write@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@PBG_J@Z + ?write@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PB_W_J@Z + ?ws@std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@1@AAV21@@Z + ?ws@std@@YAAAV?$basic_istream@GU?$char_traits@G@std@@@1@AAV21@@Z + ?ws@std@@YAAAV?$basic_istream@_WU?$char_traits@_W@std@@@1@AAV21@@Z + ?xalloc@ios_base@std@@SAHXZ + ?xsgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPAD_J@Z + ?xsgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPAG_J@Z + ?xsgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPA_W_J@Z + ?xsputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPBD_J@Z + ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPBG_J@Z + ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPB_W_J@Z + _Cosh + _Denorm + _Dnorm + _Dscale + _Dtest + _Eps + _Exp + _FCosh + _FDenorm + _FDnorm + _FDscale + _FDtest + _FEps + _FExp + _FInf + _FNan + _FRteps + _FSinh + _FSnan + _FXbig + _GetLocaleForCP + _Getcoll + _Getctype + _Getcvt + _Getdateorder + _Getwctype + _Getwctypes + _Hugeval + _Inf + _LCosh + _LDenorm + _LDscale + _LDtest + _LEps + _LExp + _LInf + _LNan + _LPoly + _LRteps + _LSinh + _LSnan + _LXbig + _LZero + _Mbrtowc + _Mtxdst + _Mtxinit + _Mtxlock + _Mtxunlock + _Nan + _Once + _Poly + _Rteps + _Sinh + _Snan + _Stod + _Stodx + _Stof + _Stofx + _Stold + _Stoldx + _Stoll + _Stollx + _Stolx + _Stoul + _Stoull + _Stoullx + _Stoulx + _Strcoll + _Strxfrm + _Tolower + _Toupper + _Towlower + _Towupper + _Wcrtomb + _Wcscoll + _Wcsxfrm + _Xbig + __Wcrtomb_lk + towctrans + wctrans + wctype + diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def new file mode 100644 index 00000000..6317e658 --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def @@ -0,0 +1,1640 @@ + + ; + _except_handler4 + ;_heap_init + ;__crtExitProcess + ;_NMSG_WRITE + ;_FF_MSGBANNER + ;_cinit + ;_setenvp + ;__crtGetEnvironmentStringsA + ;_ioinit + ;_mtinit + + _initterm=my_initterm + __dllonexit=my_dllonexit + __getmainargs=my_getmainargs + __setusermatherr=my_setusermatherr + + _onexit DATA ; the one is crazy. atonexit.obj in msvcrt.lib which calls the __imp___onexit directly. + + ; MSVCR100.DLL exports + ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z + ??0SchedulerPolicy@Concurrency@@QAA@IZZ + ??0SchedulerPolicy@Concurrency@@QAE@ABV01@@Z + ??0SchedulerPolicy@Concurrency@@QAE@XZ + ??0_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ + ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ + ??0_ReaderWriterLock@details@Concurrency@@QAE@XZ + ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ + ??0_ReentrantLock@details@Concurrency@@QAE@XZ + ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ + ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z + ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z + ??0_SpinLock@details@Concurrency@@QAE@ACJ@Z + ??0_TaskCollection@details@Concurrency@@QAE@XZ + ??0_Timer@details@Concurrency@@IAE@I_N@Z + ??0__non_rtti_object@std@@QAE@ABV01@@Z + ??0__non_rtti_object@std@@QAE@PBD@Z + ;fixme-not-in-libcmt.lib; ??0bad_cast@std@@AAE@PBQBD@Z + ??0bad_cast@std@@QAE@ABV01@@Z + ??0bad_cast@std@@QAE@PBD@Z + ??0bad_target@Concurrency@@QAE@PBD@Z + ??0bad_target@Concurrency@@QAE@XZ + ??0bad_typeid@std@@QAE@ABV01@@Z + ??0bad_typeid@std@@QAE@PBD@Z + ??0context_self_unblock@Concurrency@@QAE@PBD@Z + ??0context_self_unblock@Concurrency@@QAE@XZ + ??0context_unblock_unbalanced@Concurrency@@QAE@PBD@Z + ??0context_unblock_unbalanced@Concurrency@@QAE@XZ + ??0critical_section@Concurrency@@QAE@XZ + ??0default_scheduler_exists@Concurrency@@QAE@PBD@Z + ??0default_scheduler_exists@Concurrency@@QAE@XZ + ??0event@Concurrency@@QAE@XZ + ??0exception@std@@QAE@ABQBD@Z + ??0exception@std@@QAE@ABQBDH@Z + ??0exception@std@@QAE@ABV01@@Z + ??0exception@std@@QAE@XZ + ??0improper_lock@Concurrency@@QAE@PBD@Z + ??0improper_lock@Concurrency@@QAE@XZ + ??0improper_scheduler_attach@Concurrency@@QAE@PBD@Z + ??0improper_scheduler_attach@Concurrency@@QAE@XZ + ??0improper_scheduler_detach@Concurrency@@QAE@PBD@Z + ??0improper_scheduler_detach@Concurrency@@QAE@XZ + ??0improper_scheduler_reference@Concurrency@@QAE@PBD@Z + ??0improper_scheduler_reference@Concurrency@@QAE@XZ + ??0invalid_link_target@Concurrency@@QAE@PBD@Z + ??0invalid_link_target@Concurrency@@QAE@XZ + ??0invalid_multiple_scheduling@Concurrency@@QAE@PBD@Z + ??0invalid_multiple_scheduling@Concurrency@@QAE@XZ + ??0invalid_operation@Concurrency@@QAE@PBD@Z + ??0invalid_operation@Concurrency@@QAE@XZ + ??0invalid_oversubscribe_operation@Concurrency@@QAE@PBD@Z + ??0invalid_oversubscribe_operation@Concurrency@@QAE@XZ + ??0invalid_scheduler_policy_key@Concurrency@@QAE@PBD@Z + ??0invalid_scheduler_policy_key@Concurrency@@QAE@XZ + ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@PBD@Z + ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@XZ + ??0invalid_scheduler_policy_value@Concurrency@@QAE@PBD@Z + ??0invalid_scheduler_policy_value@Concurrency@@QAE@XZ + ??0message_not_found@Concurrency@@QAE@PBD@Z + ??0message_not_found@Concurrency@@QAE@XZ + ??0missing_wait@Concurrency@@QAE@PBD@Z + ??0missing_wait@Concurrency@@QAE@XZ + ??0nested_scheduler_missing_detach@Concurrency@@QAE@PBD@Z + ??0nested_scheduler_missing_detach@Concurrency@@QAE@XZ + ??0operation_timed_out@Concurrency@@QAE@PBD@Z + ??0operation_timed_out@Concurrency@@QAE@XZ + ??0reader_writer_lock@Concurrency@@QAE@XZ + ??0scheduler_not_attached@Concurrency@@QAE@PBD@Z + ??0scheduler_not_attached@Concurrency@@QAE@XZ + ??0scheduler_resource_allocation_error@Concurrency@@QAE@J@Z + ??0scheduler_resource_allocation_error@Concurrency@@QAE@PBDJ@Z + ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z + ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z + ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z + ??0task_canceled@details@Concurrency@@QAE@PBD@Z + ??0task_canceled@details@Concurrency@@QAE@XZ + ??0unsupported_os@Concurrency@@QAE@PBD@Z + ??0unsupported_os@Concurrency@@QAE@XZ + ??1SchedulerPolicy@Concurrency@@QAE@XZ + ??1_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ + ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ + ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ + ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ + ??1_SpinLock@details@Concurrency@@QAE@XZ + ??1_TaskCollection@details@Concurrency@@QAE@XZ + ??1_Timer@details@Concurrency@@IAE@XZ + ??1__non_rtti_object@std@@UAE@XZ + ??1bad_cast@std@@UAE@XZ + ??1bad_typeid@std@@UAE@XZ + ??1critical_section@Concurrency@@QAE@XZ + ??1event@Concurrency@@QAE@XZ + ??1exception@std@@UAE@XZ + ??1reader_writer_lock@Concurrency@@QAE@XZ + ??1scoped_lock@critical_section@Concurrency@@QAE@XZ + ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ + ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ + ??1type_info@@UAE@XZ + ??2@YAPAXI@Z + ??2@YAPAXIHPBDH@Z + ??3@YAXPAX@Z + ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$00@details@Concurrency@@QAEAAV012@ABV012@@Z + ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$0A@@details@Concurrency@@QAEAAV012@ABV012@@Z + ??4SchedulerPolicy@Concurrency@@QAEAAV01@ABV01@@Z + ??4__non_rtti_object@std@@QAEAAV01@ABV01@@Z + ??4bad_cast@std@@QAEAAV01@ABV01@@Z + ??4bad_typeid@std@@QAEAAV01@ABV01@@Z + ??4exception@std@@QAEAAV01@ABV01@@Z + ??8type_info@@QBE_NABV0@@Z + ??9type_info@@QBE_NABV0@@Z + ??_7__non_rtti_object@std@@6B@ + ??_7bad_cast@std@@6B@ + ??_7bad_typeid@std@@6B@ + ;fixme-not-in-libcmt.lib; ??_7exception@@6B@ + ??_7exception@std@@6B@ + ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ + ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ + ??_Fbad_cast@std@@QAEXXZ + ??_Fbad_typeid@std@@QAEXXZ + ??_U@YAPAXI@Z + ??_U@YAPAXIHPBDH@Z + ??_V@YAXPAX@Z + ?Alloc@Concurrency@@YAPAXI@Z + ?Block@Context@Concurrency@@SAXXZ + ?Create@CurrentScheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z + ?Create@Scheduler@Concurrency@@SAPAV12@ABVSchedulerPolicy@2@@Z + ?CreateResourceManager@Concurrency@@YAPAUIResourceManager@1@XZ + ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ + ?CurrentContext@Context@Concurrency@@SAPAV12@XZ + ?Detach@CurrentScheduler@Concurrency@@SAXXZ + ?DisableTracing@Concurrency@@YAJXZ + ?EnableTracing@Concurrency@@YAJXZ + ?Free@Concurrency@@YAXPAX@Z + ?Get@CurrentScheduler@Concurrency@@SAPAVScheduler@2@XZ + ?GetExecutionContextId@Concurrency@@YAIXZ + ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ + ?GetOSVersion@Concurrency@@YA?AW4OSVersion@IResourceManager@1@XZ + ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ + ?GetPolicyValue@SchedulerPolicy@Concurrency@@QBEIW4PolicyElementKey@2@@Z + ?GetProcessorCount@Concurrency@@YAIXZ + ?GetProcessorNodeCount@Concurrency@@YAIXZ + ?GetSchedulerId@Concurrency@@YAIXZ + ?GetSharedTimerQueue@details@Concurrency@@YAPAXXZ + ?Id@Context@Concurrency@@SAIXZ + ?Id@CurrentScheduler@Concurrency@@SAIXZ + ?IsCurrentTaskCollectionCanceling@Context@Concurrency@@SA_NXZ + ?Log2@details@Concurrency@@YAKI@Z + ?Oversubscribe@Context@Concurrency@@SAX_N@Z + ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z + ?ResetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXXZ + ?ScheduleGroupId@Context@Concurrency@@SAIXZ + ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z + ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QAEXII@Z + ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z + ?SetPolicyValue@SchedulerPolicy@Concurrency@@QAEIW4PolicyElementKey@2@I@Z + ?VirtualProcessorId@Context@Concurrency@@SAIXZ + ?Yield@Context@Concurrency@@SAXXZ + ?_Abort@_StructuredTaskCollection@details@Concurrency@@AAEXXZ + ?_Acquire@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ + ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z + ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ + ?_Acquire@_ReentrantLock@details@Concurrency@@QAEXXZ + ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z + ?_AcquireRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ + ?_AcquireWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ + ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QAEXXZ + ?_Cancel@_TaskCollection@details@Concurrency@@QAEXXZ + ?_CheckTaskCollection@_UnrealizedChore@details@Concurrency@@IAEXXZ + ?_ConcRT_Assert@details@Concurrency@@YAXPBD0H@Z + ?_ConcRT_CoreAssert@details@Concurrency@@YAXPBD0H@Z + ?_ConcRT_DumpMessage@details@Concurrency@@YAXPB_WZZ + ?_ConcRT_Trace@details@Concurrency@@YAXHPB_WZZ + ?_Copy_str@exception@std@@AAEXPBD@Z + ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ + ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ + ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAE_NXZ + ?_IsCanceling@_TaskCollection@details@Concurrency@@QAE_NXZ + ?_Name_base@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z + ?_Name_base_internal@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z + ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ + ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ + ?_Release@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ + ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ + ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ + ?_Release@_ReentrantLock@details@Concurrency@@QAEXXZ + ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ + ?_ReleaseRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ + ?_ReleaseWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ + ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ + ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ + ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z + ?_RunAndWait@_TaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z + ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z + ?_Schedule@_TaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z + ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z + ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z + ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ + ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ + ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ + ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ + ?_SpinYield@Context@Concurrency@@SAXXZ + ?_Start@_Timer@details@Concurrency@@IAEXXZ + ?_Stop@_Timer@details@Concurrency@@IAEXXZ + ?_Tidy@exception@std@@AAEXXZ + ?_Trace_ppl_function@Concurrency@@YAXABU_GUID@@EW4ConcRT_EventType@1@@Z + ?_TryAcquire@_NonReentrantBlockingLock@details@Concurrency@@QAE_NXZ + ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ + ?_TryAcquire@_ReentrantLock@details@Concurrency@@QAE_NXZ + ?_TryAcquireWrite@_ReaderWriterLock@details@Concurrency@@QAE_NXZ + ?_Type_info_dtor@type_info@@CAXPAV1@@Z + ?_Type_info_dtor_internal@type_info@@CAXPAV1@@Z + ?_UnderlyingYield@details@Concurrency@@YAXXZ + ?_ValidateExecute@@YAHP6GHXZ@Z + ?_ValidateRead@@YAHPBXI@Z + ?_ValidateWrite@@YAHPAXI@Z + ?_Value@_SpinCount@details@Concurrency@@SAIXZ + ?__ExceptionPtrAssign@@YAXPAXPBX@Z + ?__ExceptionPtrCompare@@YA_NPBX0@Z + ?__ExceptionPtrCopy@@YAXPAXPBX@Z + ?__ExceptionPtrCopyException@@YAXPAXPBX1@Z + ?__ExceptionPtrCreate@@YAXPAX@Z + ?__ExceptionPtrCurrentException@@YAXPAX@Z + ?__ExceptionPtrDestroy@@YAXPAX@Z + ?__ExceptionPtrRethrow@@YAXPBX@Z + __uncaught_exception + ?_inconsistency@@YAXXZ + ?_invalid_parameter@@YAXPBG00II@Z + ?_is_exception_typeof@@YAHABVtype_info@@PAU_EXCEPTION_POINTERS@@@Z + ?_name_internal_method@type_info@@QBEPBDPAU__type_info_node@@@Z + ?_open@@YAHPBDHH@Z + ?_query_new_handler@@YAP6AHI@ZXZ + ?_query_new_mode@@YAHXZ + ?_set_new_handler@@YAP6AHI@ZH@Z + ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z + ?_set_new_mode@@YAHH@Z + ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZH@Z + ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z + ?_sopen@@YAHPBDHHH@Z + ?_type_info_dtor_internal_method@type_info@@QAEXXZ + ?_wopen@@YAHPB_WHH@Z + ?_wsopen@@YAHPB_WHHH@Z + ?before@type_info@@QBEHABV1@@Z + ?get_error_code@scheduler_resource_allocation_error@Concurrency@@QBEJXZ + ?lock@critical_section@Concurrency@@QAEXXZ + ?lock@reader_writer_lock@Concurrency@@QAEXXZ + ?lock_read@reader_writer_lock@Concurrency@@QAEXXZ + ?name@type_info@@QBEPBDPAU__type_info_node@@@Z + ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ + ?raw_name@type_info@@QBEPBDXZ + ?reset@event@Concurrency@@QAEXXZ + ?set@event@Concurrency@@QAEXXZ + ?set_new_handler@@YAP6AXXZP6AXXZ@Z + ?set_terminate@@YAP6AXXZH@Z + ?set_terminate@@YAP6AXXZP6AXXZ@Z + ?set_unexpected@@YAP6AXXZH@Z + ?set_unexpected@@YAP6AXXZP6AXXZ@Z + ?swprintf@@YAHPAGIPBGZZ + ?swprintf@@YAHPA_WIPB_WZZ + ?terminate@@YAXXZ ;fixme-causes-trouble-with-version-in-libcmt.lib; + ?try_lock@critical_section@Concurrency@@QAE_NXZ + ?try_lock@reader_writer_lock@Concurrency@@QAE_NXZ + ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ + ?unexpected@@YAXXZ + ?unlock@critical_section@Concurrency@@QAEXXZ + ?unlock@reader_writer_lock@Concurrency@@QAEXXZ + ?vswprintf@@YAHPA_WIPB_WPAD@Z + ?wait@Concurrency@@YAXI@Z + ?wait@event@Concurrency@@QAEII@Z + ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z + ?what@exception@std@@UBEPBDXZ + $I10_OUTPUT + _CIacos + _CIasin + _CIatan + _CIatan2 + _CIcos + _CIcosh + _CIexp + _CIfmod + _CIlog + _CIlog10 + _CIpow + _CIsin + _CIsinh + _CIsqrt + _CItan + _CItanh + _CRT_RTC_INIT + _CRT_RTC_INITW + _CreateFrameInfo + _CxxThrowException + _EH_prolog + _FindAndUnlinkFrame + _Getdays + _Getmonths + _Gettnames + _HUGE + _IsExceptionObjectToBeDestroyed + _NLG_Dispatch2 + _NLG_Return + _NLG_Return2 + _Strftime + _XcptFilter + __AdjustPointer + __BuildCatchObject + __BuildCatchObjectHelper + __CppXcptFilter + __CxxCallUnwindDelDtor + __CxxCallUnwindDtor + __CxxCallUnwindStdDelDtor + __CxxCallUnwindVecDtor + __CxxDetectRethrow + __CxxExceptionFilter + __CxxFrameHandler + __CxxFrameHandler2 + __CxxFrameHandler3 + __CxxLongjmpUnwind + __CxxQueryExceptionSize + __CxxRegisterExceptionObject + __CxxUnregisterExceptionObject + __DestructExceptionObject + __FrameUnwindFilter + __RTCastToVoid + __RTDynamicCast + __RTtypeid + __STRINGTOLD + __STRINGTOLD_L + __TypeMatch + ___lc_codepage_func + ___lc_collate_cp_func + ___lc_handle_func + ___mb_cur_max_func + ___mb_cur_max_l_func + ___setlc_active_func + ___unguarded_readlc_active_add_func + __argc + __argv + __badioinfo + __clean_type_info_names_internal + __control87_2 + __create_locale + __crtCompareStringA + __crtCompareStringW + __crtLCMapStringA + __crtLCMapStringW + __daylight + __doserrno + __dstbias + ___fls_getvalue@4 + ___fls_setvalue@8 + __fpecode + __free_locale + __get_current_locale + __get_flsindex + ;fixme?; __get_tlsindex + __initenv + __iob_func + __isascii + __iscsym + __iscsymf + __iswcsym + __iswcsymf + __lconv + __lconv_init + __libm_sse2_acos + __libm_sse2_acosf + __libm_sse2_asin + __libm_sse2_asinf + __libm_sse2_atan + __libm_sse2_atan2 + __libm_sse2_atanf + __libm_sse2_cos + __libm_sse2_cosf + __libm_sse2_exp + __libm_sse2_expf + __libm_sse2_log + __libm_sse2_log10 + __libm_sse2_log10f + __libm_sse2_logf + __libm_sse2_pow + __libm_sse2_powf + __libm_sse2_sin + __libm_sse2_sinf + __libm_sse2_tan + __libm_sse2_tanf + __mb_cur_max + ;fixme?; __p___argc + ;fixme?; __p___argv + ;fixme?; __p___initenv + ;fixme?; __p___mb_cur_max + ;fixme?; __p___wargv + ;fixme?; __p___winitenv + ;fixme?; __p__acmdln + ;fixme?; __p__commode + ;fixme?; __p__daylight + ;fixme?; __p__dstbias + ;fixme?; __p__environ + ;fixme?; __p__fmode + ;fixme?; __p__iob + ;fixme?; __p__mbcasemap + ;fixme?; __p__mbctype + ;fixme?; __p__pctype + ;fixme?; __p__pgmptr + ;fixme?; __p__pwctype + ;fixme?; __p__timezone + ;fixme?; __p__tzname + ;fixme?; __p__wcmdln + ;fixme?; __p__wenviron + ;fixme?; __p__wpgmptr + + __pctype_func + __pioinfo + __pwctype_func + __pxcptinfoptrs + __report_gsfailure + __set_app_type + __set_flsgetvalue + __setlc_active + ;fixme?; __setusermatherr + __strncnt + __swprintf_l + __sys_errlist + __sys_nerr + __threadhandle + __threadid + __timezone + __toascii + __tzname + __unDName + __unDNameEx + __unDNameHelper + __unguarded_readlc_active + __vswprintf_l + __wargv + __wcserror + __wcserror_s + __wcsncnt + ;fixme?; __wgetmainargs + __winitenv + _abnormal_termination + _abs64 + _access + _access_s + _acmdln + _aligned_free + _aligned_malloc + _aligned_msize + _aligned_offset_malloc + _aligned_offset_realloc + _aligned_offset_recalloc + _aligned_realloc + _aligned_recalloc + _amsg_exit + _assert + _atodbl + _atodbl_l + _atof_l + _atoflt + _atoflt_l + _atoi64 + _atoi64_l + _atoi_l + _atol_l + _atoldbl + _atoldbl_l + _beep + _beginthread + _beginthreadex + _byteswap_uint64 + _byteswap_ulong + _byteswap_ushort + _c_exit + _cabs + _callnewh + _calloc_crt + _cexit + _cgets + _cgets_s + _cgetws + _cgetws_s + _chdir + _chdrive + _chgsign + _chkesp + _chmod + _chsize + _chsize_s + _clearfp + _close + _commit + ;fixme?; _commode + _configthreadlocale + _control87 + _controlfp + _controlfp_s + _copysign + _cprintf + _cprintf_l + _cprintf_p + _cprintf_p_l + _cprintf_s + _cprintf_s_l + _cputs + _cputws + _creat + _create_locale + _crt_debugger_hook + _cscanf + _cscanf_l + _cscanf_s + _cscanf_s_l + _ctime32 + _ctime32_s + _ctime64 + _ctime64_s + _cwait + _cwprintf + _cwprintf_l + _cwprintf_p + _cwprintf_p_l + _cwprintf_s + _cwprintf_s_l + _cwscanf + _cwscanf_l + _cwscanf_s + _cwscanf_s_l + _daylight + _difftime32 + _difftime64 + _dosmaperr + _dstbias + _dup + _dup2 + _dupenv_s + _ecvt + _ecvt_s + _encoded_null + _endthread + _endthreadex + _environ + _eof + _errno + _except_handler2 + _except_handler3 + ;fixme?; _except_handler4_common + _execl + _execle + _execlp + _execlpe + _execv + _execve + _execvp + _execvpe + _exit + _expand + _fclose_nolock + _fcloseall + _fcvt + _fcvt_s + _fdopen + _fflush_nolock + _fgetchar + _fgetwc_nolock + _fgetwchar + _filbuf + _filelength + _filelengthi64 + _fileno + _findclose + _findfirst32 + _findfirst32i64 + _findfirst64 + _findfirst64i32 + _findnext32 + _findnext32i64 + _findnext64 + _findnext64i32 + _finite + _flsbuf + _flushall + ;fixme?; _fmode + _fpclass + _fpieee_flt + _fpreset + _fprintf_l + _fprintf_p + _fprintf_p_l + _fprintf_s_l + _fputchar + _fputwc_nolock + _fputwchar + _fread_nolock + _fread_nolock_s + _free_locale + _freea + _freea_s + _freefls + _fscanf_l + _fscanf_s_l + _fseek_nolock + _fseeki64 + _fseeki64_nolock + _fsopen + _fstat32 + _fstat32i64 + _fstat64 + _fstat64i32 + _ftell_nolock + _ftelli64 + _ftelli64_nolock + _ftime32 + _ftime32_s + _ftime64 + _ftime64_s + _ftol + _fullpath + _futime32 + _futime64 + _fwprintf_l + _fwprintf_p + _fwprintf_p_l + _fwprintf_s_l + _fwrite_nolock + _fwscanf_l + _fwscanf_s_l + _gcvt + _gcvt_s + _get_current_locale + _get_daylight + _get_doserrno + _get_dstbias + _get_errno + _get_fmode + _get_heap_handle + _get_invalid_parameter_handler + _get_osfhandle + _get_output_format + _get_pgmptr + _get_printf_count_output + _get_purecall_handler + _get_terminate + _get_timezone + _get_tzname + _get_unexpected + _get_wpgmptr + _getc_nolock + _getch + _getch_nolock + _getche + _getche_nolock + _getcwd + _getdcwd + _getdcwd_nolock + _getdiskfree + _getdllprocaddr + _getdrive + _getdrives + _getmaxstdio + _getmbcp + _getpid + _getptd + _getsystime + _getw + _getwch + _getwch_nolock + _getwche + _getwche_nolock + _getws + _getws_s + _global_unwind2 + _gmtime32 + _gmtime32_s + _gmtime64 + _gmtime64_s + _heapadd + _heapchk + _heapmin + _heapset + _heapused + _heapwalk + _hypot + _hypotf + _i64toa + _i64toa_s + _i64tow + _i64tow_s + _initptd + ;fixme?; _initterm + _initterm_e + _inp + _inpd + _inpw + _invalid_parameter + _invalid_parameter_noinfo + _invalid_parameter_noinfo_noreturn + _invoke_watson + _iob + _isalnum_l + _isalpha_l + _isatty + _iscntrl_l + _isctype + _isctype_l + _isdigit_l + _isgraph_l + _isleadbyte_l + _islower_l + _ismbbalnum + _ismbbalnum_l + _ismbbalpha + _ismbbalpha_l + _ismbbgraph + _ismbbgraph_l + _ismbbkalnum + _ismbbkalnum_l + _ismbbkana + _ismbbkana_l + _ismbbkprint + _ismbbkprint_l + _ismbbkpunct + _ismbbkpunct_l + _ismbblead + _ismbblead_l + _ismbbprint + _ismbbprint_l + _ismbbpunct + _ismbbpunct_l + _ismbbtrail + _ismbbtrail_l + _ismbcalnum + _ismbcalnum_l + _ismbcalpha + _ismbcalpha_l + _ismbcdigit + _ismbcdigit_l + _ismbcgraph + _ismbcgraph_l + _ismbchira + _ismbchira_l + _ismbckata + _ismbckata_l + _ismbcl0 + _ismbcl0_l + _ismbcl1 + _ismbcl1_l + _ismbcl2 + _ismbcl2_l + _ismbclegal + _ismbclegal_l + _ismbclower + _ismbclower_l + _ismbcprint + _ismbcprint_l + _ismbcpunct + _ismbcpunct_l + _ismbcspace + _ismbcspace_l + _ismbcsymbol + _ismbcsymbol_l + _ismbcupper + _ismbcupper_l + _ismbslead + _ismbslead_l + _ismbstrail + _ismbstrail_l + _isnan + _isprint_l + _ispunct_l + _isspace_l + _isupper_l + _iswalnum_l + _iswalpha_l + _iswcntrl_l + _iswcsym_l + _iswcsymf_l + _iswctype_l + _iswdigit_l + _iswgraph_l + _iswlower_l + _iswprint_l + _iswpunct_l + _iswspace_l + _iswupper_l + _iswxdigit_l + _isxdigit_l + _itoa + _itoa_s + _itow + _itow_s + _j0 + _j1 + _jn + _kbhit + _lfind + _lfind_s + _loaddll + _local_unwind2 + _local_unwind4 + _localtime32 + _localtime32_s + _localtime64 + _localtime64_s + _lock + _lock_file + _locking + _logb + _longjmpex + _lrotl + _lrotr + _lsearch + _lsearch_s + _lseek + _lseeki64 + _ltoa + _ltoa_s + _ltow + _ltow_s + _makepath + _makepath_s + _malloc_crt + _mbbtombc + _mbbtombc_l + _mbbtype + _mbbtype_l + _mbcasemap + _mbccpy + _mbccpy_l + _mbccpy_s + _mbccpy_s_l + _mbcjistojms + _mbcjistojms_l + _mbcjmstojis + _mbcjmstojis_l + _mbclen + _mbclen_l + _mbctohira + _mbctohira_l + _mbctokata + _mbctokata_l + _mbctolower + _mbctolower_l + _mbctombb + _mbctombb_l + _mbctoupper + _mbctoupper_l + _mbctype + _mblen_l + _mbsbtype + _mbsbtype_l + _mbscat_s + _mbscat_s_l + _mbschr + _mbschr_l + _mbscmp + _mbscmp_l + _mbscoll + _mbscoll_l + _mbscpy_s + _mbscpy_s_l + _mbscspn + _mbscspn_l + _mbsdec + _mbsdec_l + _mbsicmp + _mbsicmp_l + _mbsicoll + _mbsicoll_l + _mbsinc + _mbsinc_l + _mbslen + _mbslen_l + _mbslwr + _mbslwr_l + _mbslwr_s + _mbslwr_s_l + _mbsnbcat + _mbsnbcat_l + _mbsnbcat_s + _mbsnbcat_s_l + _mbsnbcmp + _mbsnbcmp_l + _mbsnbcnt + _mbsnbcnt_l + _mbsnbcoll + _mbsnbcoll_l + _mbsnbcpy + _mbsnbcpy_l + _mbsnbcpy_s + _mbsnbcpy_s_l + _mbsnbicmp + _mbsnbicmp_l + _mbsnbicoll + _mbsnbicoll_l + _mbsnbset + _mbsnbset_l + _mbsnbset_s + _mbsnbset_s_l + _mbsncat + _mbsncat_l + _mbsncat_s + _mbsncat_s_l + _mbsnccnt + _mbsnccnt_l + _mbsncmp + _mbsncmp_l + _mbsncoll + _mbsncoll_l + _mbsncpy + _mbsncpy_l + _mbsncpy_s + _mbsncpy_s_l + _mbsnextc + _mbsnextc_l + _mbsnicmp + _mbsnicmp_l + _mbsnicoll + _mbsnicoll_l + _mbsninc + _mbsninc_l + _mbsnlen + _mbsnlen_l + _mbsnset + _mbsnset_l + _mbsnset_s + _mbsnset_s_l + _mbspbrk + _mbspbrk_l + _mbsrchr + _mbsrchr_l + _mbsrev + _mbsrev_l + _mbsset + _mbsset_l + _mbsset_s + _mbsset_s_l + _mbsspn + _mbsspn_l + _mbsspnp + _mbsspnp_l + _mbsstr + _mbsstr_l + _mbstok + _mbstok_l + _mbstok_s + _mbstok_s_l + _mbstowcs_l + _mbstowcs_s_l + _mbstrlen + _mbstrlen_l + _mbstrnlen + _mbstrnlen_l + _mbsupr + _mbsupr_l + _mbsupr_s + _mbsupr_s_l + _mbtowc_l + _memccpy + _memicmp + _memicmp_l + _mkdir + _mkgmtime32 + _mkgmtime64 + _mktemp + _mktemp_s + _mktime32 + _mktime64 + _msize + _nextafter + _open + _open_osfhandle + _outp + _outpd + _outpw + _pclose + _pctype + _pgmptr + _pipe + _popen + _printf_l + _printf_p + _printf_p_l + _printf_s_l + _purecall + _putch + _putch_nolock + _putenv + _putenv_s + _putw + _putwch + _putwch_nolock + _putws + _pwctype + _read + _realloc_crt + _recalloc + _recalloc_crt + _resetstkoflw + _rmdir + _rmtmp + _rotl + _rotl64 + _rotr + _rotr64 + _scalb + _scanf_l + _scanf_s_l + _scprintf + _scprintf_l + _scprintf_p + _scprintf_p_l + _scwprintf + _scwprintf_l + _scwprintf_p + _scwprintf_p_l + _searchenv + _searchenv_s + _seh_longjmp_unwind4 + _seh_longjmp_unwind + _set_SSE2_enable + _set_abort_behavior + _set_controlfp + _set_doserrno + _set_errno + _set_error_mode + _set_fmode + _set_invalid_parameter_handler + _set_malloc_crt_max_wait + _set_output_format + _set_printf_count_output + _set_purecall_handler + _seterrormode + _setjmp + _setjmp3 + _setmaxstdio + _setmbcp + _setmode + _setsystime + _sleep + _snprintf + _snprintf_c + _snprintf_c_l + _snprintf_l + _snprintf_s + _snprintf_s_l + _snscanf + _snscanf_l + _snscanf_s + _snscanf_s_l + _snwprintf + _snwprintf_l + _snwprintf_s + _snwprintf_s_l + _snwscanf + _snwscanf_l + _snwscanf_s + _snwscanf_s_l + _sopen + _sopen_s + _spawnl + _spawnle + _spawnlp + _spawnlpe + _spawnv + _spawnve + _spawnvp + _spawnvpe + _splitpath + _splitpath_s + _sprintf_l + _sprintf_p + _sprintf_p_l + _sprintf_s_l + _sscanf_l + _sscanf_s_l + _stat32 + _stat32i64 + _stat64 + _stat64i32 + _statusfp + _statusfp2 + _strcoll_l + _strdate + _strdate_s + _strdup + _strerror + _strerror_s + _strftime_l + _stricmp + _stricmp_l + _stricoll + _stricoll_l + _strlwr + _strlwr_l + _strlwr_s + _strlwr_s_l + _strncoll + _strncoll_l + _strnicmp + _strnicmp_l + _strnicoll + _strnicoll_l + _strnset + _strnset_s + _strrev + _strset + _strset_s + _strtime + _strtime_s + _strtod_l + _strtoi64 + _strtoi64_l + _strtol_l + _strtoui64 + _strtoui64_l + _strtoul_l + _strupr + _strupr_l + _strupr_s + _strupr_s_l + _strxfrm_l + _swab + _swprintf + _swprintf_c + _swprintf_c_l + _swprintf_p + _swprintf_p_l + _swprintf_s_l + _swscanf_l + _swscanf_s_l + _sys_errlist + _sys_nerr + _tell + _telli64 + _tempnam + _time32 + _time64 + _timezone + _tolower + _tolower_l + _toupper + _toupper_l + _towlower_l + _towupper_l + _tzname + _tzset + _ui64toa + _ui64toa_s + _ui64tow + _ui64tow_s + _ultoa + _ultoa_s + _ultow + _ultow_s + _umask + _umask_s + _ungetc_nolock + _ungetch + _ungetch_nolock + _ungetwc_nolock + _ungetwch + _ungetwch_nolock + _unlink + _unloaddll + _unlock + _unlock_file + _utime32 + _utime64 + _vcprintf + _vcprintf_l + _vcprintf_p + _vcprintf_p_l + _vcprintf_s + _vcprintf_s_l + _vcwprintf + _vcwprintf_l + _vcwprintf_p + _vcwprintf_p_l + _vcwprintf_s + _vcwprintf_s_l + _vfprintf_l + _vfprintf_p + _vfprintf_p_l + _vfprintf_s_l + _vfwprintf_l + _vfwprintf_p + _vfwprintf_p_l + _vfwprintf_s_l + _vprintf_l + _vprintf_p + _vprintf_p_l + _vprintf_s_l + _vscprintf + _vscprintf_l + _vscprintf_p + _vscprintf_p_l + _vscwprintf + _vscwprintf_l + _vscwprintf_p + _vscwprintf_p_l + _vsnprintf + _vsnprintf_c + _vsnprintf_c_l + _vsnprintf_l + _vsnprintf_s + _vsnprintf_s_l + _vsnwprintf + _vsnwprintf_l + _vsnwprintf_s + _vsnwprintf_s_l + _vsprintf_l + _vsprintf_p + _vsprintf_p_l + _vsprintf_s_l + _vswprintf + _vswprintf_c + _vswprintf_c_l + _vswprintf_l + _vswprintf_p + _vswprintf_p_l + _vswprintf_s_l + _vwprintf_l + _vwprintf_p + _vwprintf_p_l + _vwprintf_s_l + _waccess + _waccess_s + _wasctime + _wasctime_s + _wassert + _wchdir + _wchmod + ;fixme?; _wcmdln + _wcreat + _wcscoll_l + _wcsdup + _wcserror + _wcserror_s + _wcsftime_l + _wcsicmp + _wcsicmp_l + _wcsicoll + _wcsicoll_l + _wcslwr + _wcslwr_l + _wcslwr_s + _wcslwr_s_l + _wcsncoll + _wcsncoll_l + _wcsnicmp + _wcsnicmp_l + _wcsnicoll + _wcsnicoll_l + _wcsnset + _wcsnset_s + _wcsrev + _wcsset + _wcsset_s + _wcstod_l + _wcstoi64 + _wcstoi64_l + _wcstol_l + _wcstombs_l + _wcstombs_s_l + _wcstoui64 + _wcstoui64_l + _wcstoul_l + _wcsupr + _wcsupr_l + _wcsupr_s + _wcsupr_s_l + _wcsxfrm_l + _wctime32 + _wctime32_s + _wctime64 + _wctime64_s + _wctomb_l + _wctomb_s_l + _wctype + _wdupenv_s + _wenviron + _wexecl + _wexecle + _wexeclp + _wexeclpe + _wexecv + _wexecve + _wexecvp + _wexecvpe + _wfdopen + _wfindfirst32 + _wfindfirst32i64 + _wfindfirst64 + _wfindfirst64i32 + _wfindnext32 + _wfindnext32i64 + _wfindnext64 + _wfindnext64i32 + _wfopen + _wfopen_s + _wfreopen + _wfreopen_s + _wfsopen + _wfullpath + _wgetcwd + _wgetdcwd + _wgetdcwd_nolock + _wgetenv + _wgetenv_s + _wmakepath + _wmakepath_s + _wmkdir + _wmktemp + _wmktemp_s + _wopen + _wperror + _wpgmptr + _wpopen + _wprintf_l + _wprintf_p + _wprintf_p_l + _wprintf_s_l + _wputenv + _wputenv_s + _wremove + _wrename + _write + _wrmdir + _wscanf_l + _wscanf_s_l + _wsearchenv + _wsearchenv_s + _wsetlocale + _wsopen + _wsopen_s + _wspawnl + _wspawnle + _wspawnlp + _wspawnlpe + _wspawnv + _wspawnve + _wspawnvp + _wspawnvpe + _wsplitpath + _wsplitpath_s + _wstat32 + _wstat32i64 + _wstat64 + _wstat64i32 + _wstrdate + _wstrdate_s + _wstrtime + _wstrtime_s + _wsystem + _wtempnam + _wtmpnam + _wtmpnam_s + _wtof + _wtof_l + _wtoi + _wtoi64 + _wtoi64_l + _wtoi_l + _wtol + _wtol_l + _wunlink + _wutime32 + _wutime64 + _y0 + _y1 + _yn + abort + abs + acos + asctime + asctime_s + asin + atan + atan2 + atexit + atof + atoi + atol + bsearch + bsearch_s + btowc + calloc + ceil + clearerr + clearerr_s + clock + cos + cosh + div + exit + exp + fabs + fclose + feof + ferror + fflush + fgetc + fgetpos + fgets + fgetwc + fgetws + floor + fmod + fopen + fopen_s + fprintf + fprintf_s + fputc + fputs + fputwc + fputws + fread + fread_s + free + freopen + freopen_s + frexp + fscanf + fscanf_s + fseek + fsetpos + ftell + fwprintf + fwprintf_s + fwrite + fwscanf + fwscanf_s + getc + getchar + getenv + getenv_s + gets + gets_s + getwc + getwchar + is_wctype + isalnum + isalpha + iscntrl + isdigit + isgraph + isleadbyte + islower + isprint + ispunct + isspace + isupper + iswalnum + iswalpha + iswascii + iswcntrl + iswctype + iswdigit + iswgraph + iswlower + iswprint + iswpunct + iswspace + iswupper + iswxdigit + isxdigit + labs + ldexp + ldiv + llabs + lldiv + localeconv + log + log10 + longjmp + malloc + mblen + mbrlen + mbrtowc + mbsrtowcs + mbsrtowcs_s + mbstowcs + mbstowcs_s + mbtowc + memchr + memcmp + memcpy + memcpy_s + memmove + memmove_s + memset + modf + perror + pow + printf + printf_s + putc + putchar + puts + putwc + putwchar + qsort + qsort_s + raise + rand + rand_s + realloc + remove + rename + rewind + scanf + scanf_s + setbuf + setlocale + setvbuf + signal + sin + sinh + sprintf + sprintf_s + sqrt + srand + sscanf + sscanf_s + strcat + strcat_s + strchr + strcmp + strcoll + strcpy + strcpy_s + strcspn + strerror + strerror_s + strftime + strlen + strncat + strncat_s + strncmp + strncpy + strncpy_s + strnlen + strpbrk + strrchr + strspn + strstr + strtod + strtok + strtok_s + strtol + strtoul + strxfrm + swprintf_s + swscanf + swscanf_s + system + tan + tanh + tmpfile + tmpfile_s + tmpnam + tmpnam_s + tolower + toupper + towlower + towupper + ungetc + ungetwc + vfprintf + vfprintf_s + vfwprintf + vfwprintf_s + vprintf + vprintf_s + vsprintf + vsprintf_s + vswprintf_s + vwprintf + vwprintf_s + wcrtomb + wcrtomb_s + wcscat + wcscat_s + wcschr + wcscmp + wcscoll + wcscpy + wcscpy_s + wcscspn + wcsftime + wcslen + wcsncat + wcsncat_s + wcsncmp + wcsncpy + wcsncpy_s + wcsnlen + wcspbrk + wcsrchr + wcsrtombs + wcsrtombs_s + wcsspn + wcsstr + wcstod + wcstok + wcstok_s + wcstol + wcstombs + wcstombs_s + wcstoul + wcsxfrm + wctob + wctomb + wctomb_s + wmemcpy_s + wmemmove_s + wprintf + wprintf_s + wscanf + wscanf_s + diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def new file mode 100644 index 00000000..8787224f --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def @@ -0,0 +1,395 @@ +; $Id: VBoxRT-openssl-1.1plus.def $ +;; @file +; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!). +; +; This file is appended to the architecture specific .def file. +; + +; +; 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 +; + + ; ConsoleImpl.cpp uses this when VBOX_OPENSSL_FIPS is enabled. + FIPS_mode + + ; VBoxVRDP.dll - secure.cpp + BIO_free + BIO_new_file + BN_bin2bn + BN_bn2bin + BN_CTX_free + BN_CTX_new + BN_free + BN_mod_exp + BN_new + BN_num_bits + BN_set_word + EVP_PKEY_get0_RSA + i2d_X509 + MD5_Final + MD5_Init + MD5_Update + OBJ_obj2nid + PEM_read_bio_PrivateKey + PEM_read_bio_X509 + RAND_bytes + RC4 + RC4_set_key + RSA_free + RSA_generate_key_ex + RSA_get0_key + RSA_new + SHA1_Final + SHA1_Init + SHA1_Update + X509_free + X509_get_X509_PUBKEY + X509_PUBKEY_get0_param + + ; VBoxVRDP.dll - tcp_vrdp.cpp + BIO_new_socket + BIO_test_flags + OPENSSL_init_ssl + SSL_accept + SSL_CTX_free + SSL_CTX_load_verify_locations + SSL_CTX_new + SSL_CTX_set_verify + SSL_CTX_use_certificate_file + SSL_CTX_use_PrivateKey_file + SSL_free + SSL_get_certificate + SSL_new + SSL_pending + SSL_read + SSL_set_bio + SSL_set_read_ahead + SSL_write + TLSv1_server_method + X509_get_issuer_name + X509_NAME_oneline + + ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp: + EVP_aes_128_xts + EVP_aes_256_xts + EVP_CIPHER_CTX_free + EVP_CIPHER_CTX_new + EVP_DecryptFinal + EVP_DecryptInit + EVP_DecryptUpdate + EVP_EncryptFinal + EVP_EncryptInit + EVP_EncryptUpdate + EVP_MD_size + EVP_sha1 + EVP_sha256 + EVP_sha512 + PKCS5_PBKDF2_HMAC + ;exported above: RAND_bytes + + ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp: + ;exported above: EVP_aes_128_xts + ;exported above: EVP_aes_256_xts + ;exported above: EVP_CIPHER_CTX_free + ;exported above: EVP_CIPHER_CTX_new + ;exported above: EVP_DecryptFinal + ;exported above: EVP_DecryptInit + ;exported above: EVP_DecryptUpdate + ;exported above: EVP_EncryptFinal + ;exported above: EVP_EncryptInit + ;exported above: EVP_EncryptUpdate + ;exported above: RAND_bytes + + ; vboxwebsrv needs SSL support. + ASN1_STRING_data + ASN1_STRING_get0_data + ASN1_STRING_to_UTF8 + ;exported above: BIO_free + ;exported above: BIO_new_file + ;exported above: BIO_new_socket + BIO_read + BIO_write + CRYPTO_free + DH_check + DH_free + DH_generate_parameters + DH_generate_parameters_ex + DH_new + ERR_clear_error + ERR_error_string + ERR_error_string_n + ERR_get_error + ERR_peek_error + GENERAL_NAME_free + i2v_GENERAL_NAMES + OpenSSL_version_num + OPENSSL_init_crypto + ;exported above: OPENSSL_init_ssl + OPENSSL_sk_num + OPENSSL_sk_pop_free + OPENSSL_sk_value + PEM_read_bio_DHparams + RAND_load_file + RAND_pseudo_bytes + RAND_seed + RAND_status + ;exported above: RSA_free + RSA_generate_key + ;exported above: SSL_accept + SSL_clear + SSL_connect + SSL_ctrl + SSL_CTX_ctrl + ;exported above: SSL_CTX_free + SSL_CTX_get_cert_store + ;exported above: SSL_CTX_load_verify_locations + ;exported above: SSL_CTX_new + SSL_CTX_set_client_CA_list + SSL_CTX_set_default_passwd_cb + SSL_CTX_set_default_passwd_cb_userdata + SSL_CTX_set_default_verify_paths + SSL_CTX_set_options + SSL_CTX_set_session_id_context + ;exported above: SSL_CTX_set_verify + SSL_CTX_set_verify_depth + SSL_CTX_use_certificate_chain_file + ;exported above: SSL_CTX_use_PrivateKey_file + ;exported above: SSL_free + SSL_get_error + SSL_get_peer_certificate + SSL_get_verify_result + SSL_get1_session + SSL_is_init_finished + SSL_load_client_CA_file + ;exported above: SSL_new + SSL_peek + ;exported above: SSL_read + SSL_SESSION_free + ;exported above: SSL_set_bio + SSL_set_session + SSL_shutdown + SSL_want + ;exported above: SSL_write + TLS_method + ;exported above: X509_free + X509_get_ext_d2i + ;exported above: X509_get_issuer_name + X509_get_subject_name + X509_load_crl_file + X509_LOOKUP_file + X509_NAME_ENTRY_get_data + X509_NAME_get_entry + X509_NAME_get_index_by_NID + ;exported above: X509_NAME_oneline + X509_STORE_add_lookup + X509_STORE_CTX_get_current_cert + X509_STORE_CTX_get_error + X509_STORE_CTX_get_error_depth + X509_STORE_CTX_set_error + X509_STORE_set1_param + X509_STORE_set_flags + X509_verify_cert_error_string + X509_VERIFY_PARAM_free + X509_VERIFY_PARAM_new + X509_VERIFY_PARAM_set_flags + X509V3_conf_free + + ; tstRTBigNum.cpp + BN_div + BN_mul + BN_mod_exp_simple + BN_ucmp + + ; VBox-libtpms + BN_set_flags + BN_clear_free + BN_cmp + BN_dup + BN_sub + BN_add + BN_copy + RAND_add + BN_is_zero + BN_is_one + BN_value_one + BN_CTX_start + BN_CTX_get + BN_CTX_end + BN_mod_add + BN_mod_mul + BN_lshift + BN_mask_bits + BN_rshift + BN_mod_inverse + RSA_size + RSA_set0_key + RSA_get0_factors + RSA_public_encrypt + RSA_private_encrypt + RSA_private_decrypt + RSA_sign + RSA_verify + RSA_padding_add_PKCS1_type_1 + RSA_padding_add_PKCS1_type_2 + RSA_padding_check_PKCS1_type_1 + RSA_padding_check_PKCS1_type_2 + RSA_padding_add_PKCS1_OAEP + RSA_padding_check_PKCS1_OAEP + ERR_get_error_line_data + AES_set_encrypt_key + AES_set_decrypt_key + AES_decrypt + AES_encrypt + AES_cbc_encrypt + AES_ofb128_encrypt + SHA256_Init + SHA256_Update + SHA256_Final + SHA384_Init + SHA384_Update + SHA384_Final + SHA512_Init + SHA512_Update + SHA512_Final + EC_GROUP_free + EC_GROUP_set_generator + EC_GROUP_new_curve_GFp + EC_POINT_new + EC_POINT_free + EC_POINT_clear_free + EC_POINT_set_affine_coordinates + EC_POINT_get_affine_coordinates + EC_POINT_add + EC_POINTs_mul + EC_POINT_mul + Camellia_set_key + Camellia_encrypt + Camellia_decrypt + DES_ecb3_encrypt + DES_set_key_unchecked + + ; VBox-libssh + BIO_ctrl + BIO_new + BIO_new_mem_buf + BIO_s_mem + BN_bn2hex + BN_is_bit_set + d2i_DSA_SIG + d2i_ECDSA_SIG + DH_compute_key + DH_generate_key + DH_get0_key + DH_get0_pqg + DH_set0_key + DH_set0_pqg + DH_size + DSA_free + DSA_generate_key + DSA_generate_parameters_ex + DSA_get0_key + DSA_get0_pqg + DSA_new + DSA_set0_key + DSA_set0_pqg + DSA_SIG_free + DSA_SIG_get0 + DSA_SIG_new + DSA_SIG_set0 + DSA_size + EC_GROUP_cmp + EC_GROUP_get_curve_name + EC_GROUP_get_degree + EC_KEY_dup + EC_KEY_free + EC_KEY_generate_key + EC_KEY_get0_group + EC_KEY_get0_private_key + EC_KEY_get0_public_key + EC_KEY_new_by_curve_name + EC_KEY_set_asn1_flag + EC_KEY_set_private_key + EC_KEY_set_public_key + EC_POINT_cmp + EC_POINT_oct2point + EC_POINT_point2oct + ECDH_compute_key + ECDSA_SIG_free + ECDSA_SIG_get0 + ECDSA_SIG_new + ECDSA_SIG_set0 + EVP_aes_128_cbc + EVP_aes_128_ctr + EVP_aes_128_gcm + EVP_aes_192_cbc + EVP_aes_192_ctr + EVP_aes_256_cbc + EVP_aes_256_ctr + EVP_aes_256_gcm + EVP_CIPHER_CTX_ctrl + EVP_CIPHER_CTX_reset + EVP_CIPHER_CTX_set_padding + EVP_DecryptInit_ex + EVP_DigestFinal + EVP_DigestInit + EVP_DigestInit_ex + EVP_DigestSignFinal + EVP_DigestSignInit + EVP_DigestUpdate + EVP_DigestVerifyFinal + EVP_DigestVerifyInit + EVP_EncryptInit_ex + EVP_MD_CTX_free + EVP_MD_CTX_new + EVP_MD_CTX_reset + EVP_md5 + EVP_PKEY_base_id + EVP_PKEY_free + EVP_PKEY_get1_DSA + EVP_PKEY_get1_EC_KEY + EVP_PKEY_get1_RSA + EVP_PKEY_new + EVP_PKEY_set1_DSA + EVP_PKEY_set1_EC_KEY + EVP_PKEY_set1_RSA + EVP_PKEY_size + EVP_sha384 + HMAC_CTX_free + HMAC_CTX_new + HMAC_Final + HMAC_Init_ex + HMAC_Update + i2d_DSA_SIG + i2d_ECDSA_SIG + OpenSSL_version + PEM_write_bio_PrivateKey + RSA_get0_crt_params + RSA_set0_crt_params + RSA_set0_factors diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def new file mode 100644 index 00000000..ea9407a8 --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def @@ -0,0 +1,430 @@ +; $Id: VBoxRT-openssl-3.0.def $ +;; @file +; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!). +; +; This file is appended to the architecture specific .def file. +; + +; +; 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 +; + + ; VBoxVRDP.dll - secure.cpp + BIO_free + BIO_new_file + BN_bin2bn + BN_bn2bin + BN_CTX_free + BN_CTX_new + BN_free + BN_mod_exp + BN_new + BN_num_bits + BN_set_word + EVP_PKEY_get0_RSA + i2d_X509 + MD5_Final + MD5_Init + MD5_Update + OBJ_obj2nid + PEM_read_bio_PrivateKey + PEM_read_bio_X509 + RAND_bytes + RC4 + RC4_set_key + RSA_free + RSA_generate_key_ex + RSA_get0_key + RSA_new + SHA1_Final + SHA1_Init + SHA1_Update + X509_free + X509_get_X509_PUBKEY + X509_PUBKEY_get0_param + + ; VBoxVRDP.dll - tcp_vrdp.cpp + BIO_new_socket + BIO_test_flags + OPENSSL_init_ssl + SSL_accept + SSL_CTX_free + SSL_CTX_load_verify_locations + SSL_CTX_new + SSL_CTX_set_verify + SSL_CTX_use_certificate_file + SSL_CTX_use_PrivateKey_file + SSL_free + SSL_get_certificate + SSL_new + SSL_pending + SSL_read + SSL_set_bio + SSL_set_read_ahead + SSL_write + TLSv1_server_method + X509_get_issuer_name + X509_NAME_oneline + + ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp: + EVP_aes_128_xts + EVP_aes_256_xts + EVP_CIPHER_CTX_free + EVP_CIPHER_CTX_new + EVP_DecryptFinal + EVP_DecryptInit + EVP_DecryptUpdate + EVP_EncryptFinal + EVP_EncryptInit + EVP_EncryptUpdate + EVP_MD_get_size + EVP_sha1 + EVP_sha256 + EVP_sha512 + PKCS5_PBKDF2_HMAC + ;exported above: RAND_bytes + + ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp: + ;exported above: EVP_aes_128_xts + ;exported above: EVP_aes_256_xts + ;exported above: EVP_CIPHER_CTX_free + ;exported above: EVP_CIPHER_CTX_new + ;exported above: EVP_DecryptFinal + ;exported above: EVP_DecryptInit + ;exported above: EVP_DecryptUpdate + ;exported above: EVP_EncryptFinal + ;exported above: EVP_EncryptInit + ;exported above: EVP_EncryptUpdate + ;exported above: RAND_bytes + + ; vboxwebsrv needs SSL support. + ASN1_STRING_get0_data + ASN1_STRING_to_UTF8 + ;exported above: BIO_free + ;exported above: BIO_new_file + ;exported above: BIO_new_socket + BIO_read + BIO_write + CRYPTO_free + DH_check + DH_free + DH_generate_parameters + DH_generate_parameters_ex + DH_new + ERR_clear_error + ERR_error_string + ERR_error_string_n + ERR_get_error + ERR_peek_error + GENERAL_NAME_free + i2v_GENERAL_NAMES + OpenSSL_version_num + OPENSSL_init_crypto + ;exported above: OPENSSL_init_ssl + OPENSSL_sk_num + OPENSSL_sk_pop_free + OPENSSL_sk_value + PEM_read_bio_DHparams + RAND_load_file + RAND_seed + RAND_status + ;exported above: RSA_free + RSA_generate_key + ;exported above: SSL_accept + SSL_clear + SSL_connect + SSL_ctrl + SSL_CTX_ctrl + ;exported above: SSL_CTX_free + SSL_CTX_get_cert_store + ;exported above: SSL_CTX_load_verify_locations + ;exported above: SSL_CTX_new + SSL_CTX_set_client_CA_list + SSL_CTX_set_default_passwd_cb + SSL_CTX_set_default_passwd_cb_userdata + SSL_CTX_set_default_verify_paths + SSL_CTX_set_options + SSL_CTX_set_session_id_context + ;exported above: SSL_CTX_set_verify + SSL_CTX_set_verify_depth + SSL_CTX_use_certificate_chain_file + ;exported above: SSL_CTX_use_PrivateKey_file + ;exported above: SSL_free + SSL_get_error + SSL_get1_peer_certificate + SSL_get_verify_result + SSL_get1_session + SSL_is_init_finished + SSL_load_client_CA_file + ;exported above: SSL_new + SSL_peek + ;exported above: SSL_read + SSL_SESSION_free + ;exported above: SSL_set_bio + SSL_set_session + SSL_shutdown + SSL_want + ;exported above: SSL_write + TLS_method + ;exported above: X509_free + X509_get_ext_d2i + ;exported above: X509_get_issuer_name + X509_get_subject_name + X509_load_crl_file + X509_LOOKUP_file + X509_NAME_ENTRY_get_data + X509_NAME_get_entry + X509_NAME_get_index_by_NID + ;exported above: X509_NAME_oneline + X509_STORE_add_lookup + X509_STORE_CTX_get_current_cert + X509_STORE_CTX_get_error + X509_STORE_CTX_get_error_depth + X509_STORE_CTX_set_error + X509_STORE_set1_param + X509_STORE_set_flags + X509_verify_cert_error_string + X509_VERIFY_PARAM_free + X509_VERIFY_PARAM_new + X509_VERIFY_PARAM_set_flags + X509V3_conf_free + + ; tstRTBigNum.cpp + BN_div + BN_mul + BN_mod_exp_simple + BN_ucmp + + ; VBox-libtpms + BN_set_flags + BN_clear_free + BN_cmp + BN_dup + BN_sub + BN_add + BN_copy + RAND_add + BN_is_zero + BN_is_one + BN_value_one + BN_CTX_start + BN_CTX_get + BN_CTX_end + BN_mod_add + BN_mod_mul + BN_lshift + BN_mask_bits + BN_rshift + BN_mod_inverse + RSA_size + RSA_set0_key + RSA_get0_factors + RSA_public_encrypt + RSA_private_encrypt + RSA_private_decrypt + RSA_sign + RSA_verify + RSA_padding_add_PKCS1_type_1 + RSA_padding_add_PKCS1_type_2 + RSA_padding_check_PKCS1_type_1 + RSA_padding_check_PKCS1_type_2 + RSA_padding_add_PKCS1_OAEP + RSA_padding_check_PKCS1_OAEP + ERR_get_error_line_data + AES_set_encrypt_key + AES_set_decrypt_key + AES_decrypt + AES_encrypt + AES_cbc_encrypt + AES_ofb128_encrypt + SHA256_Init + SHA256_Update + SHA256_Final + SHA384_Init + SHA384_Update + SHA384_Final + SHA512_Init + SHA512_Update + SHA512_Final + EC_GROUP_free + EC_GROUP_set_generator + EC_GROUP_new_curve_GFp + EC_POINT_new + EC_POINT_free + EC_POINT_clear_free + EC_POINT_set_affine_coordinates + EC_POINT_get_affine_coordinates + EC_POINT_add + EC_POINTs_mul + EC_POINT_mul + Camellia_set_key + Camellia_encrypt + Camellia_decrypt + DES_ecb3_encrypt + DES_set_key_unchecked + + ; VBox-libssh + BIO_ctrl + BIO_new + BIO_new_mem_buf + BIO_s_mem + BN_bn2hex + BN_is_bit_set + d2i_DSA_SIG + d2i_ECDSA_SIG + DH_compute_key + DH_generate_key + DH_get0_key + DH_get0_pqg + DH_set0_key + DH_set0_pqg + DH_size + DSA_free + DSA_generate_key + DSA_generate_parameters_ex + DSA_get0_key + DSA_get0_pqg + DSA_new + DSA_set0_key + DSA_set0_pqg + DSA_SIG_free + DSA_SIG_get0 + DSA_SIG_new + DSA_SIG_set0 + DSA_size + EC_GROUP_cmp + EC_GROUP_get_curve_name + EC_GROUP_get_degree + EC_KEY_dup + EC_KEY_free + EC_KEY_generate_key + EC_KEY_get0_group + EC_KEY_get0_private_key + EC_KEY_get0_public_key + EC_KEY_new_by_curve_name + EC_KEY_set_asn1_flag + EC_KEY_set_private_key + EC_KEY_set_public_key + EC_POINT_cmp + EC_POINT_oct2point + EC_POINT_point2oct + ECDH_compute_key + ECDSA_SIG_free + ECDSA_SIG_get0 + ECDSA_SIG_new + ECDSA_SIG_set0 + EVP_aes_128_cbc + EVP_aes_128_ctr + EVP_aes_128_gcm + EVP_aes_192_cbc + EVP_aes_192_ctr + EVP_aes_256_cbc + EVP_aes_256_ctr + EVP_aes_256_gcm + EVP_CIPHER_CTX_ctrl + EVP_CIPHER_CTX_reset + EVP_CIPHER_CTX_set_padding + EVP_DecryptInit_ex + EVP_DigestFinal + EVP_DigestInit + EVP_DigestInit_ex + EVP_DigestSignFinal + EVP_DigestSignInit + EVP_DigestSignUpdate + EVP_DigestUpdate + EVP_DigestVerifyFinal + EVP_DigestVerifyInit + EVP_DigestVerifyUpdate + EVP_EncryptInit_ex + EVP_MD_CTX_free + EVP_MD_CTX_new + EVP_MD_CTX_reset + EVP_md5 + EVP_PKEY_get_base_id + EVP_PKEY_free + EVP_PKEY_get1_DSA + EVP_PKEY_get1_EC_KEY + EVP_PKEY_get1_RSA + EVP_PKEY_new + EVP_PKEY_set1_DSA + EVP_PKEY_set1_EC_KEY + EVP_PKEY_set1_RSA + EVP_PKEY_get_size + EVP_sha384 + HMAC_CTX_free + HMAC_CTX_new + HMAC_Final + HMAC_Init_ex + HMAC_Update + i2d_DSA_SIG + i2d_ECDSA_SIG + OpenSSL_version + PEM_write_bio_PrivateKey + RSA_get0_crt_params + RSA_set0_crt_params + RSA_set0_factors + ; since 0.10.5 also: + EC_KEY_up_ref + ENGINE_by_id + ENGINE_free + ENGINE_init + ENGINE_load_builtin_engines + EVP_default_properties_is_fips_enabled + EVP_PKEY_CTX_free + EVP_PKEY_CTX_new_from_name + EVP_PKEY_CTX_new_from_pkey + EVP_PKEY_CTX_set_params + EVP_PKEY_derive + EVP_PKEY_derive_init + EVP_PKEY_derive_set_peer + EVP_PKEY_eq + EVP_PKEY_fromdata + EVP_PKEY_fromdata_init + EVP_PKEY_generate + EVP_PKEY_get_bits + EVP_PKEY_get_bn_param + EVP_PKEY_keygen_init + EVP_PKEY_new_mac_key + EVP_PKEY_paramgen_init + EVP_PKEY_set_bn_param + EVP_PKEY_todata + EVP_PKEY_up_ref + OSSL_PARAM_BLD_free + OSSL_PARAM_BLD_new + OSSL_PARAM_BLD_push_BN + OSSL_PARAM_BLD_to_param + OSSL_PARAM_construct_end + OSSL_PARAM_construct_int + OSSL_PARAM_construct_uint + OSSL_PARAM_free + OSSL_PARAM_get_BN + OSSL_PARAM_locate_const + OSSL_PARAM_merge + diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-win32.def new file mode 100644 index 00000000..261b3ce1 --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-win32.def @@ -0,0 +1,62 @@ +; $Id: VBoxRT-win32.def $ +;; @file +; IPRT - Win32 ASM exports. +; + +; +; 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 +; + +EXPORTS + ASMMultU64ByU32DivByU32 + + RTTimeNanoTSLegacySyncInvarNoDelta + RTTimeNanoTSLFenceSyncInvarNoDelta + RTTimeNanoTSLegacyAsyncUseApicId + RTTimeNanoTSLegacyAsyncUseRdtscp + RTTimeNanoTSLegacyAsyncUseIdtrLim + RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId + RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp + RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim + RTTimeNanoTSLFenceAsyncUseApicId + RTTimeNanoTSLFenceAsyncUseRdtscp + RTTimeNanoTSLFenceAsyncUseIdtrLim + RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId + RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp + RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim + + RTStrMemFind32 + + ; Export aliases for noexcept affected methods. + ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64) noexcept,void *)) + ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64),void *)) + ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *) noexcept,void *,unsigned __int64)) + ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *),void *,unsigned __int64)) + diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win64.def b/src/VBox/Runtime/r3/win/VBoxRT-win64.def new file mode 100644 index 00000000..e3a83833 --- /dev/null +++ b/src/VBox/Runtime/r3/win/VBoxRT-win64.def @@ -0,0 +1,74 @@ +; $Id: VBoxRT-win64.def $ +;; @file +; IPRT - Win64 ASM exports. +; + +; +; 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 +; + +EXPORTS + ASMAtomicBitClear + ASMAtomicBitToggle + ASMAtomicBitTestAndToggle + ASMBitFirstClear + ASMBitFirstSet + ASMAtomicReadU64 + ASMAtomicXchgU8 + ASMAtomicXchgU16 + ASMGetFlags + ASMProbeReadByte + ASMSetFlags + ASMMultU64ByU32DivByU32 + ASMNopPause + + RTTimeNanoTSLegacySyncInvarNoDelta + RTTimeNanoTSLFenceSyncInvarNoDelta + RTTimeNanoTSLegacyAsyncUseApicId + RTTimeNanoTSLegacyAsyncUseRdtscp + RTTimeNanoTSLegacyAsyncUseIdtrLim + RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId + RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp + RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim + RTTimeNanoTSLFenceAsyncUseApicId + RTTimeNanoTSLFenceAsyncUseRdtscp + RTTimeNanoTSLFenceAsyncUseIdtrLim + RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId + RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp + RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim + + RTStrMemFind32 + + ; Export aliases for noexcept affected methods. + ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64) noexcept,void * __ptr64) __ptr64 + ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64),void * __ptr64) __ptr64) + ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64) noexcept,void * __ptr64,unsigned __int64) __ptr64) + ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64),void * __ptr64,unsigned __int64) __ptr64) + diff --git a/src/VBox/Runtime/r3/win/alloc-win.cpp b/src/VBox/Runtime/r3/win/alloc-win.cpp new file mode 100644 index 00000000..a9ebe7f0 --- /dev/null +++ b/src/VBox/Runtime/r3/win/alloc-win.cpp @@ -0,0 +1,210 @@ +/* $Id: alloc-win.cpp $ */ +/** @file + * IPRT - Memory Allocation, Windows. + */ + +/* + * 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 * +*********************************************************************************************************************************/ +#ifdef IPRT_NO_CRT +# define USE_VIRTUAL_ALLOC +#endif +#define LOG_GROUP RTLOGGROUP_MEM +#include <iprt/win/windows.h> + +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/errcore.h> + +#ifndef USE_VIRTUAL_ALLOC +# include <malloc.h> +#endif + + +RTDECL(void *) RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF +{ + RT_NOREF_PV(pszTag); + +#ifdef USE_VIRTUAL_ALLOC + void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE); +#else + void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE); +#endif + AssertMsg(pv, ("cb=%d lasterr=%d\n", cb, GetLastError())); + return pv; +} + + +RTDECL(void *) RTMemPageAllocExTag(size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF +{ + size_t const cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE); + RT_NOREF_PV(pszTag); + AssertReturn(!(fFlags & ~RTMEMPAGEALLOC_F_VALID_MASK), NULL); + +#ifdef USE_VIRTUAL_ALLOC + void *pv = VirtualAlloc(NULL, cbAligned, MEM_COMMIT, PAGE_READWRITE); +#else + void *pv = _aligned_malloc(cbAligned, PAGE_SIZE); +#endif + AssertMsgReturn(pv, ("cb=%d lasterr=%d\n", cb, GetLastError()), NULL); + + if (fFlags & RTMEMPAGEALLOC_F_ADVISE_LOCKED) + { + /** @todo check why we get ERROR_WORKING_SET_QUOTA here. */ + BOOL const fOkay = VirtualLock(pv, cbAligned); + AssertMsg(fOkay || GetLastError() == ERROR_WORKING_SET_QUOTA, ("pv=%p cb=%d lasterr=%d\n", pv, cb, GetLastError())); + NOREF(fOkay); + } + + if (fFlags & RTMEMPAGEALLOC_F_ZERO) + RT_BZERO(pv, cbAligned); + + return pv; +} + + +RTDECL(void *) RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF +{ + RT_NOREF_PV(pszTag); + +#ifdef USE_VIRTUAL_ALLOC + void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE); +#else + void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE); +#endif + if (pv) + { + memset(pv, 0, RT_ALIGN_Z(cb, PAGE_SIZE)); + return pv; + } + AssertMsgFailed(("cb=%d lasterr=%d\n", cb, GetLastError())); + return NULL; +} + + +RTDECL(void) RTMemPageFree(void *pv, size_t cb) RT_NO_THROW_DEF +{ + RT_NOREF_PV(cb); + + if (pv) + { +#ifdef USE_VIRTUAL_ALLOC + if (!VirtualFree(pv, 0, MEM_RELEASE)) + AssertMsgFailed(("pv=%p lasterr=%d\n", pv, GetLastError())); +#else + _aligned_free(pv); +#endif + } +} + + +RTDECL(int) RTMemProtect(void *pv, size_t cb, unsigned fProtect) RT_NO_THROW_DEF +{ + /* + * Validate input. + */ + if (cb == 0) + { + AssertMsgFailed(("!cb\n")); + return VERR_INVALID_PARAMETER; + } + if (fProtect & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)) + { + AssertMsgFailed(("fProtect=%#x\n", fProtect)); + return VERR_INVALID_PARAMETER; + } + + /* + * Convert the flags. + */ + int fProt; + Assert(!RTMEM_PROT_NONE); + switch (fProtect & (RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)) + { + case RTMEM_PROT_NONE: + fProt = PAGE_NOACCESS; + break; + + case RTMEM_PROT_READ: + fProt = PAGE_READONLY; + break; + + case RTMEM_PROT_READ | RTMEM_PROT_WRITE: + fProt = PAGE_READWRITE; + break; + + case RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC: + fProt = PAGE_EXECUTE_READWRITE; + break; + + case RTMEM_PROT_READ | RTMEM_PROT_EXEC: + fProt = PAGE_EXECUTE_READWRITE; + break; + + case RTMEM_PROT_WRITE: + fProt = PAGE_READWRITE; + break; + + case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC: + fProt = PAGE_EXECUTE_READWRITE; + break; + + case RTMEM_PROT_EXEC: + fProt = PAGE_EXECUTE_READWRITE; + break; + + /* If the compiler had any brains it would warn about this case. */ + default: + AssertMsgFailed(("fProtect=%#x\n", fProtect)); + return VERR_INTERNAL_ERROR; + } + + /* + * Align the request. + */ + cb += (uintptr_t)pv & PAGE_OFFSET_MASK; + pv = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK); + + /* + * Change the page attributes. + */ + DWORD fFlags = 0; + if (VirtualProtect(pv, cb, fProt, &fFlags)) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + diff --git a/src/VBox/Runtime/r3/win/allocex-win.cpp b/src/VBox/Runtime/r3/win/allocex-win.cpp new file mode 100644 index 00000000..dca6d053 --- /dev/null +++ b/src/VBox/Runtime/r3/win/allocex-win.cpp @@ -0,0 +1,133 @@ +/* $Id: allocex-win.cpp $ */ +/** @file + * IPRT - Memory Allocation, Extended Alloc Workers, Windows. + */ + +/* + * 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 RTMEM_NO_WRAP_TO_EF_APIS +#include <iprt/mem.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/string.h> +#include <iprt/param.h> +#include "../allocex.h" + +#include <iprt/win/windows.h> + + +static int rtMemAllocExInRange(size_t cbAlloc, uint32_t fFlags, void **ppv, uintptr_t uAddr, uintptr_t uAddrLast) +{ + /* + * Try with every possible address hint since the possible range is very limited. + */ + DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + while (uAddr <= uAddrLast) + { + MEMORY_BASIC_INFORMATION MemInfo; + SIZE_T cbRange = VirtualQuery((void *)uAddr, &MemInfo, sizeof(MemInfo)); + AssertReturn(cbRange == sizeof(MemInfo), VERR_NOT_SUPPORTED); + Assert(MemInfo.RegionSize > 0); + + if ( MemInfo.State == MEM_FREE + && MemInfo.RegionSize >= cbAlloc) + { + void *pv = VirtualAlloc((void *)uAddr, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt); + if ((uintptr_t)pv == uAddr) + { + *ppv = pv; + return VINF_SUCCESS; + } + AssertStmt(!pv, VirtualFree(pv, cbAlloc, MEM_RELEASE)); + } + + /* skip ahead */ + uintptr_t uAddrNext = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize; + if (uAddrNext <= uAddr) + break; + uAddr += uAddrNext; + } + + return VERR_NO_MEMORY; +} + + +DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv) +{ + cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE); + AssertReturn(cbAlloc <= _64K - PAGE_SIZE, VERR_NO_MEMORY); + + /* Seems this doesn't work on W7/64... */ + return rtMemAllocExInRange(cbAlloc, fFlags, ppv, PAGE_SIZE, _64K - cbAlloc); +} + + +DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv) +{ + cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE); + AssertReturn(cbAlloc <= _2G+_1G, VERR_NO_MEMORY); + + /* + * Just try first. + */ + DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); + void *pv = VirtualAlloc(NULL, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt); + if (!pv) + return VERR_NO_MEMORY; + if ((uintptr_t)pv + cbAlloc - 1 < _4G) + { + *ppv = pv; + return VINF_SUCCESS; + } + VirtualFree(pv, cbAlloc, MEM_RELEASE); + + /* + * No luck, do address scan based allocation. + */ + return rtMemAllocExInRange(cbAlloc, fFlags, ppv, _64K, _4G - cbAlloc); +} + + +DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + BOOL fRc = VirtualFree(pv, cb, MEM_RELEASE); + Assert(fRc); RT_NOREF_PV(fRc); +} + diff --git a/src/VBox/Runtime/r3/win/dir-win.cpp b/src/VBox/Runtime/r3/win/dir-win.cpp new file mode 100644 index 00000000..f0a193ff --- /dev/null +++ b/src/VBox/Runtime/r3/win/dir-win.cpp @@ -0,0 +1,169 @@ +/* $Id: dir-win.cpp $ */ +/** @file + * IPRT - Directory, Windows. + */ + +/* + * 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 +#include <iprt/win/windows.h> + +#include <iprt/dir.h> +#include <iprt/path.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include "internal/fs.h" +#include "internal/path.h" + + + +RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate) +{ + /* + * Validate the file mode. + */ + int rc; + fMode = rtFsModeNormalize(fMode, pszPath, 0, RTFS_TYPE_DIRECTORY); + if (rtFsModeIsValidPermissions(fMode)) + { + /* + * Convert to UTF-16. + */ + PRTUTF16 pwszString; + rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + /* + * Create the directory. + */ + if (CreateDirectoryW((LPCWSTR)pwszString, NULL)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + + /* + * Turn off indexing of directory through Windows Indexing Service + */ + /** @todo This FILE_ATTRIBUTE_NOT_CONTENT_INDEXED hack (for .VDI files, + * really) may cause failures on samba shares. That really sweet and + * need to be addressed differently. We shouldn't be doing this + * unless the caller actually asks for it, must less returning failure, + * for crying out loud! This is only important a couple of places in + * main, if important is the right way to put it... */ + if ( RT_SUCCESS(rc) + && !(fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET)) + { + if ( SetFileAttributesW((LPCWSTR)pwszString, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) + || (fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL) ) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + + RTPathWinFree(pwszString); + } + } + else + { + AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode)); + rc = VERR_INVALID_FMODE; + } + + LogFlow(("RTDirCreate(%p:{%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc)); + return rc; +} + + +RTDECL(int) RTDirRemove(const char *pszPath) +{ + /* + * Convert to UTF-16. + */ + PRTUTF16 pwszString; + int rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + /* + * Remove the directory. + */ + if (RemoveDirectoryW((LPCWSTR)pwszString)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTPathWinFree(pwszString); + } + + LogFlow(("RTDirRemove(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc)); + return rc; +} + + +RTDECL(int) RTDirFlush(const char *pszPath) +{ + RT_NOREF_PV(pszPath); + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename) +{ + /* + * 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(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER); + + /* + * Call the worker. + */ + int rc = rtPathWin32MoveRename(pszSrc, pszDst, + fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, + RTFS_TYPE_DIRECTORY); + + LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/direnum-win.cpp b/src/VBox/Runtime/r3/win/direnum-win.cpp new file mode 100644 index 00000000..80d36864 --- /dev/null +++ b/src/VBox/Runtime/r3/win/direnum-win.cpp @@ -0,0 +1,404 @@ +/* $Id: direnum-win.cpp $ */ +/** @file + * IPRT - Directory Enumeration, Windows. + */ + +/* + * 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 +#include <iprt/win/windows.h> + +#include <iprt/dir.h> +#include <iprt/path.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include "internal/fs.h" +#include "internal/dir.h" + + +size_t rtDirNativeGetStructSize(const char *pszPath) +{ + NOREF(pszPath); + return sizeof(RTDIRINTERNAL); +} + + +int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative)) +{ + RT_NOREF(hRelativeDir, pvNativeRelative); + + /* + * Setup the search expression. + * + * pszPathBuf is pointing to the return 4K return buffer for the RTPathReal() + * call in rtDirOpenCommon(), so all we gota do is check that we don't overflow + * it when adding the wildcard expression. + */ +/** @todo the pszPathBuf argument was removed in order to support paths longer than RTPATH_MAX. Rewrite this code. */ + size_t cbExpr; + const char *pszExpr; + if (pDir->enmFilter == RTDIRFILTER_WINNT) + { + pszExpr = pDir->pszFilter; + cbExpr = pDir->cchFilter + 1; + } + else + { + pszExpr = "*"; + cbExpr = sizeof("*"); + } + if (pDir->cchPath + cbExpr > RTPATH_MAX) + return VERR_FILENAME_TOO_LONG; + memcpy(pszPathBuf + pDir->cchPath, pszExpr, cbExpr); + + + /* + * Attempt opening the search. + */ + PRTUTF16 pwszName; + int rc = RTPathWinFromUtf8(pwszPathBuf, &pwszName, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pDir->hDir = FindFirstFileW((LPCWSTR)pwszName, &pDir->Data); + if (pDir->hDir != INVALID_HANDLE_VALUE) + pDir->fDataUnread = true; + else + { + DWORD dwErr = GetLastError(); + /* Theoretical case of an empty directory or more normal case of no matches. */ + if ( dwErr == ERROR_FILE_NOT_FOUND + || dwErr == ERROR_NO_MORE_FILES /* ???*/) + pDir->fDataUnread = false; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + RTPathWinFree(pwszName); + } + + return rc; +} + + +RTDECL(int) RTDirClose(PRTDIRINTERNAL pDir) +{ + /* + * Validate input. + */ + if (!pDir) + return VERR_INVALID_PARAMETER; + if (pDir->u32Magic != RTDIR_MAGIC) + { + AssertMsgFailed(("Invalid pDir=%p\n", pDir)); + return VERR_INVALID_PARAMETER; + } + + /* + * Close the handle. + */ + pDir->u32Magic++; + if (pDir->hDir != INVALID_HANDLE_VALUE) + { + BOOL fRc = FindClose(pDir->hDir); + Assert(fRc); + pDir->hDir = INVALID_HANDLE_VALUE; + } + RTStrFree(pDir->pszName); + pDir->pszName = NULL; + RTMemFree(pDir); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry) +{ + PPRTDIRINTERNAL pDir = hDir; + + /* + * Validate input. + */ + if (!pDir || pDir->u32Magic != RTDIR_MAGIC) + { + AssertMsgFailed(("Invalid pDir=%p\n", pDir)); + return VERR_INVALID_PARAMETER; + } + if (!pDirEntry) + { + AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry)); + return VERR_INVALID_PARAMETER; + } + size_t cbDirEntry = sizeof(*pDirEntry); + if (pcbDirEntry) + { + cbDirEntry = *pcbDirEntry; + if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRY, szName[2])) + { + AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2]))); + return VERR_INVALID_PARAMETER; + } + } + + /* + * Fetch data? + */ + if (!pDir->fDataUnread) + { + RTStrFree(pDir->pszName); + pDir->pszName = NULL; + + BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data); + if (!fRc) + { + int iErr = GetLastError(); + if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES) + return VERR_NO_MORE_FILES; + return RTErrConvertFromWin32(iErr); + } + } + + /* + * Convert the filename to UTF-8. + */ + if (!pDir->pszName) + { + int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName); + if (RT_FAILURE(rc)) + { + pDir->pszName = NULL; + return rc; + } + pDir->cchName = strlen(pDir->pszName); + } + + /* + * Check if we've got enough space to return the data. + */ + const char *pszName = pDir->pszName; + const size_t cchName = pDir->cchName; + const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName; + if (pcbDirEntry) + *pcbDirEntry = cbRequired; + if (cbRequired > cbDirEntry) + return VERR_BUFFER_OVERFLOW; + + /* + * Setup the returned data. + */ + pDir->fDataUnread = false; + pDirEntry->INodeId = 0; /** @todo we can use the fileid here if we must (see GetFileInformationByHandle). */ + pDirEntry->enmType = pDir->Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY + ? RTDIRENTRYTYPE_DIRECTORY : RTDIRENTRYTYPE_FILE; + pDirEntry->cbName = (uint16_t)cchName; + Assert(pDirEntry->cbName == cchName); + memcpy(pDirEntry->szName, pszName, cchName + 1); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + PPRTDIRINTERNAL pDir = hDir; + /** @todo Symlinks: Find[First|Next]FileW will return info about + the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */ + /* + * Validate input. + */ + if (!pDir || pDir->u32Magic != RTDIR_MAGIC) + { + AssertMsgFailed(("Invalid pDir=%p\n", pDir)); + return VERR_INVALID_PARAMETER; + } + if (!pDirEntry) + { + AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry)); + return VERR_INVALID_PARAMETER; + } + if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING + || enmAdditionalAttribs > RTFSOBJATTRADD_LAST) + { + AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs)); + return VERR_INVALID_PARAMETER; + } + AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + size_t cbDirEntry = sizeof(*pDirEntry); + if (pcbDirEntry) + { + cbDirEntry = *pcbDirEntry; + if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2])) + { + AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2]))); + return VERR_INVALID_PARAMETER; + } + } + + /* + * Fetch data? + */ + if (!pDir->fDataUnread) + { + RTStrFree(pDir->pszName); + pDir->pszName = NULL; + + BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data); + if (!fRc) + { + int iErr = GetLastError(); + if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES) + return VERR_NO_MORE_FILES; + return RTErrConvertFromWin32(iErr); + } + } + + /* + * Convert the filename to UTF-8. + */ + if (!pDir->pszName) + { + int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName); + if (RT_FAILURE(rc)) + { + pDir->pszName = NULL; + return rc; + } + pDir->cchName = strlen(pDir->pszName); + } + + /* + * Check if we've got enough space to return the data. + */ + const char *pszName = pDir->pszName; + const size_t cchName = pDir->cchName; + const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName; + if (pcbDirEntry) + *pcbDirEntry = cbRequired; + if (cbRequired > cbDirEntry) + return VERR_BUFFER_OVERFLOW; + + /* + * Setup the returned data. + */ + pDir->fDataUnread = false; + pDirEntry->cbName = (uint16_t)cchName; + Assert(pDirEntry->cbName == cchName); + memcpy(pDirEntry->szName, pszName, cchName + 1); + if (pDir->Data.cAlternateFileName[0]) + { + /* copy and calc length */ + PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName; + PRTUTF16 pwszDst = pDirEntry->wszShortName; + uint32_t off = 0; + while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off]) + { + pwszDst[off] = pwszSrc[off]; + off++; + } + pDirEntry->cwcShortName = (uint16_t)off; + + /* zero the rest */ + do + pwszDst[off++] = '\0'; + while (off < RT_ELEMENTS(pDirEntry->wszShortName)); + } + else + { + memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName)); + pDirEntry->cwcShortName = 0; + } + + pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32) + | (uint64_t)pDir->Data.nFileSizeLow; + pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject; + + Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime)); + RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime); + RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime); + RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime); + pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime; + + pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, + pszName, cchName, pDir->Data.dwReserved0, 0); + + /* + * Requested attributes (we cannot provide anything actually). + */ + switch (enmAdditionalAttribs) + { + case RTFSOBJATTRADD_EASIZE: + pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; + pDirEntry->Info.Attr.u.EASize.cb = 0; + break; + + case RTFSOBJATTRADD_UNIX: + pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX; + pDirEntry->Info.Attr.u.Unix.uid = ~0U; + pDirEntry->Info.Attr.u.Unix.gid = ~0U; + pDirEntry->Info.Attr.u.Unix.cHardlinks = 1; + pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */ + pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */ + pDirEntry->Info.Attr.u.Unix.fFlags = 0; + pDirEntry->Info.Attr.u.Unix.GenerationId = 0; + pDirEntry->Info.Attr.u.Unix.Device = 0; + break; + + case RTFSOBJATTRADD_NOTHING: + pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; + break; + + case RTFSOBJATTRADD_UNIX_OWNER: + pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; + pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U; + pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ + break; + + case RTFSOBJATTRADD_UNIX_GROUP: + pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; + pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U; + pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0'; + break; + + default: + AssertMsgFailed(("Impossible!\n")); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r3/win/dllmain-win.cpp b/src/VBox/Runtime/r3/win/dllmain-win.cpp new file mode 100644 index 00000000..b52d059b --- /dev/null +++ b/src/VBox/Runtime/r3/win/dllmain-win.cpp @@ -0,0 +1,100 @@ +/* $Id: dllmain-win.cpp $ */ +/** @file + * IPRT - Win32 DllMain (Ring-3). + */ + +/* + * 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/win/windows.h> +#include <iprt/thread.h> +#include <iprt/param.h> +#include "internal/thread.h" + + + +/** + * Increases the load count on the IPRT DLL so it won't unload. + * + * This is a separate function so as to not overflow the stack of threads with + * very little of it. + * + * @param hModule The IPRT DLL module handle. + */ +DECL_NO_INLINE(static, void) EnsureNoUnload(HMODULE hModule) +{ + WCHAR wszName[RTPATH_MAX]; + SetLastError(NO_ERROR); + if ( GetModuleFileNameW(hModule, wszName, RT_ELEMENTS(wszName)) > 0 + && GetLastError() == NO_ERROR) + { + int cExtraLoads = 32; + while (cExtraLoads-- > 0) + LoadLibraryW(wszName); + } +} + + +/** + * The Dll main entry point. + */ +BOOL __stdcall DllMain(HANDLE hModule, DWORD dwReason, PVOID pvReserved) +{ + RT_NOREF_PV(pvReserved); + + switch (dwReason) + { + /* + * When attaching to a process, we'd like to make sure IPRT stays put + * and doesn't get unloaded. + */ + case DLL_PROCESS_ATTACH: + EnsureNoUnload((HMODULE)hModule); + break; + + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + default: + /* ignore */ + break; + + case DLL_THREAD_DETACH: + rtThreadWinTlsDestruction(); + rtThreadNativeDetach(); + break; + } + return TRUE; +} + diff --git a/src/VBox/Runtime/r3/win/env-win.cpp b/src/VBox/Runtime/r3/win/env-win.cpp new file mode 100644 index 00000000..89577098 --- /dev/null +++ b/src/VBox/Runtime/r3/win/env-win.cpp @@ -0,0 +1,520 @@ +/* $Id: env-win.cpp $ */ +/** @file + * IPRT - Environment, Posix. + */ + +/* + * 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> + +#ifdef IPRT_NO_CRT +# include <iprt/asm.h> +#endif +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/mem.h> +#include <iprt/utf16.h> + +#ifndef IPRT_NO_CRT +# include <stdlib.h> +# include <errno.h> +#endif +#include <iprt/win/windows.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +#ifdef IPRT_NO_CRT +static uint32_t volatile g_idxGetEnvBufs = 0; +static char *g_apszGetEnvBufs[64]; /* leak */ +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue); + + +#if defined(RT_ARCH_X86) && defined(RT_OS_WINDOWS) +/** + * This is a workaround for NT 3.x not setting the last error. + */ +static DWORD rtEnvNt31CheckEmpty(PCRTUTF16 pwszVar) +{ + /* Check the version first: */ + DWORD dwVersion = GetVersion(); + if (RT_BYTE1(dwVersion) != 3) + return 0; + + /* When called with an empty buffer, we should get 1 if empty value + and 0 if not found: */ + DWORD cwcNeeded = GetEnvironmentVariableW(pwszVar, NULL, 0); + return cwcNeeded == 0 ? ERROR_ENVVAR_NOT_FOUND : NO_ERROR; +} +#endif + + +RTDECL(bool) RTEnvExistsBad(const char *pszVar) +{ +#ifndef IPRT_NO_CRT + return RTEnvGetBad(pszVar) != NULL; +#else + return RTEnvExistsUtf8(pszVar); +#endif +} + + +RTDECL(bool) RTEnvExist(const char *pszVar) +{ +#ifndef IPRT_NO_CRT + return RTEnvExistsBad(pszVar); +#else + return RTEnvExistsUtf8(pszVar); +#endif +} + + +RTDECL(bool) RTEnvExistsUtf8(const char *pszVar) +{ + AssertReturn(strchr(pszVar, '=') == NULL, false); + + PRTUTF16 pwszVar; + int rc = RTStrToUtf16(pszVar, &pwszVar); + AssertRCReturn(rc, false); + +#ifndef IPRT_NO_CRT + bool fRet = _wgetenv(pwszVar) != NULL; +#else + DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0); + bool fRet = dwRet != 0; +#endif + + RTUtf16Free(pwszVar); + return fRet; +} + + +RTDECL(const char *) RTEnvGetBad(const char *pszVar) +{ + AssertReturn(strchr(pszVar, '=') == NULL, NULL); +#ifndef IPRT_NO_CRT + return getenv(pszVar); +#else + /* + * Query the value into heap buffer which we give a lifetime of 64 + * RTEnvGetBad calls. + */ + char *pszValue = RTEnvDup(pszVar); + if (pszValue) + { + RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */ + + uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs); + char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue); + RTStrFree(pszOld); + } + return pszValue; +#endif +} + + +RTDECL(const char *) RTEnvGet(const char *pszVar) +{ + return RTEnvGetBad(pszVar); +} + +RTDECL(int) RTEnvGetUtf8(const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual) +{ + AssertPtrReturn(pszVar, VERR_INVALID_POINTER); + AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER); + AssertReturn(pszValue || !cbValue, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER); + AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER); + AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME); + + if (pcchActual) + *pcchActual = 0; + + /* + * Convert the name to UTF-16. + */ + PRTUTF16 pwszVar; + int rc = RTStrToUtf16(pszVar, &pwszVar); + AssertRCReturn(rc, rc); + + /* + * Query the variable. First try with a medium sized stack buffer (too + * small for your typical PATH, but large enough for most other things). + */ + RTUTF16 wszValue[512]; + uint32_t cwcValueBuf = RT_ELEMENTS(wszValue); + PRTUTF16 pwszValue = wszValue; + PRTUTF16 pwszValueFree = NULL; + + for (unsigned iTry = 0;; iTry++) + { + /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND. + Note! Assume that the CRT transparently updates the process + environment and that we don't need to use _wgetenv_s here. */ + SetLastError(NO_ERROR); + DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf); + DWORD dwErr = GetLastError(); + + if (cwcValueRet < cwcValueBuf) + { +#ifdef RT_ARCH_X86 + if (cwcValueRet == 0 && dwErr == NO_ERROR) + dwErr = rtEnvNt31CheckEmpty(pwszVar); +#endif + if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */ + { + if (cbValue) + rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual); + else + rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual); + } + else + { + Assert(cwcValueRet == 0); + Assert(dwErr != NO_ERROR); + rc = RTErrConvertFromWin32(dwErr); + } + break; + } + + /* + * Insufficient buffer, so increase it. The first re-try will use the + * returned size, further re-tries will max out with a multiple of the + * stack buffer till we reaches 32KB chars (128 loops). + */ + Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW); + RTMemTmpFree(pwszValueFree); + AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */); + + cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry); + pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16)); + AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY); + } + + RTMemTmpFree(pwszValueFree); + RTUtf16Free(pwszVar); + return rc; +} + + +RTDECL(char *) RTEnvDup(const char *pszVar) +{ + AssertPtrReturn(pszVar, NULL); + + /* + * Convert the name to UTF-16. + */ + PRTUTF16 pwszVar; + int rc = RTStrToUtf16(pszVar, &pwszVar); + AssertRCReturn(rc, NULL); + + /* + * Query the variable. First try with a medium sized stack buffer (too + * small for your typical PATH, but large enough for most other things). + */ + char *pszRet = NULL; + RTUTF16 wszValue[512]; + uint32_t cwcValueBuf = RT_ELEMENTS(wszValue); + PRTUTF16 pwszValue = wszValue; + PRTUTF16 pwszValueFree = NULL; + + for (unsigned iTry = 0;; iTry++) + { + /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND. + Note! Assume that the CRT transparently updates the process + environment and that we don't need to use _wgetenv_s here. */ + SetLastError(NO_ERROR); + DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf); + DWORD dwErr = GetLastError(); + + if (cwcValueRet < cwcValueBuf) + { +#ifdef RT_ARCH_X86 + if (cwcValueRet == 0 && dwErr == NO_ERROR) + dwErr = rtEnvNt31CheckEmpty(pwszVar); +#endif + if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */ + { + rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL); + if (RT_FAILURE(rc)) + pszRet = NULL; + } + else + { + Assert(cwcValueRet == 0); + Assert(dwErr != NO_ERROR); + } + break; + } + + /* + * Insufficient buffer, so increase it. The first re-try will use the + * returned size, further re-tries will max out with a multiple of the + * stack buffer till we reaches 32KB chars (128 loops). + */ + Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW); + RTMemTmpFree(pwszValueFree); + AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */); + + cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry); + pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16)); + AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY); + } + + RTMemTmpFree(pwszValueFree); + RTUtf16Free(pwszVar); + return pszRet; +} + + +RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue) +{ +#ifndef IPRT_NO_CRT + /** @todo putenv is a source memory leaks. deal with this on a per system basis. */ + if (!putenv((char *)pszVarEqualValue)) + return 0; + return RTErrConvertFromErrno(errno); +#else + return RTEnvPutUtf8(pszVarEqualValue); +#endif +} + + +RTDECL(int) RTEnvPut(const char *pszVarEqualValue) +{ +#ifndef IPRT_NO_CRT + return RTEnvPutBad(pszVarEqualValue); +#else + return RTEnvPutUtf8(pszVarEqualValue); +#endif +} + + +RTDECL(int) RTEnvPutUtf8(const char *pszVarEqualValue) +{ + PRTUTF16 pwszVarEqualValue; + int rc = RTStrToUtf16(pszVarEqualValue, &pwszVarEqualValue); + if (RT_SUCCESS(rc)) + { +#ifndef IPRT_NO_CRT + if (!_wputenv(pwszVarEqualValue)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromErrno(errno); +#else + PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '='); + if (pwszValue) + { + *pwszValue++ = '\0'; + + SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */ + if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL)) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_ENVVAR_NOT_FOUND) + { + Assert(!*pwszValue); + rc = VINF_SUCCESS; + } + else + { + Assert(*pwszValue); + rc = RTErrConvertFromWin32(GetLastError()); + } + } + } + else + rc = VERR_INVALID_PARAMETER; +#endif + RTUtf16Free(pwszVarEqualValue); + } + return rc; +} + + + +RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue) +{ +#ifndef IPRT_NO_CRT + AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME); + int rc; + if (!RTEnvExist(pszVar)) + rc = VINF_ENV_VAR_NOT_FOUND; + else + { + errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */); + if (rcErrno == 0) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromErrno(rcErrno); + } + return rc; +#else + return RTEnvSetUtf8(pszVar, pszValue); +#endif +} + + +RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue) +{ +#ifndef IPRT_NO_CRT + return RTEnvSetBad(pszVar, pszValue); +#else + return RTEnvSetUtf8(pszVar, pszValue); +#endif +} + + +/** + * Worker common to RTEnvSetUtf8() and rtEnvSetExWorker(). + */ +int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue) +{ + PRTUTF16 pwszVar; + int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszValue; + rc = RTStrToUtf16(pszValue, &pwszValue); + if (RT_SUCCESS(rc)) + { +#ifndef IPRT_NO_CRT + errno_t rcErrno = _wputenv_s(pwszVar, + *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */); + if (rcErrno == 0) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromErrno(rcErrno); +#else + SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */ + if (SetEnvironmentVariableW(pwszVar, pwszValue)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); +#endif + RTUtf16Free(pwszValue); + } + RTUtf16Free(pwszVar); + } + return rc; +} + + +RTDECL(int) RTEnvSetUtf8(const char *pszVar, const char *pszValue) +{ + size_t cchVar = strlen(pszVar); + AssertReturn(memchr(pszVar, '=', cchVar) == NULL, VERR_ENV_INVALID_VAR_NAME); + return rtEnvSetUtf8Worker(pszVar, cchVar, pszValue); +} + + +RTDECL(int) RTEnvUnsetBad(const char *pszVar) +{ +#ifndef IPRT_NO_CRT + AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME); + int rc; + if (!RTEnvExist(pszVar)) + rc = VINF_ENV_VAR_NOT_FOUND; + else + { + errno_t rcErrno = _putenv_s(pszVar, NULL); + if (rcErrno == 0) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromErrno(rcErrno); + } + return rc; +#else + return RTEnvUnsetUtf8(pszVar); +#endif +} + + +RTDECL(int) RTEnvUnset(const char *pszVar) +{ +#ifndef IPRT_NO_CRT + return RTEnvUnsetBad(pszVar); +#else + return RTEnvUnsetUtf8(pszVar); +#endif +} + + +RTDECL(int) RTEnvUnsetUtf8(const char *pszVar) +{ + AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME); + + PRTUTF16 pwszVar; + int rc = RTStrToUtf16(pszVar, &pwszVar); + if (RT_SUCCESS(rc)) + { +#ifndef IPRT_NO_CRT + if (_wgetenv(pwszVar)) + { + errno_t rcErrno = _wputenv_s(pwszVar, NULL); + if (rcErrno == 0) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromErrno(rcErrno); + } + else + rc = VINF_ENV_VAR_NOT_FOUND; +#else + SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */ + if (SetEnvironmentVariableW(pwszVar, NULL)) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr); + } +#endif + RTUtf16Free(pwszVar); + } + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/errvars-win.cpp b/src/VBox/Runtime/r3/win/errvars-win.cpp new file mode 100644 index 00000000..6d8b4814 --- /dev/null +++ b/src/VBox/Runtime/r3/win/errvars-win.cpp @@ -0,0 +1,108 @@ +/* $Id: errvars-win.cpp $ */ +/** @file + * IPRT - Save and Restore Error Variables, Windows Ring-3. + */ + +/* + * 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/win/winsock2.h> +#ifndef IPRT_NO_CRT +# include <errno.h> +#endif + +#include <iprt/errcore.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include "internal/magics.h" +#include "internal-r3-win.h" + + + +RTDECL(PRTERRVARS) RTErrVarsSave(PRTERRVARS pVars) +{ + pVars->ai32Vars[0] = RTERRVARS_MAGIC; + pVars->ai32Vars[1] = GetLastError(); + pVars->ai32Vars[2] = g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED; +#ifndef IPRT_NO_CRT + pVars->ai32Vars[3] = errno; +#endif + return pVars; +} + + +RTDECL(void) RTErrVarsRestore(PCRTERRVARS pVars) +{ + AssertReturnVoid(pVars->ai32Vars[0] == RTERRVARS_MAGIC); +#ifndef IPRT_NO_CRT + errno = pVars->ai32Vars[3]; +#endif + if ( pVars->ai32Vars[2] != WSANOTINITIALISED + && g_pfnWSASetLastError) + g_pfnWSASetLastError(pVars->ai32Vars[2]); + SetLastError(pVars->ai32Vars[1]); +} + + +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] + && pVars1->ai32Vars[1] == pVars2->ai32Vars[1] + && pVars1->ai32Vars[2] == pVars2->ai32Vars[2] +#ifndef IPRT_NO_CRT + && pVars1->ai32Vars[3] == pVars2->ai32Vars[3] +#endif + ; +} + + +RTDECL(bool) RTErrVarsHaveChanged(PCRTERRVARS pVars) +{ + Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC); + + return pVars->ai32Vars[0] != RTERRVARS_MAGIC + || (uint32_t)pVars->ai32Vars[1] != GetLastError() + || pVars->ai32Vars[2] != (g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED) +#ifndef IPRT_NO_CRT + || pVars->ai32Vars[3] != errno +#endif + ; + +} + diff --git a/src/VBox/Runtime/r3/win/fileaio-win.cpp b/src/VBox/Runtime/r3/win/fileaio-win.cpp new file mode 100644 index 00000000..a3fb934d --- /dev/null +++ b/src/VBox/Runtime/r3/win/fileaio-win.cpp @@ -0,0 +1,554 @@ +/* $Id: fileaio-win.cpp $ */ +/** @file + * IPRT - File async I/O, native implementation for the Windows host platform. + */ + +/* + * 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 + +#include <iprt/asm.h> +#include <iprt/file.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include "internal/fileaio.h" + +#include <iprt/win/windows.h> + +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Transfer direction. + */ +typedef enum TRANSFERDIRECTION +{ + TRANSFERDIRECTION_INVALID = 0, + /** Read. */ + TRANSFERDIRECTION_READ, + /** Write. */ + TRANSFERDIRECTION_WRITE, + /** The usual 32-bit hack. */ + TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff +} TRANSFERDIRECTION; + +/** + * Async I/O completion context state. + */ +typedef struct RTFILEAIOCTXINTERNAL +{ + /** handle to I/O completion port. */ + HANDLE hIoCompletionPort; + /** Current number of requests pending. */ + volatile int32_t cRequests; + /** Flag whether the thread was woken up. */ + volatile bool fWokenUp; + /** Flag whether the thread is currently waiting. */ + volatile bool fWaiting; + /** Flags given during creation. */ + uint32_t fFlags; + /** Magic value (RTFILEAIOCTX_MAGIC). */ + uint32_t u32Magic; +} RTFILEAIOCTXINTERNAL; +/** Pointer to an internal context structure. */ +typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL; + +/** + * Async I/O request state. + */ +typedef struct RTFILEAIOREQINTERNAL +{ + /** Overlapped structure. */ + OVERLAPPED Overlapped; + /** Current state the request is in. */ + RTFILEAIOREQSTATE enmState; + /** The file handle. */ + HANDLE hFile; + /** Kind of transfer Read/Write. */ + TRANSFERDIRECTION enmTransferDirection; + /** Number of bytes to transfer. */ + size_t cbTransfer; + /** Pointer to the buffer. */ + void *pvBuf; + /** Opaque user data. */ + void *pvUser; + /** Flag whether the request completed. */ + bool fCompleted; + /** Number of bytes transferred successfully. */ + size_t cbTransfered; + /** Error code of the completed request. */ + int Rc; + /** Completion context we are assigned to. */ + PRTFILEAIOCTXINTERNAL pCtxInt; + /** Magic value (RTFILEAIOREQ_MAGIC). */ + uint32_t u32Magic; +} RTFILEAIOREQINTERNAL; +/** Pointer to an internal request structure. */ +typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Id for the wakeup event. */ +#define AIO_CONTEXT_WAKEUP_EVENT 1 +/** Converts a pointer to an OVERLAPPED structure to a internal request. */ +#define OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped) ( (PRTFILEAIOREQINTERNAL)((uintptr_t)(pOverlapped) - RT_UOFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) ) + +RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits) +{ + AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER); + + /* No limits known. */ + pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS; + pAioLimits->cbBufferAlignment = 0; + + return VINF_SUCCESS; +} + +RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq) +{ + AssertPtrReturn(phReq, VERR_INVALID_POINTER); + + PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL)); + if (RT_UNLIKELY(!pReqInt)) + return VERR_NO_MEMORY; + + pReqInt->pCtxInt = NULL; + pReqInt->fCompleted = false; + pReqInt->u32Magic = RTFILEAIOREQ_MAGIC; + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + + *phReq = (RTFILEAIOREQ)pReqInt; + + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq) +{ + /* + * Validate the handle and ignore nil. + */ + if (hReq == NIL_RTFILEAIOREQ) + return VINF_SUCCESS; + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + + /* + * Trash the magic and free it. + */ + ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC); + RTMemFree(pReqInt); + return VINF_SUCCESS; +} + +/** + * Worker setting up the request. + */ +DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile, + TRANSFERDIRECTION enmTransferDirection, + RTFOFF off, void *pvBuf, size_t cbTransfer, + void *pvUser) +{ + /* + * Validate the input. + */ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + Assert(hFile != NIL_RTFILE); + AssertPtr(pvBuf); + Assert(off >= 0); + Assert(cbTransfer > 0); + + pReqInt->enmTransferDirection = enmTransferDirection; + pReqInt->hFile = (HANDLE)RTFileToNative(hFile); + pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff); + pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32); + pReqInt->cbTransfer = cbTransfer; + pReqInt->pvBuf = pvBuf; + pReqInt->pvUser = pvUser; + pReqInt->fCompleted = false; + + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off, + void *pvBuf, size_t cbRead, void *pvUser) +{ + return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ, + off, pvBuf, cbRead, pvUser); +} + +RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off, + void const *pvBuf, size_t cbWrite, void *pvUser) +{ + return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE, + off, (void *)pvBuf, cbWrite, pvUser); +} + +RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE); + RT_NOREF_PV(pvUser); + + return VERR_NOT_SUPPORTED; +} + +RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL); + + return pReqInt->pvUser; +} + +RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED); + + /** + * @todo r=aeichner It is not possible to cancel specific + * requests on Windows before Vista. + * CancelIo cancels all requests for a file issued by the + * calling thread and CancelIoEx which does what we need + * is only available from Vista and up. + * The solution is to return VERR_FILE_AIO_IN_PROGRESS + * if the request didn't completed yet (checked above). + * Shouldn't be a big issue because a request is normally + * only canceled if it exceeds a timeout which is quite huge. + */ + return VERR_FILE_AIO_COMPLETED; +} + +RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered) +{ + int rc = VINF_SUCCESS; + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED); + + rc = pReqInt->Rc; + if (pcbTransfered && RT_SUCCESS(rc)) + *pcbTransfered = pReqInt->cbTransfered; + + return rc; +} + +RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, uint32_t fFlags) +{ + AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + RT_NOREF_PV(cAioReqsMax); + + if ( g_pfnCreateIoCompletionPort + && g_pfnGetQueuedCompletionStatus + && g_pfnPostQueuedCompletionStatus) + { + PRTFILEAIOCTXINTERNAL pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL)); + if (RT_UNLIKELY(!pCtxInt)) + return VERR_NO_MEMORY; + + pCtxInt->hIoCompletionPort = g_pfnCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort)) + { + RTMemFree(pCtxInt); + return VERR_NO_MEMORY; + } + + pCtxInt->fFlags = fFlags; + pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC; + + *phAioCtx = (RTFILEAIOCTX)pCtxInt; + + return VINF_SUCCESS; + } + return VERR_NOT_SUPPORTED; +} + +RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx) +{ + /* Validate the handle and ignore nil. */ + if (hAioCtx == NIL_RTFILEAIOCTX) + return VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + + /* Cannot destroy a busy context. */ + if (RT_UNLIKELY(pCtxInt->cRequests)) + return VERR_FILE_AIO_BUSY; + + CloseHandle(pCtxInt->hIoCompletionPort); + ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD); + RTMemFree(pCtxInt); + + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile) +{ + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + + if ( g_pfnCreateIoCompletionPort + && g_pfnGetQueuedCompletionStatus + && g_pfnPostQueuedCompletionStatus) + { + int rc = VINF_SUCCESS; + HANDLE hTemp = g_pfnCreateIoCompletionPort((HANDLE)RTFileToNative(hFile), pCtxInt->hIoCompletionPort, 0, 1); + if (hTemp != pCtxInt->hIoCompletionPort) + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; + } + return VERR_NOT_SUPPORTED; +} + +RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx) +{ + RT_NOREF_PV(hAioCtx); + return RTFILEAIO_UNLIMITED_REQS; +} + +RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs) +{ + /* + * Parameter validation. + */ + int rc = VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER); + Assert(cReqs <= INT32_MAX); + AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); + size_t i; + + for (i = 0; i < cReqs; i++) + { + PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i]; + BOOL fSucceeded; + + Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer); + if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ) + { + fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf, + (DWORD)pReqInt->cbTransfer, NULL, + &pReqInt->Overlapped); + } + else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE) + { + fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf, + (DWORD)pReqInt->cbTransfer, NULL, + &pReqInt->Overlapped); + } + else + { + fSucceeded = false; + AssertMsgFailed(("Invalid transfer direction\n")); + } + + if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING)) + { + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + rc = RTErrConvertFromWin32(GetLastError()); + pReqInt->Rc = rc; + break; + } + RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED); + } + + ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i); + + return rc; +} + +RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies, + PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs) +{ + /* + * Validate the parameters, making sure to always set pcReqs. + */ + AssertPtrReturn(pcReqs, VERR_INVALID_POINTER); + *pcReqs = 0; /* always set */ + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); + AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER); + AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE); + + /* + * Can't wait if there are no requests around. + */ + if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0) + && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS)) + return VERR_FILE_AIO_NO_REQUEST; + + /* Wait for at least one. */ + if (!cMinReqs) + cMinReqs = 1; + + /* + * Loop until we're woken up, hit an error (incl timeout), or + * have collected the desired number of requests. + */ + int rc = VINF_SUCCESS; + int cRequestsCompleted = 0; + while ( !pCtxInt->fWokenUp + && cMinReqs > 0) + { + uint64_t StartNanoTS = 0; + DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies; + DWORD cbTransfered; + LPOVERLAPPED pOverlapped; + ULONG_PTR lCompletionKey; + BOOL fSucceeded; + + if (cMillies != RT_INDEFINITE_WAIT) + StartNanoTS = RTTimeNanoTS(); + + ASMAtomicXchgBool(&pCtxInt->fWaiting, true); + fSucceeded = g_pfnGetQueuedCompletionStatus(pCtxInt->hIoCompletionPort, + &cbTransfered, + &lCompletionKey, + &pOverlapped, + dwTimeout); + ASMAtomicXchgBool(&pCtxInt->fWaiting, false); + if ( !fSucceeded + && !pOverlapped) + { + /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */ + rc = RTErrConvertFromWin32(GetLastError()); + break; + } + + /* Check if we got woken up. */ + if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT) + { + Assert(fSucceeded && !pOverlapped); + break; + } + + /* A request completed. */ + PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped); + AssertPtr(pReqInt); + Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC); + + /* Mark the request as finished. */ + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + + pReqInt->cbTransfered = cbTransfered; + if (fSucceeded) + pReqInt->Rc = VINF_SUCCESS; + else + { + DWORD errCode = GetLastError(); + pReqInt->Rc = RTErrConvertFromWin32(errCode); + if (pReqInt->Rc == VERR_UNRESOLVED_ERROR) + LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode)); + } + + pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt; + + /* Update counter. */ + cMinReqs--; + + if (cMillies != RT_INDEFINITE_WAIT) + { + /* Recalculate timeout. */ + uint64_t NanoTS = RTTimeNanoTS(); + uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000; + if (cMilliesElapsed < cMillies) + cMillies -= cMilliesElapsed; + else + cMillies = 0; + } + } + + /* + * Update the context state and set the return value. + */ + *pcReqs = cRequestsCompleted; + ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted); + + /* + * Clear the wakeup flag and set rc. + */ + bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false); + + if ( fWokenUp + && RT_SUCCESS(rc)) + rc = VERR_INTERRUPTED; + + return rc; +} + +RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx) +{ + int rc = VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + + bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true); + bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting); + + if ( !fWokenUp + && fWaiting) + { + BOOL fSucceeded = g_pfnPostQueuedCompletionStatus(pCtxInt->hIoCompletionPort, + 0, AIO_CONTEXT_WAKEUP_EVENT, + NULL); + + if (!fSucceeded) + rc = RTErrConvertFromWin32(GetLastError()); + } + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/fileio-win.cpp b/src/VBox/Runtime/r3/win/fileio-win.cpp new file mode 100644 index 00000000..3feba3a2 --- /dev/null +++ b/src/VBox/Runtime/r3/win/fileio-win.cpp @@ -0,0 +1,1549 @@ +/* $Id: fileio-win.cpp $ */ +/** @file + * IPRT - File I/O, native implementation for the Windows host platform. + */ + +/* + * 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 +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +#endif +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/file.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/utf16.h> +#include "internal/file.h" +#include "internal/fs.h" +#include "internal/path.h" +#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +typedef BOOL WINAPI FNVERIFYCONSOLEIOHANDLE(HANDLE); +typedef FNVERIFYCONSOLEIOHANDLE *PFNVERIFYCONSOLEIOHANDLE; /* No, nobody fell on the keyboard, really! */ + + +/** + * This is wrapper around the ugly SetFilePointer api. + * + * It's equivalent to SetFilePointerEx which we so unfortunately cannot use because of + * it not being present in NT4 GA. + * + * @returns Success indicator. Extended error information obtainable using GetLastError(). + * @param hFile Filehandle. + * @param offSeek Offset to seek. + * @param poffNew Where to store the new file offset. NULL allowed. + * @param uMethod Seek method. (The windows one!) + */ +DECLINLINE(bool) MySetFilePointer(RTFILE hFile, uint64_t offSeek, uint64_t *poffNew, unsigned uMethod) +{ + bool fRc; + LARGE_INTEGER off; + + off.QuadPart = offSeek; +#if 1 + if (off.LowPart != INVALID_SET_FILE_POINTER) + { + off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod); + fRc = off.LowPart != INVALID_SET_FILE_POINTER; + } + else + { + SetLastError(NO_ERROR); + off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod); + fRc = GetLastError() == NO_ERROR; + } +#else + fRc = SetFilePointerEx((HANDLE)RTFileToNative(hFile), off, &off, uMethod); +#endif + if (fRc && poffNew) + *poffNew = off.QuadPart; + return fRc; +} + + +/** + * Helper for checking if a VERR_DISK_FULL isn't a VERR_FILE_TOO_BIG. + * @returns VERR_DISK_FULL or VERR_FILE_TOO_BIG. + */ +static int rtFileWinCheckIfDiskReallyFull(RTFILE hFile, uint64_t cbDesired) +{ + /* + * Windows doesn't appear to have a way to query the file size limit of a + * file system, so we have to deduce the limit from the file system driver name. + * This means it will only work for known file systems. + */ + if (cbDesired >= _2G - 1) + { + uint64_t cbMaxFile = UINT64_MAX; + RTFSTYPE enmFsType; + int rc = rtNtQueryFsType((HANDLE)RTFileToNative(hFile), &enmFsType); + if (RT_SUCCESS(rc)) + switch (enmFsType) + { + case RTFSTYPE_NTFS: + case RTFSTYPE_EXFAT: + case RTFSTYPE_UDF: + cbMaxFile = UINT64_C(0xffffffffffffffff); /* (May be limited by IFS.) */ + break; + + case RTFSTYPE_ISO9660: + cbMaxFile = 8 *_1T; + break; + + case RTFSTYPE_FAT: + cbMaxFile = _4G; + break; + + case RTFSTYPE_HPFS: + cbMaxFile = _2G; + break; + + default: + break; + } + if (cbDesired >= cbMaxFile) + return VERR_FILE_TOO_BIG; + } + return VERR_DISK_FULL; +} + + +RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative) +{ + HANDLE h = (HANDLE)uNative; + AssertCompile(sizeof(h) == sizeof(uNative)); + if (h == INVALID_HANDLE_VALUE) + { + AssertMsgFailed(("%p\n", uNative)); + *pFile = NIL_RTFILE; + return VERR_INVALID_HANDLE; + } + *pFile = (RTFILE)h; + return VINF_SUCCESS; +} + + +RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile) +{ + AssertReturn(hFile != NIL_RTFILE, (RTHCINTPTR)INVALID_HANDLE_VALUE); + return (RTHCINTPTR)hFile; +} + + +RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen) +{ + return RTFileOpenEx(pszFilename, fOpen, pFile, NULL); +} + + +RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken) +{ + /* + * Validate input. + */ + AssertReturn(phFile, VERR_INVALID_PARAMETER); + *phFile = NIL_RTFILE; + if (penmActionTaken) + *penmActionTaken = RTFILEACTION_INVALID; + AssertReturn(pszFilename, VERR_INVALID_PARAMETER); + + /* + * Merge forced open flags and validate them. + */ + int rc = rtFileRecalcAndValidateFlags(&fOpen); + if (RT_FAILURE(rc)) + return rc; + + /* + * Determine disposition, access, share mode, creation flags, and security attributes + * for the CreateFile API call. + */ + DWORD dwCreationDisposition; + switch (fOpen & RTFILE_O_ACTION_MASK) + { + case RTFILE_O_OPEN: + dwCreationDisposition = fOpen & RTFILE_O_TRUNCATE ? TRUNCATE_EXISTING : OPEN_EXISTING; + break; + case RTFILE_O_OPEN_CREATE: + dwCreationDisposition = OPEN_ALWAYS; + break; + case RTFILE_O_CREATE: + dwCreationDisposition = CREATE_NEW; + break; + case RTFILE_O_CREATE_REPLACE: + dwCreationDisposition = CREATE_ALWAYS; + break; + default: + AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS); + } + + DWORD dwDesiredAccess; + switch (fOpen & RTFILE_O_ACCESS_MASK) + { + case RTFILE_O_READ: + dwDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */ + break; + case RTFILE_O_WRITE: + dwDesiredAccess = fOpen & RTFILE_O_APPEND + ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA + : FILE_GENERIC_WRITE; + break; + case RTFILE_O_READWRITE: + dwDesiredAccess = fOpen & RTFILE_O_APPEND + ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) + : FILE_GENERIC_READ | FILE_GENERIC_WRITE; + break; + case RTFILE_O_ATTR_ONLY: + if (fOpen & RTFILE_O_ACCESS_ATTR_MASK) + { + dwDesiredAccess = 0; + break; + } + RT_FALL_THRU(); + default: + AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS); + } + if (dwCreationDisposition == TRUNCATE_EXISTING) + /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */ + dwDesiredAccess |= GENERIC_WRITE; + + /* RTFileSetMode needs following rights as well. */ + switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK) + { + case RTFILE_O_ACCESS_ATTR_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_ACCESS_ATTR_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_ACCESS_ATTR_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + default: + /* Attributes access is the same as the file access. */ + switch (fOpen & RTFILE_O_ACCESS_MASK) + { + case RTFILE_O_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + case RTFILE_O_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break; + default: + AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS); + } + } + + DWORD dwShareMode; + switch (fOpen & RTFILE_O_DENY_MASK) + { + case RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_READ; break; + case RTFILE_O_DENY_READWRITE: dwShareMode = 0; break; + + case RTFILE_O_DENY_NOT_DELETE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; break; + case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:dwShareMode = FILE_SHARE_DELETE; break; + default: + AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS); + } + + SECURITY_ATTRIBUTES SecurityAttributes; + PSECURITY_ATTRIBUTES pSecurityAttributes = NULL; + if (fOpen & RTFILE_O_INHERIT) + { + SecurityAttributes.nLength = sizeof(SecurityAttributes); + SecurityAttributes.lpSecurityDescriptor = NULL; + SecurityAttributes.bInheritHandle = TRUE; + pSecurityAttributes = &SecurityAttributes; + } + + DWORD dwFlagsAndAttributes; + dwFlagsAndAttributes = !(fOpen & RTFILE_O_TEMP_AUTO_DELETE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_TEMPORARY; + if (fOpen & RTFILE_O_TEMP_AUTO_DELETE) + fOpen |= FILE_FLAG_DELETE_ON_CLOSE; + if (fOpen & RTFILE_O_WRITE_THROUGH) + dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; + if (fOpen & RTFILE_O_ASYNC_IO) + dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED; + if (fOpen & RTFILE_O_NO_CACHE) + { + dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING; + dwDesiredAccess &= ~FILE_APPEND_DATA; + } + + /* + * Open/Create the file. + */ + PRTUTF16 pwszFilename; + rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + HANDLE hFile = CreateFileW(pwszFilename, + dwDesiredAccess, + dwShareMode, + pSecurityAttributes, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL); + DWORD const dwErr = GetLastError(); + if (hFile != INVALID_HANDLE_VALUE) + { + /* + * Calculate the action taken value. + */ + RTFILEACTION enmActionTaken; + switch (dwCreationDisposition) + { + case CREATE_NEW: + enmActionTaken = RTFILEACTION_CREATED; + break; + case CREATE_ALWAYS: + AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr)); + enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_REPLACED : RTFILEACTION_CREATED; + break; + case OPEN_EXISTING: + enmActionTaken = RTFILEACTION_OPENED; + break; + case OPEN_ALWAYS: + AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr)); + enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_OPENED : RTFILEACTION_CREATED; + break; + case TRUNCATE_EXISTING: + enmActionTaken = RTFILEACTION_TRUNCATED; + break; + default: + AssertMsgFailed(("%d %#x\n", dwCreationDisposition, dwCreationDisposition)); + enmActionTaken = RTFILEACTION_INVALID; + break; + } + + /* + * Turn off indexing of directory through Windows Indexing Service if + * we created a new file or replaced an existing one. + */ + if ( (fOpen & RTFILE_O_NOT_CONTENT_INDEXED) + && ( enmActionTaken == RTFILEACTION_CREATED + || enmActionTaken == RTFILEACTION_REPLACED) ) + { + /** @todo there must be a way to do this via the handle! */ + if (!SetFileAttributesW(pwszFilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) + rc = RTErrConvertFromWin32(GetLastError()); + } + /* + * If RTFILEACTION_OPENED, we may need to truncate the file. + */ + else if ( (fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_ACTION_MASK)) == (RTFILE_O_TRUNCATE | RTFILE_O_OPEN_CREATE) + && enmActionTaken == RTFILEACTION_OPENED) + { + if (SetEndOfFile(hFile)) + enmActionTaken = RTFILEACTION_TRUNCATED; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + if (penmActionTaken) + *penmActionTaken = enmActionTaken; + if (RT_SUCCESS(rc)) + { + *phFile = (RTFILE)hFile; + Assert((HANDLE)*phFile == hFile); + RTPathWinFree(pwszFilename); + return VINF_SUCCESS; + } + + CloseHandle(hFile); + } + else + { + if ( penmActionTaken + && dwCreationDisposition == CREATE_NEW + && dwErr == ERROR_FILE_EXISTS) + *penmActionTaken = RTFILEACTION_ALREADY_EXISTS; + rc = RTErrConvertFromWin32(dwErr); + } + RTPathWinFree(pwszFilename); + } + return rc; +} + + +RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess) +{ + AssertReturn( fAccess == RTFILE_O_READ + || fAccess == RTFILE_O_WRITE + || fAccess == RTFILE_O_READWRITE, + VERR_INVALID_PARAMETER); + return RTFileOpen(phFile, "NUL", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN); +} + + +RTDECL(int) RTFileDup(RTFILE hFileSrc, uint64_t fFlags, PRTFILE phFileNew) +{ + /* + * Validate input. + */ + AssertPtrReturn(phFileNew, VERR_INVALID_POINTER); + *phFileNew = NIL_RTFILE; + AssertPtrReturn(phFileNew, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~(uint64_t)RTFILE_O_INHERIT), VERR_INVALID_FLAGS); + + /* + * Do the job. + */ + HANDLE hNew = INVALID_HANDLE_VALUE; + if (DuplicateHandle(GetCurrentProcess(), (HANDLE)RTFileToNative(hFileSrc), + GetCurrentProcess(), &hNew, 0, RT_BOOL(fFlags & RTFILE_O_INHERIT), DUPLICATE_SAME_ACCESS)) + { + *phFileNew = (RTFILE)hNew; + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +} + + +RTR3DECL(int) RTFileClose(RTFILE hFile) +{ + if (hFile == NIL_RTFILE) + return VINF_SUCCESS; + if (CloseHandle((HANDLE)RTFileToNative(hFile))) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + + +RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle) +{ + DWORD dwStdHandle; + switch (enmStdHandle) + { + case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break; + case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break; + case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break; + default: + AssertFailedReturn(NIL_RTFILE); + } + + HANDLE hNative = GetStdHandle(dwStdHandle); + if (hNative == INVALID_HANDLE_VALUE) + return NIL_RTFILE; + + RTFILE hFile = (RTFILE)(uintptr_t)hNative; + AssertReturn((HANDLE)(uintptr_t)hFile == hNative, NIL_RTFILE); + return hFile; +} + + +RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual) +{ + static ULONG aulSeekRecode[] = + { + FILE_BEGIN, + FILE_CURRENT, + FILE_END, + }; + + /* + * Validate input. + */ + if (uMethod > RTFILE_SEEK_END) + { + AssertMsgFailed(("Invalid uMethod=%d\n", uMethod)); + return VERR_INVALID_PARAMETER; + } + + /* + * Execute the seek. + */ + if (MySetFilePointer(hFile, offSeek, poffActual, aulSeekRecode[uMethod])) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + + +RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + if (cbToRead <= 0) + { + if (pcbRead) + *pcbRead = 0; + return VINF_SUCCESS; + } + ULONG cbToReadAdj = (ULONG)cbToRead; + AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG); + + ULONG cbRead = 0; + if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, NULL)) + { + if (pcbRead) + /* Caller can handle partial reads. */ + *pcbRead = cbRead; + else + { + /* Caller expects everything to be read. */ + while (cbToReadAdj > cbRead) + { + ULONG cbReadPart = 0; + if (!ReadFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToReadAdj - cbRead, &cbReadPart, NULL)) + return RTErrConvertFromWin32(GetLastError()); + if (cbReadPart == 0) + return VERR_EOF; + cbRead += cbReadPart; + } + } + return VINF_SUCCESS; + } + + /* + * If it's a console, we might bump into out of memory conditions in the + * ReadConsole call. + */ + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_NOT_ENOUGH_MEMORY) + { + ULONG cbChunk = cbToReadAdj / 2; + if (cbChunk > 16*_1K) + cbChunk = 16*_1K; + else + cbChunk = RT_ALIGN_32(cbChunk, 256); + + cbRead = 0; + while (cbToReadAdj > cbRead) + { + ULONG cbToReadNow = RT_MIN(cbChunk, cbToReadAdj - cbRead); + ULONG cbReadPart = 0; + if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadNow, &cbReadPart, NULL)) + { + /* If we failed because the buffer is too big, shrink it and + try again. */ + dwErr = GetLastError(); + if ( dwErr == ERROR_NOT_ENOUGH_MEMORY + && cbChunk > 8) + { + cbChunk /= 2; + continue; + } + return RTErrConvertFromWin32(dwErr); + } + cbRead += cbReadPart; + + /* Return if the caller can handle partial reads, otherwise try + fill the buffer all the way up. */ + if (pcbRead) + { + *pcbRead = cbRead; + break; + } + if (cbReadPart == 0) + return VERR_EOF; + } + return VINF_SUCCESS; + } + + return RTErrConvertFromWin32(dwErr); +} + + +RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + ULONG cbToReadAdj = (ULONG)cbToRead; + AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG); + + OVERLAPPED Overlapped; + Overlapped.Offset = (uint32_t)off; + Overlapped.OffsetHigh = (uint32_t)(off >> 32); + Overlapped.hEvent = NULL; + Overlapped.Internal = 0; + Overlapped.InternalHigh = 0; + + ULONG cbRead = 0; + if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, &Overlapped)) + { + if (pcbRead) + /* Caller can handle partial reads. */ + *pcbRead = cbRead; + else + { + /* Caller expects everything to be read. */ + while (cbToReadAdj > cbRead) + { + Overlapped.Offset = (uint32_t)(off + cbRead); + Overlapped.OffsetHigh = (uint32_t)((off + cbRead) >> 32); + Overlapped.hEvent = NULL; + Overlapped.Internal = 0; + Overlapped.InternalHigh = 0; + + ULONG cbReadPart = 0; + if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadAdj - cbRead, + &cbReadPart, &Overlapped)) + return RTErrConvertFromWin32(GetLastError()); + if (cbReadPart == 0) + return VERR_EOF; + cbRead += cbReadPart; + } + } + return VINF_SUCCESS; + } + + /* We will get an EOF error when using overlapped I/O. So, make sure we don't + return it when pcbhRead is not NULL. */ + DWORD dwErr = GetLastError(); + if (pcbRead && dwErr == ERROR_HANDLE_EOF) + { + *pcbRead = 0; + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(dwErr); +} + + +RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + if (cbToWrite <= 0) + return VINF_SUCCESS; + ULONG const cbToWriteAdj = (ULONG)cbToWrite; + AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG); + + ULONG cbWritten = 0; + if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, NULL)) + { + if (pcbWritten) + /* Caller can handle partial writes. */ + *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */ + else + { + /* Caller expects everything to be written. */ + while (cbWritten < cbToWriteAdj) + { + ULONG cbWrittenPart = 0; + if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten, + cbToWriteAdj - cbWritten, &cbWrittenPart, NULL)) + { + int rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_DISK_FULL) + rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj - cbWritten); + return rc; + } + if (cbWrittenPart == 0) + return VERR_WRITE_ERROR; + cbWritten += cbWrittenPart; + } + } + return VINF_SUCCESS; + } + + /* + * If it's a console, we might bump into out of memory conditions in the + * WriteConsole call. + */ + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_NOT_ENOUGH_MEMORY) + { + ULONG cbChunk = cbToWriteAdj / 2; + if (cbChunk > _32K) + cbChunk = _32K; + else + cbChunk = RT_ALIGN_32(cbChunk, 256); + + cbWritten = 0; + while (cbWritten < cbToWriteAdj) + { + ULONG cbToWriteNow = RT_MIN(cbChunk, cbToWriteAdj - cbWritten); + ULONG cbWrittenPart = 0; + if (!WriteFile((HANDLE)RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWriteNow, &cbWrittenPart, NULL)) + { + /* If we failed because the buffer is too big, shrink it and + try again. */ + dwErr = GetLastError(); + if ( dwErr == ERROR_NOT_ENOUGH_MEMORY + && cbChunk > 8) + { + cbChunk /= 2; + continue; + } + int rc = RTErrConvertFromWin32(dwErr); + if (rc == VERR_DISK_FULL) + rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteNow); + return rc; + } + cbWritten += cbWrittenPart; + + /* Return if the caller can handle partial writes, otherwise try + write out everything. */ + if (pcbWritten) + { + *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */ + break; + } + if (cbWrittenPart == 0) + return VERR_WRITE_ERROR; + } + return VINF_SUCCESS; + } + + int rc = RTErrConvertFromWin32(dwErr); + if (rc == VERR_DISK_FULL) + rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj); + return rc; +} + + +RTDECL(int) RTFileWriteAt(RTFILE hFile, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + ULONG const cbToWriteAdj = (ULONG)cbToWrite; + AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG); + + OVERLAPPED Overlapped; + Overlapped.Offset = (uint32_t)off; + Overlapped.OffsetHigh = (uint32_t)(off >> 32); + Overlapped.hEvent = NULL; + Overlapped.Internal = 0; + Overlapped.InternalHigh = 0; + + ULONG cbWritten = 0; + if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, &Overlapped)) + { + if (pcbWritten) + /* Caller can handle partial writes. */ + *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */ + else + { + /* Caller expects everything to be written. */ + while (cbWritten < cbToWriteAdj) + { + Overlapped.Offset = (uint32_t)(off + cbWritten); + Overlapped.OffsetHigh = (uint32_t)((off + cbWritten) >> 32); + Overlapped.hEvent = NULL; + Overlapped.Internal = 0; + Overlapped.InternalHigh = 0; + + ULONG cbWrittenPart = 0; + if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten, + cbToWriteAdj - cbWritten, &cbWrittenPart, &Overlapped)) + { + int rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_DISK_FULL) + rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj); + return rc; + } + if (cbWrittenPart == 0) + return VERR_WRITE_ERROR; + cbWritten += cbWrittenPart; + } + } + return VINF_SUCCESS; + } + + int rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_DISK_FULL) + rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj); + return rc; +} + + +RTR3DECL(int) RTFileFlush(RTFILE hFile) +{ + if (!FlushFileBuffers((HANDLE)RTFileToNative(hFile))) + { + int rc = GetLastError(); + Log(("FlushFileBuffers failed with %d\n", rc)); + return RTErrConvertFromWin32(rc); + } + return VINF_SUCCESS; +} + +#if 1 + +/** + * Checks the the two handles refers to the same file. + * + * @returns true if the same file, false if different ones or invalid handles. + * @param hFile1 Handle \#1. + * @param hFile2 Handle \#2. + */ +static bool rtFileIsSame(HANDLE hFile1, HANDLE hFile2) +{ + /* + * We retry in case CreationTime or the Object ID is being modified and there + * aren't any IndexNumber (file ID) on this kind of file system. + */ + for (uint32_t iTries = 0; iTries < 3; iTries++) + { + /* + * Fetch data to compare (being a little lazy here). + */ + struct + { + HANDLE hFile; + NTSTATUS rcObjId; + FILE_OBJECTID_INFORMATION ObjId; + FILE_ALL_INFORMATION All; + FILE_FS_VOLUME_INFORMATION Vol; + } auData[2]; + auData[0].hFile = hFile1; + auData[1].hFile = hFile2; + + for (uintptr_t i = 0; i < RT_ELEMENTS(auData); i++) + { + RT_ZERO(auData[i].ObjId); + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + auData[i].rcObjId = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].ObjId, sizeof(auData[i].ObjId), + FileObjectIdInformation); + + RT_ZERO(auData[i].All); + RTNT_IO_STATUS_BLOCK_REINIT(&Ios); + NTSTATUS rcNt = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].All, sizeof(auData[i].All), + FileAllInformation); + AssertReturn(rcNt == STATUS_BUFFER_OVERFLOW /* insufficient space for name info */ || NT_SUCCESS(rcNt), false); + + union + { + FILE_FS_VOLUME_INFORMATION Info; + uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096]; + } uVol; + RT_ZERO(uVol.Info); + RTNT_IO_STATUS_BLOCK_REINIT(&Ios); + rcNt = NtQueryVolumeInformationFile(auData[i].hFile, &Ios, &uVol, sizeof(uVol), FileFsVolumeInformation); + if (NT_SUCCESS(rcNt)) + auData[i].Vol = uVol.Info; + else + RT_ZERO(auData[i].Vol); + } + + /* + * Compare it. + */ + if ( auData[0].All.StandardInformation.Directory + == auData[1].All.StandardInformation.Directory) + { /* likely */ } + else + break; + + if ( (auData[0].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) + == (auData[1].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT))) + { /* likely */ } + else + break; + + if ( auData[0].Vol.VolumeSerialNumber + == auData[1].Vol.VolumeSerialNumber) + { /* likely */ } + else + break; + + if ( auData[0].All.InternalInformation.IndexNumber.QuadPart + == auData[1].All.InternalInformation.IndexNumber.QuadPart) + { /* likely */ } + else + break; + + if ( !NT_SUCCESS(auData[0].rcObjId) + || memcmp(&auData[0].ObjId, &auData[1].ObjId, RT_UOFFSETOF(FILE_OBJECTID_INFORMATION, ExtendedInfo)) == 0) + { + if ( auData[0].All.BasicInformation.CreationTime.QuadPart + == auData[1].All.BasicInformation.CreationTime.QuadPart) + return true; + } + } + + return false; +} + + +/** + * If @a hFile is opened in append mode, try return a handle with + * FILE_WRITE_DATA permissions. + * + * @returns Duplicate handle. + * @param hFile The NT handle to check & duplicate. + * + * @todo It would be much easier to implement this by not dropping the + * FILE_WRITE_DATA access and instead have the RTFileWrite APIs + * enforce the appending. That will require keeping additional + * information along side the handle (instance structure). However, on + * windows you can grant append permissions w/o giving people access to + * overwrite existing data, so the RTFileOpenEx code would have to deal + * with those kinds of STATUS_ACCESS_DENIED too then. + */ +static HANDLE rtFileReOpenAppendOnlyWithFullWriteAccess(HANDLE hFile) +{ + OBJECT_BASIC_INFORMATION BasicInfo = {0}; + ULONG cbActual = 0; + NTSTATUS rcNt = NtQueryObject(hFile, ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbActual); + if (NT_SUCCESS(rcNt)) + { + if ((BasicInfo.GrantedAccess & (FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA) + { + /* + * We cannot use NtDuplicateObject here as it is not possible to + * upgrade the access on files, only making it more strict. So, + * query the path and re-open it (we could do by file/object/whatever + * id too, but that may not work with all file systems). + */ + for (uint32_t i = 0; i < 16; i++) + { + UNICODE_STRING NtName; + int rc = RTNtPathFromHandle(&NtName, hFile, 0); + AssertRCReturn(rc, INVALID_HANDLE_VALUE); + + HANDLE hDupFile = RTNT_INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, &NtName, BasicInfo.Attributes & ~OBJ_INHERIT, NULL, NULL); + + rcNt = NtCreateFile(&hDupFile, + BasicInfo.GrantedAccess | FILE_WRITE_DATA, + &ObjAttr, + &Ios, + NULL /* AllocationSize*/, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT /*??*/, + NULL /*EaBuffer*/, + 0 /*EaLength*/); + RTUtf16Free(NtName.Buffer); + if (NT_SUCCESS(rcNt)) + { + /* + * Check that we've opened the same file. + */ + if (rtFileIsSame(hFile, hDupFile)) + return hDupFile; + NtClose(hDupFile); + } + } + AssertFailed(); + } + } + return INVALID_HANDLE_VALUE; +} + +#endif + +RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize) +{ +#if 1 + HANDLE hNtFile = (HANDLE)RTFileToNative(hFile); + HANDLE hDupFile = INVALID_HANDLE_VALUE; + union + { + FILE_END_OF_FILE_INFORMATION Eof; + FILE_ALLOCATION_INFORMATION Alloc; + } uInfo; + + /* + * Change the EOF marker. + * + * HACK ALERT! If the file was opened in RTFILE_O_APPEND mode, we will have + * to re-open it with FILE_WRITE_DATA access to get the job done. + * This how ftruncate on a unixy system would work but not how + * it is done on Windows where appending is a separate permission + * rather than just a write modifier, making this hack totally wrong. + */ + /** @todo The right way to fix this is either to add a RTFileSetSizeEx function + * for specifically requesting the unixy behaviour, or add an additional + * flag to RTFileOpen[Ex] to request the unixy append behaviour there. + * The latter would require saving the open flags in a instance data + * structure, which is a bit of a risky move, though something we should + * do in 6.2 (or later). + * + * Note! Because handle interitance, it is not realyan option to + * always use FILE_WRITE_DATA and implement the RTFILE_O_APPEND + * bits in RTFileWrite and friends. Besides, it's not like + * RTFILE_O_APPEND is so clearly defined anyway - see + * RTFileWriteAt. + */ + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + uInfo.Eof.EndOfFile.QuadPart = cbSize; + NTSTATUS rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation); + if (rcNt == STATUS_ACCESS_DENIED) + { + hDupFile = rtFileReOpenAppendOnlyWithFullWriteAccess(hNtFile); + if (hDupFile != INVALID_HANDLE_VALUE) + { + hNtFile = hDupFile; + uInfo.Eof.EndOfFile.QuadPart = cbSize; + rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation); + } + } + + if (NT_SUCCESS(rcNt)) + { + /* + * Change the allocation. + */ + uInfo.Alloc.AllocationSize.QuadPart = cbSize; + rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Alloc), FileAllocationInformation); + } + + /* + * Close the temporary file handle: + */ + if (hDupFile != INVALID_HANDLE_VALUE) + NtClose(hDupFile); + + if (RT_SUCCESS(rcNt)) + return VINF_SUCCESS; + return RTErrConvertFromNtStatus(rcNt); + +#else /* this version of the code will fail to truncate files when RTFILE_O_APPEND is in effect, which isn't what we want... */ + /* + * Get current file pointer. + */ + int rc; + uint64_t offCurrent; + if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT)) + { + /* + * Set new file pointer. + */ + if (MySetFilePointer(hFile, cbSize, NULL, FILE_BEGIN)) + { + /* set file pointer */ + if (SetEndOfFile((HANDLE)RTFileToNative(hFile))) + { + /* + * Restore file pointer and return. + * If the old pointer was beyond the new file end, ignore failure. + */ + if ( MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN) + || offCurrent > cbSize) + return VINF_SUCCESS; + } + + /* + * Failed, try restoring the file pointer. + */ + rc = GetLastError(); + MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN); + + if (rc == ERROR_DISK_FULL) + return rtFileWinCheckIfDiskReallyFull(hFile, cbSize); + } + else + rc = GetLastError(); + } + else + rc = GetLastError(); + + return RTErrConvertFromWin32(rc); +#endif +} + + +RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize) +{ + /* + * GetFileSize works for most handles. + */ + ULARGE_INTEGER Size; + Size.LowPart = GetFileSize((HANDLE)RTFileToNative(hFile), &Size.HighPart); + if (Size.LowPart != INVALID_FILE_SIZE) + { + *pcbSize = Size.QuadPart; + return VINF_SUCCESS; + } + int rc = RTErrConvertFromWin32(GetLastError()); + + /* + * Could it be a volume or a disk? + */ + DISK_GEOMETRY DriveGeo; + DWORD cbDriveGeo; + if (DeviceIoControl((HANDLE)RTFileToNative(hFile), + IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL)) + { + if ( DriveGeo.MediaType == FixedMedia + || DriveGeo.MediaType == RemovableMedia) + { + *pcbSize = DriveGeo.Cylinders.QuadPart + * DriveGeo.TracksPerCylinder + * DriveGeo.SectorsPerTrack + * DriveGeo.BytesPerSector; + + GET_LENGTH_INFORMATION DiskLenInfo; + DWORD Ignored; + if (DeviceIoControl((HANDLE)RTFileToNative(hFile), + IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, + &DiskLenInfo, sizeof(DiskLenInfo), &Ignored, (LPOVERLAPPED)NULL)) + { + /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */ + *pcbSize = DiskLenInfo.Length.QuadPart; + } + return VINF_SUCCESS; + } + } + + /* + * Return the GetFileSize result if not a volume/disk. + */ + return rc; +} + + +RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax) +{ + /** @todo r=bird: + * We might have to make this code OS version specific... In the worse + * case, we'll have to try GetVolumeInformationByHandle on vista and fall + * back on NtQueryVolumeInformationFile(,,,, FileFsAttributeInformation) + * else where, and check for known file system names. (For LAN shares we'll + * have to figure out the remote file system.) */ + RT_NOREF_PV(hFile); RT_NOREF_PV(pcbMax); + return VERR_NOT_IMPLEMENTED; +} + + +RTR3DECL(bool) RTFileIsValid(RTFILE hFile) +{ + if (hFile != NIL_RTFILE) + { + DWORD dwType = GetFileType((HANDLE)RTFileToNative(hFile)); + switch (dwType) + { + case FILE_TYPE_CHAR: + case FILE_TYPE_DISK: + case FILE_TYPE_PIPE: + case FILE_TYPE_REMOTE: + return true; + + case FILE_TYPE_UNKNOWN: + if (GetLastError() == NO_ERROR) + return true; + break; + + default: + break; + } + } + return false; +} + + +#define LOW_DWORD(u64) ((DWORD)u64) +#define HIGH_DWORD(u64) (((DWORD *)&u64)[1]) + +RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock) +{ + Assert(offLock >= 0); + + /* Check arguments. */ + if (fLock & ~RTFILE_LOCK_MASK) + { + AssertMsgFailed(("Invalid fLock=%08X\n", fLock)); + return VERR_INVALID_PARAMETER; + } + + /* Prepare flags. */ + Assert(RTFILE_LOCK_WRITE); + DWORD dwFlags = (fLock & RTFILE_LOCK_WRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0; + Assert(RTFILE_LOCK_WAIT); + if (!(fLock & RTFILE_LOCK_WAIT)) + dwFlags |= LOCKFILE_FAIL_IMMEDIATELY; + + /* Windows structure. */ + OVERLAPPED Overlapped; + memset(&Overlapped, 0, sizeof(Overlapped)); + Overlapped.Offset = LOW_DWORD(offLock); + Overlapped.OffsetHigh = HIGH_DWORD(offLock); + + /* Note: according to Microsoft, LockFileEx API call is available starting from NT 3.5 */ + if (LockFileEx((HANDLE)RTFileToNative(hFile), dwFlags, 0, LOW_DWORD(cbLock), HIGH_DWORD(cbLock), &Overlapped)) + return VINF_SUCCESS; + + return RTErrConvertFromWin32(GetLastError()); +} + + +RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock) +{ + Assert(offLock >= 0); + + /* Check arguments. */ + if (fLock & ~RTFILE_LOCK_MASK) + { + AssertMsgFailed(("Invalid fLock=%08X\n", fLock)); + return VERR_INVALID_PARAMETER; + } + + /* Remove old lock. */ + int rc = RTFileUnlock(hFile, offLock, cbLock); + if (RT_FAILURE(rc)) + return rc; + + /* Set new lock. */ + rc = RTFileLock(hFile, fLock, offLock, cbLock); + if (RT_SUCCESS(rc)) + return rc; + + /* Try to restore old lock. */ + unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE; + rc = RTFileLock(hFile, fLockOld, offLock, cbLock); + if (RT_SUCCESS(rc)) + return VERR_FILE_LOCK_VIOLATION; + else + return VERR_FILE_LOCK_LOST; +} + + +RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock) +{ + Assert(offLock >= 0); + + if (UnlockFile((HANDLE)RTFileToNative(hFile), + LOW_DWORD(offLock), HIGH_DWORD(offLock), + LOW_DWORD(cbLock), HIGH_DWORD(cbLock))) + return VINF_SUCCESS; + + return RTErrConvertFromWin32(GetLastError()); +} + + + +RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs) +{ + /* + * Validate input. + */ + if (hFile == NIL_RTFILE) + { + AssertMsgFailed(("Invalid hFile=%RTfile\n", hFile)); + return VERR_INVALID_PARAMETER; + } + if (!pObjInfo) + { + AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo)); + return VERR_INVALID_PARAMETER; + } + if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING + || enmAdditionalAttribs > RTFSOBJATTRADD_LAST) + { + AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs)); + return VERR_INVALID_PARAMETER; + } + + /* + * Query file info. + */ + HANDLE hHandle = (HANDLE)RTFileToNative(hFile); +#if 1 + uint64_t auBuf[168 / sizeof(uint64_t)]; /* Missing FILE_ALL_INFORMATION here. */ + int rc = rtPathNtQueryInfoFromHandle(hFile, auBuf, sizeof(auBuf), pObjInfo, enmAdditionalAttribs, NULL, 0); + if (RT_SUCCESS(rc)) + return rc; + + /* + * Console I/O handles make trouble here. On older windows versions they + * end up with ERROR_INVALID_HANDLE when handed to the above API, while on + * more recent ones they cause different errors to appear. + * + * Thus, we must ignore the latter and doubly verify invalid handle claims. + * We use the undocumented VerifyConsoleIoHandle to do this, falling back on + * GetFileType should it not be there. + */ + if ( rc == VERR_INVALID_HANDLE + || rc == VERR_ACCESS_DENIED + || rc == VERR_UNEXPECTED_FS_OBJ_TYPE) + { + static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL; + static bool volatile s_fInitialized = false; + PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle; + if (s_fInitialized) + pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle; + else + { + pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle"); + ASMAtomicWriteBool(&s_fInitialized, true); + } + if ( pfnVerifyConsoleIoHandle + ? !pfnVerifyConsoleIoHandle(hHandle) + : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR) + return VERR_INVALID_HANDLE; + } + /* + * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console + * I/O handles and null device handles. We must ignore these just like the + * above invalid handle error. + */ + else if (rc != VERR_INVALID_FUNCTION && rc != VERR_IO_BAD_COMMAND) + return rc; + + RT_ZERO(*pObjInfo); + pObjInfo->Attr.enmAdditional = enmAdditionalAttribs; + pObjInfo->Attr.fMode = rtFsModeFromDos(RTFS_DOS_NT_DEVICE, "", 0, 0, 0); + return VINF_SUCCESS; +#else + + BY_HANDLE_FILE_INFORMATION Data; + if (!GetFileInformationByHandle(hHandle, &Data)) + { + /* + * Console I/O handles make trouble here. On older windows versions they + * end up with ERROR_INVALID_HANDLE when handed to the above API, while on + * more recent ones they cause different errors to appear. + * + * Thus, we must ignore the latter and doubly verify invalid handle claims. + * We use the undocumented VerifyConsoleIoHandle to do this, falling back on + * GetFileType should it not be there. + */ + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_INVALID_HANDLE) + { + static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL; + static bool volatile s_fInitialized = false; + PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle; + if (s_fInitialized) + pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle; + else + { + pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle"); + ASMAtomicWriteBool(&s_fInitialized, true); + } + if ( pfnVerifyConsoleIoHandle + ? !pfnVerifyConsoleIoHandle(hHandle) + : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR) + return VERR_INVALID_HANDLE; + } + /* + * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console I/O + * handles. We must ignore these just like the above invalid handle error. + */ + else if (dwErr != ERROR_INVALID_FUNCTION) + return RTErrConvertFromWin32(dwErr); + + RT_ZERO(Data); + Data.dwFileAttributes = RTFS_DOS_NT_DEVICE; + } + + /* + * Setup the returned data. + */ + pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32) + | (uint64_t)Data.nFileSizeLow; + pObjInfo->cbAllocated = pObjInfo->cbObject; + + Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime)); + RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime); + RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime); + RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime); + pObjInfo->ChangeTime = pObjInfo->ModificationTime; + + pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, "", 0, + RTFSMODE_SYMLINK_REPARSE_TAG /* (symlink or not, doesn't usually matter here) */); + + /* + * Requested attributes (we cannot provide anything actually). + */ + switch (enmAdditionalAttribs) + { + case RTFSOBJATTRADD_NOTHING: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; + break; + + case RTFSOBJATTRADD_UNIX: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX; + pObjInfo->Attr.u.Unix.uid = ~0U; + pObjInfo->Attr.u.Unix.gid = ~0U; + pObjInfo->Attr.u.Unix.cHardlinks = Data.nNumberOfLinks ? Data.nNumberOfLinks : 1; + pObjInfo->Attr.u.Unix.INodeIdDevice = Data.dwVolumeSerialNumber; + pObjInfo->Attr.u.Unix.INodeId = RT_MAKE_U64(Data.nFileIndexLow, Data.nFileIndexHigh); + pObjInfo->Attr.u.Unix.fFlags = 0; + pObjInfo->Attr.u.Unix.GenerationId = 0; + pObjInfo->Attr.u.Unix.Device = 0; + break; + + case RTFSOBJATTRADD_UNIX_OWNER: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; + pObjInfo->Attr.u.UnixOwner.uid = ~0U; + pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ + break; + + case RTFSOBJATTRADD_UNIX_GROUP: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; + pObjInfo->Attr.u.UnixGroup.gid = ~0U; + pObjInfo->Attr.u.UnixGroup.szName[0] = '\0'; + break; + + case RTFSOBJATTRADD_EASIZE: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; + pObjInfo->Attr.u.EASize.cb = 0; + break; + + default: + AssertMsgFailed(("Impossible!\n")); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +#endif +} + + +RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + RT_NOREF_PV(pChangeTime); /* Not exposed thru the windows API we're using. */ + + if (!pAccessTime && !pModificationTime && !pBirthTime) + return VINF_SUCCESS; /* NOP */ + + FILETIME CreationTimeFT; + PFILETIME pCreationTimeFT = NULL; + if (pBirthTime) + pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT); + + FILETIME LastAccessTimeFT; + PFILETIME pLastAccessTimeFT = NULL; + if (pAccessTime) + pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT); + + FILETIME LastWriteTimeFT; + PFILETIME pLastWriteTimeFT = NULL; + if (pModificationTime) + pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT); + + int rc = VINF_SUCCESS; + if (!SetFileTime((HANDLE)RTFileToNative(hFile), pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT)) + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTFileSetTimes(%RTfile, %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n", + hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc)); + } + return rc; +} + + +#if 0 /* RTFileSetMode is implemented by RTFileSetMode-r3-nt.cpp */ +/* This comes from a source file with a different set of system headers (DDK) + * so it can't be declared in a common header, like internal/file.h. + */ +extern int rtFileNativeSetAttributes(HANDLE FileHandle, ULONG FileAttributes); + + +RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode) +{ + /* + * Normalize the mode and call the API. + */ + fMode = rtFsModeNormalize(fMode, NULL, 0); + if (!rtFsModeIsValid(fMode)) + return VERR_INVALID_PARAMETER; + + ULONG FileAttributes = (fMode & RTFS_DOS_MASK) >> RTFS_DOS_SHIFT; + int Err = rtFileNativeSetAttributes((HANDLE)hFile, FileAttributes); + if (Err != ERROR_SUCCESS) + { + int rc = RTErrConvertFromWin32(Err); + Log(("RTFileSetMode(%RTfile, %RTfmode): rtFileNativeSetAttributes (0x%08X) failed with err %d (%Rrc)\n", + hFile, fMode, FileAttributes, Err, rc)); + return rc; + } + return VINF_SUCCESS; +} +#endif + + +/* RTFileQueryFsSizes is implemented by ../nt/RTFileQueryFsSizes-nt.cpp */ + + +RTR3DECL(int) RTFileDelete(const char *pszFilename) +{ + PRTUTF16 pwszFilename; + int rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + if (!DeleteFileW(pwszFilename)) + rc = RTErrConvertFromWin32(GetLastError()); + RTPathWinFree(pwszFilename); + } + + return rc; +} + + +RTDECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszSrc, VERR_INVALID_POINTER); + AssertPtrReturn(pszDst, VERR_INVALID_POINTER); + AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER); + + /* + * Hand it on to the worker. + */ + int rc = rtPathWin32MoveRename(pszSrc, pszDst, + fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, + RTFS_TYPE_FILE); + + LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", + pszSrc, pszSrc, pszDst, pszDst, fRename, rc)); + return rc; + +} + + +RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszSrc, VERR_INVALID_POINTER); + AssertPtrReturn(pszDst, VERR_INVALID_POINTER); + AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER); + + /* + * Hand it on to the worker. + */ + int rc = rtPathWin32MoveRename(pszSrc, pszDst, + fMove & RTFILEMOVE_FLAGS_REPLACE + ? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING + : MOVEFILE_COPY_ALLOWED, + RTFS_TYPE_FILE); + + LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", + pszSrc, pszSrc, pszDst, pszDst, fMove, rc)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/fs-win.cpp b/src/VBox/Runtime/r3/win/fs-win.cpp new file mode 100644 index 00000000..4210ffed --- /dev/null +++ b/src/VBox/Runtime/r3/win/fs-win.cpp @@ -0,0 +1,440 @@ +/* $Id: fs-win.cpp $ */ +/** @file + * IPRT - File System, Win32. + */ + +/* + * 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/win/windows.h> + +#include <iprt/fs.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/param.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include "internal/fs.h" + +/* from ntdef.h */ +typedef LONG NTSTATUS; + +/* from ntddk.h */ +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FSINFOCLASS { + FileFsAttributeInformation = 5, +} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; + +/* from ntifs.h */ + +typedef struct _FILE_FS_ATTRIBUTE_INFORMATION { + ULONG FileSystemAttributes; + LONG MaximumComponentNameLength; + ULONG FileSystemNameLength; + WCHAR FileSystemName[1]; +} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION; + +extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS); + +/** + * Checks quickly if this is an correct root specification. + * Root specs ends with a slash of some kind. + * + * @returns indicator. + * @param pszFsPath Path to check. + */ +static bool rtFsIsRoot(const char *pszFsPath) +{ + /* + * UNC has exactly two slashes.. + * + * Anything else starting with slashe(s) requires + * expansion and will have to take the long road. + */ + if (RTPATH_IS_SLASH(pszFsPath[0])) + { + if ( !RTPATH_IS_SLASH(pszFsPath[1]) + || RTPATH_IS_SLASH(pszFsPath[2])) + return false; + + /* end of machine name */ + const char *pszSlash = strpbrk(pszFsPath + 2, "\\/"); + if (!pszSlash) + return false; + + /* end of service name. */ + pszSlash = strpbrk(pszSlash + 1, "\\/"); + if (!pszSlash) + return false; + + return pszSlash[1] == '\0'; + } + + /* + * Ok the other alternative is driver letter. + */ + return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z' + && pszFsPath[1] == ':' + && RTPATH_IS_SLASH(pszFsPath[2]) + && !pszFsPath[3]; +} + + + +/** + * Finds the root of the specified volume. + * + * @returns iprt status code. + * @param pszFsPath Path within the filesystem. Verified as one byte or more. + * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(), + */ +static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot) +{ + /* + * Do straight forward stuff first, + */ + if (rtFsIsRoot(pszFsPath)) + return RTStrToUtf16(pszFsPath, ppwszFsRoot); + + /* + * Expand and add slash (if required). + */ + char szFullPath[RTPATH_MAX]; + int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath)); + if (RT_FAILURE(rc)) + return rc; + size_t cb = strlen(szFullPath); + if (!RTPATH_IS_SLASH(szFullPath[cb - 1])) + { + AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG); + szFullPath[cb] = '\\'; + szFullPath[++cb] = '\0'; + } + + /* + * Convert the path. + */ + rc = RTStrToUtf16(szFullPath, ppwszFsRoot); + if (RT_FAILURE(rc)) + return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc; + + /* + * Walk the path until our proper API is happy or there is no more path left. + */ + PRTUTF16 pwszStart = *ppwszFsRoot; + if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0)) + { + PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart); + PRTUTF16 pwszMin = pwszStart + 2; + do + { + /* Strip off the last path component. */ + while (pwszEnd-- > pwszMin) + if (RTPATH_IS_SLASH(*pwszEnd)) + break; + AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */ + pwszEnd[1] = '\0'; + } while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0)); + } + + return VINF_SUCCESS; +} + +/** + * Frees string returned by rtFsGetRoot(). + */ +static void rtFsFreeRoot(PRTUTF16 pwszFsRoot) +{ + RTUtf16Free(pwszFsRoot); +} + + +RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree, + uint32_t *pcbBlock, uint32_t *pcbSector) +{ + /* + * Validate & get valid root path. + */ + AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); + AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER); + PRTUTF16 pwszFsRoot; + int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot); + if (RT_FAILURE(rc)) + return rc; + + /* + * Free and total. + */ + if (pcbTotal || pcbFree) + { + ULARGE_INTEGER cbTotal; + ULARGE_INTEGER cbFree; + if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL)) + { + if (pcbTotal) + *pcbTotal = cbTotal.QuadPart; + if (pcbFree) + *pcbFree = cbFree.QuadPart; + } + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n", + pszFsPath, Err, rc)); + } + } + + /* + * Block and sector size. + */ + if ( RT_SUCCESS(rc) + && (pcbBlock || pcbSector)) + { + DWORD dwDummy1, dwDummy2; + DWORD cbSector; + DWORD cSectorsPerCluster; + if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2)) + { + if (pcbBlock) + *pcbBlock = cbSector * cSectorsPerCluster; + if (pcbSector) + *pcbSector = cbSector; + } + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n", + pszFsPath, Err, rc)); + } + } + + rtFsFreeRoot(pwszFsRoot); + return rc; +} + + +/** + * Query the serial number of a filesystem. + * + * @returns iprt status code. + * @param pszFsPath Path within the mounted filesystem. + * @param pu32Serial Where to store the serial number. + */ +RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial) +{ + /* + * Validate & get valid root path. + */ + AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); + AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER); + AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER); + PRTUTF16 pwszFsRoot; + int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot); + if (RT_FAILURE(rc)) + return rc; + + /* + * Do work. + */ + DWORD dwMaxName; + DWORD dwFlags; + DWORD dwSerial; + if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0)) + *pu32Serial = dwSerial; + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n", + pszFsPath, Err, rc)); + } + + rtFsFreeRoot(pwszFsRoot); + return rc; +} + + +/** + * Query the properties of a mounted filesystem. + * + * @returns iprt status code. + * @param pszFsPath Path within the mounted filesystem. + * @param pProperties Where to store the properties. + */ +RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties) +{ + /* + * Validate & get valid root path. + */ + AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); + AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER); + AssertPtrReturn(pProperties, VERR_INVALID_POINTER); + PRTUTF16 pwszFsRoot; + int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot); + if (RT_FAILURE(rc)) + return rc; + + /* + * Do work. + */ + DWORD dwMaxName; + DWORD dwFlags; + DWORD dwSerial; + if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0)) + { + memset(pProperties, 0, sizeof(*pProperties)); + pProperties->cbMaxComponent = dwMaxName; + pProperties->fFileCompression = !!(dwFlags & FILE_FILE_COMPRESSION); + pProperties->fCompressed = !!(dwFlags & FILE_VOLUME_IS_COMPRESSED); + pProperties->fReadOnly = !!(dwFlags & FILE_READ_ONLY_VOLUME); + pProperties->fSupportsUnicode = !!(dwFlags & FILE_UNICODE_ON_DISK); + pProperties->fCaseSensitive = false; /* win32 is case preserving only */ + /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS + * as well perchance? If so, better mention it instead of just setting + * fCaseSensitive to false. */ + pProperties->fRemote = false; /* no idea yet */ + } + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n", + pszFsPath, Err, rc)); + } + + rtFsFreeRoot(pwszFsRoot); + return rc; +} + + +RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath) +{ + return false; +} + + +/** + * Internal helper for comparing a WCHAR string with a char string. + * + * @returns @c true if equal, @c false if not. + * @param pwsz1 The first string. + * @param cch1 The length of the first string, in bytes. + * @param psz2 The second string. + * @param cch2 The length of the second string. + */ +static bool rtFsWinAreEqual(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2) +{ + if (cch1 != cch2 * 2) + return false; + while (cch2-- > 0) + { + unsigned ch1 = *pwsz1++; + unsigned ch2 = (unsigned char)*psz2++; + if (ch1 != ch2) + return false; + } + return true; +} + + +RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType) +{ + *penmType = RTFSTYPE_UNKNOWN; + + AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); + AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER); + + /* + * Convert the path and try open it. + */ + PRTUTF16 pwszFsPath; + int rc = RTPathWinFromUtf8(&pwszFsPath, pszFsPath, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + HANDLE hFile = CreateFileW(pwszFsPath, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + /* + * Use the NT api directly to get the file system name. + */ + char abBuf[8192]; + IO_STATUS_BLOCK Ios; + NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, + abBuf, sizeof(abBuf), + FileFsAttributeInformation); + if (rcNt >= 0) + { + PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf; +#define IS_FS(szName) \ + rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FileSystemNameLength, szName, sizeof(szName) - 1) + if (IS_FS("NTFS")) + *penmType = RTFSTYPE_NTFS; + else if (IS_FS("FAT")) + *penmType = RTFSTYPE_FAT; + else if (IS_FS("FAT32")) + *penmType = RTFSTYPE_FAT; + else if (IS_FS("EXFAT")) + *penmType = RTFSTYPE_EXFAT; + else if (IS_FS("VBoxSharedFolderFS")) + *penmType = RTFSTYPE_VBOXSHF; +#undef IS_FS + } + else + rc = RTErrConvertFromNtStatus(rcNt); + CloseHandle(hFile); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + RTPathWinFree(pwszFsPath); + } + return rc; +} diff --git a/src/VBox/Runtime/r3/win/init-win.cpp b/src/VBox/Runtime/r3/win/init-win.cpp new file mode 100644 index 00000000..92cd2665 --- /dev/null +++ b/src/VBox/Runtime/r3/win/init-win.cpp @@ -0,0 +1,919 @@ +/* $Id: init-win.cpp $ */ +/** @file + * IPRT - Init Ring-3, Windows Specific Code. + */ + +/* + * 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_DEFAULT +#include <iprt/nt/nt-and-windows.h> +#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR +# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200 +# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800 +#endif + +#include "internal-r3-win.h" +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/param.h> +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include "../init.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef VOID (WINAPI *PFNGETCURRENTTHREADSTACKLIMITS)(PULONG_PTR puLow, PULONG_PTR puHigh); +typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI * PFNSETUNHANDLEDEXCEPTIONFILTER)(LPTOP_LEVEL_EXCEPTION_FILTER); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Windows DLL loader protection level. */ +DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt = RTR3WINLDRPROT_NONE; +/** Our simplified windows version. */ +DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer = kRTWinOSType_UNKNOWN; +/** Extended windows version information. */ +DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx; + +/** The native kernel32.dll handle. */ +DECL_HIDDEN_DATA(HMODULE) g_hModKernel32 = NULL; +/** GetSystemWindowsDirectoryW or GetWindowsDirectoryW (NT4). */ +DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW = NULL; +/** The GetCurrentThreadStackLimits API. */ +static PFNGETCURRENTTHREADSTACKLIMITS g_pfnGetCurrentThreadStackLimits = NULL; +/** The previous unhandled exception filter. */ +static LPTOP_LEVEL_EXCEPTION_FILTER g_pfnUnhandledXcptFilter = NULL; +/** SystemTimeToTzSpecificLocalTime. */ +DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime = NULL; +/** CreateWaitableTimerEx . */ +DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW = NULL; +DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation = NULL; +DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation = NULL; +DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent = NULL; +DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime = NULL; +DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask = NULL; +DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask = NULL; +DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort = NULL; +DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus = NULL; +DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus = NULL; +DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent = NULL; +DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter = NULL; +DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter = NULL; + +/** The native ntdll.dll handle. */ +DECL_HIDDEN_DATA(HMODULE) g_hModNtDll = NULL; +/** NtQueryFullAttributesFile */ +DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile = NULL; +/** NtDuplicateToken (NT 3.51). */ +DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken = NULL; +/** NtAlertThread (NT 3.51). */ +DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread = NULL; + +/** Either ws2_32.dll (NT4+) or wsock32.dll (NT3.x). */ +DECL_HIDDEN_DATA(HMODULE) g_hModWinSock = NULL; +/** Set if we're dealing with old winsock. */ +DECL_HIDDEN_DATA(bool) g_fOldWinSock = false; +/** WSAStartup */ +DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup = NULL; +/** WSACleanup */ +DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup = NULL; +/** Pointner to WSAGetLastError (for RTErrVarsSave). */ +DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError = NULL; +/** Pointner to WSASetLastError (for RTErrVarsRestore). */ +DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError = NULL; +/** WSACreateEvent */ +DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent = NULL; +/** WSACloseEvent */ +DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent = NULL; +/** WSASetEvent */ +DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent = NULL; +/** WSAEventSelect */ +DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect = NULL; +/** WSAEnumNetworkEvents */ +DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents = NULL; +/** WSASocketW */ +DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW = NULL; +/** WSASend */ +DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend = NULL; +/** socket */ +DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket = NULL; +/** closesocket */ +DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket = NULL; +/** recv */ +DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv = NULL; +/** send */ +DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend = NULL; +/** recvfrom */ +DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom = NULL; +/** sendto */ +DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto = NULL; +/** bind */ +DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind = NULL; +/** listen */ +DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten = NULL; +/** accept */ +DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept = NULL; +/** connect */ +DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect = NULL; +/** shutdown */ +DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown = NULL; +/** getsockopt */ +DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt = NULL; +/** setsockopt */ +DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt = NULL; +/** ioctlsocket */ +DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket = NULL; +/** getpeername */ +DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername = NULL; +/** getsockname */ +DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname = NULL; +/** __WSAFDIsSet */ +DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet = NULL; +/** select */ +DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect = NULL; +/** gethostbyname */ +DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname = NULL; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS); + + +/** + * Translates OSVERSIONINOFEX into a Windows OS type. + * + * @returns The Windows OS type. + * @param pOSInfoEx The OS info returned by Windows. + * + * @remarks This table has been assembled from Usenet postings, personal + * observations, and reading other people's code. Please feel + * free to add to it or correct it. + * <pre> + dwPlatFormID dwMajorVersion dwMinorVersion dwBuildNumber +95 1 4 0 950 +95 SP1 1 4 0 >950 && <=1080 +95 OSR2 1 4 <10 >1080 +98 1 4 10 1998 +98 SP1 1 4 10 >1998 && <2183 +98 SE 1 4 10 >=2183 +ME 1 4 90 3000 + +NT 3.51 2 3 51 1057 +NT 4 2 4 0 1381 +2000 2 5 0 2195 +XP 2 5 1 2600 +2003 2 5 2 3790 +Vista 2 6 0 + +CE 1.0 3 1 0 +CE 2.0 3 2 0 +CE 2.1 3 2 1 +CE 3.0 3 3 0 +</pre> + */ +static RTWINOSTYPE rtR3InitWinSimplifiedVersion(OSVERSIONINFOEXW const *pOSInfoEx) +{ + RTWINOSTYPE enmVer = kRTWinOSType_UNKNOWN; + BYTE const bProductType = pOSInfoEx->wProductType; + DWORD const dwPlatformId = pOSInfoEx->dwPlatformId; + DWORD const dwMinorVersion = pOSInfoEx->dwMinorVersion; + DWORD const dwMajorVersion = pOSInfoEx->dwMajorVersion; + DWORD const dwBuildNumber = pOSInfoEx->dwBuildNumber & 0xFFFF; /* Win 9x needs this. */ + + if ( dwPlatformId == VER_PLATFORM_WIN32_WINDOWS + && dwMajorVersion == 4) + { + if ( dwMinorVersion < 10 + && dwBuildNumber == 950) + enmVer = kRTWinOSType_95; + else if ( dwMinorVersion < 10 + && dwBuildNumber > 950 + && dwBuildNumber <= 1080) + enmVer = kRTWinOSType_95SP1; + else if ( dwMinorVersion < 10 + && dwBuildNumber > 1080) + enmVer = kRTWinOSType_95OSR2; + else if ( dwMinorVersion == 10 + && dwBuildNumber == 1998) + enmVer = kRTWinOSType_98; + else if ( dwMinorVersion == 10 + && dwBuildNumber > 1998 + && dwBuildNumber < 2183) + enmVer = kRTWinOSType_98SP1; + else if ( dwMinorVersion == 10 + && dwBuildNumber >= 2183) + enmVer = kRTWinOSType_98SE; + else if (dwMinorVersion == 90) + enmVer = kRTWinOSType_ME; + } + else if (dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if (dwMajorVersion == 3) + { + if ( dwMinorVersion < 50) + enmVer = kRTWinOSType_NT310; + else if (dwMinorVersion == 50) + enmVer = kRTWinOSType_NT350; + else + enmVer = kRTWinOSType_NT351; + } + else if (dwMajorVersion == 4) + enmVer = kRTWinOSType_NT4; + else if (dwMajorVersion == 5) + { + if (dwMinorVersion == 0) + enmVer = kRTWinOSType_2K; + else if (dwMinorVersion == 1) + enmVer = kRTWinOSType_XP; + else + enmVer = kRTWinOSType_2003; + } + else if (dwMajorVersion == 6) + { + if (dwMinorVersion == 0) + enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008 : kRTWinOSType_VISTA; + else if (dwMinorVersion == 1) + enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008R2 : kRTWinOSType_7; + else if (dwMinorVersion == 2) + enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012 : kRTWinOSType_8; + else if (dwMinorVersion == 3) + enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012R2 : kRTWinOSType_81; + else if (dwMinorVersion == 4) + enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2016 : kRTWinOSType_10; + else + enmVer = kRTWinOSType_NT_UNKNOWN; + } + else if (dwMajorVersion == 10) + { + if (dwMinorVersion == 0) + { + /* The version detection for server 2019, server 2022 and windows 11 + are by build number. Stupid, stupid, Microsoft. */ + if (bProductType == VER_NT_WORKSTATION) + enmVer = dwBuildNumber >= 22000 ? kRTWinOSType_11 : kRTWinOSType_10; + else + enmVer = dwBuildNumber >= 20348 ? kRTWinOSType_2022 + : dwBuildNumber >= 17763 ? kRTWinOSType_2019 : kRTWinOSType_2016; + } + else + enmVer = kRTWinOSType_NT_UNKNOWN; + } + else + enmVer = kRTWinOSType_NT_UNKNOWN; + } + + return enmVer; +} + + +/** + * Initializes the global variables related to windows version. + */ +static void rtR3InitWindowsVersion(void) +{ + Assert(g_hModNtDll != NULL); + + /* + * ASSUMES OSVERSIONINFOEX starts with the exact same layout as OSVERSIONINFO (safe). + */ + AssertCompileMembersSameSizeAndOffset(OSVERSIONINFOEX, szCSDVersion, OSVERSIONINFO, szCSDVersion); + AssertCompileMemberOffset(OSVERSIONINFOEX, wServicePackMajor, sizeof(OSVERSIONINFO)); + + /* + * Use the NT version of RtlGetVersion (since w2k) so we don't get fooled + * by the standard compatibility shims. (Sandboxes may still fool us.) + * + * Note! This API was added in windows 2000 together with the extended + * version info structure (OSVERSIONINFOEXW), so there is no need + * to retry with the smaller version (OSVERSIONINFOW). + */ + RT_ZERO(g_WinOsInfoEx); + g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + LONG (__stdcall *pfnRtlGetVersion)(OSVERSIONINFOEXW *); + *(FARPROC *)&pfnRtlGetVersion = GetProcAddress(g_hModNtDll, "RtlGetVersion"); + LONG rcNt = -1; + if (pfnRtlGetVersion) + rcNt = pfnRtlGetVersion(&g_WinOsInfoEx); + if (rcNt != 0) + { + /* + * Couldn't find it or it failed, try the windows version of the API. + * The GetVersionExW API was added in NT 3.51, however only the small + * structure version existed till windows 2000. We'll try the larger + * structure version first, anyway, just in case. + */ + RT_ZERO(g_WinOsInfoEx); + g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + BOOL (__stdcall *pfnGetVersionExW)(OSVERSIONINFOW *); + *(FARPROC *)&pfnGetVersionExW = GetProcAddress(g_hModKernel32, "GetVersionExW"); + + if (!pfnGetVersionExW || !pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx)) + { + /* + * If that didn't work either, just get the basic version bits. + */ + RT_ZERO(g_WinOsInfoEx); + g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + if (pfnGetVersionExW && pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx)) + Assert(g_WinOsInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT || g_WinOsInfoEx.dwMajorVersion < 5); + else + { + /* + * Okay, nothing worked, so use GetVersion. + * + * This should only happen if we're on NT 3.1 or NT 3.50. + * It should never happen for 64-bit builds. + */ +#ifdef RT_ARCH_X86 + RT_ZERO(g_WinOsInfoEx); + DWORD const dwVersion = GetVersion(); + + /* Common fields: */ + g_WinOsInfoEx.dwMajorVersion = dwVersion & 0xff; + g_WinOsInfoEx.dwMinorVersion = (dwVersion >> 8) & 0xff; + if (!(dwVersion & RT_BIT_32(31))) + g_WinOsInfoEx.dwBuildNumber = dwVersion >> 16; + else + g_WinOsInfoEx.dwBuildNumber = 511; + g_WinOsInfoEx.dwPlatformId = VER_PLATFORM_WIN32_NT; + /** @todo get CSD from registry. */ +#else + AssertBreakpoint(); + RT_ZERO(g_WinOsInfoEx); +#endif + } + +#ifdef RT_ARCH_X86 + /* + * Fill in some of the extended info too. + */ + g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); /* Pretend. */ + g_WinOsInfoEx.wProductType = VER_NT_WORKSTATION; + NT_PRODUCT_TYPE enmProdType = NtProductWinNt; + if (RtlGetNtProductType(&enmProdType)) + g_WinOsInfoEx.wProductType = (BYTE)enmProdType; + /** @todo parse the CSD string to figure that version. */ +#endif + } + } + + if (g_WinOsInfoEx.dwOSVersionInfoSize) + g_enmWinVer = rtR3InitWinSimplifiedVersion(&g_WinOsInfoEx); +} + + +/** + * Resolves the winsock error APIs. + */ +static void rtR3InitWinSockApis(void) +{ + /* + * Try get ws2_32.dll, then try load it, then finally fall back to the old + * wsock32.dll. We use RTLdrLoadSystem to the loading as it has all the fancy + * logic for safely doing that. + */ + g_hModWinSock = GetModuleHandleW(L"ws2_32.dll"); + if (g_hModWinSock == NULL) + { + RTLDRMOD hLdrMod; + int rc = RTLdrLoadSystem("ws2_32.dll", true /*fNoUnload*/, &hLdrMod); + if (RT_FAILURE(rc)) + { + rc = RTLdrLoadSystem("wsock32.dll", true /*fNoUnload*/, &hLdrMod); + if (RT_FAILURE(rc)) + { + AssertMsgFailed(("rc=%Rrc\n", rc)); + return; + } + g_fOldWinSock = true; + } + g_hModWinSock = (HMODULE)RTLdrGetNativeHandle(hLdrMod); + RTLdrClose(hLdrMod); + } + + g_pfnWSAStartup = (decltype(g_pfnWSAStartup)) GetProcAddress(g_hModWinSock, "WSAStartup"); + g_pfnWSACleanup = (decltype(g_pfnWSACleanup)) GetProcAddress(g_hModWinSock, "WSACleanup"); + g_pfnWSAGetLastError = (decltype(g_pfnWSAGetLastError)) GetProcAddress(g_hModWinSock, "WSAGetLastError"); + g_pfnWSASetLastError = (decltype(g_pfnWSASetLastError)) GetProcAddress(g_hModWinSock, "WSASetLastError"); + g_pfnWSACreateEvent = (decltype(g_pfnWSACreateEvent)) GetProcAddress(g_hModWinSock, "WSACreateEvent"); + g_pfnWSACloseEvent = (decltype(g_pfnWSACloseEvent)) GetProcAddress(g_hModWinSock, "WSACloseEvent"); + g_pfnWSASetEvent = (decltype(g_pfnWSASetEvent)) GetProcAddress(g_hModWinSock, "WSASetEvent"); + g_pfnWSAEventSelect = (decltype(g_pfnWSAEventSelect)) GetProcAddress(g_hModWinSock, "WSAEventSelect"); + g_pfnWSAEnumNetworkEvents = (decltype(g_pfnWSAEnumNetworkEvents))GetProcAddress(g_hModWinSock,"WSAEnumNetworkEvents"); + g_pfnWSASocketW = (decltype(g_pfnWSASocketW)) GetProcAddress(g_hModWinSock, "WSASocketW"); + g_pfnWSASend = (decltype(g_pfnWSASend)) GetProcAddress(g_hModWinSock, "WSASend"); + g_pfnsocket = (decltype(g_pfnsocket)) GetProcAddress(g_hModWinSock, "socket"); + g_pfnclosesocket = (decltype(g_pfnclosesocket)) GetProcAddress(g_hModWinSock, "closesocket"); + g_pfnrecv = (decltype(g_pfnrecv)) GetProcAddress(g_hModWinSock, "recv"); + g_pfnsend = (decltype(g_pfnsend)) GetProcAddress(g_hModWinSock, "send"); + g_pfnrecvfrom = (decltype(g_pfnrecvfrom)) GetProcAddress(g_hModWinSock, "recvfrom"); + g_pfnsendto = (decltype(g_pfnsendto)) GetProcAddress(g_hModWinSock, "sendto"); + g_pfnbind = (decltype(g_pfnbind)) GetProcAddress(g_hModWinSock, "bind"); + g_pfnlisten = (decltype(g_pfnlisten)) GetProcAddress(g_hModWinSock, "listen"); + g_pfnaccept = (decltype(g_pfnaccept)) GetProcAddress(g_hModWinSock, "accept"); + g_pfnconnect = (decltype(g_pfnconnect)) GetProcAddress(g_hModWinSock, "connect"); + g_pfnshutdown = (decltype(g_pfnshutdown)) GetProcAddress(g_hModWinSock, "shutdown"); + g_pfngetsockopt = (decltype(g_pfngetsockopt)) GetProcAddress(g_hModWinSock, "getsockopt"); + g_pfnsetsockopt = (decltype(g_pfnsetsockopt)) GetProcAddress(g_hModWinSock, "setsockopt"); + g_pfnioctlsocket = (decltype(g_pfnioctlsocket)) GetProcAddress(g_hModWinSock, "ioctlsocket"); + g_pfngetpeername = (decltype(g_pfngetpeername)) GetProcAddress(g_hModWinSock, "getpeername"); + g_pfngetsockname = (decltype(g_pfngetsockname)) GetProcAddress(g_hModWinSock, "getsockname"); + g_pfn__WSAFDIsSet = (decltype(g_pfn__WSAFDIsSet)) GetProcAddress(g_hModWinSock, "__WSAFDIsSet"); + g_pfnselect = (decltype(g_pfnselect)) GetProcAddress(g_hModWinSock, "select"); + g_pfngethostbyname = (decltype(g_pfngethostbyname)) GetProcAddress(g_hModWinSock, "gethostbyname"); + + Assert(g_pfnWSAStartup); + Assert(g_pfnWSACleanup); + Assert(g_pfnWSAGetLastError); + Assert(g_pfnWSASetLastError); + Assert(g_pfnWSACreateEvent || g_fOldWinSock); + Assert(g_pfnWSACloseEvent || g_fOldWinSock); + Assert(g_pfnWSASetEvent || g_fOldWinSock); + Assert(g_pfnWSAEventSelect || g_fOldWinSock); + Assert(g_pfnWSAEnumNetworkEvents || g_fOldWinSock); + Assert(g_pfnWSASocketW || g_fOldWinSock); + Assert(g_pfnWSASend || g_fOldWinSock); + Assert(g_pfnsocket); + Assert(g_pfnclosesocket); + Assert(g_pfnrecv); + Assert(g_pfnsend); + Assert(g_pfnrecvfrom); + Assert(g_pfnsendto); + Assert(g_pfnbind); + Assert(g_pfnlisten); + Assert(g_pfnaccept); + Assert(g_pfnconnect); + Assert(g_pfnshutdown); + Assert(g_pfngetsockopt); + Assert(g_pfnsetsockopt); + Assert(g_pfnioctlsocket); + Assert(g_pfngetpeername); + Assert(g_pfngetsockname); + Assert(g_pfn__WSAFDIsSet); + Assert(g_pfnselect); + Assert(g_pfngethostbyname); +} + + +static int rtR3InitNativeObtrusiveWorker(uint32_t fFlags) +{ + /* + * Disable error popups. + */ + UINT fOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | fOldErrMode); + + /* + * Restrict DLL searching for the process on windows versions which allow + * us to do so. + * - The first trick works on XP SP1+ and disables the searching of the + * current directory. + * - The second trick is W7 w/ KB2533623 and W8+, it restrict the DLL + * searching to the application directory (except when + * RTR3INIT_FLAGS_STANDALONE_APP is given) and the System32 directory. + */ + int rc = VINF_SUCCESS; + + typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR); + PFNSETDLLDIRECTORY pfnSetDllDir = (PFNSETDLLDIRECTORY)GetProcAddress(g_hModKernel32, "SetDllDirectoryW"); + if (pfnSetDllDir) + { + if (pfnSetDllDir(L"")) + g_enmWinLdrProt = RTR3WINLDRPROT_NO_CWD; + else + rc = VERR_INTERNAL_ERROR_3; + } + + /** @bugref{6861} Observed GUI issues on Vista (32-bit and 64-bit) when using + * SetDefaultDllDirectories. + * @bugref{8194} Try use SetDefaultDllDirectories on Vista for standalone apps + * despite potential GUI issues. */ + if ( g_enmWinVer > kRTWinOSType_VISTA + || (fFlags & RTR3INIT_FLAGS_STANDALONE_APP)) + { + typedef BOOL(WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD); + PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs; + pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(g_hModKernel32, "SetDefaultDllDirectories"); + if (pfnSetDefDllDirs) + { + DWORD fDllDirs = LOAD_LIBRARY_SEARCH_SYSTEM32; + if (!(fFlags & RTR3INIT_FLAGS_STANDALONE_APP)) + fDllDirs |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR; + if (pfnSetDefDllDirs(fDllDirs)) + g_enmWinLdrProt = fDllDirs & LOAD_LIBRARY_SEARCH_APPLICATION_DIR ? RTR3WINLDRPROT_SAFE : RTR3WINLDRPROT_SAFER; + else if (RT_SUCCESS(rc)) + rc = VERR_INTERNAL_ERROR_4; + } + } + + /* + * Register an unhandled exception callback if we can. + */ + g_pfnGetCurrentThreadStackLimits = (PFNGETCURRENTTHREADSTACKLIMITS)GetProcAddress(g_hModKernel32, "GetCurrentThreadStackLimits"); + g_pfnSetUnhandledExceptionFilter = (decltype(SetUnhandledExceptionFilter) *)GetProcAddress(g_hModKernel32, "SetUnhandledExceptionFilter"); + g_pfnUnhandledExceptionFilter = (decltype(UnhandledExceptionFilter) *) GetProcAddress(g_hModKernel32, "UnhandledExceptionFilter"); + if (g_pfnSetUnhandledExceptionFilter && !g_pfnUnhandledXcptFilter) + { + g_pfnUnhandledXcptFilter = g_pfnSetUnhandledExceptionFilter(rtR3WinUnhandledXcptFilter); + AssertStmt(g_pfnUnhandledXcptFilter != rtR3WinUnhandledXcptFilter, g_pfnUnhandledXcptFilter = NULL); + } + + return rc; +} + + +DECLHIDDEN(int) rtR3InitNativeFirst(uint32_t fFlags) +{ + /* + * Make sure we've got the handles of the two main Windows NT dlls. + */ + g_hModKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (g_hModKernel32 == NULL) + return VERR_INTERNAL_ERROR_2; + g_hModNtDll = GetModuleHandleW(L"ntdll.dll"); + if (g_hModNtDll == NULL) + return VERR_INTERNAL_ERROR_2; + + rtR3InitWindowsVersion(); + + int rc = VINF_SUCCESS; + if (!(fFlags & RTR3INIT_FLAGS_UNOBTRUSIVE)) + rc = rtR3InitNativeObtrusiveWorker(fFlags); + + /* + * Resolve some kernel32.dll APIs we may need but aren't necessarily + * present in older windows versions. + */ + g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetSystemWindowsDirectoryW"); + if (g_pfnGetSystemWindowsDirectoryW) + g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetWindowsDirectoryW"); + g_pfnSystemTimeToTzSpecificLocalTime = (decltype(SystemTimeToTzSpecificLocalTime) *)GetProcAddress(g_hModKernel32, "SystemTimeToTzSpecificLocalTime"); + g_pfnCreateWaitableTimerExW = (PFNCREATEWAITABLETIMEREX) GetProcAddress(g_hModKernel32, "CreateWaitableTimerExW"); + g_pfnGetHandleInformation = (decltype(GetHandleInformation) *) GetProcAddress(g_hModKernel32, "GetHandleInformation"); + g_pfnSetHandleInformation = (decltype(SetHandleInformation) *) GetProcAddress(g_hModKernel32, "SetHandleInformation"); + g_pfnIsDebuggerPresent = (decltype(IsDebuggerPresent) *) GetProcAddress(g_hModKernel32, "IsDebuggerPresent"); + g_pfnGetSystemTimeAsFileTime = (decltype(GetSystemTimeAsFileTime) *) GetProcAddress(g_hModKernel32, "GetSystemTimeAsFileTime"); + g_pfnGetProcessAffinityMask = (decltype(GetProcessAffinityMask) *) GetProcAddress(g_hModKernel32, "GetProcessAffinityMask"); + g_pfnSetThreadAffinityMask = (decltype(SetThreadAffinityMask) *) GetProcAddress(g_hModKernel32, "SetThreadAffinityMask"); + g_pfnCreateIoCompletionPort = (decltype(CreateIoCompletionPort) *) GetProcAddress(g_hModKernel32, "CreateIoCompletionPort"); + g_pfnGetQueuedCompletionStatus = (decltype(GetQueuedCompletionStatus) *) GetProcAddress(g_hModKernel32, "GetQueuedCompletionStatus"); + g_pfnPostQueuedCompletionStatus = (decltype(PostQueuedCompletionStatus) *)GetProcAddress(g_hModKernel32, "PostQueuedCompletionStatus"); + g_pfnIsProcessorFeaturePresent = (decltype(IsProcessorFeaturePresent) *) GetProcAddress(g_hModKernel32, "IsProcessorFeaturePresent"); + + Assert(g_pfnGetHandleInformation || g_enmWinVer < kRTWinOSType_NT351); + Assert(g_pfnSetHandleInformation || g_enmWinVer < kRTWinOSType_NT351); + Assert(g_pfnIsDebuggerPresent || g_enmWinVer < kRTWinOSType_NT4); + Assert(g_pfnGetSystemTimeAsFileTime || g_enmWinVer < kRTWinOSType_NT4); + Assert(g_pfnGetProcessAffinityMask || g_enmWinVer < kRTWinOSType_NT350); + Assert(g_pfnSetThreadAffinityMask || g_enmWinVer < kRTWinOSType_NT350); + Assert(g_pfnCreateIoCompletionPort || g_enmWinVer < kRTWinOSType_NT350); + Assert(g_pfnGetQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350); + Assert(g_pfnPostQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350); + Assert(g_pfnIsProcessorFeaturePresent || g_enmWinVer < kRTWinOSType_NT4); + + /* + * Resolve some ntdll.dll APIs that weren't there in early NT versions. + */ + g_pfnNtQueryFullAttributesFile = (PFNNTQUERYFULLATTRIBUTESFILE)GetProcAddress(g_hModNtDll, "NtQueryFullAttributesFile"); + g_pfnNtDuplicateToken = (PFNNTDUPLICATETOKEN)GetProcAddress( g_hModNtDll, "NtDuplicateToken"); + g_pfnNtAlertThread = (decltype(NtAlertThread) *)GetProcAddress( g_hModNtDll, "NtAlertThread"); + + /* + * Resolve the winsock error getter and setter so assertions can save those too. + */ + rtR3InitWinSockApis(); + + return rc; +} + + +DECLHIDDEN(void) rtR3InitNativeObtrusive(uint32_t fFlags) +{ + rtR3InitNativeObtrusiveWorker(fFlags); +} + + +DECLHIDDEN(int) rtR3InitNativeFinal(uint32_t fFlags) +{ + /* Nothing to do here. */ + RT_NOREF_PV(fFlags); + return VINF_SUCCESS; +} + + +/** + * Unhandled exception filter callback. + * + * Will try log stuff. + */ +static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS pPtrs) +{ + /* + * Try get the logger and log exception details. + * + * Note! We'll be using RTLogLoggerWeak for now, though we should probably add + * a less deadlock prone API here and gives up pretty fast if it + * cannot get the lock... + */ + PRTLOGGER pLogger = RTLogRelGetDefaultInstanceWeak(); + if (!pLogger) + pLogger = RTLogGetDefaultInstanceWeak(); + if (pLogger) + { + RTLogLoggerWeak(pLogger, NULL, "\n!!! rtR3WinUnhandledXcptFilter caught an exception on thread %p in %u !!!\n", + RTThreadNativeSelf(), RTProcSelf()); + + /* + * Dump the exception record. + */ + uintptr_t uXcptPC = 0; + PEXCEPTION_RECORD pXcptRec = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ExceptionRecord) ? pPtrs->ExceptionRecord : NULL; + if (pXcptRec) + { + RTLogLoggerWeak(pLogger, NULL, "\nExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p\n", + pXcptRec->ExceptionCode, pXcptRec->ExceptionFlags, pXcptRec->ExceptionAddress); + for (uint32_t i = 0; i < RT_MIN(pXcptRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++) + RTLogLoggerWeak(pLogger, NULL, "ExceptionInformation[%d]=%p\n", i, pXcptRec->ExceptionInformation[i]); + uXcptPC = (uintptr_t)pXcptRec->ExceptionAddress; + + /* Nested? Display one level only. */ + PEXCEPTION_RECORD pNestedRec = pXcptRec->ExceptionRecord; + if (RT_VALID_PTR(pNestedRec)) + { + RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p (nested %p)\n", + pNestedRec->ExceptionCode, pNestedRec->ExceptionFlags, pNestedRec->ExceptionAddress, + pNestedRec->ExceptionRecord); + for (uint32_t i = 0; i < RT_MIN(pNestedRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++) + RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionInformation[%d]=%p\n", i, pNestedRec->ExceptionInformation[i]); + uXcptPC = (uintptr_t)pNestedRec->ExceptionAddress; + } + } + + /* + * Dump the context record. + */ + volatile char szMarker[] = "stackmarker"; + uintptr_t uXcptSP = (uintptr_t)&szMarker[0]; + PCONTEXT pXcptCtx = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ContextRecord) ? pPtrs->ContextRecord : NULL; + if (pXcptCtx) + { +#ifdef RT_ARCH_AMD64 + RTLogLoggerWeak(pLogger, NULL, "\ncs:rip=%04x:%016RX64\n", pXcptCtx->SegCs, pXcptCtx->Rip); + RTLogLoggerWeak(pLogger, NULL, "ss:rsp=%04x:%016RX64 rbp=%016RX64\n", pXcptCtx->SegSs, pXcptCtx->Rsp, pXcptCtx->Rbp); + RTLogLoggerWeak(pLogger, NULL, "rax=%016RX64 rcx=%016RX64 rdx=%016RX64 rbx=%016RX64\n", + pXcptCtx->Rax, pXcptCtx->Rcx, pXcptCtx->Rdx, pXcptCtx->Rbx); + RTLogLoggerWeak(pLogger, NULL, "rsi=%016RX64 rdi=%016RX64 rsp=%016RX64 rbp=%016RX64\n", + pXcptCtx->Rsi, pXcptCtx->Rdi, pXcptCtx->Rsp, pXcptCtx->Rbp); + RTLogLoggerWeak(pLogger, NULL, "r8 =%016RX64 r9 =%016RX64 r10=%016RX64 r11=%016RX64\n", + pXcptCtx->R8, pXcptCtx->R9, pXcptCtx->R10, pXcptCtx->R11); + RTLogLoggerWeak(pLogger, NULL, "r12=%016RX64 r13=%016RX64 r14=%016RX64 r15=%016RX64\n", + pXcptCtx->R12, pXcptCtx->R13, pXcptCtx->R14, pXcptCtx->R15); + RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n", + pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags); + RTLogLoggerWeak(pLogger, NULL, "p1home=%016RX64 p2home=%016RX64 pe3home=%016RX64\n", + pXcptCtx->P1Home, pXcptCtx->P2Home, pXcptCtx->P3Home); + RTLogLoggerWeak(pLogger, NULL, "p4home=%016RX64 p5home=%016RX64 pe6home=%016RX64\n", + pXcptCtx->P4Home, pXcptCtx->P5Home, pXcptCtx->P6Home); + RTLogLoggerWeak(pLogger, NULL, " LastBranchToRip=%016RX64 LastBranchFromRip=%016RX64\n", + pXcptCtx->LastBranchToRip, pXcptCtx->LastBranchFromRip); + RTLogLoggerWeak(pLogger, NULL, "LastExceptionToRip=%016RX64 LastExceptionFromRip=%016RX64\n", + pXcptCtx->LastExceptionToRip, pXcptCtx->LastExceptionFromRip); + uXcptSP = pXcptCtx->Rsp; + uXcptPC = pXcptCtx->Rip; + +#elif defined(RT_ARCH_X86) + RTLogLoggerWeak(pLogger, NULL, "\ncs:eip=%04x:%08RX32\n", pXcptCtx->SegCs, pXcptCtx->Eip); + RTLogLoggerWeak(pLogger, NULL, "ss:esp=%04x:%08RX32 ebp=%08RX32\n", pXcptCtx->SegSs, pXcptCtx->Esp, pXcptCtx->Ebp); + RTLogLoggerWeak(pLogger, NULL, "eax=%08RX32 ecx=%08RX32 edx=%08RX32 ebx=%08RX32\n", + pXcptCtx->Eax, pXcptCtx->Ecx, pXcptCtx->Edx, pXcptCtx->Ebx); + RTLogLoggerWeak(pLogger, NULL, "esi=%08RX32 edi=%08RX32 esp=%08RX32 ebp=%08RX32\n", + pXcptCtx->Esi, pXcptCtx->Edi, pXcptCtx->Esp, pXcptCtx->Ebp); + RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n", + pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags); + uXcptSP = pXcptCtx->Esp; + uXcptPC = pXcptCtx->Eip; +#endif + } + + /* + * Dump stack. + */ + uintptr_t uStack = (uintptr_t)(void *)&szMarker[0]; + uStack -= uStack & 15; + + size_t cbToDump = PAGE_SIZE - (uStack & PAGE_OFFSET_MASK); + if (cbToDump < 512) + cbToDump += PAGE_SIZE; + size_t cbToXcpt = uXcptSP - uStack; + while (cbToXcpt > cbToDump && cbToXcpt <= _16K) + cbToDump += PAGE_SIZE; + ULONG_PTR uLow = (uintptr_t)&szMarker[0]; + ULONG_PTR uHigh = (uintptr_t)&szMarker[0]; + if (g_pfnGetCurrentThreadStackLimits) + { + g_pfnGetCurrentThreadStackLimits(&uLow, &uHigh); + size_t cbToTop = RT_MAX(uLow, uHigh) - uStack; + if (cbToTop < _1M) + cbToDump = cbToTop; + } + + RTLogLoggerWeak(pLogger, NULL, "\nStack %p, dumping %#x bytes (low=%p, high=%p)\n", uStack, cbToDump, uLow, uHigh); + RTLogLoggerWeak(pLogger, NULL, "%.*RhxD\n", cbToDump, uStack); + + /* + * Try figure the thread name. + * + * Note! This involves the thread db lock, so it may deadlock, which + * is why it's at the end. + */ + RTLogLoggerWeak(pLogger, NULL, "Thread ID: %p\n", RTThreadNativeSelf()); + RTLogLoggerWeak(pLogger, NULL, "Thread name: %s\n", RTThreadSelfName()); + RTLogLoggerWeak(pLogger, NULL, "Thread IPRT: %p\n", RTThreadSelf()); + + /* + * Try dump the load information. + */ + PPEB pPeb = RTNtCurrentPeb(); + if (RT_VALID_PTR(pPeb)) + { + PPEB_LDR_DATA pLdrData = pPeb->Ldr; + if (RT_VALID_PTR(pLdrData)) + { + PLDR_DATA_TABLE_ENTRY pFound = NULL; + LIST_ENTRY * const pList = &pLdrData->InMemoryOrderModuleList; + LIST_ENTRY *pListEntry = pList->Flink; + uint32_t cLoops = 0; + RTLogLoggerWeak(pLogger, NULL, + "\nLoaded Modules:\n" + "%-*s[*] Timestamp Path\n", sizeof(void *) * 4 + 2 - 1, "Address range" + ); + while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024) + { + PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1]; + char chInd = ' '; + if (uXcptPC - (uintptr_t)pLdrEntry->DllBase < cbLength) + { + chInd = '*'; + pFound = pLdrEntry; + } + + if ( RT_VALID_PTR(pLdrEntry->FullDllName.Buffer) + && pLdrEntry->FullDllName.Length > 0 + && pLdrEntry->FullDllName.Length < _8K + && (pLdrEntry->FullDllName.Length & 1) == 0 + && pLdrEntry->FullDllName.Length <= pLdrEntry->FullDllName.MaximumLength) + RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 %.*ls\n", + pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd, + pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Length / sizeof(RTUTF16), + pLdrEntry->FullDllName.Buffer); + else + RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 <bad or missing: %p LB %#x max %#x\n", + pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd, + pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Buffer, pLdrEntry->FullDllName.Length, + pLdrEntry->FullDllName.MaximumLength); + + /* advance */ + pListEntry = pListEntry->Flink; + cLoops++; + } + + /* + * Use the above to pick out code addresses on the stack. + */ + if ( cLoops < 1024 + && uXcptSP - uStack < cbToDump) + { + RTLogLoggerWeak(pLogger, NULL, "\nPotential code addresses on the stack:\n"); + if (pFound) + { + if ( RT_VALID_PTR(pFound->FullDllName.Buffer) + && pFound->FullDllName.Length > 0 + && pFound->FullDllName.Length < _8K + && (pFound->FullDllName.Length & 1) == 0 + && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength) + RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %#010RX32 bytes into %.*ls\n", + sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase), + pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer); + else + RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %08RX32 into module at %p\n", + sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase), + pFound->DllBase); + } + + uintptr_t const *puStack = (uintptr_t const *)uXcptSP; + uintptr_t cLeft = (cbToDump - (uXcptSP - uStack)) / sizeof(uintptr_t); + while (cLeft-- > 0) + { + uintptr_t uPtr = *puStack; + if (RT_VALID_PTR(uPtr)) + { + /* Search the module table. */ + pFound = NULL; + cLoops = 0; + pListEntry = pList->Flink; + while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024) + { + PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1]; + if (uPtr - (uintptr_t)pLdrEntry->DllBase < cbLength) + { + pFound = pLdrEntry; + break; + } + + /* advance */ + pListEntry = pListEntry->Flink; + cLoops++; + } + + if (pFound) + { + if ( RT_VALID_PTR(pFound->FullDllName.Buffer) + && pFound->FullDllName.Length > 0 + && pFound->FullDllName.Length < _8K + && (pFound->FullDllName.Length & 1) == 0 + && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength) + RTLogLoggerWeak(pLogger, NULL, "%p: %p - %#010RX32 bytes into %.*ls\n", + puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase), + pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer); + else + RTLogLoggerWeak(pLogger, NULL, "%p: %p - %08RX32 into module at %p\n", + puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase), pFound->DllBase); + } + } + + puStack++; + } + } + } + + /* + * Dump the command line if we have one. We do this last in case it crashes. + */ + PRTL_USER_PROCESS_PARAMETERS pProcParams = pPeb->ProcessParameters; + if (RT_VALID_PTR(pProcParams)) + { + if (RT_VALID_PTR(pProcParams->CommandLine.Buffer) + && pProcParams->CommandLine.Length > 0 + && pProcParams->CommandLine.Length <= pProcParams->CommandLine.MaximumLength + && !(pProcParams->CommandLine.Length & 1) + && !(pProcParams->CommandLine.MaximumLength & 1)) + RTLogLoggerWeak(pLogger, NULL, "PEB/CommandLine: %.*ls\n", + pProcParams->CommandLine.Length / sizeof(RTUTF16), pProcParams->CommandLine.Buffer); + } + } + } + + /* + * Do the default stuff, never mind us. + */ + if (g_pfnUnhandledXcptFilter) + return g_pfnUnhandledXcptFilter(pPtrs); + return EXCEPTION_CONTINUE_SEARCH; +} + diff --git a/src/VBox/Runtime/r3/win/internal-r3-win.h b/src/VBox/Runtime/r3/win/internal-r3-win.h new file mode 100644 index 00000000..562ac0ed --- /dev/null +++ b/src/VBox/Runtime/r3/win/internal-r3-win.h @@ -0,0 +1,244 @@ +/* $Id: internal-r3-win.h $ */ +/** @file + * IPRT - some Windows OS type constants. + */ + +/* + * 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 + */ + +#ifndef IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h +#define IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "internal/iprt.h" +#include <iprt/types.h> + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Windows OS type as determined by rtSystemWinOSType(). + * + * @note ASSUMPTIONS are made regarding ordering. Win 9x should come first, then + * NT. The Win9x and NT versions should internally be ordered in ascending + * version/code-base order. + */ +typedef enum RTWINOSTYPE +{ + kRTWinOSType_UNKNOWN = 0, + kRTWinOSType_9XFIRST = 1, + kRTWinOSType_95 = kRTWinOSType_9XFIRST, + kRTWinOSType_95SP1, + kRTWinOSType_95OSR2, + kRTWinOSType_98, + kRTWinOSType_98SP1, + kRTWinOSType_98SE, + kRTWinOSType_ME, + kRTWinOSType_9XLAST = 99, + kRTWinOSType_NTFIRST = 100, + kRTWinOSType_NT310 = kRTWinOSType_NTFIRST, + kRTWinOSType_NT350, + kRTWinOSType_NT351, + kRTWinOSType_NT4, + kRTWinOSType_2K, /* 5.0 */ + kRTWinOSType_XP, /* 5.1 */ + kRTWinOSType_XP64, /* 5.2, workstation */ + kRTWinOSType_2003, /* 5.2 */ + kRTWinOSType_VISTA, /* 6.0, workstation */ + kRTWinOSType_2008, /* 6.0, server */ + kRTWinOSType_7, /* 6.1, workstation */ + kRTWinOSType_2008R2, /* 6.1, server */ + kRTWinOSType_8, /* 6.2, workstation */ + kRTWinOSType_2012, /* 6.2, server */ + kRTWinOSType_81, /* 6.3, workstation */ + kRTWinOSType_2012R2, /* 6.3, server */ + kRTWinOSType_10, /* 10.0, workstation */ + kRTWinOSType_2016, /* 10.0 1607, server */ + kRTWinOSType_2019, /* 10.0 1809, server */ + kRTWinOSType_2022, /* 10.0 21H2, server */ + kRTWinOSType_11, /* 11.0, workstation */ + kRTWinOSType_NT_UNKNOWN = 199, + kRTWinOSType_NT_LAST = kRTWinOSType_UNKNOWN +} RTWINOSTYPE; + +/** + * Windows loader protection level. + */ +typedef enum RTR3WINLDRPROT +{ + RTR3WINLDRPROT_INVALID = 0, + RTR3WINLDRPROT_NONE, + RTR3WINLDRPROT_NO_CWD, + RTR3WINLDRPROT_SAFE, + RTR3WINLDRPROT_SAFER +} RTR3WINLDRPROT; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt; +extern DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer; +#ifdef _WINDEF_ +extern DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx; + +extern DECL_HIDDEN_DATA(HMODULE) g_hModKernel32; +typedef UINT (WINAPI *PFNGETWINSYSDIR)(LPWSTR,UINT); +extern DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW; +typedef HANDLE (WINAPI *PFNCREATEWAITABLETIMEREX)(LPSECURITY_ATTRIBUTES, LPCWSTR, DWORD, DWORD); +extern DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW; +extern DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime; +extern DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation; +extern DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation; +extern DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent; +extern DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime; +extern DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask; +extern DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask; +extern DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort; +extern DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus; +extern DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus; +extern DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter; +extern DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter; +extern DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent; + + +extern DECL_HIDDEN_DATA(HMODULE) g_hModNtDll; +typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *); +extern DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile; +typedef NTSTATUS (NTAPI *PFNNTDUPLICATETOKEN)(HANDLE, ACCESS_MASK, struct _OBJECT_ATTRIBUTES *, BOOLEAN, TOKEN_TYPE, PHANDLE); +extern DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken; +#ifdef IPRT_INCLUDED_nt_nt_h +extern DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread; +#endif + +extern DECL_HIDDEN_DATA(HMODULE) g_hModWinSock; + +/** WSAStartup */ +typedef int (WINAPI *PFNWSASTARTUP)(WORD, struct WSAData *); +/** WSACleanup */ +typedef int (WINAPI *PFNWSACLEANUP)(void); +/** WSAGetLastError */ +typedef int (WINAPI *PFNWSAGETLASTERROR)(void); +/** WSASetLastError */ +typedef int (WINAPI *PFNWSASETLASTERROR)(int); +/** WSACreateEvent */ +typedef HANDLE (WINAPI *PFNWSACREATEEVENT)(void); +/** WSASetEvent */ +typedef BOOL (WINAPI *PFNWSASETEVENT)(HANDLE); +/** WSACloseEvent */ +typedef BOOL (WINAPI *PFNWSACLOSEEVENT)(HANDLE); +/** WSAEventSelect */ +typedef BOOL (WINAPI *PFNWSAEVENTSELECT)(UINT_PTR, HANDLE, LONG); +/** WSAEnumNetworkEvents */ +typedef int (WINAPI *PFNWSAENUMNETWORKEVENTS)(UINT_PTR, HANDLE, struct _WSANETWORKEVENTS *); +/** WSASocketW */ +typedef UINT_PTR (WINAPI *PFNWSASOCKETW)(int, int, int, struct _WSAPROTOCOL_INFOW *, unsigned, DWORD); +/** WSASend */ +typedef int (WINAPI *PFNWSASEND)(UINT_PTR, struct _WSABUF *, DWORD, LPDWORD, DWORD dwFlags, + struct _OVERLAPPED *, uintptr_t /*LPWSAOVERLAPPED_COMPLETION_ROUTINE*/); + +/** socket */ +typedef UINT_PTR (WINAPI *PFNWINSOCKSOCKET)(int, int, int); +/** closesocket */ +typedef int (WINAPI *PFNWINSOCKCLOSESOCKET)(UINT_PTR); +/** recv */ +typedef int (WINAPI *PFNWINSOCKRECV)(UINT_PTR, char *, int, int); +/** send */ +typedef int (WINAPI *PFNWINSOCKSEND)(UINT_PTR, const char *, int, int); +/** recvfrom */ +typedef int (WINAPI *PFNWINSOCKRECVFROM)(UINT_PTR, char *, int, int, struct sockaddr *, int *); +/** sendto */ +typedef int (WINAPI *PFNWINSOCKSENDTO)(UINT_PTR, const char *, int, int, const struct sockaddr *, int); +/** bind */ +typedef int (WINAPI *PFNWINSOCKBIND)(UINT_PTR, const struct sockaddr *, int); +/** listen */ +typedef int (WINAPI *PFNWINSOCKLISTEN)(UINT_PTR, int); +/** accept */ +typedef UINT_PTR (WINAPI *PFNWINSOCKACCEPT)(UINT_PTR, struct sockaddr *, int *); +/** connect */ +typedef int (WINAPI *PFNWINSOCKCONNECT)(UINT_PTR, const struct sockaddr *, int); +/** shutdown */ +typedef int (WINAPI *PFNWINSOCKSHUTDOWN)(UINT_PTR, int); +/** getsockopt */ +typedef int (WINAPI *PFNWINSOCKGETSOCKOPT)(UINT_PTR, int, int, char *, int *); +/** setsockopt */ +typedef int (WINAPI *PFNWINSOCKSETSOCKOPT)(UINT_PTR, int, int, const char *, int); +/** ioctlsocket */ +typedef int (WINAPI *PFNWINSOCKIOCTLSOCKET)(UINT_PTR, long, unsigned long *); +/** getpeername */ +typedef int (WINAPI *PFNWINSOCKGETPEERNAME)(UINT_PTR, struct sockaddr *, int *); +/** getsockname */ +typedef int (WINAPI *PFNWINSOCKGETSOCKNAME)(UINT_PTR, struct sockaddr *, int *); +/** __WSAFDIsSet */ +typedef int (WINAPI *PFNWINSOCK__WSAFDISSET)(UINT_PTR, struct fd_set *); +/** select */ +typedef int (WINAPI *PFNWINSOCKSELECT)(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timeval *); +/** gethostbyname */ +typedef struct hostent *(WINAPI *PFNWINSOCKGETHOSTBYNAME)(const char *); + +extern DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup; +extern DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup; +extern DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError; +extern DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError; +extern DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent; +extern DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent; +extern DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent; +extern DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect; +extern DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents; +extern DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW; +extern DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend; +extern DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket; +extern DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket; +extern DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv; +extern DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend; +extern DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom; +extern DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto; +extern DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind; +extern DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten; +extern DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept; +extern DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect; +extern DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown; +extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt; +extern DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt; +extern DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket; +extern DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername; +extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname; +extern DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet; +extern DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect; +extern DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname; +#endif + + +#endif /* !IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h */ + diff --git a/src/VBox/Runtime/r3/win/krnlmod-win.cpp b/src/VBox/Runtime/r3/win/krnlmod-win.cpp new file mode 100644 index 00000000..255cc2cf --- /dev/null +++ b/src/VBox/Runtime/r3/win/krnlmod-win.cpp @@ -0,0 +1,331 @@ +/* $Id: krnlmod-win.cpp $ */ +/** @file + * IPRT - Kernel module, Windows. + */ + +/* + * Copyright (C) 2017-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_SYSTEM +#include <iprt/nt/nt.h> + +#include <iprt/krnlmod.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/types.h> + + +/** + * Internal kernel information record state. + */ +typedef struct RTKRNLMODINFOINT +{ + /** Reference counter. */ + volatile uint32_t cRefs; + /** Reference count for the kernel module. */ + uint32_t cRefKrnlMod; + /** Load address of the kernel module. */ + RTR0UINTPTR uLoadAddr; + /** Size of the kernel module. */ + size_t cbKrnlMod; + /** Pointer to the driver name. */ + const char *pszName; + /** Size of the name in characters including the zero terminator. */ + size_t cchFilePath; + /** Module name - variable in size. */ + char achFilePath[1]; +} RTKRNLMODINFOINT; +/** Pointer to the internal kernel module information record. */ +typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT; +/** Pointer to a const internal kernel module information record. */ +typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT; + + +/** + * Destroy the given kernel module information record. + * + * @param pThis The record to destroy. + */ +static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis) +{ + RTMemFree(pThis); +} + + +/** + * Queries the complete kernel modules structure and returns a pointer to it. + * + * @returns IPRT status code. + * @param ppKrnlMods Where to store the pointer to the kernel module list on success. + * Free with RTMemFree(). + */ +static int rtKrnlModWinQueryKrnlMods(PRTL_PROCESS_MODULES *ppKrnlMods) +{ + int rc = VINF_SUCCESS; + RTL_PROCESS_MODULES KrnlModsSize; + + NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &KrnlModsSize, sizeof(KrnlModsSize), NULL); + if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH) + { + ULONG cbKrnlMods = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[KrnlModsSize.NumberOfModules]); + PRTL_PROCESS_MODULES pKrnlMods = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbKrnlMods); + if (RT_LIKELY(pKrnlMods)) + { + rcNt = NtQuerySystemInformation(SystemModuleInformation, pKrnlMods, cbKrnlMods, NULL); + if (NT_SUCCESS(rcNt)) + *ppKrnlMods = pKrnlMods; + else + rc = RTErrConvertFromNtStatus(rcNt); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = RTErrConvertFromNtStatus(rcNt); + + return rc; +} + +/** + * Creates a new kernel module information record for the given module. + * + * @returns IPRT status code. + * @param pModInfo The kernel module information. + * @param phKrnlModInfo Where to store the handle to the kernel module information record + * on success. + */ +static int rtKrnlModWinInfoCreate(PRTL_PROCESS_MODULE_INFORMATION pModInfo, PRTKRNLMODINFO phKrnlModInfo) +{ + int rc = VINF_SUCCESS; + RT_NOREF2(pModInfo, phKrnlModInfo); + size_t cchFilePath = strlen((const char *)&pModInfo->FullPathName[0]) + 1; + PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achFilePath[cchFilePath])); + if (RT_LIKELY(pThis)) + { + memcpy(&pThis->achFilePath[0], &pModInfo->FullPathName[0], cchFilePath); + pThis->cchFilePath = cchFilePath; + pThis->cRefs = 1; + pThis->cbKrnlMod = pModInfo->ImageSize; + pThis->uLoadAddr = (RTR0UINTPTR)pModInfo->ImageBase; + pThis->pszName = pModInfo->OffsetToFileName >= cchFilePath + ? NULL + : pThis->achFilePath + pModInfo->OffsetToFileName; + + *phKrnlModInfo = pThis; + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER); + + int rc = VERR_NOT_IMPLEMENTED; + + return rc; +} + + +RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER); + + int rc = VERR_NOT_IMPLEMENTED; + + return rc; +} + + +RTDECL(uint32_t) RTKrnlModLoadedGetCount(void) +{ + uint32_t cKrnlMods = 0; + RTL_PROCESS_MODULES ProcMods; + + NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &ProcMods, sizeof(ProcMods), NULL); + if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH) + cKrnlMods = ProcMods.NumberOfModules; + + return cKrnlMods; +} + + +RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax, + uint32_t *pcEntries) +{ + if (cEntriesMax > 0) + AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER); + + PRTL_PROCESS_MODULES pKrnlMods = NULL; + int rc = rtKrnlModWinQueryKrnlMods(&pKrnlMods); + if (RT_SUCCESS(rc)) + { + if (pKrnlMods->NumberOfModules <= cEntriesMax) + { + for (unsigned i = 0; i < pKrnlMods->NumberOfModules; i++) + { + pKrnlMods->Modules[i].FullPathName[255] = '\0'; /* Paranoia */ + rc = rtKrnlModWinInfoCreate(&pKrnlMods->Modules[i], &pahKrnlModInfo[i]); + if (RT_FAILURE(rc)) + { + while (i-- > 0) + RTKrnlModInfoRelease(pahKrnlModInfo[i]); + break; + } + } + } + else + rc = VERR_BUFFER_OVERFLOW; + + if (pcEntries) + *pcEntries = pKrnlMods->NumberOfModules; + + RTMemFree(pKrnlMods); + } + + return rc; +} + + +RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + return cRefs; +} + + +RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + if (!pThis) + return 0; + AssertPtrReturn(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + if (cRefs == 0) + rtKrnlModInfoDestroy(pThis); + return cRefs; +} + + +RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return pThis->cRefKrnlMod; +} + + +RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, NULL); + + return pThis->pszName; +} + + +RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, NULL); + + return &pThis->achFilePath[0]; +} + + +RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return pThis->cbKrnlMod; +} + + +RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return pThis->uLoadAddr; +} + + +RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx, + PRTKRNLMODINFO phKrnlModInfoRef) +{ + RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef); + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTKrnlModLoadByName(const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_PARAMETER); + + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTKrnlModLoadByPath(const char *pszPath) +{ + AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER); + + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTKrnlModUnloadByName(const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_PARAMETER); + + return VERR_NOT_SUPPORTED; +} diff --git a/src/VBox/Runtime/r3/win/ldrNative-win.cpp b/src/VBox/Runtime/r3/win/ldrNative-win.cpp new file mode 100644 index 00000000..2ff98a4c --- /dev/null +++ b/src/VBox/Runtime/r3/win/ldrNative-win.cpp @@ -0,0 +1,326 @@ +/* $Id: ldrNative-win.cpp $ */ +/** @file + * IPRT - Binary Image Loader, Win32 native. + */ + +/* + * 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_LDR +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/ldr.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include <iprt/once.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "internal/ldr.h" +#include "internal-r3-win.h" + +#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR +# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100) +# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200) +# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800) +#endif + + +DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo) +{ + Assert(sizeof(*phHandle) >= sizeof(HMODULE)); + AssertReturn(!(fFlags & RTLDRLOAD_FLAGS_GLOBAL), VERR_INVALID_FLAGS); + AssertLogRelMsgReturn(RTPathStartsWithRoot(pszFilename), /* Relative names will still be applied to the search path. */ + ("pszFilename='%s'\n", pszFilename), + VERR_INTERNAL_ERROR_2); + AssertReleaseMsg(g_hModKernel32, + ("rtldrNativeLoad(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename)); + + /* + * Convert to UTF-16 and make sure it got a .DLL suffix. + */ + /** @todo Implement long path support for native DLL loading on windows. @bugref{9248} */ + int rc; + RTUTF16 *pwszNative = NULL; + if (RTPathHasSuffix(pszFilename) || (fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX)) + rc = RTStrToUtf16(pszFilename, &pwszNative); + else + { + size_t cwcAlloc; + rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcAlloc); + if (RT_SUCCESS(rc)) + { + cwcAlloc += sizeof(".DLL"); + pwszNative = RTUtf16Alloc(cwcAlloc * sizeof(RTUTF16)); + if (pwszNative) + { + size_t cwcNative; + rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszNative, cwcAlloc, &cwcNative); + if (RT_SUCCESS(rc)) + rc = RTUtf16CopyAscii(&pwszNative[cwcNative], cwcAlloc - cwcNative, ".DLL"); + } + else + rc = VERR_NO_UTF16_MEMORY; + } + } + if (RT_SUCCESS(rc)) + { + /* Convert slashes just to be on the safe side. */ + for (size_t off = 0;; off++) + { + RTUTF16 wc = pwszNative[off]; + if (wc == '/') + pwszNative[off] = '\\'; + else if (!wc) + break; + } + + /* + * Attempt load. + */ + HMODULE hmod; + static int s_iSearchDllLoadDirSupported = 0; + if ( !(fFlags & RTLDRLOAD_FLAGS_NT_SEARCH_DLL_LOAD_DIR) + || s_iSearchDllLoadDirSupported < 0) + hmod = LoadLibraryExW(pwszNative, NULL, 0); + else + { + hmod = LoadLibraryExW(pwszNative, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 + | LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + if (s_iSearchDllLoadDirSupported == 0) + { + if (hmod != NULL || GetLastError() != ERROR_INVALID_PARAMETER) + s_iSearchDllLoadDirSupported = 1; + else + { + s_iSearchDllLoadDirSupported = -1; + hmod = LoadLibraryExW(pwszNative, NULL, 0); + } + } + } + if (hmod) + { + *phHandle = (uintptr_t)hmod; + RTUtf16Free(pwszNative); + return VINF_SUCCESS; + } + + /* + * Try figure why it failed to load. + */ + DWORD dwErr = GetLastError(); + rc = RTErrConvertFromWin32(dwErr); + rc = RTErrInfoSetF(pErrInfo, rc, "GetLastError=%u", dwErr); + } + else + rc = RTErrInfoSetF(pErrInfo, rc, "Error converting UTF-8 to UTF-16 string."); + RTUtf16Free(pwszNative); + return rc; +} + + +DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue) +{ + PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod; + FARPROC pfn = GetProcAddress((HMODULE)pModNative->hNative, pszSymbol); + if (pfn) + { + *ppvValue = (void *)pfn; + return VINF_SUCCESS; + } + *ppvValue = NULL; + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod) +{ + PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod; + if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD) + || FreeLibrary((HMODULE)pModNative->hNative)) + { + pModNative->hNative = (uintptr_t)INVALID_HANDLE_VALUE; + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod) +{ + AssertReleaseMsg(g_hModKernel32, + ("rtldrNativeLoadSystem(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename)); + + /* + * Resolve side-by-side resolver API. + */ + static bool volatile s_fInitialized = false; + static decltype(RtlDosApplyFileIsolationRedirection_Ustr) *s_pfnApplyRedir = NULL; + if (!s_fInitialized) + { + s_pfnApplyRedir = (decltype(s_pfnApplyRedir))GetProcAddress(g_hModNtDll, + "RtlDosApplyFileIsolationRedirection_Ustr"); + ASMCompilerBarrier(); + s_fInitialized = true; + } + + /* + * We try WinSxS via undocumented NTDLL API and flal back on the System32 + * directory. No other locations are supported. + */ + int rc = VERR_TRY_AGAIN; + char szPath[RTPATH_MAX]; + char *pszPath = szPath; + + /* Get the windows system32 directory so we can sanity check the WinSxS result. */ + WCHAR wszSysDir[MAX_PATH]; + UINT cwcSysDir = GetSystemDirectoryW(wszSysDir, MAX_PATH); + if (cwcSysDir >= MAX_PATH) + return VERR_FILENAME_TOO_LONG; + + /* Try side-by-side first (see COMCTL32.DLL). */ + if (s_pfnApplyRedir) + { + size_t cwcName = 0; + RTUTF16 wszName[MAX_PATH]; + PRTUTF16 pwszName = wszName; + int rc2 = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszName, RT_ELEMENTS(wszName), &cwcName); + if (RT_SUCCESS(rc2)) + { + static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll"); + WCHAR wszPath[MAX_PATH]; + UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath }; + UNICODE_STRING UniStrDynamic = { 0, 0, NULL }; + PUNICODE_STRING pUniStrResult = NULL; + UNICODE_STRING UniStrName = + { (USHORT)(cwcName * sizeof(RTUTF16)), (USHORT)((cwcName + 1) * sizeof(RTUTF16)), wszName }; + + NTSTATUS rcNt = s_pfnApplyRedir(1 /*fFlags*/, + &UniStrName, + (PUNICODE_STRING)&s_DefaultSuffix, + &UniStrStatic, + &UniStrDynamic, + &pUniStrResult, + NULL /*pNewFlags*/, + NULL /*pcbFilename*/, + NULL /*pcbNeeded*/); + if (NT_SUCCESS(rcNt)) + { + /* + * Check that the resolved path has similarities to the + * system directory. + * + * ASSUMES the windows directory is a root directory and + * that both System32 and are on the same level. So, we'll + * have 2 matching components (or more if the resolver + * returns a system32 path for some reason). + */ + unsigned cMatchingComponents = 0; + size_t off = 0; + while (off < pUniStrResult->Length) + { + RTUTF16 wc1 = wszSysDir[off]; + RTUTF16 wc2 = pUniStrResult->Buffer[off]; + if (!RTPATH_IS_SLASH(wc1)) + { + if (wc1 == wc2) + off++; + else if ( wc1 < 127 + && wc2 < 127 + && RT_C_TO_LOWER(wc1) == RT_C_TO_LOWER(wc2) ) + off++; + else + break; + } + else if (RTPATH_IS_SLASH(wc2)) + { + cMatchingComponents += off > 0; + do + off++; + while ( off < pUniStrResult->Length + && RTPATH_IS_SLASH(wszSysDir[off]) + && RTPATH_IS_SLASH(pUniStrResult->Buffer[off])); + } + else + break; + } + if (cMatchingComponents >= 2) + { + pszPath = szPath; + rc2 = RTUtf16ToUtf8Ex(pUniStrResult->Buffer, pUniStrResult->Length / sizeof(RTUTF16), + &pszPath, sizeof(szPath), NULL); + if (RT_SUCCESS(rc2)) + rc = VINF_SUCCESS; + } + else + AssertMsgFailed(("%s -> '%*.ls'\n", pszFilename, pUniStrResult->Length, pUniStrResult->Buffer)); + RtlFreeUnicodeString(&UniStrDynamic); + } + } + else + AssertMsgFailed(("%Rrc\n", rc)); + } + + /* If the above didn't succeed, create a system32 path. */ + if (RT_FAILURE(rc)) + { + rc = RTUtf16ToUtf8Ex(wszSysDir, RTSTR_MAX, &pszPath, sizeof(szPath), NULL); + if (RT_SUCCESS(rc)) + { + rc = RTPathAppend(szPath, sizeof(szPath), pszFilename); + if (pszExt && RT_SUCCESS(rc)) + rc = RTStrCat(szPath, sizeof(szPath), pszExt); + } + } + + /* Do the actual loading, if we were successful constructing a name. */ + if (RT_SUCCESS(rc)) + { + if (RTFileExists(szPath)) + rc = RTLdrLoadEx(szPath, phLdrMod, fFlags, NULL); + else + rc = VERR_MODULE_NOT_FOUND; + } + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/localipc-win.cpp b/src/VBox/Runtime/r3/win/localipc-win.cpp new file mode 100644 index 00000000..6d56ab91 --- /dev/null +++ b/src/VBox/Runtime/r3/win/localipc-win.cpp @@ -0,0 +1,1778 @@ +/* $Id: localipc-win.cpp $ */ +/** @file + * IPRT - Local IPC, Windows Implementation Using Named Pipes. + * + * @note This code only works on W2K because of the dependency on + * ConvertStringSecurityDescriptorToSecurityDescriptor. + */ + +/* + * 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_LOCALIPC +#include <iprt/nt/nt-and-windows.h> /* Need NtCancelIoFile and a few Rtl functions. */ +#include <sddl.h> +#include <aclapi.h> + +#include "internal/iprt.h" +#include <iprt/localipc.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include <iprt/utf16.h> + +#include "internal/magics.h" +#include "internal-r3-win.h" + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Pipe prefix string. */ +#define RTLOCALIPC_WIN_PREFIX L"\\\\.\\pipe\\IPRT-" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Local IPC service instance, Windows. + */ +typedef struct RTLOCALIPCSERVERINT +{ + /** The magic (RTLOCALIPCSERVER_MAGIC). */ + uint32_t u32Magic; + /** The creation flags. */ + uint32_t fFlags; + /** Critical section protecting the structure. */ + RTCRITSECT CritSect; + /** The number of references to the instance. + * @remarks The reference counting isn't race proof. */ + uint32_t volatile cRefs; + /** Indicates that there is a pending cancel request. */ + bool volatile fCancelled; + /** The named pipe handle. */ + HANDLE hNmPipe; + /** The handle to the event object we're using for overlapped I/O. */ + HANDLE hEvent; + /** The overlapped I/O structure. */ + OVERLAPPED OverlappedIO; + /** The full pipe name (variable length). */ + RTUTF16 wszName[1]; +} RTLOCALIPCSERVERINT; +/** Pointer to a local IPC server instance (Windows). */ +typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT; + + +/** + * Local IPC session instance, Windows. + * + * This is a named pipe and we should probably merge the pipe code with this to + * save work and code duplication. + */ +typedef struct RTLOCALIPCSESSIONINT +{ + /** The magic (RTLOCALIPCSESSION_MAGIC). */ + uint32_t u32Magic; + /** Critical section protecting the structure. */ + RTCRITSECT CritSect; + /** The number of references to the instance. + * @remarks The reference counting isn't race proof. */ + uint32_t volatile cRefs; + /** Set if the zero byte read that the poll code using is pending. */ + bool fZeroByteRead; + /** Indicates that there is a pending cancel request. */ + bool volatile fCancelled; + /** Set if this is the server side, clear if the client. */ + bool fServerSide; + /** The named pipe handle. */ + HANDLE hNmPipe; + struct + { + RTTHREAD hActiveThread; + /** The handle to the event object we're using for overlapped I/O. */ + HANDLE hEvent; + /** The overlapped I/O structure. */ + OVERLAPPED OverlappedIO; + } + /** Overlapped reads. */ + Read, + /** Overlapped writes. */ + Write; +#if 0 /* Non-blocking writes are not yet supported. */ + /** Bounce buffer for writes. */ + uint8_t *pbBounceBuf; + /** Amount of used buffer space. */ + size_t cbBounceBufUsed; + /** Amount of allocated buffer space. */ + size_t cbBounceBufAlloc; +#endif + /** Buffer for the zero byte read. + * Used in RTLocalIpcSessionWaitForData(). */ + uint8_t abBuf[8]; +} RTLOCALIPCSESSIONINT; +/** Pointer to a local IPC session instance (Windows). */ +typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession); + + +/** + * DACL for block all network access and local users other than the creator/owner. + * + * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid) + * + * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes + * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded + * value 0x0012019b in the client ACE. The server-side still needs + * setting FILE_CREATE_PIPE_INSTANCE although. + * It expands to: + * 0x00000001 - FILE_READ_DATA + * 0x00000008 - FILE_READ_EA + * 0x00000080 - FILE_READ_ATTRIBUTES + * 0x00020000 - READ_CONTROL + * 0x00100000 - SYNCHRONIZE + * 0x00000002 - FILE_WRITE_DATA + * 0x00000010 - FILE_WRITE_EA + * 0x00000100 - FILE_WRITE_ATTRIBUTES + * = 0x0012019b (client) + * + (only for server): + * 0x00000004 - FILE_CREATE_PIPE_INSTANCE + * = 0x0012019f + * + * @todo Triple check this! + * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate? + * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking + * it just to get progress - the service runs as local system. + * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting + * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution + * is to go the annoying route of OpenProcessToken, QueryTokenInformation, + * ConvertSidToStringSid and then use the result... Suggestions are very welcome + */ +#define RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_DACL SDDL_DELIMINATOR \ + SDDL_ACE_BEGIN SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK SDDL_ACE_END \ + SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END +#define RTLOCALIPC_WIN_SDDL_SERVER \ + RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE SDDL_ACE_END +#define RTLOCALIPC_WIN_SDDL_CLIENT \ + RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END +static NTSTATUS rtLocalIpcBuildDacl(PACL pDacl, bool fServer) +{ + static SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY; + static SID_IDENTIFIER_AUTHORITY s_WorldAuth = SECURITY_WORLD_SID_AUTHORITY; + union + { + SID Sid; + uint8_t abPadding[SECURITY_MAX_SID_SIZE]; + } Network, LocalSystem, Everyone; + + + /* 1. SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK */ + NTSTATUS rcNt = RtlInitializeSid(&Network.Sid, &s_NtAuth, 1); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + *RtlSubAuthoritySid(&Network.Sid, 0) = SECURITY_NETWORK_RID; + + rcNt = RtlAddAccessDeniedAce(pDacl, ACL_REVISION, GENERIC_ALL, &Network.Sid); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + + /* 2. SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM */ + rcNt = RtlInitializeSid(&LocalSystem.Sid, &s_NtAuth, 1); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + *RtlSubAuthoritySid(&LocalSystem.Sid, 0) = SECURITY_LOCAL_SYSTEM_RID; + + rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, FILE_ALL_ACCESS, &Network.Sid); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + + + /* 3. server: SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE + client: SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE */ + rcNt = RtlInitializeSid(&Everyone.Sid, &s_NtAuth, 1); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + *RtlSubAuthoritySid(&Everyone.Sid, 0) = SECURITY_WORLD_RID; + + DWORD const fAccess = FILE_READ_DATA /* 0x00000001 */ + | FILE_WRITE_DATA /* 0x00000002 */ + | FILE_CREATE_PIPE_INSTANCE * fServer /* 0x00000004 */ + | FILE_READ_EA /* 0x00000008 */ + | FILE_WRITE_EA /* 0x00000010 */ + | FILE_READ_ATTRIBUTES /* 0x00000080 */ + | FILE_WRITE_ATTRIBUTES /* 0x00000100 */ + | READ_CONTROL /* 0x00020000 */ + | SYNCHRONIZE; /* 0x00100000*/ + Assert(fAccess == (fServer ? 0x0012019fU : 0x0012019bU)); + + rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, fAccess, &Network.Sid); + AssertReturn(NT_SUCCESS(rcNt), rcNt); + + return true; +} + + +/** + * Builds and allocates the security descriptor required for securing the local pipe. + * + * @return IPRT status code. + * @param ppDesc Where to store the allocated security descriptor on success. + * Must be free'd using LocalFree(). + * @param fServer Whether it's for a server or client instance. + */ +static int rtLocalIpcServerWinAllocSecurityDescriptor(PSECURITY_DESCRIPTOR *ppDesc, bool fServer) +{ + int rc; + PSECURITY_DESCRIPTOR pSecDesc = NULL; + +#if 0 + /* + * Resolve the API the first time around. + */ + static bool volatile s_fResolvedApis = false; + /** advapi32.dll API ConvertStringSecurityDescriptorToSecurityDescriptorW. */ + static decltype(ConvertStringSecurityDescriptorToSecurityDescriptorW) *s_pfnSSDLToSecDescW = NULL; + + if (!s_fResolvedApis) + { + s_pfnSSDLToSecDescW + = (decltype(s_pfnSSDLToSecDescW))RTLdrGetSystemSymbol("advapi32.dll", + "ConvertStringSecurityDescriptorToSecurityDescriptorW"); + ASMCompilerBarrier(); + s_fResolvedApis = true; + } + if (s_pfnSSDLToSecDescW) + { + /* + * We'll create a security descriptor from a SDDL that denies + * access to network clients (this is local IPC after all), it + * makes some further restrictions to prevent non-authenticated + * users from screwing around. + */ + PCRTUTF16 pwszSDDL = fServer ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT; + ULONG cbSecDesc = 0; + SetLastError(0); + if (s_pfnSSDLToSecDescW(pwszSDDL, SDDL_REVISION_1, &pSecDesc, &cbSecDesc)) + { + DWORD dwErr = GetLastError(); RT_NOREF(dwErr); + AssertPtr(pSecDesc); + *ppDesc = pSecDesc; + return VINF_SUCCESS; + } + + rc = RTErrConvertFromWin32(GetLastError()); + } + else +#endif + { + /* + * Manually construct the descriptor. + * + * This is a bit crude. The 8KB is probably 50+ times more than what we need. + */ + uint32_t const cbAlloc = SECURITY_DESCRIPTOR_MIN_LENGTH * 2 + _8K; + pSecDesc = LocalAlloc(LMEM_FIXED, cbAlloc); + if (!pSecDesc) + return VERR_NO_MEMORY; + RT_BZERO(pSecDesc, cbAlloc); + + uint32_t const cbDacl = cbAlloc - SECURITY_DESCRIPTOR_MIN_LENGTH * 2; + PACL const pDacl = (PACL)((uint8_t *)pSecDesc + SECURITY_DESCRIPTOR_MIN_LENGTH * 2); + + if ( InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION) + && InitializeAcl(pDacl, cbDacl, ACL_REVISION)) + { + if (rtLocalIpcBuildDacl(pDacl, fServer)) + { + *ppDesc = pSecDesc; + return VINF_SUCCESS; + } + rc = VERR_GENERAL_FAILURE; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + LocalFree(pSecDesc); + } + return rc; +} + + +/** + * Creates a named pipe instance. + * + * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen. + * + * @return IPRT status code. + * @param phNmPipe Where to store the named pipe handle on success. + * This will be set to INVALID_HANDLE_VALUE on failure. + * @param pwszPipeName The named pipe name, full, UTF-16 encoded. + * @param fFirst Set on the first call (from RTLocalIpcServerCreate), + * otherwise clear. Governs the + * FILE_FLAG_FIRST_PIPE_INSTANCE flag. + */ +static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, PCRTUTF16 pwszPipeName, bool fFirst) +{ + *phNmPipe = INVALID_HANDLE_VALUE; + + /* + * Create a security descriptor blocking access to the pipe via network. + */ + PSECURITY_DESCRIPTOR pSecDesc; + int rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, fFirst /* Server? */); + if (RT_SUCCESS(rc)) + { +#if 0 + { /* Just for checking the security descriptor out in the debugger (!sd <addr> doesn't work): */ + DWORD dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, NULL, NULL, pSecDesc); + __debugbreak(); RT_NOREF(dwRet); + + PTRUSTEE_W pOwner = NULL; + PTRUSTEE_W pGroup = NULL; + ULONG cAces = 0; + PEXPLICIT_ACCESS_W paAces = NULL; + ULONG cAuditEntries = 0; + PEXPLICIT_ACCESS_W paAuditEntries = NULL; + dwRet = LookupSecurityDescriptorPartsW(&pOwner, NULL, NULL, NULL, NULL, NULL, pSecDesc); + dwRet = LookupSecurityDescriptorPartsW(NULL, &pGroup, NULL, NULL, NULL, NULL, pSecDesc); + dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, &cAces, &paAces, NULL, NULL, pSecDesc); + dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, &cAuditEntries, &paAuditEntries, pSecDesc); + __debugbreak(); RT_NOREF(dwRet); + } +#endif + + /* + * Now, create the pipe. + */ + SECURITY_ATTRIBUTES SecAttrs; + SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES); + SecAttrs.lpSecurityDescriptor = pSecDesc; + SecAttrs.bInheritHandle = FALSE; + + DWORD fOpenMode = PIPE_ACCESS_DUPLEX + | PIPE_WAIT + | FILE_FLAG_OVERLAPPED; + if ( fFirst + && ( g_enmWinVer >= kRTWinOSType_XP + || ( g_enmWinVer == kRTWinOSType_2K + && g_WinOsInfoEx.wServicePackMajor >= 2) ) ) + fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Introduced with W2K SP2 */ + + HANDLE hNmPipe = CreateNamedPipeW(pwszPipeName, /* lpName */ + fOpenMode, /* dwOpenMode */ + PIPE_TYPE_BYTE, /* dwPipeMode */ + PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */ + PAGE_SIZE, /* nOutBufferSize (advisory) */ + PAGE_SIZE, /* nInBufferSize (ditto) */ + 30*1000, /* nDefaultTimeOut = 30 sec */ + &SecAttrs); /* lpSecurityAttributes */ + if (hNmPipe != INVALID_HANDLE_VALUE) + { +#if 0 /* For checking access control stuff in windbg (doesn't work): */ + PSECURITY_DESCRIPTOR pSecDesc2 = NULL; + PACL pDacl = NULL; + DWORD dwRet; + dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSecDesc2); + PACL pSacl = NULL; + dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, SACL_SECURITY_INFORMATION, NULL, NULL, NULL, &pSacl, &pSecDesc2); + dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, &pSacl, &pSecDesc2); + PSID pSidOwner = NULL; + dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSecDesc2); + PSID pSidGroup = NULL; + dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, NULL, &pSidGroup, NULL, NULL, &pSecDesc2); + __debugbreak(); + RT_NOREF(dwRet); +#endif + *phNmPipe = hNmPipe; + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + LocalFree(pSecDesc); + } + return rc; +} + + +/** + * Validates the user specified name. + * + * @returns IPRT status code. + * @param pszName The name to validate. + * @param pcwcFullName Where to return the UTF-16 length of the full name. + * @param fNative Whether it's a native name or a portable name. + */ +static int rtLocalIpcWinValidateName(const char *pszName, size_t *pcwcFullName, bool fNative) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(*pszName, VERR_INVALID_NAME); + + if (!fNative) + { + size_t cwcName = RT_ELEMENTS(RTLOCALIPC_WIN_PREFIX) - 1; + for (;;) + { + char ch = *pszName++; + if (!ch) + break; + AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME); + AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME); + AssertReturn(ch != '\\', VERR_INVALID_NAME); + AssertReturn(ch != '/', VERR_INVALID_NAME); + cwcName++; + } + *pcwcFullName = cwcName; + } + else + { + int rc = RTStrCalcUtf16LenEx(pszName, RTSTR_MAX, pcwcFullName); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} + + +/** + * Constructs the full pipe name as UTF-16. + * + * @returns IPRT status code. + * @param pszName The user supplied name. ASSUMES reasonable length + * for now, so no long path prefixing needed. + * @param pwszFullName The output buffer. + * @param cwcFullName The output buffer size excluding the terminator. + * @param fNative Whether the user supplied name is a native or + * portable one. + */ +static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative) +{ + if (!fNative) + { + static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX; + Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix)); + memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix)); + cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1; + pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1; + } + return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL); +} + + +RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags) +{ + /* + * Validate parameters. + */ + AssertPtrReturn(phServer, VERR_INVALID_POINTER); + *phServer = NIL_RTLOCALIPCSERVER; + AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS); + size_t cwcFullName; + int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME)); + if (RT_SUCCESS(rc)) + { + /* + * Allocate and initialize the instance data. + */ + size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]); + PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis); + AssertReturn(pThis, VERR_NO_MEMORY); + + pThis->u32Magic = RTLOCALIPCSERVER_MAGIC; + pThis->cRefs = 1; /* the one we return */ + pThis->fCancelled = false; + + rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME)); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); + if (pThis->hEvent != NULL) + { + RT_ZERO(pThis->OverlappedIO); + pThis->OverlappedIO.Internal = STATUS_PENDING; + pThis->OverlappedIO.hEvent = pThis->hEvent; + + rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */); + if (RT_SUCCESS(rc)) + { + *phServer = pThis; + return VINF_SUCCESS; + } + + BOOL fRc = CloseHandle(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + int rc2 = RTCritSectDelete(&pThis->CritSect); + AssertRC(rc2); + } + } + RTMemFree(pThis); + } + return rc; +} + + +/** + * Retains a reference to the server instance. + * + * @param pThis The server instance. + */ +DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs); +} + + +/** + * Call when the reference count reaches 0. + * + * Caller owns the critsect. + * + * @returns VINF_OBJECT_DESTROYED + * @param pThis The instance to destroy. + */ +DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis) +{ + Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC); + pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC; + + BOOL fRc = CloseHandle(pThis->hNmPipe); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + pThis->hNmPipe = INVALID_HANDLE_VALUE; + + fRc = CloseHandle(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + pThis->hEvent = NULL; + + RTCritSectLeave(&pThis->CritSect); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + return VINF_OBJECT_DESTROYED; +} + + +/** + * Server instance destructor. + * + * @returns VINF_OBJECT_DESTROYED + * @param pThis The server instance. + */ +DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis) +{ + RTCritSectEnter(&pThis->CritSect); + return rtLocalIpcServerWinDestroy(pThis); +} + + +/** + * Releases a reference to the server instance. + * + * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed. + * @param pThis The server instance. + */ +DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis) +{ + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (!cRefs) + return rtLocalIpcServerDtor(pThis); + return VINF_SUCCESS; +} + + +/** + * Releases a reference to the server instance and leaves the critsect. + * + * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed. + * @param pThis The server instance. + */ +DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis) +{ + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (!cRefs) + return rtLocalIpcServerWinDestroy(pThis); + return RTCritSectLeave(&pThis->CritSect); +} + + + +RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer) +{ + /* + * Validate input. + */ + if (hServer == NIL_RTLOCALIPCSERVER) + return VINF_SUCCESS; + PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE); + + /* + * Cancel any thread currently busy using the server, + * leaving the cleanup to it. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER); + + RTCritSectEnter(&pThis->CritSect); + + /* Cancel everything. */ + ASMAtomicUoWriteBool(&pThis->fCancelled, true); + if (pThis->cRefs > 1) + { + BOOL fRc = SetEvent(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + } + + return rtLocalIpcServerReleaseAndUnlock(pThis); +} + + +RTDECL(int) RTLocalIpcServerGrantGroupAccess(RTLOCALIPCSERVER hServer, RTGID gid) +{ + RT_NOREF_PV(hServer); RT_NOREF(gid); + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession) +{ + /* + * Validate input. + */ + PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(phClientSession, VERR_INVALID_POINTER); + + /* + * Enter the critsect before inspecting the object further. + */ + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, rc); + + rtLocalIpcServerRetain(pThis); + if (!pThis->fCancelled) + { + ResetEvent(pThis->hEvent); + + RTCritSectLeave(&pThis->CritSect); + + /* + * Try connect a client. We need to use overlapped I/O here because + * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy. + */ + SetLastError(NO_ERROR); + BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO); + DWORD dwErr = fRc ? NO_ERROR : GetLastError(); + if ( !fRc + && dwErr == ERROR_IO_PENDING) + { + WaitForSingleObject(pThis->hEvent, INFINITE); + DWORD dwIgnored; + fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/); + dwErr = fRc ? NO_ERROR : GetLastError(); + } + + RTCritSectEnter(&pThis->CritSect); + if ( !pThis->fCancelled /* Event signalled but not cancelled? */ + && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC) + { + /* + * Still alive, some error or an actual client. + * + * If it's the latter we'll have to create a new pipe instance that + * replaces the current one for the server. The current pipe instance + * will be assigned to the client session. + */ + if ( fRc + || dwErr == ERROR_PIPE_CONNECTED) + { + HANDLE hNmPipe; + rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */); + if (RT_SUCCESS(rc)) + { + HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */ + pThis->hNmPipe = hNmPipe; + rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession); + } + else + { + /* + * We failed to create a new instance for the server, disconnect + * the client and fail. Don't try service the client here. + */ + fRc = DisconnectNamedPipe(pThis->hNmPipe); + AssertMsg(fRc, ("%d\n", GetLastError())); + } + } + else + rc = RTErrConvertFromWin32(dwErr); + } + else + { + /* + * Cancelled. + * + * Cancel the overlapped io if it didn't complete (must be done + * in the this thread) or disconnect the client. + */ + Assert(pThis->fCancelled); + if ( fRc + || dwErr == ERROR_PIPE_CONNECTED) + fRc = DisconnectNamedPipe(pThis->hNmPipe); + else if (dwErr == ERROR_IO_PENDING) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = NtCancelIoFile(pThis->hNmPipe, &Ios); + fRc = NT_SUCCESS(rcNt); + } + else + fRc = TRUE; + AssertMsg(fRc, ("%d\n", GetLastError())); + rc = VERR_CANCELLED; + } + } + else + { + /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */ + rc = VERR_CANCELLED; + } + rtLocalIpcServerReleaseAndUnlock(pThis); + return rc; +} + + +RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer) +{ + /* + * Validate input. + */ + PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE); + + /* + * Enter the critical section, then set the cancellation flag + * and signal the event (to wake up anyone in/at WaitForSingleObject). + */ + rtLocalIpcServerRetain(pThis); + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + ASMAtomicUoWriteBool(&pThis->fCancelled, true); + + BOOL fRc = SetEvent(pThis->hEvent); + if (fRc) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + AssertMsgFailed(("dwErr=%u\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + + rtLocalIpcServerReleaseAndUnlock(pThis); + } + else + rtLocalIpcServerRelease(pThis); + return rc; +} + + +/** + * Create a session instance for a new server client or a client connect. + * + * @returns IPRT status code. + * + * @param ppSession Where to store the session handle on success. + * @param hNmPipeSession The named pipe handle if server calling, + * INVALID_HANDLE_VALUE if client connect. This will + * be consumed by this session, meaning on failure to + * create the session it will be closed. + */ +static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession) +{ + AssertPtr(ppSession); + + /* + * Allocate and initialize the session instance data. + */ + int rc; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTLOCALIPCSESSION_MAGIC; + pThis->cRefs = 1; /* our ref */ + pThis->fCancelled = false; + pThis->fZeroByteRead = false; + pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE; + pThis->hNmPipe = hNmPipeSession; +#if 0 /* Non-blocking writes are not yet supported. */ + pThis->pbBounceBuf = NULL; + pThis->cbBounceBufAlloc = 0; + pThis->cbBounceBufUsed = 0; +#endif + rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); + if (pThis->Read.hEvent != NULL) + { + pThis->Read.OverlappedIO.Internal = STATUS_PENDING; + pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent; + pThis->Read.hActiveThread = NIL_RTTHREAD; + + pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); + if (pThis->Write.hEvent != NULL) + { + pThis->Write.OverlappedIO.Internal = STATUS_PENDING; + pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent; + pThis->Write.hActiveThread = NIL_RTTHREAD; + + *ppSession = pThis; + return VINF_SUCCESS; + } + + CloseHandle(pThis->Read.hEvent); + } + + /* bail out */ + rc = RTErrConvertFromWin32(GetLastError()); + RTCritSectDelete(&pThis->CritSect); + } + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + + if (hNmPipeSession != INVALID_HANDLE_VALUE) + { + BOOL fRc = CloseHandle(hNmPipeSession); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + } + return rc; +} + + +RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags) +{ + /* + * Validate input. + */ + AssertPtrReturn(phSession, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS); + + size_t cwcFullName; + int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME)); + if (RT_SUCCESS(rc)) + { + /* + * Create a session (shared with server client session creation). + */ + PRTLOCALIPCSESSIONINT pThis; + rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE); + if (RT_SUCCESS(rc)) + { + /* + * Try open the pipe. + */ + PSECURITY_DESCRIPTOR pSecDesc; + rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, false /*fServer*/); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16)); + if (pwszFullName) + rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName, + RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME)); + else + rc = VERR_NO_UTF16_MEMORY; + if (RT_SUCCESS(rc)) + { + SECURITY_ATTRIBUTES SecAttrs; + SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES); + SecAttrs.lpSecurityDescriptor = pSecDesc; + SecAttrs.bInheritHandle = FALSE; + + /* The SECURITY_XXX flags are needed in order to prevent the server from impersonating with + this thread's security context (supported at least back to NT 3.51). See @bugref{9773}. */ + HANDLE hPipe = CreateFileW(pwszFullName, + GENERIC_READ | GENERIC_WRITE, + 0 /*no sharing*/, + &SecAttrs, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, + NULL /*no template handle*/); + if (hPipe != INVALID_HANDLE_VALUE) + { + pThis->hNmPipe = hPipe; + + LocalFree(pSecDesc); + RTUtf16Free(pwszFullName); + + /* + * We're done! + */ + *phSession = pThis; + return VINF_SUCCESS; + } + + rc = RTErrConvertFromWin32(GetLastError()); + } + + RTUtf16Free(pwszFullName); + LocalFree(pSecDesc); + } + + /* destroy the session handle. */ + CloseHandle(pThis->Read.hEvent); + CloseHandle(pThis->Write.hEvent); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + } + } + return rc; +} + + +/** + * Cancells all pending I/O operations, forcing the methods to return with + * VERR_CANCELLED (unless they've got actual data to return). + * + * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose. + * + * @returns IPRT status code. + * @param pThis The client session instance. + */ +static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis) +{ + ASMAtomicUoWriteBool(&pThis->fCancelled, true); + + /* + * Call NtCancelIoFile since this call cancels both read and write + * oriented operations. + */ + if ( pThis->fZeroByteRead + || pThis->Read.hActiveThread != NIL_RTTHREAD + || pThis->Write.hActiveThread != NIL_RTTHREAD) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NtCancelIoFile(pThis->hNmPipe, &Ios); + } + + /* + * Set both event semaphores. + */ + BOOL fRc = SetEvent(pThis->Read.hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + fRc = SetEvent(pThis->Write.hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + + return VINF_SUCCESS; +} + + +/** + * Retains a reference to the session instance. + * + * @param pThis The client session instance. + */ +DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs); +} + + +RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2 && cRefs); + return cRefs; +} + + +/** + * Call when the reference count reaches 0. + * + * Caller owns the critsect. + * + * @returns VINF_OBJECT_DESTROYED + * @param pThis The instance to destroy. + */ +DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis) +{ + BOOL fRc = CloseHandle(pThis->hNmPipe); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + pThis->hNmPipe = INVALID_HANDLE_VALUE; + + fRc = CloseHandle(pThis->Write.hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); + pThis->Write.hEvent = NULL; + + fRc = CloseHandle(pThis->Read.hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); + pThis->Read.hEvent = NULL; + + int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + return VINF_OBJECT_DESTROYED; +} + + +/** + * Releases a reference to the session instance and unlock it. + * + * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate. + * @param pThis The session instance. + */ +DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis) +{ + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (!cRefs) + return rtLocalIpcSessionWinDestroy(pThis); + + int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2); + Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs)); + return VINF_SUCCESS; +} + + +RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession) +{ + if (hSession == NIL_RTLOCALIPCSESSION) + return 0; + + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (cRefs) + Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs)); + else + { + RTCritSectEnter(&pThis->CritSect); + rtLocalIpcSessionWinDestroy(pThis); + } + return cRefs; +} + + +RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession) +{ + /* + * Validate input. + */ + if (hSession == NIL_RTLOCALIPCSESSION) + return VINF_SUCCESS; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + + /* + * Invalidate the instance, cancel all outstanding I/O and drop our reference. + */ + RTCritSectEnter(&pThis->CritSect); + rtLocalIpcWinCancel(pThis); + return rtLocalIpcSessionReleaseAndUnlock(pThis); +} + + +/** + * Handles WaitForSingleObject return value when waiting for a zero byte read. + * + * The zero byte read is started by the RTLocalIpcSessionWaitForData method and + * left pending when the function times out. This saves us the problem of + * NtCancelIoFile messing with all active I/O operations and the trouble of + * restarting the zero byte read the next time the method is called. However + * should RTLocalIpcSessionRead be called after a failed + * RTLocalIpcSessionWaitForData call, the zero byte read will still be pending + * and it must wait for it to complete before the OVERLAPPEDIO structure can be + * reused. + * + * Thus, both functions will do WaitForSingleObject and share this routine to + * handle the outcome. + * + * @returns IPRT status code. + * @param pThis The session instance. + * @param rcWait The WaitForSingleObject return code. + */ +static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait) +{ + int rc; + DWORD cbRead = 42; + if (rcWait == WAIT_OBJECT_0) + { + if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/)) + { + Assert(cbRead == 0); + rc = VINF_SUCCESS; + pThis->fZeroByteRead = false; + } + else if (pThis->fCancelled) + rc = VERR_CANCELLED; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + /* We try get the result here too, just in case we're lucky, but no waiting. */ + DWORD dwErr = GetLastError(); + if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/)) + { + Assert(cbRead == 0); + rc = VINF_SUCCESS; + pThis->fZeroByteRead = false; + } + else if (rcWait == WAIT_TIMEOUT) + rc = VERR_TIMEOUT; + else if (rcWait == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(dwErr); + } + return rc; +} + + +RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + /* pcbRead is optional. */ + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rtLocalIpcSessionRetain(pThis); + if (pThis->Read.hActiveThread == NIL_RTTHREAD) + { + pThis->Read.hActiveThread = RTThreadSelf(); + + size_t cbTotalRead = 0; + while (cbToRead > 0) + { + DWORD cbRead = 0; + if (!pThis->fCancelled) + { + /* + * Wait for pending zero byte read, if necessary. + * Note! It cannot easily be cancelled due to concurrent current writes. + */ + if (!pThis->fZeroByteRead) + { /* likely */ } + else + { + RTCritSectLeave(&pThis->CritSect); + DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN); + RTCritSectEnter(&pThis->CritSect); + + rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait); + if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT) + continue; + break; + } + + /* + * Kick of a an overlapped read. It should return immediately if + * there is bytes in the buffer. If not, we'll cancel it and see + * what we get back. + */ + rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE); + RTCritSectLeave(&pThis->CritSect); + + if (ReadFile(pThis->hNmPipe, pvBuf, + cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0, + &cbRead, &pThis->Read.OverlappedIO)) + { + RTCritSectEnter(&pThis->CritSect); + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE); + + RTCritSectEnter(&pThis->CritSect); + if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + { + if (pThis->fCancelled) + rc = VERR_CANCELLED; + else + rc = RTErrConvertFromWin32(GetLastError()); + break; + } + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailedBreak(("%Rrc\n", rc)); + } + } + else + { + rc = VERR_CANCELLED; + break; + } + + /* Advance. */ + cbToRead -= cbRead; + cbTotalRead += cbRead; + pvBuf = (uint8_t *)pvBuf + cbRead; + } + + if (pcbRead) + { + *pcbRead = cbTotalRead; + if ( RT_FAILURE(rc) + && cbTotalRead + && rc != VERR_INVALID_POINTER) + rc = VINF_SUCCESS; + } + + pThis->Read.hActiveThread = NIL_RTTHREAD; + } + else + rc = VERR_WRONG_ORDER; + rtLocalIpcSessionReleaseAndUnlock(pThis); + } + + return rc; +} + + +RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertPtrReturn(pcbRead, VERR_INVALID_POINTER); + *pcbRead = 0; + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rtLocalIpcSessionRetain(pThis); + if (pThis->Read.hActiveThread == NIL_RTTHREAD) + { + pThis->Read.hActiveThread = RTThreadSelf(); + + for (;;) + { + DWORD cbRead = 0; + if (!pThis->fCancelled) + { + /* + * Wait for pending zero byte read, if necessary. + * Note! It cannot easily be cancelled due to concurrent current writes. + */ + if (!pThis->fZeroByteRead) + { /* likely */ } + else + { + RTCritSectLeave(&pThis->CritSect); + DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0); + RTCritSectEnter(&pThis->CritSect); + + rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait); + if (RT_SUCCESS(rc)) + continue; + + if (rc == VERR_TIMEOUT) + rc = VINF_TRY_AGAIN; + break; + } + + /* + * Figure out how much we can read (cannot try and cancel here + * like in the anonymous pipe code). + */ + DWORD cbAvailable; + if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)) + { + if (cbAvailable == 0 || cbToRead == 0) + { + *pcbRead = 0; + rc = VINF_TRY_AGAIN; + break; + } + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + break; + } + if (cbAvailable > cbToRead) + cbAvailable = (DWORD)cbToRead; + + /* + * Kick of a an overlapped read. It should return immediately, so we + * don't really need to leave the critsect here. + */ + rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE); + if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO)) + { + *pcbRead = cbRead; + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0); + if (rcWait == WAIT_TIMEOUT) + { + RTCritSectLeave(&pThis->CritSect); + rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE); + RTCritSectEnter(&pThis->CritSect); + } + if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/)) + { + *pcbRead = cbRead; + rc = VINF_SUCCESS; + } + else + { + if (pThis->fCancelled) + rc = VERR_CANCELLED; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailedBreak(("%Rrc\n", rc)); + } + } + else + rc = VERR_CANCELLED; + break; + } + + pThis->Read.hActiveThread = NIL_RTTHREAD; + } + else + rc = VERR_WRONG_ORDER; + rtLocalIpcSessionReleaseAndUnlock(pThis); + } + + return rc; +} + + +#if 0 /* Non-blocking writes are not yet supported. */ +/** + * Common worker for handling I/O completion. + * + * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + */ +static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis) +{ + int rc; + DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0); + if (rcWait == WAIT_OBJECT_0) + { + DWORD cbWritten = 0; + if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE)) + { + for (;;) + { + if (cbWritten >= pThis->cbBounceBufUsed) + { + pThis->fIOPending = false; + rc = VINF_SUCCESS; + break; + } + + /* resubmit the remainder of the buffer - can this actually happen? */ + memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten); + rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE); + if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, + &cbWritten, &pThis->OverlappedIO)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_IO_PENDING) + rc = VINF_TRY_AGAIN; + else + { + pThis->fIOPending = false; + if (dwErr == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(dwErr); + } + break; + } + Assert(cbWritten > 0); + } + } + else + { + pThis->fIOPending = false; + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else if (rcWait == WAIT_TIMEOUT) + rc = VINF_TRY_AGAIN; + else + { + pThis->fIOPending = false; + if (rcWait == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + return rc; +} +#endif + + +RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToWrite, VERR_INVALID_PARAMETER); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rtLocalIpcSessionRetain(pThis); + if (pThis->Write.hActiveThread == NIL_RTTHREAD) + { + pThis->Write.hActiveThread = RTThreadSelf(); + + /* + * Try write everything. No bounce buffering necessary. + */ + size_t cbTotalWritten = 0; + while (cbToWrite > 0) + { + DWORD cbWritten = 0; + if (!pThis->fCancelled) + { + BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE); + RTCritSectLeave(&pThis->CritSect); + + DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0; + fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO); + if (fRc) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_IO_PENDING) + { + DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE); + if (rcWait == WAIT_OBJECT_0) + { + if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (rcWait == WAIT_TIMEOUT) + rc = VERR_TIMEOUT; + else if (rcWait == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (dwErr == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(dwErr); + } + + if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */ + cbWritten = cbToWriteInThisIteration; + + RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + break; + } + else + { + rc = VERR_CANCELLED; + break; + } + + /* Advance. */ + pvBuf = (char const *)pvBuf + cbWritten; + cbTotalWritten += cbWritten; + cbToWrite -= cbWritten; + } + + pThis->Write.hActiveThread = NIL_RTTHREAD; + } + else + rc = VERR_WRONG_ORDER; + rtLocalIpcSessionReleaseAndUnlock(pThis); + } + + return rc; +} + + +RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + if (pThis->Write.hActiveThread == NIL_RTTHREAD) + { + /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until + * all data was written (or an error occurred). */ + /** @todo r=bird: above comment is misinformed. + * Implement this as soon as we want an explicit asynchronous version of + * RTLocalIpcSessionWrite on Windows. */ + rc = VINF_SUCCESS; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + + uint64_t const msStart = RTTimeMilliTS(); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rtLocalIpcSessionRetain(pThis); + if (pThis->Read.hActiveThread == NIL_RTTHREAD) + { + pThis->Read.hActiveThread = RTThreadSelf(); + + /* + * Wait loop. + */ + for (unsigned iLoop = 0;; iLoop++) + { + /* + * Check for cancellation before we continue. + */ + if (!pThis->fCancelled) + { /* likely */ } + else + { + rc = VERR_CANCELLED; + break; + } + + /* + * Prep something we can wait on. + */ + HANDLE hWait = INVALID_HANDLE_VALUE; + if (pThis->fZeroByteRead) + hWait = pThis->Read.OverlappedIO.hEvent; + else + { + /* Peek at the pipe buffer and see how many bytes it contains. */ + DWORD cbAvailable; + if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL) + && cbAvailable) + { + rc = VINF_SUCCESS; + break; + } + + /* Start a zero byte read operation that we can wait on. */ + if (cMillies == 0) + { + rc = VERR_TIMEOUT; + break; + } + BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc); + DWORD cbRead = 0; + if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO)) + { + rc = VINF_SUCCESS; + if (iLoop > 10) + RTThreadYield(); + } + else if (GetLastError() == ERROR_IO_PENDING) + { + pThis->fZeroByteRead = true; + hWait = pThis->Read.OverlappedIO.hEvent; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + if (RT_FAILURE(rc)) + break; + } + + /* + * Check for timeout. + */ + DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */ + if (cMillies == RT_INDEFINITE_WAIT) + cMsMaxWait = INFINITE; + else if ( hWait != INVALID_HANDLE_VALUE + || iLoop > 10) + { + uint64_t cMsElapsed = RTTimeMilliTS() - msStart; + if (cMsElapsed <= cMillies) + cMsMaxWait = cMillies - (uint32_t)cMsElapsed; + else if (iLoop == 0) + cMsMaxWait = cMillies ? 1 : 0; + else + { + rc = VERR_TIMEOUT; + break; + } + } + + /* + * Wait and collect the result. + */ + if (hWait != INVALID_HANDLE_VALUE) + { + RTCritSectLeave(&pThis->CritSect); + + DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait); + + int rc2 = RTCritSectEnter(&pThis->CritSect); + AssertRC(rc2); + + rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait); + break; + } + } + + pThis->Read.hActiveThread = NIL_RTTHREAD; + } + + rtLocalIpcSessionReleaseAndUnlock(pThis); + } + + return rc; +} + + +RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession) +{ + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + + /* + * Enter the critical section, then set the cancellation flag + * and signal the event (to wake up anyone in/at WaitForSingleObject). + */ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + rtLocalIpcSessionRetain(pThis); + rc = rtLocalIpcWinCancel(pThis); + rtLocalIpcSessionReleaseAndUnlock(pThis); + } + + return rc; +} + + +RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess) +{ + RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess); + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid) +{ + RT_NOREF_PV(hSession); RT_NOREF_PV(pUid); + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid) +{ + RT_NOREF_PV(hSession); RT_NOREF_PV(pGid); + return VERR_NOT_SUPPORTED; +} + diff --git a/src/VBox/Runtime/r3/win/mp-win.cpp b/src/VBox/Runtime/r3/win/mp-win.cpp new file mode 100644 index 00000000..b76ab254 --- /dev/null +++ b/src/VBox/Runtime/r3/win/mp-win.cpp @@ -0,0 +1,822 @@ +/* $Id: mp-win.cpp $ */ +/** @file + * IPRT - Multiprocessor, Windows. + */ + +/* + * 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_SYSTEM +#include <iprt/win/windows.h> + +#include <iprt/mp.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/cpuset.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/once.h> +#include <iprt/param.h> +#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) +# include <iprt/asm-amd64-x86.h> +#endif +#if defined(VBOX) && !defined(IN_GUEST) && !defined(IN_RT_STATIC) +# include <VBox/sup.h> +# define IPRT_WITH_GIP_MP_INFO +#else +# undef IPRT_WITH_GIP_MP_INFO +#endif + +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** @def RTMPWIN_UPDATE_GIP_GLOBALS + * Does lazy (re-)initialization using information provieded by GIP. */ +#ifdef IPRT_WITH_GIP_MP_INFO +# define RTMPWIN_UPDATE_GIP_GLOBALS() \ + do { RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); } while (0) +#else +# define RTMPWIN_UPDATE_GIP_GLOBALS() do { } while (0) +#endif + +/** @def RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP + * Does lazy (re-)initialization using information provieded by GIP and + * declare and initalize a pGip local variable. */ +#ifdef IPRT_WITH_GIP_MP_INFO +#define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() \ + PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; \ + if (pGip) \ + { \ + if ( pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC \ + && RTOnce(&g_MpInitOnceGip, rtMpWinInitOnceGip, NULL) == VINF_SUCCESS) \ + { \ + if (g_cRtMpWinActiveCpus >= pGip->cOnlineCpus) \ + { /* likely */ } \ + else \ + rtMpWinRefreshGip(); \ + } \ + else \ + pGip = NULL; \ + } else do { } while (0) +#else +# define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() do { } while (0) +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Initialize once. */ +static RTONCE g_MpInitOnce = RTONCE_INITIALIZER; +#ifdef IPRT_WITH_GIP_MP_INFO +/** Initialize once using GIP. */ +static RTONCE g_MpInitOnceGip = RTONCE_INITIALIZER; +#endif + +static decltype(GetMaximumProcessorCount) *g_pfnGetMaximumProcessorCount; +//static decltype(GetActiveProcessorCount) *g_pfnGetActiveProcessorCount; +static decltype(GetCurrentProcessorNumber) *g_pfnGetCurrentProcessorNumber; +static decltype(GetCurrentProcessorNumberEx) *g_pfnGetCurrentProcessorNumberEx; +static decltype(GetLogicalProcessorInformation) *g_pfnGetLogicalProcessorInformation; +static decltype(GetLogicalProcessorInformationEx) *g_pfnGetLogicalProcessorInformationEx; + + +/** The required buffer size for getting group relations. */ +static uint32_t g_cbRtMpWinGrpRelBuf; +/** The max number of CPUs (RTMpGetCount). */ +static uint32_t g_cRtMpWinMaxCpus; +/** The max number of CPU cores (RTMpGetCoreCount). */ +static uint32_t g_cRtMpWinMaxCpuCores; +/** The max number of groups. */ +static uint32_t g_cRtMpWinMaxCpuGroups; +/** The number of active CPUs the last time we checked. */ +static uint32_t volatile g_cRtMpWinActiveCpus; +/** Static per group info. + * @remarks With 256 entries this takes up 33KB. + * @sa g_aRtMpNtCpuGroups */ +static struct +{ + /** The max CPUs in the group. */ + uint16_t cMaxCpus; + /** The number of active CPUs at the time of initialization. */ + uint16_t cActiveCpus; + /** CPU set indexes for each CPU in the group. */ + int16_t aidxCpuSetMembers[64]; +} g_aRtMpWinCpuGroups[256]; +/** Maps CPU set indexes to RTCPUID. + * @sa g_aidRtMpNtByCpuSetIdx */ +RTCPUID g_aidRtMpWinByCpuSetIdx[RTCPUSET_MAX_CPUS]; + + +/** + * @callback_method_impl{FNRTONCE, + * Resolves dynamic imports and initializes globals.} + */ +static DECLCALLBACK(int32_t) rtMpWinInitOnce(void *pvUser) +{ + RT_NOREF(pvUser); + + Assert(g_WinOsInfoEx.dwOSVersionInfoSize != 0); + Assert(g_hModKernel32 != NULL); + + /* + * Resolve dynamic APIs. + */ +#define RESOLVE_API(a_szMod, a_FnName) \ + do { \ + RT_CONCAT(g_pfn,a_FnName) = (decltype(a_FnName) *)GetProcAddress(g_hModKernel32, #a_FnName); \ + } while (0) + RESOLVE_API("kernel32.dll", GetMaximumProcessorCount); + //RESOLVE_API("kernel32.dll", GetActiveProcessorCount); - slow :/ + RESOLVE_API("kernel32.dll", GetCurrentProcessorNumber); + RESOLVE_API("kernel32.dll", GetCurrentProcessorNumberEx); + RESOLVE_API("kernel32.dll", GetLogicalProcessorInformation); + RESOLVE_API("kernel32.dll", GetLogicalProcessorInformationEx); + + /* + * Reset globals. + */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++) + g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID; + for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpWinCpuGroups); idxGroup++) + { + g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = 0; + g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0; + for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++) + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1; + } + + /* + * Query group information, partitioning CPU IDs and CPU set indexes. + * + * We ASSUME the GroupInfo index is the same as the group number. + * + * We CANNOT ASSUME that the kernel CPU indexes are assigned in any given + * way, though they usually are in group order by active processor. So, + * we do that to avoid trouble. We must use information provided thru GIP + * if we want the kernel CPU set indexes. Even there, the inactive CPUs + * wont have sensible indexes. Sigh. + * + * We try to assign IDs to inactive CPUs in the same manner as mp-r0drv-nt.cpp + * + * Note! We will die (AssertFatal) if there are too many processors! + */ + union + { + SYSTEM_INFO SysInfo; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Info; + uint8_t abPaddingG[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) + + sizeof(PROCESSOR_GROUP_INFO) * 256]; + uint8_t abPaddingC[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) + + (sizeof(PROCESSOR_RELATIONSHIP) + sizeof(GROUP_AFFINITY)) + * RTCPUSET_MAX_CPUS]; + } uBuf; + if (g_pfnGetLogicalProcessorInformationEx) + { + /* Query the information. */ + DWORD cbData = sizeof(uBuf); + AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, &uBuf.Info, &cbData) != FALSE, + ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf))); + AssertFatalMsg(uBuf.Info.Relationship == RelationGroup, + ("Relationship = %u, expected %u!\n", uBuf.Info.Relationship, RelationGroup)); + AssertFatalMsg(uBuf.Info.Group.MaximumGroupCount <= RT_ELEMENTS(g_aRtMpWinCpuGroups), + ("MaximumGroupCount is %u, we only support up to %u!\n", + uBuf.Info.Group.MaximumGroupCount, RT_ELEMENTS(g_aRtMpWinCpuGroups))); + + AssertMsg(uBuf.Info.Group.MaximumGroupCount == uBuf.Info.Group.ActiveGroupCount, /* 2nd assumption mentioned above. */ + ("%u vs %u\n", uBuf.Info.Group.MaximumGroupCount, uBuf.Info.Group.ActiveGroupCount)); + AssertFatal(uBuf.Info.Group.MaximumGroupCount >= uBuf.Info.Group.ActiveGroupCount); + + g_cRtMpWinMaxCpuGroups = uBuf.Info.Group.MaximumGroupCount; + + /* Count max cpus (see mp-r0drv0-nt.cpp) why we don't use GetMaximumProcessorCount(ALL). */ + uint32_t idxGroup; + g_cRtMpWinMaxCpus = 0; + for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++) + g_cRtMpWinMaxCpus += uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount; + + /* Process the active groups. */ + uint32_t cActive = 0; + uint32_t cInactive = 0; + uint32_t idxCpu = 0; + uint32_t idxCpuSetNextInactive = g_cRtMpWinMaxCpus - 1; + for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++) + { + PROCESSOR_GROUP_INFO const *pGroupInfo = &uBuf.Info.Group.GroupInfo[idxGroup]; + g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = pGroupInfo->MaximumProcessorCount; + g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = pGroupInfo->ActiveProcessorCount; + for (uint32_t idxMember = 0; idxMember < pGroupInfo->MaximumProcessorCount; idxMember++) + { + if (pGroupInfo->ActiveProcessorMask & RT_BIT_64(idxMember)) + { + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu; + g_aidRtMpWinByCpuSetIdx[idxCpu] = idxCpu; + idxCpu++; + cActive++; + } + else + { + if (idxCpuSetNextInactive >= idxCpu) + { + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive; + g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive; + idxCpuSetNextInactive--; + } + cInactive++; + } + } + } + g_cRtMpWinActiveCpus = cActive; + Assert(cActive + cInactive <= g_cRtMpWinMaxCpus); + Assert(idxCpu <= idxCpuSetNextInactive + 1); + Assert(idxCpu <= g_cRtMpWinMaxCpus); + + /* Just in case the 2nd assumption doesn't hold true and there are inactive groups. */ + for (; idxGroup < uBuf.Info.Group.MaximumGroupCount; idxGroup++) + { + DWORD cMaxMembers = g_pfnGetMaximumProcessorCount(idxGroup); + g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers; + g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0; + for (uint32_t idxMember = 0; idxMember < cMaxMembers; idxMember++) + { + if (idxCpuSetNextInactive >= idxCpu) + { + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive; + g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive; + idxCpuSetNextInactive--; + } + cInactive++; + } + } + Assert(cActive + cInactive <= g_cRtMpWinMaxCpus); + Assert(idxCpu <= idxCpuSetNextInactive + 1); + } + else + { + /* Legacy: */ + GetSystemInfo(&uBuf.SysInfo); + g_cRtMpWinMaxCpuGroups = 1; + g_cRtMpWinMaxCpus = uBuf.SysInfo.dwNumberOfProcessors; + g_aRtMpWinCpuGroups[0].cMaxCpus = uBuf.SysInfo.dwNumberOfProcessors; + g_aRtMpWinCpuGroups[0].cActiveCpus = uBuf.SysInfo.dwNumberOfProcessors; + + for (uint32_t idxMember = 0; idxMember < uBuf.SysInfo.dwNumberOfProcessors; idxMember++) + { + g_aRtMpWinCpuGroups[0].aidxCpuSetMembers[idxMember] = idxMember; + g_aidRtMpWinByCpuSetIdx[idxMember] = idxMember; + } + } + + AssertFatalMsg(g_cRtMpWinMaxCpus <= RTCPUSET_MAX_CPUS, + ("g_cRtMpWinMaxCpus=%u (%#x); RTCPUSET_MAX_CPUS=%u\n", g_cRtMpWinMaxCpus, g_cRtMpWinMaxCpus, RTCPUSET_MAX_CPUS)); + + g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) + + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO); + + /* + * Get information about cores. + * + * Note! This will only give us info about active processors according to + * MSDN, we'll just have to hope that CPUs aren't hotplugged after we + * initialize here (or that the API consumers doesn't care too much). + */ + /** @todo A hot CPU plug event would be nice. */ + g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus; + if (g_pfnGetLogicalProcessorInformationEx) + { + /* Query the information. */ + DWORD cbData = sizeof(uBuf); + AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationProcessorCore, &uBuf.Info, &cbData) != FALSE, + ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf))); + g_cRtMpWinMaxCpuCores = 0; + for (uint32_t off = 0; off < cbData; ) + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pCur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)&uBuf.abPaddingG[off]; + AssertFatalMsg(pCur->Relationship == RelationProcessorCore, + ("off = %#x, Relationship = %u, expected %u!\n", off, pCur->Relationship, RelationProcessorCore)); + g_cRtMpWinMaxCpuCores++; + off += pCur->Size; + } + +#if ARCH_BITS == 32 + if (g_cRtMpWinMaxCpuCores > g_cRtMpWinMaxCpus) + { + /** @todo WOW64 trouble where the emulation environment has folded the high + * processor masks (63..32) into the low (31..0), hiding some + * processors from us. Currently we don't deal with that. */ + g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus; + } + else + AssertStmt(g_cRtMpWinMaxCpuCores > 0, g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); +#else + AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus, + g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); +#endif + } + else + { + /* + * Sadly, on XP and Server 2003, even if the API is present, it does not tell us + * how many physical cores there are (any package will look like a single core). + * That is worse than not using the API at all, so just skip it unless it's Vista+. + */ + if ( g_pfnGetLogicalProcessorInformation + && g_WinOsInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT + && g_WinOsInfoEx.dwMajorVersion >= 6) + { + /* Query the info. */ + DWORD cbSysProcInfo = _4K; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL; + BOOL fRc = FALSE; + do + { + cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256); + void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo); + if (!pv) + break; + paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv; + fRc = g_pfnGetLogicalProcessorInformation(paSysInfo, &cbSysProcInfo); + } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER); + if (fRc) + { + /* Count the cores in the result. */ + g_cRtMpWinMaxCpuCores = 0; + uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]); + while (i-- > 0) + if (paSysInfo[i].Relationship == RelationProcessorCore) + g_cRtMpWinMaxCpuCores++; + + AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus, + g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus); + } + RTMemFree(paSysInfo); + } + } + + return VINF_SUCCESS; +} + + +#ifdef IPRT_WITH_GIP_MP_INFO +/** + * @callback_method_impl{FNRTONCE, Updates globals with information from GIP.} + */ +static DECLCALLBACK(int32_t) rtMpWinInitOnceGip(void *pvUser) +{ + RT_NOREF(pvUser); + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + + PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; + if ( pGip + && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC) + { + /* + * Update globals. + */ + if (g_cRtMpWinMaxCpus != pGip->cPossibleCpus) + g_cRtMpWinMaxCpus = pGip->cPossibleCpus; + if (g_cRtMpWinActiveCpus != pGip->cOnlineCpus) + g_cRtMpWinActiveCpus = pGip->cOnlineCpus; + Assert(g_cRtMpWinMaxCpuGroups == pGip->cPossibleCpuGroups); + if (g_cRtMpWinMaxCpuGroups != pGip->cPossibleCpuGroups) + { + g_cRtMpWinMaxCpuGroups = pGip->cPossibleCpuGroups; + g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) + + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO); + } + + /* + * Update CPU set IDs. + */ + for (unsigned i = g_cRtMpWinMaxCpus; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++) + g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID; + + unsigned const cbGip = pGip->cPages * PAGE_SIZE; + for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++) + { + uint32_t idxMember; + uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup]; + if (offCpuGroup < cbGip) + { + PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup); + uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers; + AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers), + cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers)); + g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers; + g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers); + + for (idxMember = 0; idxMember < cMaxMembers; idxMember++) + { + int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember]; + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet; + if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx)) +# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember); +# else + g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet; +# endif + } + } + else + idxMember = 0; + for (; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers); idxMember++) + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1; + } + } + + return VINF_SUCCESS; +} + + +/** + * Refreshes globals from GIP after one or more CPUs were added. + * + * There are potential races here. We might race other threads and we may race + * more CPUs being added. + */ +static void rtMpWinRefreshGip(void) +{ + PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; + if ( pGip + && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC) + { + /* + * Since CPUs cannot be removed, we only have to update the IDs and + * indexes of CPUs that we think are inactive and the group member counts. + */ + for (;;) + { + unsigned const cbGip = pGip->cPages * PAGE_SIZE; + uint32_t const cGipActiveCpus = pGip->cOnlineCpus; + uint32_t const cMyActiveCpus = ASMAtomicReadU32(&g_cRtMpWinActiveCpus); + ASMCompilerBarrier(); + + for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++) + { + uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup]; + if (offCpuGroup < cbGip) + { + PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup); + uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers; + AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers), + cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers)); + for (uint32_t idxMember = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus; idxMember < cMaxMembers; idxMember++) + { + int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember]; + g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet; + if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx)) +# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember); +# else + g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet; +# endif + } + g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers); + g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers); + } + else + Assert(g_aRtMpWinCpuGroups[idxGroup].cActiveCpus == 0); + } + + ASMCompilerBarrier(); + if (cGipActiveCpus == pGip->cOnlineCpus) + if (ASMAtomicCmpXchgU32(&g_cRtMpWinActiveCpus, cGipActiveCpus, cMyActiveCpus)) + break; + } + } +} + +#endif /* IPRT_WITH_GIP_MP_INFO */ + + +/* + * Conversion between CPU ID and set index. + */ + +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + if (idCpu != NIL_RTCPUID) + return RTMpSetIndexFromCpuGroupMember(rtMpCpuIdGetGroup(idCpu), rtMpCpuIdGetGroupMember(idCpu)); + return -1; + +#else + /* 1:1 mapping, just do range checking. */ + return idCpu < g_cRtMpWinMaxCpus ? idCpu : -1; +#endif +} + + +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + if ((unsigned)iCpu < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx)) + { + RTCPUID idCpu = g_aidRtMpWinByCpuSetIdx[iCpu]; + +#if defined(IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER) && defined(RT_STRICT) + /* Check the correctness of the mapping table. */ + RTCPUID idCpuGip = NIL_RTCPUID; + if ( pGip + && (unsigned)iCpu < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)) + { + unsigned idxSupCpu = pGip->aiCpuFromCpuSetIdx[idxGuess]; + if (idxSupCpu < pGip->cCpus) + if (pGip->aCPUs[idxSupCpu].enmState != SUPGIPCPUSTATE_INVALID) + idCpuGip = pGip->aCPUs[idxSupCpu].idCpu; + } + AssertMsg(idCpu == idCpuGip, ("table:%#x gip:%#x\n", idCpu, idCpuGip)); +#endif + + return idCpu; + } + return NIL_RTCPUID; +} + + +RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + if (idxGroup < g_cRtMpWinMaxCpuGroups) + if (idxMember < g_aRtMpWinCpuGroups[idxGroup].cMaxCpus) + return g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]; + return -1; +} + + +RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + if (idxGroup < g_cRtMpWinMaxCpuGroups) + { + if (pcActive) + *pcActive = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus; + return g_aRtMpWinCpuGroups[idxGroup].cMaxCpus; + } + if (pcActive) + *pcActive = 0; + return 0; +} + + +RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + return g_cRtMpWinMaxCpuGroups; +} + + + +/* + * Get current CPU. + */ + +RTDECL(RTCPUID) RTMpCpuId(void) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + PROCESSOR_NUMBER ProcNum; + ProcNum.Group = 0; + ProcNum.Number = 0xff; + if (g_pfnGetCurrentProcessorNumberEx) + g_pfnGetCurrentProcessorNumberEx(&ProcNum); + else if (g_pfnGetCurrentProcessorNumber) + { + DWORD iCpu = g_pfnGetCurrentProcessorNumber(); + Assert(iCpu < g_cRtMpWinMaxCpus); + ProcNum.Number = iCpu; + } + else + { +#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) + ProcNum.Number = ASMGetApicId(); +#else +# error "Not ported to this architecture." + return NIL_RTCPUID; +#endif + } + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number); +#else + return RTMpSetIndexFromCpuGroupMember(ProcNum.Group, ProcNum.Number); +#endif +} + + +/* + * Possible CPUs and cores. + */ + +RTDECL(RTCPUID) RTMpGetMaxCpuId(void) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + +#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER + return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpWinMaxCpuGroups - 1, + g_aRtMpWinCpuGroups[g_cRtMpWinMaxCpuGroups - 1].cMaxCpus - 1); +#else + return g_cRtMpWinMaxCpus - 1; +#endif +} + + +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + /* Any CPU between 0 and g_cRtMpWinMaxCpus are possible. */ + return idCpu < g_cRtMpWinMaxCpus; +} + + +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet) +{ + RTCPUID iCpu = RTMpGetCount(); + RTCpuSetEmpty(pSet); + while (iCpu-- > 0) + RTCpuSetAddByIndex(pSet, iCpu); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetCount(void) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + return g_cRtMpWinMaxCpus; +} + + +RTDECL(RTCPUID) RTMpGetCoreCount(void) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + RTMPWIN_UPDATE_GIP_GLOBALS(); + + return g_cRtMpWinMaxCpuCores; +} + + +/* + * Online CPUs and cores. + */ + +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) +{ + RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL); + +#ifdef IPRT_WITH_GIP_MP_INFO + RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); + if (pGip) + { + *pSet = pGip->OnlineCpuSet; + return pSet; + } +#endif + + if (g_pfnGetLogicalProcessorInformationEx) + { + /* + * Get the group relation info. + * + * In addition to the ASSUMPTIONS that are documented in rtMpWinInitOnce, + * we ASSUME that PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the + * active processor mask width. + */ + /** @todo this is not correct for WOW64 */ + DWORD cbInfo = g_cbRtMpWinGrpRelBuf; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)alloca(cbInfo); + AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbInfo) != FALSE, + ("last error = %u, cbInfo = %u (in %u)\n", GetLastError(), cbInfo, g_cbRtMpWinGrpRelBuf)); + AssertFatalMsg(pInfo->Relationship == RelationGroup, + ("Relationship = %u, expected %u!\n", pInfo->Relationship, RelationGroup)); + AssertFatalMsg(pInfo->Group.MaximumGroupCount == g_cRtMpWinMaxCpuGroups, + ("MaximumGroupCount is %u, expected %u!\n", pInfo->Group.MaximumGroupCount, g_cRtMpWinMaxCpuGroups)); + + RTCpuSetEmpty(pSet); + for (uint32_t idxGroup = 0; idxGroup < pInfo->Group.MaximumGroupCount; idxGroup++) + { + Assert(pInfo->Group.GroupInfo[idxGroup].MaximumProcessorCount == g_aRtMpWinCpuGroups[idxGroup].cMaxCpus); + Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount <= g_aRtMpWinCpuGroups[idxGroup].cMaxCpus); + + KAFFINITY fActive = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorMask; + if (fActive != 0) + { +#ifdef RT_STRICT + uint32_t cMembersLeft = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount; +#endif + int const cMembers = g_aRtMpWinCpuGroups[idxGroup].cMaxCpus; + for (int idxMember = 0; idxMember < cMembers; idxMember++) + { + if (fActive & 1) + { +#ifdef RT_STRICT + cMembersLeft--; +#endif + RTCpuSetAddByIndex(pSet, g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]); + fActive >>= 1; + if (!fActive) + break; + } + else + { + fActive >>= 1; + } + } + Assert(cMembersLeft == 0); + } + else + Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount == 0); + } + + return pSet; + } + + /* + * Legacy fallback code. + */ + SYSTEM_INFO SysInfo; + GetSystemInfo(&SysInfo); + return RTCpuSetFromU64(pSet, SysInfo.dwActiveProcessorMask); +} + + +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) +{ + RTCPUSET Set; + return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu); +} + + +RTDECL(RTCPUID) RTMpGetOnlineCount(void) +{ +#ifdef IPRT_WITH_GIP_MP_INFO + RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); + if (pGip) + return pGip->cOnlineCpus; +#endif + + RTCPUSET Set; + RTMpGetOnlineSet(&Set); + return RTCpuSetCount(&Set); +} + + +RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void) +{ + /** @todo this isn't entirely correct, but whatever. */ + return RTMpGetCoreCount(); +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp new file mode 100644 index 00000000..5f74b4f5 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp @@ -0,0 +1,57 @@ +/* $Id: nocrt-RTLogWriteStdErr-win.cpp $ */ +/** @file + * IPRT - Log To StdErr, Windows no-CRT. + */ + +/* + * 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 <iprt/win/windows.h> + + +RTDECL(void) RTLogWriteStdErr(const char *pch, size_t cb) +{ + HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE); + if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE) + { + DWORD cbIgn; /* NT3.1 requires the return size parameter. */ + WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */ + } +} +RT_EXPORT_SYMBOL(RTLogWriteStdErr); + diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp new file mode 100644 index 00000000..56e29134 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp @@ -0,0 +1,58 @@ +/* $Id: nocrt-RTLogWriteStdOut-win.cpp $ */ +/** @file + * IPRT - Log To StdOut, Windows no-CRT. + */ + +/* + * 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 <iprt/win/windows.h> + + +RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb) +{ + /** @todo should flush the stdout stream first... */ + HANDLE hStdErr = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE) + { + DWORD cbIgn; /* NT3.1 requires the return size parameter. */ + WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */ + } +} +RT_EXPORT_SYMBOL(RTLogWriteStdOut); + diff --git a/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm new file mode 100644 index 00000000..1943ad69 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm @@ -0,0 +1,44 @@ +; $Id: nocrt-WinMainCRTStartup-win.asm $ +;; @file +; IPRT - Alias WinMainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp. +; + +; +; 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 +; + + +%include "iprt/asmdefs.mac" + +extern NAME(CustomMainEntrypoint) +BEGINPROC WinMainCRTStartup + jmp NAME(CustomMainEntrypoint) +ENDPROC WinMainCRTStartup + diff --git a/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp new file mode 100644 index 00000000..128b58df --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp @@ -0,0 +1,115 @@ +/* $Id: nocrt-alloc-win.cpp $ */ +/** @file + * IPRT - No-CRT - Basic allocators, Windows. + */ + +/* + * 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/mem.h> + +#include <iprt/nt/nt-and-windows.h> + + +#undef RTMemTmpFree +RTDECL(void) RTMemTmpFree(void *pv) +{ + HeapFree(GetProcessHeap(), 0, pv); +} + + +#undef RTMemFree +RTDECL(void) RTMemFree(void *pv) +{ + HeapFree(GetProcessHeap(), 0, pv); +} + + +#undef RTMemTmpAllocTag +RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) +{ + RT_NOREF(pszTag); + return HeapAlloc(GetProcessHeap(), 0, cb); +} + + +#undef RTMemTmpAllocZTag +RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag) +{ + RT_NOREF(pszTag); + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb); +} + + +#undef RTMemAllocTag +RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag) +{ + RT_NOREF(pszTag); + return HeapAlloc(GetProcessHeap(), 0, cb); +} + + +#undef RTMemAllocZTag +RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag) +{ + RT_NOREF(pszTag); + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb); +} + + +#undef RTMemAllocVarTag +RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag) +{ + return RTMemAllocTag(cbUnaligned, pszTag); +} + + +#undef RTMemAllocZVarTag +RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag) +{ + return RTMemAllocZTag(cbUnaligned, pszTag); +} + + +#undef RTMemReallocTag +RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag) +{ + RT_NOREF(pszTag); + if (pvOld) + return HeapReAlloc(GetProcessHeap(), 0, pvOld, cbNew); + return HeapAlloc(GetProcessHeap(), 0, cbNew); +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm new file mode 100644 index 00000000..881463ec --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm @@ -0,0 +1,44 @@ +; $Id: nocrt-atexit-win.asm $ +;; @file +; IPRT - Alias atexit to rtnocrt_atexit in nocrt-startup-exe-win.cpp. +; + +; +; 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 +; + + +%include "iprt/asmdefs.mac" + +extern NAME(nocrt_atexit) +BEGINPROC atexit + jmp NAME(nocrt_atexit) +ENDPROC atexit + diff --git a/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp new file mode 100644 index 00000000..c9bfa1b4 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp @@ -0,0 +1,150 @@ +/* $Id: nocrt-fatal-write-win.cpp $ */ +/** @file + * IPRT - No-CRT - Fatal Message Writing Primitives, Windows. + */ + +/* + * 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 "internal/nocrt.h" + +#include <iprt/win/windows.h> +#include <iprt/string.h> + + +/** @todo invent some kind of weak linking with the debug and release + * loggers, e.g. theset some innocent function we can call to do + * logging and we'll call it if it isn't NULL. */ + +void rtNoCrtFatalWrite(const char *pchMsg, size_t cchMsg) +{ + DWORD cbIgn; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), pchMsg, (DWORD)cchMsg, &cbIgn, NULL); +} + + +void rtNoCrtFatalWriteBegin(const char *pchMsg, size_t cchMsg) +{ + rtNoCrtFatalWrite(pchMsg, cchMsg); +} + + +void rtNoCrtFatalWriteEnd(const char *pchMsg, size_t cchMsg) +{ + rtNoCrtFatalWrite(pchMsg, cchMsg); +} + + +void rtNoCrtFatalWritePtr(void const *pv) +{ + char szValue[128]; +#if ARCH_BITS == 64 + ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), (uintptr_t)pv, 16, 16, 16, RTSTR_F_WIDTH | RTSTR_F_64BIT); +#elif ARCH_BITS == 32 + ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), (uintptr_t)pv, 16, 8, 8, RTSTR_F_WIDTH | RTSTR_F_32BIT); +#else +# error ARCH_BITS +#endif + rtNoCrtFatalWrite(szValue, (size_t)cchValue); +} + + +void rtNoCrtFatalWriteX64(uint64_t uValue) +{ + char szValue[128]; + ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_64BIT); + rtNoCrtFatalWrite(szValue, (size_t)cchValue); +} + + +void rtNoCrtFatalWriteX32(uint32_t uValue) +{ + char szValue[128]; + ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT); + rtNoCrtFatalWrite(szValue, (size_t)cchValue); +} + + +void rtNoCrtFatalWriteRc(int rc) +{ + char szValue[128]; + ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT | RTSTR_F_VALSIGNED); + rtNoCrtFatalWrite(szValue, (size_t)cchValue); +} + +void rtNoCrtFatalWriteWinRc(uint32_t rc) +{ + char szValue[168]; + ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT); + szValue[cchValue++] = ' '; + szValue[cchValue++] = '('; + cchValue += RTStrFormatU32(&szValue[cchValue], sizeof(szValue) - cchValue, rc, 16, 0, 0, RTSTR_F_32BIT | RTSTR_F_SPECIAL); + szValue[cchValue++] = ')'; + rtNoCrtFatalWrite(szValue, (size_t)cchValue); +} + + +void rtNoCrtFatalWriteStr(const char *pszString) +{ + if (RT_VALID_PTR(pszString)) + { + size_t cch = 0; + while (pszString[cch] != '\0') + cch++; + rtNoCrtFatalWrite(pszString, cch); + } + else + { + rtNoCrtFatalWrite(RT_STR_TUPLE("<pszString=")); + rtNoCrtFatalWritePtr(pszString); + rtNoCrtFatalWrite(RT_STR_TUPLE(">")); + } +} + + +void rtNoCrtFatalMsg(const char *pchMsg, size_t cchMsg) +{ + rtNoCrtFatalWriteBegin(pchMsg, cchMsg); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("")); +} + + +void rtNoCrtFatalMsgWithRc(const char *pchMsg, size_t cchMsg, int rc) +{ + rtNoCrtFatalWriteBegin(pchMsg, cchMsg); + rtNoCrtFatalWriteRc(rc); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm new file mode 100644 index 00000000..54651dd1 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm @@ -0,0 +1,44 @@ +; $Id: nocrt-mainCRTStartup-win.asm $ +;; @file +; IPRT - Alias mainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp. +; + +; +; 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 +; + + +%include "iprt/asmdefs.mac" + +extern NAME(CustomMainEntrypoint) +BEGINPROC mainCRTStartup + jmp NAME(CustomMainEntrypoint) +ENDPROC mainCRTStartup + diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp new file mode 100644 index 00000000..598339db --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp @@ -0,0 +1,211 @@ +/* $Id: nocrt-startup-common-win.cpp $ */ +/** @file + * IPRT - No-CRT - Common Windows startup code. + */ + +/* + * 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/nocrt.h" +#include "internal/process.h" + +#include <iprt/nt/nt-and-windows.h> +#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE +# include <iprt/assert.h> +#endif +#include <iprt/getopt.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "internal/compiler-vcc.h" +#include "internal/process.h" + + +#ifdef RT_ARCH_X86 +/** + * NT 3.1 does not know about the IMAGE_SECTION_HEADER::Misc.VirtualSize field + * and will therefore not handle merging initialized and uninitialized data into + * the same section. + * + * We work around this by manually zeroing the uninitialized data before any + * other code has been executed. + */ +void rtVccWinInitBssOnNt3(void *pvImageBase) +{ + /* We are called really early on, so we must figure out the NT version + on our own. It doesn't have to be all that accurate, though, as only + NT 3.10 is affected (3.50 isn't). */ + DWORD const dwRawVer = GetVersion(); + DWORD const uMajorVer = RT_BYTE1(dwRawVer); + DWORD const uMinorVer = RT_BYTE2(dwRawVer); + if (uMajorVer != 3 || uMinorVer >= 50) + return; + + /* + * Locate the NT headers. + */ + PIMAGE_NT_HEADERS pNtHdrs; + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)pvImageBase; + if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE) + pNtHdrs = (PIMAGE_NT_HEADERS)((uintptr_t)pvImageBase + pDosHdr->e_lfanew); + else + pNtHdrs = (PIMAGE_NT_HEADERS)pvImageBase; + if (pNtHdrs->Signature == IMAGE_NT_SIGNATURE) + { + /* + * Locate the section table and walk thru it, memsetting anything that + * wasn't loaded from the file. + */ + PIMAGE_SECTION_HEADER paSHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs->OptionalHeader + + pNtHdrs->FileHeader.SizeOfOptionalHeader); + uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections; + for (uint32_t i = 0; i < cSections; i++) + { + if (paSHdrs[i].Misc.VirtualSize > paSHdrs[i].SizeOfRawData) + { + /* ASSUMES VirtualAddress is still an RVAs */ + uint8_t *pbToZero = (uint8_t *)pvImageBase + paSHdrs[i].VirtualAddress + paSHdrs[i].SizeOfRawData; + uint32_t const cbToZero = paSHdrs[i].Misc.VirtualSize - paSHdrs[i].SizeOfRawData; + +# if 0 /* very very crude debugging */ + const char *pszHex = "0123456789abcdef"; + char szMsg[128]; + char *psz = szMsg; + *psz++ = 'd'; *psz++ = 'b'; *psz++ = 'g'; *psz++ = ':'; *psz++ = ' '; + for (uint32_t q = 0, u = i; q < 8; q++, u >>= 4) + psz[7 - q] = pszHex[u & 0xf]; + psz += 8; + *psz++ = ':'; + *psz++ = ' '; + for (uint32_t q = 0, u = (uint32_t)pbToZero; q < 8; q++, u >>= 4) + psz[7 - q] = pszHex[u & 0xf]; + psz += 8; + *psz++ = ' '; + *psz++ = 'L'; + *psz++ = 'B'; + *psz++ = ' '; + for (uint32_t q = 0, u = cbToZero; q < 8; q++, u >>= 4) + psz[7 - q] = pszHex[u & 0xf]; + psz += 8; + *psz++ = ' '; + for (uint32_t q = 0; q < 8; q++) + *psz++ = paSHdrs[i].Name[q] ? paSHdrs[i].Name[q] : ' '; + *psz++ = ' '; *psz++ = '/'; *psz++ = ' '; *psz++ = 'v'; *psz++ = 'e'; *psz++ = 'r'; *psz++ = ' '; + *psz++ = pszHex[(uMajorVer >> 4) & 0xf]; + *psz++ = pszHex[uMajorVer & 0xf]; + *psz++ = '.'; + *psz++ = pszHex[(uMinorVer >> 4) & 0xf]; + *psz++ = pszHex[uMinorVer & 0xf]; + *psz++ = '\r'; *psz++ = '\n'; + *psz = '\0'; + DWORD cbIgn; + HANDLE hOut = RTNtCurrentPeb()->ProcessParameters->StandardOutput; + if (hOut == NULL || hOut == INVALID_HANDLE_VALUE) + hOut = RTNtCurrentPeb()->ProcessParameters->StandardError; + if (hOut == NULL || hOut == INVALID_HANDLE_VALUE) + hOut = RTNtCurrentPeb()->ProcessParameters->ConsoleHandle; + if (hOut == NULL || hOut == INVALID_HANDLE_VALUE) + hOut = GetStdHandle(STD_OUTPUT_HANDLE); + WriteFile(hOut, szMsg, psz - szMsg, &cbIgn, NULL); +# endif + + if (paSHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE) + memset(pbToZero, 0, cbToZero); + else + { + /* The section is not writable, so temporarily make it writable. */ + PVOID pvAligned = pbToZero - ((uintptr_t)pbToZero & PAGE_OFFSET_MASK); + ULONG cbAligned = RT_ALIGN_32(cbToZero + ((uintptr_t)pbToZero & PAGE_OFFSET_MASK), PAGE_SIZE); + ULONG fNewProt = paSHdrs[i].Characteristics & IMAGE_SCN_MEM_EXECUTE + ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; + ULONG fOldProt = fNewProt; + NTSTATUS rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fNewProt, &fOldProt); + if (NT_SUCCESS(rcNt)) + { + memset(pbToZero, 0, cbToZero); + + rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fOldProt, &fNewProt); + } + else + RT_BREAKPOINT(); + } + } + } + } + else + RT_BREAKPOINT(); +} +#endif + + +void rtVccWinInitProcExecPath(void) +{ + WCHAR wszPath[RTPATH_MAX]; + UINT cwcPath = GetModuleFileNameW(NULL, wszPath, RT_ELEMENTS(wszPath)); + if (cwcPath) + { + char *pszDst = g_szrtProcExePath; + int rc = RTUtf16ToUtf8Ex(wszPath, cwcPath, &pszDst, sizeof(g_szrtProcExePath), &g_cchrtProcExePath); + if (RT_SUCCESS(rc)) + { + g_cchrtProcExeDir = g_offrtProcName = RTPathFilename(pszDst) - g_szrtProcExePath; + while ( g_cchrtProcExeDir >= 2 + && RTPATH_IS_SLASH(g_szrtProcExePath[g_cchrtProcExeDir - 1]) + && g_szrtProcExePath[g_cchrtProcExeDir - 2] != ':') + g_cchrtProcExeDir--; + } + else + { +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("initProcExecPath: RTUtf16ToUtf8Ex failed: %Rrc\n", rc); +#else + rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("initProcExecPath: RTUtf16ToUtf8Ex failed: "), rc); +#endif + } + } + else + { +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("initProcExecPath: GetModuleFileNameW failed: %Rhrc\n", GetLastError()); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("initProcExecPath: GetModuleFileNameW failed: ")); + rtNoCrtFatalWriteWinRc(GetLastError()); + rtNoCrtFatalWrite(RT_STR_TUPLE("\r\n")); +#endif + } +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp new file mode 100644 index 00000000..77bf0951 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp @@ -0,0 +1,162 @@ +/* $Id: nocrt-startup-dll-win.cpp $ */ +/** @file + * IPRT - No-CRT - Windows EXE startup code. + * + * @note Does not run static constructors and destructors! + */ + +/* + * 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 "internal/process.h" + +#include <iprt/nt/nt-and-windows.h> +#include <iprt/getopt.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#ifdef IPRT_NO_CRT +# include <iprt/asm.h> +# include <iprt/nocrt/stdlib.h> +#endif + +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static volatile int32_t g_cAttached = 0; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved); + + +DECL_NO_INLINE(static, BOOL) rtVccDllMainForward(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) +{ + return DllMain(hInstance, dwReason, pvReserved); +} + + + +DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessAttach(HINSTANCE hInstance, LPVOID pvReserved) +{ + /* + * Initialize the CRT the first time thru. + */ + if (g_cAttached == 0) + { + rtVccWinInitProcExecPath(); + + int rc = rtVccInitializersRunInit(); + if (RT_FAILURE(rc)) + return FALSE; + } + g_cAttached++; + + /* + * Call the DllMain function. + */ + BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_ATTACH, pvReserved); + + /* + * On failure, we call the DllMain function again, decrement the init counter + * and probably run termination callbacks. + */ + if (!fRet) + { + rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved); + if (--g_cAttached == 0) + { + rtVccTermRunAtExit(); + rtVccInitializersRunTerm(); + } + } + return fRet; +} + + +DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessDetach(HINSTANCE hInstance, LPVOID pvReserved) +{ + /* + * Make sure there isn't an imbalance before calling DllMain and shutting + * down our own internals. + */ + if (g_cAttached <= 0) + return FALSE; + + /* + * Call DllMain. + */ + BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved); + + /* + * Work g_cAttached and probably do uninitialization. We'll do this regardless + * of what DllMain returned. + */ + if (--g_cAttached == 0) + { + rtVccTermRunAtExit(); + rtVccInitializersRunTerm(); + } + return fRet; +} + + +extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: +#ifdef RT_ARCH_X86 + rtVccWinInitBssOnNt3((PVOID)hInstance); +#endif + rtVccInitSecurityCookie(); /* This function must be minimal because of this! */ + return rtVccDllMainProcessAttach(hInstance, pvReserved); + + case DLL_PROCESS_DETACH: + return rtVccDllMainProcessDetach(hInstance, pvReserved); + + default: + return rtVccDllMainForward(hInstance, dwReason, pvReserved); + } +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp new file mode 100644 index 00000000..543ea05c --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp @@ -0,0 +1,174 @@ +/* $Id: nocrt-startup-exe-win.cpp $ */ +/** @file + * IPRT - No-CRT - Windows EXE startup code. + * + * @note Does not run static constructors and destructors! + */ + +/* + * 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/nocrt.h" +#include "internal/process.h" + +#include <iprt/nt/nt-and-windows.h> +#include <iprt/getopt.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* External Symbols * +*********************************************************************************************************************************/ +extern int main(int argc, char **argv, char **envp); /* in program */ +#ifndef IPRT_NO_CRT +extern DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams); /* nocrt-streams-win.cpp */ /** @todo put in header */ +#endif + + +static int rtTerminateProcess(int32_t rcExit, bool fDoAtExit) +{ +#ifdef IPRT_NO_CRT + /* + * Run atexit callback in reverse order. + */ + if (fDoAtExit) + { + rtVccTermRunAtExit(); + rtVccInitializersRunTerm(); + } +#else + RT_NOREF(fDoAtExit); +#endif + + /* + * Terminate. + */ + for (;;) + NtTerminateProcess(NtCurrentProcess(), rcExit); +} + + +DECLASM(void) CustomMainEntrypoint(void) +{ + /* Looks like might have gotten the PPEB as parameter here before NT4, + however, there the EXE entry function clearly takes no parameters. + So, we have to retrieve the PEB our selves here. */ + PPEB_COMMON const pPeb = RTNtCurrentPeb(); + + /* + * Initialize stuff. + */ +#ifdef IPRT_NO_CRT +# ifdef RT_ARCH_X86 + rtVccWinInitBssOnNt3(pPeb->ImageBaseAddress); +# endif + rtVccInitSecurityCookie(); +#else + InitStdHandles(pPeb->ProcessParameters); +#endif + rtVccWinInitProcExecPath(); + + RTEXITCODE rcExit; +#ifdef IPRT_NO_CRT + AssertCompile(sizeof(rcExit) == sizeof(int)); + rcExit = (RTEXITCODE)rtVccInitializersRunInit(); + if (rcExit == RTEXITCODE_SUCCESS) +#endif + { + /* + * Get and convert the command line to argc/argv format. + */ + rcExit = RTEXITCODE_INIT; + UNICODE_STRING const *pCmdLine = pPeb->ProcessParameters ? &pPeb->ProcessParameters->CommandLine : NULL; + if (pCmdLine) + { + char *pszCmdLine = NULL; + int rc = RTUtf16ToUtf8Ex(pCmdLine->Buffer, pCmdLine->Length / sizeof(WCHAR), &pszCmdLine, 0, NULL); + if (RT_SUCCESS(rc)) + { + char **papszArgv; + int cArgs = 0; + rc = RTGetOptArgvFromString(&papszArgv, &cArgs, pszCmdLine, + RTGETOPTARGV_CNV_MODIFY_INPUT | RTGETOPTARGV_CNV_QUOTE_MS_CRT, NULL); + if (RT_SUCCESS(rc)) + { + /* + * Call the main function. + */ + AssertCompile(sizeof(rcExit) == sizeof(int)); + rcExit = (RTEXITCODE)main(cArgs, papszArgv, NULL /*envp*/); + } + else +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("Error parsing command line: %Rrc\n", rc); +#else + rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Error parsing command line: "), rc); +#endif + } + else +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("Failed to convert command line to UTF-8: %Rrc\n", rc); +#else + rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Failed to convert command line to UTF-8: "), rc); +#endif + } + else +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("No command line\n"); +#else + rtNoCrtFatalMsg(RT_STR_TUPLE("No command line\r\n")); +#endif + rtTerminateProcess(rcExit, true /*fDoAtExit*/); + } +#ifdef IPRT_NO_CRT + else + { +# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTMsgError("A C static initializor failed (%d)\n", rcExit); +# else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("A C static initializor failed (")); + rtNoCrtFatalWriteWinRc(rcExit); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +# endif + rtTerminateProcess(rcExit, false /*fDoAtExit*/); + } +#endif +} + diff --git a/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp new file mode 100644 index 00000000..269c9bd2 --- /dev/null +++ b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp @@ -0,0 +1,223 @@ +/* $Id: nocrt-streams-win.cpp $ */ +/** @file + * IPRT - No-CRT - minimal stream 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/stream.h> + +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/ctype.h> +#include <iprt/file.h> +#include <iprt/string.h> + + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct PRINTFBUF +{ + HANDLE hNative; + size_t offBuf; + char szBuf[128]; +} PRINTFBUF; + +struct RTSTREAM +{ + int iStream; + HANDLE hNative; + RTFILE hFile; +}; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define MAKE_SURE_WE_HAVE_HFILE_RETURN(a_pStream) do { \ + if ((a_pStream)->hFile != NIL_RTFILE) \ + break; \ + int rc = RTFileFromNative(&(a_pStream)->hFile, (uintptr_t)(a_pStream)->hNative); \ + AssertRCReturn(rc, rc); \ + } while (0) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +RTSTREAM g_aStdStreams[3] = +{ + { 0, NULL, NIL_RTFILE }, + { 1, NULL, NIL_RTFILE }, + { 2, NULL, NIL_RTFILE }, +}; + +RTSTREAM *g_pStdIn = &g_aStdStreams[0]; +RTSTREAM *g_pStdOut = &g_aStdStreams[1]; +RTSTREAM *g_pStdErr = &g_aStdStreams[2]; + + + +DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams) +{ + if (pParams) + { + g_pStdIn->hNative = pParams->StandardInput; + g_pStdOut->hNative = pParams->StandardOutput; + g_pStdErr->hNative = pParams->StandardError; + } +} + + +static void FlushPrintfBuffer(PRINTFBUF *pBuf) +{ + if (pBuf->offBuf) + { + DWORD cbWritten = 0; + WriteFile(pBuf->hNative, pBuf->szBuf, (DWORD)pBuf->offBuf, &cbWritten, NULL); + pBuf->offBuf = 0; + pBuf->szBuf[0] = '\0'; + } +} + + +/** @callback_method_impl{FNRTSTROUTPUT} */ +static DECLCALLBACK(size_t) MyPrintfOutputter(void *pvArg, const char *pachChars, size_t cbChars) +{ + PRINTFBUF *pBuf = (PRINTFBUF *)pvArg; + if (cbChars != 0) + { + size_t offSrc = 0; + while (offSrc < cbChars) + { + size_t cbLeft = sizeof(pBuf->szBuf) - pBuf->offBuf - 1; + if (cbLeft > 0) + { + size_t cbToCopy = RT_MIN(cbChars - offSrc, cbLeft); + memcpy(&pBuf->szBuf[pBuf->offBuf], &pachChars[offSrc], cbToCopy); + pBuf->offBuf += cbToCopy; + pBuf->szBuf[pBuf->offBuf] = '\0'; + if (cbLeft > cbToCopy) + break; + offSrc += cbToCopy; + } + FlushPrintfBuffer(pBuf); + } + } + else /* Special zero byte write at the end of the formatting. */ + FlushPrintfBuffer(pBuf); + return cbChars; +} + + +RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args) +{ + PRINTFBUF Buf; + Buf.hNative = pStream->hNative; + Buf.offBuf = 0; + Buf.szBuf[0] = '\0'; + + return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, args); +} + + +RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + int rc = RTStrmPrintfV(pStream, pszFormat, args); + va_end(args); + return rc; +} + + +RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list va) +{ + PRINTFBUF Buf; + Buf.hNative = g_pStdOut->hNative; + Buf.offBuf = 0; + Buf.szBuf[0] = '\0'; + + return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, va); +} + + +RTR3DECL(int) RTPrintf(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + int rc = RTPrintfV(pszFormat, va); + va_end(va); + return rc; +} + +#ifndef IPRT_MINIMAL_STREAM + +# if 0 +RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream); + return RTFileRead(pStream->hFile, pvBuf, cbToRead, pcbRead); +} +# endif + + +RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream); + return RTFileWrite(pStream->hFile, pvBuf, cbToWrite, pcbWritten); +} + + +RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream) +{ + MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream); + return RTFileFlush(pStream->hFile); +} + + +RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet) +{ + AssertReturn(fBinary != (int)false, VERR_NOT_IMPLEMENTED); + AssertReturn(fCurrentCodeSet <= (int)false, VERR_NOT_IMPLEMENTED); + RT_NOREF(pStream); + return VINF_SUCCESS; +} + +#endif diff --git a/src/VBox/Runtime/r3/win/ntdll-mini-implib.def b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def new file mode 100644 index 00000000..c382847e --- /dev/null +++ b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def @@ -0,0 +1,163 @@ +; $Id: ntdll-mini-implib.def $ +;; @file +; IPRT - Minimal NTDLL import library defintion file. +; + +; +; 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 +; + +LIBRARY ntdll.dll +EXPORTS + ; Exported name - The name x86 name sought by the linker. + ; - This needs to be defined as a symbol, we generate assembly. + + CsrClientCallServer ;;= _CsrClientCallServer@16 + + NtAlertThread ;;= _NtAlertThread@4 + NtAllocateVirtualMemory ;;= _NtAllocateVirtualMemory@24 + NtCancelIoFile ;;= _NtCancelIoFile@8 + NtClearEvent ;;= _NtClearEvent@4 + NtClose ;;= _NtClose@4 + NtCreateEvent ;;= _NtCreateEvent@20 + NtCreateFile ;;= _NtCreateFile@44 + NtCreateSection ;;= _NtCreateSection@28 + NtCreateSymbolicLinkObject ;;= _NtCreateSymbolicLinkObject@16 + NtDelayExecution ;;= _NtDelayExecution@8 + NtDeviceIoControlFile ;;= _NtDeviceIoControlFile@40 + NtDuplicateObject ;;= _NtDuplicateObject@28 + NtEnumerateKey ;;= _NtEnumerateKey@24 + NtFlushBuffersFile ;;= _NtFlushBuffersFile@8 + NtFlushVirtualMemory ;;= _NtFlushVirtualMemory@16 + NtFreeVirtualMemory ;;= _NtFreeVirtualMemory@16 + NtGetContextThread ;;= _NtGetContextThread@8 + NtMapViewOfSection ;;= _NtMapViewOfSection@40 + NtOpenDirectoryObject ;;= _NtOpenDirectoryObject@12 + NtOpenEvent ;;= _NtOpenEvent@12 + NtOpenKey ;;= _NtOpenKey@12 + NtOpenProcess ;;= _NtOpenProcess@16 + NtOpenProcessToken ;;= _NtOpenProcessToken@12 + NtOpenSymbolicLinkObject ;;= _NtOpenSymbolicLinkObject@12 + NtOpenThread ;;= _NtOpenThread@16 + NtOpenThreadToken ;;= _NtOpenThreadToken@16 + NtProtectVirtualMemory ;;= _NtProtectVirtualMemory@20 + NtQueryAttributesFile ;;= _NtQueryAttributesFile@8 + NtQueryDirectoryFile ;;= _NtQueryDirectoryFile@44 + NtQueryDirectoryObject ;;= _NtQueryDirectoryObject@28 + NtQueryFullAttributesFile ;;= _NtQueryFullAttributesFile@8 + NtQueryEvent ;;= _NtQueryEvent@20 + NtQueryInformationFile ;;= _NtQueryInformationFile@20 + NtQueryInformationProcess ;;= _NtQueryInformationProcess@20 + NtQueryInformationThread ;;= _NtQueryInformationThread@20 + NtQueryInformationToken ;;= _NtQueryInformationToken@20 + NtQueryKey ;;= _NtQueryKey@20 + NtQueryObject ;;= _NtQueryObject@20 + NtQuerySection ;;= _NtQuerySection@20 + NtQuerySecurityObject ;;= _NtQuerySecurityObject@20 + NtQuerySymbolicLinkObject ;;= _NtQuerySymbolicLinkObject@12 + NtQuerySystemInformation ;;= _NtQuerySystemInformation@16 + NtQueryTimerResolution ;;= _NtQueryTimerResolution@12 + NtQueryValueKey ;;= _NtQueryValueKey@24 + NtQueryVirtualMemory ;;= _NtQueryVirtualMemory@24 + NtQueryVolumeInformationFile ;;= _NtQueryVolumeInformationFile@20 + NtReadFile ;;= _NtReadFile@36 + NtReadVirtualMemory ;;= _NtReadVirtualMemory@20 + NtResetEvent ;;= _NtResetEvent@8 + NtResumeProcess ;;= _NtResumeProcess@4 + NtResumeThread ;;= _NtResumeThread@8 + NtSetContextThread ;;= _NtSetContextThread@8 + NtSetEvent ;;= _NtSetEvent@8 + NtSetInformationFile ;;= _NtSetInformationFile@20 + NtSetInformationObject ;;= _NtSetInformationObject@16 + NtSetInformationProcess ;;= _NtSetInformationProcess@16 + NtSetInformationThread ;;= _NtSetInformationThread@16 + NtSetTimerResolution ;;= _NtSetTimerResolution@12 + NtSuspendProcess ;;= _NtSuspendProcess@4 + NtSuspendThread ;;= _NtSuspendThread@8 + NtTerminateProcess ;;= _NtTerminateProcess@8 + NtTerminateThread ;;= _NtTerminateThread@8 + NtUnmapViewOfSection ;;= _NtUnmapViewOfSection@8 + NtWaitForMultipleObjects ;;= _NtWaitForMultipleObjects@20 + NtWaitForSingleObject ;;= _NtWaitForSingleObject@12 + NtWriteFile ;;= _NtWriteFile@36 + NtWriteVirtualMemory ;;= _NtWriteVirtualMemory@20 + NtYieldExecution ;;= _NtYieldExecution@0 + + LdrInitializeThunk ;;= _LdrInitializeThunk@12 + LdrRegisterDllNotification ;;= _LdrRegisterDllNotification@16 + LdrLoadDll ;;= _LdrLoadDll@16 + LdrUnloadDll ;;= _LdrUnloadDll@4 + LdrGetDllHandle ;;= _LdrGetDllHandle@16 + LdrGetDllHandleEx ;;= _LdrGetDllHandleEx@20 + LdrGetDllHandleByMapping ;;= _LdrGetDllHandleByMapping@8 + LdrGetDllHandleByName ;;= _LdrGetDllHandleByName@12 + LdrAddRefDll ;;= _LdrAddRefDll@8 + LdrGetProcedureAddress ;;= _LdrGetProcedureAddress@12 + LdrGetProcedureAddressEx ;;= _LdrGetProcedureAddressEx@16 + LdrLockLoaderLock ;;= _LdrLockLoaderLock@12 + LdrUnlockLoaderLock ;;= _LdrUnlockLoaderLock@8 + + RtlAcquirePebLock ;;= _RtlAcquirePebLock@0 + RtlAddAccessAllowedAce ;;= _RtlAddAccessAllowedAce@16 + RtlAddAccessDeniedAce ;;= _RtlAddAccessDeniedAce@16 + RtlAllocateHeap ;;= _RtlAllocateHeap@12 + RtlCompactHeap ;;= _RtlCompactHeap@8 + RtlCopySid ;;= _RtlCopySid@12 + RtlCreateAcl ;;= _RtlCreateAcl@12 + RtlCreateHeap ;;= _RtlCreateHeap@24 + RtlCreateProcessParameters ;;= _RtlCreateProcessParameters@40 + RtlCreateSecurityDescriptor ;;= _RtlCreateSecurityDescriptor@8 + RtlCreateUserProcess ;;= _RtlCreateUserProcess@40 + RtlCreateUserThread ;;= _RtlCreateUserThread@40 + RtlDestroyProcessParameters ;;= _RtlDestroyProcessParameters@4 + RtlDosApplyFileIsolationRedirection_Ustr ;;= _RtlDosApplyFileIsolationRedirection_Ustr@36 + RtlEqualSid ;;= _RtlEqualSid@8 + RtlExitUserProcess ;;= _RtlExitUserProcess@4 + RtlExitUserThread ;;= _RtlExitUserThread@4 + RtlExpandEnvironmentStrings_U ;;= _RtlExpandEnvironmentStrings_U@16 + RtlFreeHeap ;;= _RtlFreeHeap@12 + RtlFreeUnicodeString ;;= _RtlFreeUnicodeString@4 + RtlGetLastNtStatus ;;= _RtlGetLastNtStatus@0 + RtlGetLastWin32Error ;;= _RtlGetLastWin32Error@0 + RtlGetVersion ;;= _RtlGetVersion@4 + RtlGetNtProductType ;;= _RtlGetNtProductType@4 + RtlInitializeSid ;;= _RtlInitializeSid@12 + RtlNtStatusToDosError ;;= _RtlNtStatusToDosError@4 + RtlReAllocateHeap ;;= _RtlReAllocateHeap@16 + RtlReleasePebLock ;;= _RtlReleasePebLock@0 + RtlRestoreLastWin32Error ;;= _RtlRestoreLastWin32Error@4 + RtlSetDaclSecurityDescriptor ;;= _RtlSetDaclSecurityDescriptor@16 + RtlSetLastWin32Error ;;= _RtlSetLastWin32Error@4 + RtlSetLastWin32ErrorAndNtStatusFromNtStatus ;;= _RtlSetLastWin32ErrorAndNtStatusFromNtStatus@4 + RtlSizeHeap ;;= _RtlSizeHeap@12 + RtlSubAuthoritySid ;;= _RtlSubAuthoritySid@8 + RtlQueryPerformanceCounter ;;= _RtlQueryPerformanceCounter@4 + RtlGetSystemTimePrecise ;;= _RtlGetSystemTimePrecise@0 + diff --git a/src/VBox/Runtime/r3/win/path-win.cpp b/src/VBox/Runtime/r3/win/path-win.cpp new file mode 100644 index 00000000..587de0f6 --- /dev/null +++ b/src/VBox/Runtime/r3/win/path-win.cpp @@ -0,0 +1,743 @@ +/* $Id: path-win.cpp $ */ +/** @file + * IPRT - Path manipulation. + */ + +/* + * 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 <iprt/win/windows.h> +#include <iprt/win/shlobj.h> + +#include <iprt/path.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include <iprt/utf16.h> +#include "internal/path.h" +#include "internal/fs.h" + +/* Needed for lazy loading SHGetFolderPathW in RTPathUserDocuments(). */ +typedef HRESULT WINAPI FNSHGETFOLDERPATHW(HWND, int, HANDLE, DWORD, LPWSTR); +typedef FNSHGETFOLDERPATHW *PFNSHGETFOLDERPATHW; + + +RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath) +{ + /* + * Convert to UTF-16, call Win32 APIs, convert back. + */ + PRTUTF16 pwszPath; + int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + LPWSTR lpFile; + WCHAR wsz[RTPATH_MAX]; + rc = GetFullPathNameW((LPCWSTR)pwszPath, RT_ELEMENTS(wsz), &wsz[0], &lpFile); + if (rc > 0 && rc < RT_ELEMENTS(wsz)) + { + /* Check that it exists. (Use RTPathAbs() to just resolve the name.) */ + DWORD dwAttr = GetFileAttributesW(wsz); + if (dwAttr != INVALID_FILE_ATTRIBUTES) + rc = RTUtf16ToUtf8Ex((PRTUTF16)&wsz[0], RTSTR_MAX, &pszRealPath, cchRealPath, NULL); + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (rc <= 0) + rc = RTErrConvertFromWin32(GetLastError()); + else + rc = VERR_FILENAME_TOO_LONG; + + RTPathWinFree(pwszPath); + } + return rc; +} + +#if 0 +RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath) +{ + /* + * Validation. + */ + AssertPtr(pszAbsPath); + AssertPtr(pszPath); + if (RT_UNLIKELY(!*pszPath)) + return VERR_INVALID_PARAMETER; + + /* + * Convert to UTF-16, call Win32 API, convert back. + */ + LPWSTR pwszPath; + int rc = RTStrToUtf16(pszPath, &pwszPath); + if (!RT_SUCCESS(rc)) + return (rc); + + LPWSTR pwszFile; /* Ignored */ + RTUTF16 wsz[RTPATH_MAX]; + rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile); + if (rc > 0 && rc < RT_ELEMENTS(wsz)) + { + size_t cch; + rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch); + if (RT_SUCCESS(rc)) + { +# if 1 /** @todo This code is completely bonkers. */ + /* + * Remove trailing slash if the path may be pointing to a directory. + * (See posix variant.) + */ + if ( cch > 1 + && RTPATH_IS_SLASH(pszAbsPath[cch - 1]) + && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2]) + && !RTPATH_IS_SLASH(pszAbsPath[cch - 2])) + pszAbsPath[cch - 1] = '\0'; +# endif + } + } + else if (rc <= 0) + rc = RTErrConvertFromWin32(GetLastError()); + else + rc = VERR_FILENAME_TOO_LONG; + + RTUtf16Free(pwszPath); + return rc; +} +#endif + + +RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath) +{ + /* + * Validate input + */ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cchPath, VERR_INVALID_PARAMETER); + + RTUTF16 wszPath[RTPATH_MAX]; + bool fValidFolderPath = false; + + /* + * Try with Windows XP+ functionality first. + */ + RTLDRMOD hShell32; + int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32); + if (RT_SUCCESS(rc)) + { + PFNSHGETFOLDERPATHW pfnSHGetFolderPathW; + rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW); + if (RT_SUCCESS(rc)) + { + HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, wszPath); + fValidFolderPath = (hrc == S_OK); + } + RTLdrClose(hShell32); + } + + DWORD dwAttr; + if ( !fValidFolderPath + || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES + || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + /* + * Fall back to Windows specific environment variables. HOME is not used. + */ + if ( !GetEnvironmentVariableW(L"USERPROFILE", &wszPath[0], RTPATH_MAX) + || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES + || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + /* %HOMEDRIVE%%HOMEPATH% */ + if (!GetEnvironmentVariableW(L"HOMEDRIVE", &wszPath[0], RTPATH_MAX)) + return VERR_PATH_NOT_FOUND; + size_t const cwc = RTUtf16Len(&wszPath[0]); + if ( !GetEnvironmentVariableW(L"HOMEPATH", &wszPath[cwc], RTPATH_MAX - (DWORD)cwc) + || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES + || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + return VERR_PATH_NOT_FOUND; + } + } + + /* + * Convert and return. + */ + return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL); +} + + +RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath) +{ + /* + * Validate input + */ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cchPath, VERR_INVALID_PARAMETER); + + RTLDRMOD hShell32; + int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32); + if (RT_SUCCESS(rc)) + { + PFNSHGETFOLDERPATHW pfnSHGetFolderPathW; + rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW); + if (RT_SUCCESS(rc)) + { + RTUTF16 wszPath[RTPATH_MAX]; + HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, wszPath); + if ( hrc == S_OK /* Found */ + || hrc == S_FALSE) /* Found, but doesn't exist */ + { + /* + * Convert and return. + */ + RTLdrClose(hShell32); + return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL); + } + } + RTLdrClose(hShell32); + } + return VERR_PATH_NOT_FOUND; +} + + +#if 0 /* use nt version of this */ + +RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs) +{ + return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK); +} +#endif +#if 0 + + +RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(*pszPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER); + AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING + && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST, + ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs), + VERR_INVALID_PARAMETER); + AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + + /* + * Query file info. + */ + uint32_t uReparseTag = RTFSMODE_SYMLINK_REPARSE_TAG; + WIN32_FILE_ATTRIBUTE_DATA Data; + PRTUTF16 pwszPath; + int rc = RTStrToUtf16(pszPath, &pwszPath); + if (RT_FAILURE(rc)) + return rc; + if (!GetFileAttributesExW(pwszPath, GetFileExInfoStandard, &Data)) + { + /* Fallback to FindFileFirst in case of sharing violation. */ + if (GetLastError() == ERROR_SHARING_VIOLATION) + { + WIN32_FIND_DATAW FindData; + HANDLE hDir = FindFirstFileW(pwszPath, &FindData); + if (hDir == INVALID_HANDLE_VALUE) + { + rc = RTErrConvertFromWin32(GetLastError()); + RTUtf16Free(pwszPath); + return rc; + } + FindClose(hDir); + + Data.dwFileAttributes = FindData.dwFileAttributes; + Data.ftCreationTime = FindData.ftCreationTime; + Data.ftLastAccessTime = FindData.ftLastAccessTime; + Data.ftLastWriteTime = FindData.ftLastWriteTime; + Data.nFileSizeHigh = FindData.nFileSizeHigh; + Data.nFileSizeLow = FindData.nFileSizeLow; + uReparseTag = FindData.dwReserved0; + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + RTUtf16Free(pwszPath); + return rc; + } + } + + /* + * Getting the information for the link target is a bit annoying and + * subject to the same access violation mess as above.. :/ + */ + /** @todo we're too lazy wrt to error paths here... */ + if ( (Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + && ((fFlags & RTPATH_F_FOLLOW_LINK) || uReparseTag != RTFSMODE_SYMLINK_REPARSE_TAG)) + { + HANDLE hFinal = CreateFileW(pwszPath, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (hFinal != INVALID_HANDLE_VALUE) + { + BY_HANDLE_FILE_INFORMATION FileData; + if (GetFileInformationByHandle(hFinal, &FileData)) + { + Data.dwFileAttributes = FileData.dwFileAttributes; + Data.ftCreationTime = FileData.ftCreationTime; + Data.ftLastAccessTime = FileData.ftLastAccessTime; + Data.ftLastWriteTime = FileData.ftLastWriteTime; + Data.nFileSizeHigh = FileData.nFileSizeHigh; + Data.nFileSizeLow = FileData.nFileSizeLow; + uReparseTag = 0; + } + CloseHandle(hFinal); + } + else if (GetLastError() != ERROR_SHARING_VIOLATION) + { + rc = RTErrConvertFromWin32(GetLastError()); + RTUtf16Free(pwszPath); + return rc; + } + } + + RTUtf16Free(pwszPath); + + /* + * Setup the returned data. + */ + pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32) + | (uint64_t)Data.nFileSizeLow; + pObjInfo->cbAllocated = pObjInfo->cbObject; + + Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime)); + RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime); + RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime); + RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime); + pObjInfo->ChangeTime = pObjInfo->ModificationTime; + + pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, + pszPath, strlen(pszPath), uReparseTag); + + /* + * Requested attributes (we cannot provide anything actually). + */ + switch (enmAdditionalAttribs) + { + case RTFSOBJATTRADD_NOTHING: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; + break; + + case RTFSOBJATTRADD_UNIX: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX; + pObjInfo->Attr.u.Unix.uid = ~0U; + pObjInfo->Attr.u.Unix.gid = ~0U; + pObjInfo->Attr.u.Unix.cHardlinks = 1; + pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */ + pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */ + pObjInfo->Attr.u.Unix.fFlags = 0; + pObjInfo->Attr.u.Unix.GenerationId = 0; + pObjInfo->Attr.u.Unix.Device = 0; + break; + + case RTFSOBJATTRADD_UNIX_OWNER: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER; + pObjInfo->Attr.u.UnixOwner.uid = ~0U; + pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */ + break; + + case RTFSOBJATTRADD_UNIX_GROUP: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP; + pObjInfo->Attr.u.UnixGroup.gid = ~0U; + pObjInfo->Attr.u.UnixGroup.szName[0] = '\0'; + break; + + case RTFSOBJATTRADD_EASIZE: + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE; + pObjInfo->Attr.u.EASize.cb = 0; + break; + + default: + AssertMsgFailed(("Impossible!\n")); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +} + +#endif /* using NT version*/ + + +RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK); +} + + +RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(*pszPath, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER); + AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER); + AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER); + AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER); + AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + + /* + * Convert the path. + */ + PRTUTF16 pwszPath; + int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + HANDLE hFile; + if (fFlags & RTPATH_F_FOLLOW_LINK) + hFile = CreateFileW(pwszPath, + FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ + NULL, /* security attribs */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, + NULL); + else + { +/** @todo Symlink: Test RTPathSetTimesEx on Windows. (The code is disabled + * because it's not tested yet.) */ +#if 0 //def FILE_FLAG_OPEN_REPARSE_POINT + hFile = CreateFileW(pwszPath, + FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ + NULL, /* security attribs */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + + if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) +#endif + hFile = CreateFileW(pwszPath, + FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */ + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */ + NULL, /* security attribs */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, + NULL); + } + if (hFile != INVALID_HANDLE_VALUE) + { + /* + * Check if it's a no-op. + */ + if (!pAccessTime && !pModificationTime && !pBirthTime) + rc = VINF_SUCCESS; /* NOP */ + else + { + /* + * Convert the input and call the API. + */ + FILETIME CreationTimeFT; + PFILETIME pCreationTimeFT = NULL; + if (pBirthTime) + pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT); + + FILETIME LastAccessTimeFT; + PFILETIME pLastAccessTimeFT = NULL; + if (pAccessTime) + pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT); + + FILETIME LastWriteTimeFT; + PFILETIME pLastWriteTimeFT = NULL; + if (pModificationTime) + pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT); + + if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT)) + rc = VINF_SUCCESS; + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n", + pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc)); + } + } + BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc); + } + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err)); + } + + RTPathWinFree(pwszPath); + } + + LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n", + pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime, + pChangeTime, pChangeTime, pBirthTime, pBirthTime)); + return rc; +} + + + + +/** + * Internal worker for RTFileRename and RTFileMove. + * + * @returns iprt status code. + * @param pszSrc The source filename. + * @param pszDst The destination filename. + * @param fFlags The windows MoveFileEx flags. + * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0, + * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the + * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's + * not a directory (we are NOT checking whether it's a file). + */ +DECLHIDDEN(int) rtPathWin32MoveRename(const char *pszSrc, const char *pszDst, uint32_t fFlags, RTFMODE fFileType) +{ + /* + * Convert the strings. + */ + PRTUTF16 pwszSrc; + int rc = RTPathWinFromUtf8(&pwszSrc, pszSrc, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszDst; + rc = RTPathWinFromUtf8(&pwszDst, pszDst, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * Check object type if requested. + * This is open to race conditions. + */ + if (fFileType) + { + DWORD dwAttr = GetFileAttributesW(pwszSrc); + if (dwAttr == INVALID_FILE_ATTRIBUTES) + rc = RTErrConvertFromWin32(GetLastError()); + else if (RTFS_IS_DIRECTORY(fFileType)) + rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY; + else + rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VERR_IS_A_DIRECTORY : VINF_SUCCESS; + } + if (RT_SUCCESS(rc)) + { + if (MoveFileExW(pwszSrc, pwszDst, fFlags)) + rc = VINF_SUCCESS; + else + { + DWORD Err = GetLastError(); + rc = RTErrConvertFromWin32(Err); + Log(("MoveFileExW('%s', '%s', %#x, %RTfmode): fails with rc=%Rrc & lasterr=%d\n", + pszSrc, pszDst, fFlags, fFileType, rc, Err)); + } + } + RTPathWinFree(pwszDst); + } + RTPathWinFree(pwszSrc); + } + return rc; +} + + +RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename) +{ + /* + * 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(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER); + + /* + * Call the worker. + */ + int rc = rtPathWin32MoveRename(pszSrc, pszDst, fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, 0); + + LogFlow(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc)); + return rc; +} + + +RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink) +{ + RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink); + return VERR_NOT_IMPLEMENTED; +} + + +RTDECL(bool) RTPathExists(const char *pszPath) +{ + return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK); +} + + +RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszPath, false); + AssertReturn(*pszPath, false); + Assert(RTPATH_F_IS_VALID(fFlags, 0)); + + /* + * Try query file info. + */ + DWORD dwAttr; + PRTUTF16 pwszPath; + int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + dwAttr = GetFileAttributesW(pwszPath); + RTPathWinFree(pwszPath); + } + else + dwAttr = INVALID_FILE_ATTRIBUTES; + if (dwAttr == INVALID_FILE_ATTRIBUTES) + return false; + +#ifdef FILE_ATTRIBUTE_REPARSE_POINT + if ( (fFlags & RTPATH_F_FOLLOW_LINK) + && (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)) + { + AssertFailed(); + /** @todo Symlinks: RTPathExists+RTPathExistsEx is misbehaving on symbolic + * links on Windows. */ + } +#endif + + return true; +} + + +RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath) +{ + int rc; + + if (cchPath > 0) + { + /* + * GetCurrentDirectory may in some cases omit the drive letter, according + * to MSDN, thus the GetFullPathName call. + */ + RTUTF16 wszCurPath[RTPATH_MAX]; + if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath)) + { + RTUTF16 wszFullPath[RTPATH_MAX]; + if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL)) + { + if ( wszFullPath[1] == ':' + && RT_C_IS_LOWER(wszFullPath[0])) + wszFullPath[0] = RT_C_TO_UPPER(wszFullPath[0]); + + rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cchPath, NULL); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_BUFFER_OVERFLOW; + return rc; +} + + +RTDECL(int) RTPathSetCurrent(const char *pszPath) +{ + /* + * Validate input. + */ + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(*pszPath, VERR_INVALID_PARAMETER); + + /* + * This interface is almost identical to the Windows API. + */ + PRTUTF16 pwszPath; + int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /** @todo improve the slash stripping a bit? */ + size_t cwc = RTUtf16Len(pwszPath); + if ( cwc >= 2 + && ( pwszPath[cwc - 1] == L'/' + || pwszPath[cwc - 1] == L'\\') + && pwszPath[cwc - 2] != ':') + pwszPath[cwc - 1] = L'\0'; + + if (!SetCurrentDirectoryW(pwszPath)) + rc = RTErrConvertFromWin32(GetLastError()); + + RTPathWinFree(pwszPath); + } + return rc; +} + + +RTDECL(int) RTPathGetCurrentOnDrive(char chDrive, char *pszPath, size_t cbPath) +{ + int rc; + if (cbPath > 0) + { + WCHAR wszInput[4]; + wszInput[0] = chDrive; + wszInput[1] = ':'; + wszInput[2] = '\0'; + RTUTF16 wszFullPath[RTPATH_MAX]; + if (GetFullPathNameW(wszInput, RTPATH_MAX, wszFullPath, NULL)) + rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL); + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_BUFFER_OVERFLOW; + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/pathint-win.cpp b/src/VBox/Runtime/r3/win/pathint-win.cpp new file mode 100644 index 00000000..81799cc5 --- /dev/null +++ b/src/VBox/Runtime/r3/win/pathint-win.cpp @@ -0,0 +1,204 @@ +/* $Id: pathint-win.cpp $ */ +/** @file + * IPRT - Windows, Internal Path stuff. + */ + +/* + * 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/path.h> + +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/errcore.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include <iprt/nt/nt-and-windows.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The max number of non-null characters we pass to an Win32 API. + * You would think that MAX_PATH gives this length, however CreateDirectoryW was + * found to fail on Windows 10 (1803++) if given a perfectly formed path argument + * of 248 or more characters. Same when going thru UNC. + * + * So, to be conservative, we put the max number of characters in a non-\\?\ + * path to 243, not counting the terminator. + */ +#define ACTUAL_MAX_PATH 243 + + +DECL_NO_INLINE(static, bool) rtPathWinTryConvertToAbs(PRTUTF16 *ppwszPath) +{ + RTUTF16 wszFullPath[MAX_PATH + 1]; + DWORD cwcFull = GetFullPathNameW(*ppwszPath, MAX_PATH + 1, wszFullPath, NULL); + if (cwcFull <= ACTUAL_MAX_PATH) + { + RTUtf16Free(*ppwszPath); + PRTUTF16 const pwszCopy = RTUtf16Dup(wszFullPath); + *ppwszPath = pwszCopy; + if (pwszCopy) + return true; + } + return false; +} + + +RTDECL(int) RTPathWinFromUtf8(PRTUTF16 *ppwszPath, const char *pszPath, uint32_t fFlags) +{ + Assert(fFlags == 0); + RT_NOREF(fFlags); + + /* + * Do a straight conversion first. + */ + *ppwszPath = NULL; + size_t cwcResult = 0; + int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, ppwszPath, 0, &cwcResult); + if (RT_SUCCESS(rc)) + { + /* + * Check the resulting length. This is straight forward for absolute + * paths, but gets complicated for relative ones. + */ + if (cwcResult <= ACTUAL_MAX_PATH) + { + if (RT_C_IS_ALPHA(pszPath[0]) && pszPath[1] == ':') + { + if (RTPATH_IS_SLASH(pszPath[2])) + return VINF_SUCCESS; + + /* Drive relative path. Found no simple way of getting the current + path of a drive, so we try convert it to an absolute path and see + how that works out. It is what the API we're calling will have to + do anyway, so this should perform just as well. */ + if (rtPathWinTryConvertToAbs(ppwszPath)) + return VINF_SUCCESS; + } + else if (RTPATH_IS_SLASH(pszPath[0])) + { + if ( RTPATH_IS_SLASH(pszPath[1]) + && !RTPATH_IS_SLASH(pszPath[2]) + && pszPath[2] != '\0') + { + /* Passthru prefix '\\?\' is fine. */ + if ( pszPath[2] == '?' + && !RTPATH_IS_SLASH(pszPath[3])) + return VINF_SUCCESS; + + /* UNC requires a longer prefix ('\??\UNC\' instead of '\??\'), so + subtract 3 chars from the max limit to be on the safe side. */ + if (cwcResult <= ACTUAL_MAX_PATH - 3) + return VINF_SUCCESS; + } + else + { + /* Drive relative. Win32 will prepend a two letter drive specification. */ + if (cwcResult <= ACTUAL_MAX_PATH - 2) + return VINF_SUCCESS; + } + } + else + { + /* Relative to CWD. We can use the API to get it's current length. + Any race conditions here is entirely the caller's problem. */ + size_t cwcCwd = GetCurrentDirectoryW(0, NULL); + if (cwcCwd + cwcResult <= ACTUAL_MAX_PATH - 1) + return VINF_SUCCESS; + } + } + /* + * We're good if the caller already supplied the passthru/length prefix: '\\?\' + */ + else if ( pszPath[1] == '?' + && RTPATH_IS_SLASH(pszPath[3]) + && RTPATH_IS_SLASH(pszPath[1]) + && RTPATH_IS_SLASH(pszPath[0])) + return VINF_SUCCESS; + + /* + * Long path requiring \\?\ prefixing. + * + * We piggy back on the NT conversion here and ASSUME RTUtf16Free is the right + * way to free the result. + */ + RTUtf16Free(*ppwszPath); + *ppwszPath = NULL; + + struct _UNICODE_STRING NtName = { 0, 0, NULL }; + HANDLE hRootDir = NULL; + rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath); + if (RT_SUCCESS(rc)) + { + /* No root dir handle. */ + if (hRootDir == NULL) + { + /* Convert the NT '\??\' prefix to a win32 passthru prefix '\\?\' */ + if ( NtName.Buffer[0] == '\\' + && NtName.Buffer[1] == '?' + && NtName.Buffer[2] == '?' + && NtName.Buffer[3] == '\\') + { + NtName.Buffer[1] = '\\'; + + /* Zero termination paranoia. */ + if (NtName.Buffer[NtName.Length / sizeof(RTUTF16)] == '\0') + { + *ppwszPath = NtName.Buffer; + return VINF_SUCCESS; + } + AssertMsgFailed(("Length=%u %.*ls\n", NtName.Length, NtName.Length / sizeof(RTUTF16), NtName.Buffer)); + } + else + AssertMsgFailed(("%ls\n", NtName.Buffer)); + } + else + AssertMsgFailed(("%s\n", pszPath)); + RTNtPathFree(&NtName, &hRootDir); + } + } + return rc; +} + + +RTDECL(void) RTPathWinFree(PRTUTF16 pwszPath) +{ + RTUtf16Free(pwszPath); +} + diff --git a/src/VBox/Runtime/r3/win/pipe-win.cpp b/src/VBox/Runtime/r3/win/pipe-win.cpp new file mode 100644 index 00000000..93bf9afc --- /dev/null +++ b/src/VBox/Runtime/r3/win/pipe-win.cpp @@ -0,0 +1,1537 @@ +/* $Id: pipe-win.cpp $ */ +/** @file + * IPRT - Anonymous Pipes, Windows 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 * +*********************************************************************************************************************************/ +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/pipe.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/poll.h> +#include <iprt/process.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include "internal/pipe.h" +#include "internal/magics.h" +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The pipe buffer size we prefer. */ +#define RTPIPE_NT_SIZE _64K + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RTPIPEINTERNAL +{ + /** Magic value (RTPIPE_MAGIC). */ + uint32_t u32Magic; + /** The pipe handle. */ + HANDLE hPipe; + /** Set if this is the read end, clear if it's the write end. */ + bool fRead; + /** RTPipeFromNative: Leave native handle open on RTPipeClose. */ + bool fLeaveOpen; + /** Set if there is already pending I/O. */ + bool fIOPending; + /** Set if the zero byte read that the poll code using is pending. */ + bool fZeroByteRead; + /** Set if the pipe is broken. */ + bool fBrokenPipe; + /** Set if we've promised that the handle is writable. */ + bool fPromisedWritable; + /** Set if created inheritable. */ + bool fCreatedInheritable; + /** Usage counter. */ + uint32_t cUsers; + /** The overlapped I/O structure we use. */ + OVERLAPPED Overlapped; + /** Bounce buffer for writes. */ + uint8_t *pbBounceBuf; + /** Amount of used buffer space. */ + size_t cbBounceBufUsed; + /** Amount of allocated buffer space. */ + size_t cbBounceBufAlloc; + /** The handle of the poll set currently polling on this pipe. + * We can only have one poller at the time (lazy bird). */ + RTPOLLSET hPollSet; + /** Critical section protecting the above members. + * (Taking the lazy/simple approach.) */ + RTCRITSECT CritSect; + /** Buffer for the zero byte read. */ + uint8_t abBuf[8]; +} RTPIPEINTERNAL; + + + +/** + * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API. + * + * @returns Success indicator (true/false). + * @param pThis The pipe. + * @param pInfo The info structure. + */ +static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo) +{ + IO_STATUS_BLOCK Ios; + RT_ZERO(Ios); + RT_ZERO(*pInfo); + NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation); + return rcNt >= 0; +} + + +RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags) +{ + AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER); + AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Create the read end of the pipe. + */ + DWORD dwErr; + HANDLE hPipeR; + HANDLE hPipeW; + int rc; + for (;;) + { + static volatile uint32_t g_iNextPipe = 0; + char szName[128]; + RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe)); + + SECURITY_ATTRIBUTES SecurityAttributes; + PSECURITY_ATTRIBUTES pSecurityAttributes = NULL; + if (fFlags & RTPIPE_C_INHERIT_READ) + { + SecurityAttributes.nLength = sizeof(SecurityAttributes); + SecurityAttributes.lpSecurityDescriptor = NULL; + SecurityAttributes.bInheritHandle = TRUE; + pSecurityAttributes = &SecurityAttributes; + } + + DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED; +#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE + dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; +#endif + + DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; +#ifdef PIPE_REJECT_REMOTE_CLIENTS + dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS; +#endif + + hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE, + NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes); +#ifdef PIPE_REJECT_REMOTE_CLIENTS + if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) + { + dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS; + hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE, + NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes); + } +#endif +#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE + if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) + { + dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE; + hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE, + NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes); + } +#endif + if (hPipeR != INVALID_HANDLE_VALUE) + { + /* + * Connect to the pipe (the write end). + * We add FILE_READ_ATTRIBUTES here to make sure we can query the + * pipe state later on. + */ + pSecurityAttributes = NULL; + if (fFlags & RTPIPE_C_INHERIT_WRITE) + { + SecurityAttributes.nLength = sizeof(SecurityAttributes); + SecurityAttributes.lpSecurityDescriptor = NULL; + SecurityAttributes.bInheritHandle = TRUE; + pSecurityAttributes = &SecurityAttributes; + } + + hPipeW = CreateFileA(szName, + GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/, + 0 /*dwShareMode*/, + pSecurityAttributes, + OPEN_EXISTING /* dwCreationDisposition */, + FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/, + NULL /*hTemplateFile*/); + if (hPipeW != INVALID_HANDLE_VALUE) + break; + dwErr = GetLastError(); + CloseHandle(hPipeR); + } + else + dwErr = GetLastError(); + if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */ + && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */) + return RTErrConvertFromWin32(dwErr); + /* else: try again with a new name */ + } + + /* + * Create the two handles. + */ + RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (pThisR) + { + RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (pThisW) + { + rc = RTCritSectInit(&pThisR->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectInit(&pThisW->CritSect); + if (RT_SUCCESS(rc)) + { + pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/, + TRUE /*fInitialState*/, NULL /*pName*/); + if (pThisR->Overlapped.hEvent != NULL) + { + pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/, + TRUE /*fInitialState*/, NULL /*pName*/); + if (pThisW->Overlapped.hEvent != NULL) + { + pThisR->u32Magic = RTPIPE_MAGIC; + pThisW->u32Magic = RTPIPE_MAGIC; + pThisR->hPipe = hPipeR; + pThisW->hPipe = hPipeW; + pThisR->fRead = true; + pThisW->fRead = false; + pThisR->fLeaveOpen = false; + pThisW->fLeaveOpen = false; + //pThisR->fIOPending = false; + //pThisW->fIOPending = false; + //pThisR->fZeroByteRead = false; + //pThisW->fZeroByteRead = false; + //pThisR->fBrokenPipe = false; + //pThisW->fBrokenPipe = false; + //pThisW->fPromisedWritable = false; + //pThisR->fPromisedWritable = false; + pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE); + pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ); + //pThisR->cUsers = 0; + //pThisW->cUsers = 0; + //pThisR->pbBounceBuf = NULL; + //pThisW->pbBounceBuf = NULL; + //pThisR->cbBounceBufUsed = 0; + //pThisW->cbBounceBufUsed = 0; + //pThisR->cbBounceBufAlloc = 0; + //pThisW->cbBounceBufAlloc = 0; + pThisR->hPollSet = NIL_RTPOLLSET; + pThisW->hPollSet = NIL_RTPOLLSET; + + *phPipeRead = pThisR; + *phPipeWrite = pThisW; + return VINF_SUCCESS; + } + CloseHandle(pThisR->Overlapped.hEvent); + } + RTCritSectDelete(&pThisW->CritSect); + } + RTCritSectDelete(&pThisR->CritSect); + } + RTMemFree(pThisW); + } + else + rc = VERR_NO_MEMORY; + RTMemFree(pThisR); + } + else + rc = VERR_NO_MEMORY; + + CloseHandle(hPipeR); + CloseHandle(hPipeW); + return rc; +} + + +/** + * Common worker for handling I/O completion. + * + * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + */ +static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis) +{ + int rc; + DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0); + if (dwRc == WAIT_OBJECT_0) + { + DWORD cbWritten = 0; + if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE)) + { + for (;;) + { + if (cbWritten >= pThis->cbBounceBufUsed) + { + pThis->fIOPending = false; + rc = VINF_SUCCESS; + break; + } + + /* resubmit the remainder of the buffer - can this actually happen? */ + pThis->cbBounceBufUsed -= cbWritten; + memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed); + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, &cbWritten, &pThis->Overlapped)) + { + DWORD const dwErr = GetLastError(); + if (dwErr == ERROR_IO_PENDING) + rc = VINF_TRY_AGAIN; + else + { + pThis->fIOPending = false; + if (dwErr == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + } + break; + } + Assert(cbWritten > 0); + } + } + else + { + pThis->fIOPending = false; + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else if (dwRc == WAIT_TIMEOUT) + rc = VINF_TRY_AGAIN; + else + { + pThis->fIOPending = false; + if (dwRc == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + return rc; +} + + + +RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen) +{ + RTPIPEINTERNAL *pThis = hPipe; + if (pThis == NIL_RTPIPE) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + /* + * Do the cleanup. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE); + RTCritSectEnter(&pThis->CritSect); + Assert(pThis->cUsers == 0); + + if (!pThis->fRead && pThis->fIOPending) + rtPipeWriteCheckCompletion(pThis); + + if (!fLeaveOpen && !pThis->fLeaveOpen) + CloseHandle(pThis->hPipe); + pThis->hPipe = INVALID_HANDLE_VALUE; + + CloseHandle(pThis->Overlapped.hEvent); + pThis->Overlapped.hEvent = NULL; + + RTMemFree(pThis->pbBounceBuf); + pThis->pbBounceBuf = NULL; + + RTCritSectLeave(&pThis->CritSect); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTPipeClose(RTPIPE hPipe) +{ + return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/); +} + + +RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags) +{ + AssertPtrReturn(phPipe, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER); + AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER); + + /* + * Get and validate the pipe handle info. + */ + HANDLE hNative = (HANDLE)hNativePipe; + AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE); + + DWORD cMaxInstances; + DWORD fInfo; + if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances)) + return RTErrConvertFromWin32(GetLastError()); + /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin + seems to hand us such pipes when capturing output (@bugref{9397}), so just + ignore skip this check: + AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */ + AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE); + + DWORD cInstances; + DWORD fState; + if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0)) + return RTErrConvertFromWin32(GetLastError()); + AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE); + AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE); + AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE); + + /* + * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it + * and see if we need to duplicate it to make that call work. + */ + RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL)); + if (!pThis) + return VERR_NO_MEMORY; + int rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/, + TRUE /*fInitialState*/, NULL /*pName*/); + if (pThis->Overlapped.hEvent != NULL) + { + pThis->u32Magic = RTPIPE_MAGIC; + pThis->hPipe = hNative; + pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ); + pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN); + //pThis->fIOPending = false; + //pThis->fZeroByteRead = false; + //pThis->fBrokenPipe = false; + //pThis->fPromisedWritable = false; + pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT); + //pThis->cUsers = 0; + //pThis->pbBounceBuf = NULL; + //pThis->cbBounceBufUsed = 0; + //pThis->cbBounceBufAlloc = 0; + pThis->hPollSet = NIL_RTPOLLSET; + + HANDLE hNative2 = INVALID_HANDLE_VALUE; + FILE_PIPE_LOCAL_INFORMATION Info; + RT_ZERO(Info); + if ( g_pfnSetHandleInformation + && rtPipeQueryNtInfo(pThis, &Info)) + rc = VINF_SUCCESS; + else + { + if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/, + GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/, + pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/, + !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/, + 0 /*dwOptions*/)) + { + pThis->hPipe = hNative2; + if (rtPipeQueryNtInfo(pThis, &Info)) + { + pThis->fLeaveOpen = false; + rc = VINF_SUCCESS; + } + else + { + rc = VERR_ACCESS_DENIED; + CloseHandle(hNative2); + } + } + else + hNative2 = INVALID_HANDLE_VALUE; + } + if (RT_SUCCESS(rc)) + { + /* + * Verify the pipe state and correct the inheritability. + */ + AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE + || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE + || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE, + VERR_INVALID_HANDLE); + AssertStmt( Info.NamedPipeConfiguration + == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END + ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND) + : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) ) + || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX, + VERR_INVALID_HANDLE); + if ( RT_SUCCESS(rc) + && hNative2 == INVALID_HANDLE_VALUE + && !g_pfnSetHandleInformation(hNative, + HANDLE_FLAG_INHERIT /*dwMask*/, + fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0)) + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("%Rrc\n", rc)); + } + if (RT_SUCCESS(rc)) + { + /* + * Ok, we're good! If we replaced the handle, make sure it's not a standard + * handle if we think we need to close it. + */ + if (hNative2 != INVALID_HANDLE_VALUE) + { + if ( !(fFlags & RTPIPE_N_LEAVE_OPEN) + && hNative != GetStdHandle(STD_INPUT_HANDLE) + && hNative != GetStdHandle(STD_OUTPUT_HANDLE) + && hNative != GetStdHandle(STD_ERROR_HANDLE) ) + CloseHandle(hNative); + } + *phPipe = pThis; + return VINF_SUCCESS; + } + } + + /* Bail out. */ + if (hNative2 != INVALID_HANDLE_VALUE) + CloseHandle(hNative2); + CloseHandle(pThis->Overlapped.hEvent); + } + RTCritSectDelete(&pThis->CritSect); + } + RTMemFree(pThis); + return rc; +} + + +RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, -1); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1); + + return (RTHCINTPTR)pThis->hPipe; +} + + +RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, false); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false); + + return pThis->fCreatedInheritable; +} + + +RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pcbRead); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent readers, sorry. */ + if (pThis->cUsers == 0) + { + pThis->cUsers++; + + /* + * Kick off a an overlapped read. It should return immediately if + * there are bytes in the buffer. If not, we'll cancel it and see + * what we get back. + */ + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + DWORD cbRead = 0; + if ( cbToRead == 0 + || ReadFile(pThis->hPipe, pvBuf, + cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0, + &cbRead, &pThis->Overlapped)) + { + *pcbRead = cbRead; + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + pThis->fIOPending = true; + RTCritSectLeave(&pThis->CritSect); + + /* We use NtCancelIoFile here because the CancelIo API + providing access to it wasn't available till NT4. This + code needs to work (or at least load) with NT 3.1 */ + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios); + if (!NT_SUCCESS(rcNt)) + WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE); + + if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)) + { + *pcbRead = cbRead; + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_OPERATION_ABORTED) + { + *pcbRead = 0; + rc = VINF_TRY_AGAIN; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTCritSectEnter(&pThis->CritSect); + pThis->fIOPending = false; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent readers, sorry. */ + if (pThis->cUsers == 0) + { + pThis->cUsers++; + + size_t cbTotalRead = 0; + while (cbToRead > 0) + { + /* + * Kick of a an overlapped read. It should return immediately if + * there is bytes in the buffer. If not, we'll cancel it and see + * what we get back. + */ + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + DWORD cbRead = 0; + pThis->fIOPending = true; + RTCritSectLeave(&pThis->CritSect); + + if (ReadFile(pThis->hPipe, pvBuf, + cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0, + &cbRead, &pThis->Overlapped)) + rc = VINF_SUCCESS; + else if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE); + if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTCritSectEnter(&pThis->CritSect); + pThis->fIOPending = false; + if (RT_FAILURE(rc)) + break; + + /* advance */ + cbToRead -= cbRead; + cbTotalRead += cbRead; + pvBuf = (uint8_t *)pvBuf + cbRead; + } + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + + if (pcbRead) + { + *pcbRead = cbTotalRead; + if ( RT_FAILURE(rc) + && cbTotalRead + && rc != VERR_INVALID_POINTER) + rc = VINF_SUCCESS; + } + + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pcbWritten); + AssertPtr(pvBuf); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent writers, sorry. */ + if (pThis->cUsers == 0) + { + pThis->cUsers++; + + /* If I/O is pending, check if it has completed. */ + if (pThis->fIOPending) + rc = rtPipeWriteCheckCompletion(pThis); + else + rc = VINF_SUCCESS; + if (rc == VINF_SUCCESS) + { + Assert(!pThis->fIOPending); + + /* Adjust the number of bytes to write to fit into the current + buffer quota, unless we've promised stuff in RTPipeSelectOne. + WriteQuotaAvailable better not be zero when it shouldn't!! */ + FILE_PIPE_LOCAL_INFORMATION Info; + if ( !pThis->fPromisedWritable + && cbToWrite > 0 + && rtPipeQueryNtInfo(pThis, &Info)) + { + if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE) + rc = VERR_BROKEN_PIPE; + /** @todo fixme: To get the pipe writing support to work the + * block below needs to be commented out until a + * way is found to address the problem of the incorrectly + * set field Info.WriteQuotaAvailable. + * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite + * possibely what lead to the misunderstanding here wrt to + * WriteQuotaAvailable updating. */ +#if 0 + else if ( cbToWrite >= Info.WriteQuotaAvailable + && Info.OutboundQuota != 0 + && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc) + ) + { + cbToWrite = Info.WriteQuotaAvailable; + if (!cbToWrite) + rc = VINF_TRY_AGAIN; + } +#endif + } + pThis->fPromisedWritable = false; + + /* Do the bounce buffering. */ + if ( pThis->cbBounceBufAlloc < cbToWrite + && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE) + { + if (cbToWrite > RTPIPE_NT_SIZE) + cbToWrite = RTPIPE_NT_SIZE; + void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K)); + if (pv) + { + pThis->pbBounceBuf = (uint8_t *)pv; + pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K); + } + else + rc = VERR_NO_MEMORY; + } + else if (cbToWrite > RTPIPE_NT_SIZE) + cbToWrite = RTPIPE_NT_SIZE; + if (RT_SUCCESS(rc) && cbToWrite) + { + memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite); + pThis->cbBounceBufUsed = (uint32_t)cbToWrite; + + /* Submit the write. */ + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + DWORD cbWritten = 0; + if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, + &cbWritten, &pThis->Overlapped)) + { + *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */ + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + *pcbWritten = cbToWrite; + pThis->fIOPending = true; + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (RT_SUCCESS(rc)) + *pcbWritten = 0; + } + else if (RT_SUCCESS(rc)) + *pcbWritten = 0; + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; +} + + +RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + AssertPtr(pvBuf); + AssertPtrNull(pcbWritten); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent writers, sorry. */ + if (pThis->cUsers == 0) + { + pThis->cUsers++; + + /* + * If I/O is pending, wait for it to complete. + */ + if (pThis->fIOPending) + { + rc = rtPipeWriteCheckCompletion(pThis); + while (rc == VINF_TRY_AGAIN) + { + Assert(pThis->fIOPending); + HANDLE hEvent = pThis->Overlapped.hEvent; + RTCritSectLeave(&pThis->CritSect); + WaitForSingleObject(hEvent, INFINITE); + RTCritSectEnter(&pThis->CritSect); + } + } + if (RT_SUCCESS(rc)) + { + Assert(!pThis->fIOPending); + pThis->fPromisedWritable = false; + + /* + * Try write everything. + * No bounce buffering, cUsers protects us. + */ + size_t cbTotalWritten = 0; + while (cbToWrite > 0) + { + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + pThis->fIOPending = true; + RTCritSectLeave(&pThis->CritSect); + + DWORD cbWritten = 0; + DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0; + if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped)) + rc = VINF_SUCCESS; + else if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE); + if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (GetLastError() == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTCritSectEnter(&pThis->CritSect); + pThis->fIOPending = false; + if (RT_FAILURE(rc)) + break; + + /* advance */ + if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */ + cbWritten = cbToWriteInThisIteration; + pvBuf = (char const *)pvBuf + cbWritten; + cbTotalWritten += cbWritten; + cbToWrite -= cbWritten; + } + + if (pcbWritten) + { + *pcbWritten = cbTotalWritten; + if ( RT_FAILURE(rc) + && cbTotalWritten + && rc != VERR_INVALID_POINTER) + rc = VINF_SUCCESS; + } + } + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + + pThis->cUsers--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + return rc; + +#if 0 /** @todo r=bird: What's this? */ + int rc = rtPipeTryBlocking(pThis); + if (RT_SUCCESS(rc)) + { + size_t cbTotalWritten = 0; + while (cbToWrite > 0) + { + ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX)); + if (cbWritten < 0) + { + rc = RTErrConvertFromErrno(errno); + break; + } + + /* advance */ + pvBuf = (char const *)pvBuf + cbWritten; + cbTotalWritten += cbWritten; + cbToWrite -= cbWritten; + } + + if (pcbWritten) + { + *pcbWritten = cbTotalWritten; + if ( RT_FAILURE(rc) + && cbTotalWritten + && rc != VERR_INVALID_POINTER) + rc = VINF_SUCCESS; + } + + ASMAtomicDecU32(&pThis->u32State); + } + return rc; +#endif +} + + +RTDECL(int) RTPipeFlush(RTPIPE hPipe) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED); + + if (!FlushFileBuffers(pThis->hPipe)) + { + int rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + return rc; + } + return VINF_SUCCESS; +} + + +RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + uint64_t const StartMsTS = RTTimeMilliTS(); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + return rc; + for (unsigned iLoop = 0;; iLoop++) + { + HANDLE hWait = INVALID_HANDLE_VALUE; + if (pThis->fRead) + { + if (pThis->fIOPending) + hWait = pThis->Overlapped.hEvent; + else + { + /* Peek at the pipe buffer and see how many bytes it contains. */ + DWORD cbAvailable; + if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL) + && cbAvailable > 0) + { + rc = VINF_SUCCESS; + break; + } + + /* Start a zero byte read operation that we can wait on. */ + if (cMillies == 0) + { + rc = VERR_TIMEOUT; + break; + } + AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5); + rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE); + DWORD cbRead = 0; + if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped)) + { + rc = VINF_SUCCESS; + if (iLoop > 10) + RTThreadYield(); + } + else if (GetLastError() == ERROR_IO_PENDING) + { + pThis->cUsers++; + pThis->fIOPending = true; + pThis->fZeroByteRead = true; + hWait = pThis->Overlapped.hEvent; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else + { + if (pThis->fIOPending) + { + rc = rtPipeWriteCheckCompletion(pThis); + if (RT_FAILURE(rc)) + break; + } + if (pThis->fIOPending) + hWait = pThis->Overlapped.hEvent; + else + { + FILE_PIPE_LOCAL_INFORMATION Info; +#if 1 + /* We can always write one bounce buffer full of data regardless of + the pipe buffer state. We must of course take this into account, + or code like "Full write buffer" test in tstRTPipe gets confused. */ + rc = VINF_SUCCESS; + if (rtPipeQueryNtInfo(pThis, &Info)) + { + /* Check for broken pipe. */ + if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE) + pThis->fPromisedWritable = true; + else + rc = VERR_BROKEN_PIPE; + } + else + pThis->fPromisedWritable = true; + break; + +#else /* old code: */ + if (rtPipeQueryNtInfo(pThis, &Info)) + { + /* Check for broken pipe. */ + if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE) + { + rc = VERR_BROKEN_PIPE; + break; + } + + /* Check for available write buffer space. */ + if (Info.WriteQuotaAvailable > 0) + { + pThis->fPromisedWritable = false; + rc = VINF_SUCCESS; + break; + } + + /* delayed buffer alloc or timeout: phony promise + later: See if we still can associate a semaphore with + the pipe, like on OS/2. */ + if (Info.OutboundQuota == 0 || cMillies) + { + pThis->fPromisedWritable = true; + rc = VINF_SUCCESS; + break; + } + } + else + { + pThis->fPromisedWritable = true; + rc = VINF_SUCCESS; + break; + } +#endif + } + } + if (RT_FAILURE(rc)) + break; + + /* + * Check for timeout. + */ + DWORD cMsMaxWait = INFINITE; + if ( cMillies != RT_INDEFINITE_WAIT + && ( hWait != INVALID_HANDLE_VALUE + || iLoop > 10) + ) + { + uint64_t cElapsed = RTTimeMilliTS() - StartMsTS; + if (cElapsed >= cMillies) + { + rc = VERR_TIMEOUT; + break; + } + cMsMaxWait = cMillies - (uint32_t)cElapsed; + } + + /* + * Wait. + */ + if (hWait != INVALID_HANDLE_VALUE) + { + RTCritSectLeave(&pThis->CritSect); + + DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait); + if (dwRc == WAIT_OBJECT_0) + rc = VINF_SUCCESS; + else if (dwRc == WAIT_TIMEOUT) + rc = VERR_TIMEOUT; + else if (dwRc == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + if ( RT_FAILURE(rc) + && pThis->u32Magic != RTPIPE_MAGIC) + return rc; + + RTCritSectEnter(&pThis->CritSect); + if (pThis->fZeroByteRead) + { + pThis->cUsers--; + pThis->fIOPending = false; + if (rc != VINF_SUCCESS) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NtCancelIoFile(pThis->hPipe, &Ios); + } + DWORD cbRead = 0; + GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/); + } + if (RT_FAILURE(rc)) + break; + } + } + + if (rc == VERR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + + RTCritSectLeave(&pThis->CritSect); + return rc; +} + + +RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ); + AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_FAILURE(rc)) + return rc; + + /** @todo The file size should give the same info and be slightly faster... */ + DWORD cbAvailable = 0; + if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)) + { +#if ARCH_BITS == 32 + /* + * Kludge! + * + * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted + * by the read position in the current message/buffer, so it could + * potentially be too high. This may cause the caller to try read more + * data than what's actually available, which may cause the read to + * block when the caller thought it wouldn't. + * + * To get an accurate readable size, we have to provide an output + * buffer and see how much we actually get back in it, as the data + * peeking works correctly (as you would expect). + */ + if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64) + { /* No data available or kernel shouldn't be affected. */ } + else + { + for (unsigned i = 0; ; i++) + { + uint8_t abBufStack[_16K]; + void *pvBufFree = NULL; + void *pvBuf; + DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64); + if (cbBuf <= sizeof(abBufStack)) + { + pvBuf = abBufStack; + /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */ + } + else + { + pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf); + if (!pvBuf) + { + rc = VERR_NO_TMP_MEMORY; + cbAvailable = 1; + break; + } + } + + DWORD cbAvailable2 = 0; + DWORD cbRead = 0; + BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL); + Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n", + i, cbAvailable, cbRead, cbAvailable2, cbBuf)); + + RTMemTmpFree(pvBufFree); + + if (fRc) + { + if (cbAvailable2 <= cbBuf || i >= 10) + cbAvailable = cbRead; + else + { + cbAvailable = cbAvailable2; + continue; + } + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + cbAvailable = 1; + } + break; + } + } +#endif + *pcbReadable = cbAvailable; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTCritSectLeave(&pThis->CritSect); + return rc; +} + + +RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, 0); + + rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead); + + FILE_PIPE_LOCAL_INFORMATION Info; + if (rtPipeQueryNtInfo(pThis, &Info)) + { + pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota; + pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable; + } + + RTCritSectLeave(&pThis->CritSect); + return VINF_SUCCESS; +} + + +int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE); + + AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER); + AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER); + + /* Later: Try register an event handle with the pipe like on OS/2, there is + a file control for doing this obviously intended for the OS/2 subsys. + The question is whether this still exists on Vista and W7. */ + *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent; + return VINF_SUCCESS; +} + + +/** + * Checks for pending events. + * + * @returns Event mask or 0. + * @param pThis The pipe handle. + * @param fEvents The desired events. + */ +static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents) +{ + uint32_t fRetEvents = 0; + if (pThis->fBrokenPipe) + fRetEvents |= RTPOLL_EVT_ERROR; + else if (pThis->fRead) + { + if (!pThis->fIOPending) + { + DWORD cbAvailable; + if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)) + { + if ( (fEvents & RTPOLL_EVT_READ) + && cbAvailable > 0) + fRetEvents |= RTPOLL_EVT_READ; + } + else + { + if (GetLastError() == ERROR_BROKEN_PIPE) + pThis->fBrokenPipe = true; + fRetEvents |= RTPOLL_EVT_ERROR; + } + } + } + else + { + if (pThis->fIOPending) + { + rtPipeWriteCheckCompletion(pThis); + if (pThis->fBrokenPipe) + fRetEvents |= RTPOLL_EVT_ERROR; + } + if ( !pThis->fIOPending + && !fRetEvents) + { + FILE_PIPE_LOCAL_INFORMATION Info; + if (rtPipeQueryNtInfo(pThis, &Info)) + { + /* Check for broken pipe. */ + if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE) + { + fRetEvents = RTPOLL_EVT_ERROR; + pThis->fBrokenPipe = true; + } + + /* Check if there is available buffer space. */ + if ( !fRetEvents + && (fEvents & RTPOLL_EVT_WRITE) + && ( Info.WriteQuotaAvailable > 0 + || Info.OutboundQuota == 0) + ) + fRetEvents |= RTPOLL_EVT_WRITE; + } + else if (fEvents & RTPOLL_EVT_WRITE) + fRetEvents |= RTPOLL_EVT_WRITE; + } + } + + return fRetEvents; +} + + +/** + * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is + * clear, starts whatever actions we've got running during the poll call. + * + * @returns 0 if no pending events, actions initiated if @a fNoWait is clear. + * Event mask (in @a fEvents) and no actions if the handle is ready + * already. + * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a + * different poll set. + * + * @param hPipe The pipe handle. + * @param hPollSet The poll set handle (for access checks). + * @param fEvents The events we're polling for. + * @param fFinalEntry Set if this is the final entry for this handle + * in this poll set. This can be used for dealing + * with duplicate entries. + * @param fNoWait Set if it's a zero-wait poll call. Clear if + * we'll wait for an event to occur. + */ +uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait) +{ + /** @todo All this polling code could be optimized to make fewer system + * calls; like for instance the ResetEvent calls. */ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, UINT32_MAX); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX); + RT_NOREF_PV(fFinalEntry); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, UINT32_MAX); + + /* Check that this is the only current use of this pipe. */ + uint32_t fRetEvents; + if ( pThis->cUsers == 0 + || pThis->hPollSet == hPollSet) + { + /* Check what the current events are. */ + fRetEvents = rtPipePollCheck(pThis, fEvents); + if ( !fRetEvents + && !fNoWait) + { + /* Make sure the event semaphore has been reset. */ + if (!pThis->fIOPending) + { + rc = ResetEvent(pThis->Overlapped.hEvent); + Assert(rc == TRUE); + } + + /* Kick off the zero byte read thing if applicable. */ + if ( !pThis->fIOPending + && pThis->fRead + && (fEvents & RTPOLL_EVT_READ) + ) + { + DWORD cbRead = 0; + if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped)) + fRetEvents = rtPipePollCheck(pThis, fEvents); + else if (GetLastError() == ERROR_IO_PENDING) + { + pThis->fIOPending = true; + pThis->fZeroByteRead = true; + } + else + fRetEvents = RTPOLL_EVT_ERROR; + } + + /* If we're still set for the waiting, record the poll set and + mark the pipe used. */ + if (!fRetEvents) + { + pThis->cUsers++; + pThis->hPollSet = hPollSet; + } + } + } + else + { + AssertFailed(); + fRetEvents = UINT32_MAX; + } + + RTCritSectLeave(&pThis->CritSect); + return fRetEvents; +} + + +/** + * Called after a WaitForMultipleObjects returned in order to check for pending + * events and stop whatever actions that rtPipePollStart() initiated. + * + * @returns Event mask or 0. + * + * @param hPipe The pipe handle. + * @param fEvents The events we're polling for. + * @param fFinalEntry Set if this is the final entry for this handle + * in this poll set. This can be used for dealing + * with duplicate entries. Only keep in mind that + * this method is called in reverse order, so the + * first call will have this set (when the entire + * set was processed). + * @param fHarvestEvents Set if we should check for pending events. + */ +uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents) +{ + RTPIPEINTERNAL *pThis = hPipe; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0); + RT_NOREF_PV(fFinalEntry); + RT_NOREF_PV(fHarvestEvents); + + int rc = RTCritSectEnter(&pThis->CritSect); + AssertRCReturn(rc, 0); + + Assert(pThis->cUsers > 0); + + + /* Cancel the zero byte read. */ + uint32_t fRetEvents = 0; + if (pThis->fZeroByteRead) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NtCancelIoFile(pThis->hPipe, &Ios); + + DWORD cbRead = 0; + if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/) + && GetLastError() != ERROR_OPERATION_ABORTED) + fRetEvents = RTPOLL_EVT_ERROR; + + pThis->fIOPending = false; + pThis->fZeroByteRead = false; + } + + /* harvest events. */ + fRetEvents |= rtPipePollCheck(pThis, fEvents); + + /* update counters. */ + pThis->cUsers--; + /** @todo This isn't sane, or is it? See OS/2 impl. */ + if (!pThis->cUsers) + pThis->hPollSet = NIL_RTPOLLSET; + + RTCritSectLeave(&pThis->CritSect); + return fRetEvents; +} + diff --git a/src/VBox/Runtime/r3/win/process-win.cpp b/src/VBox/Runtime/r3/win/process-win.cpp new file mode 100644 index 00000000..18ed9ab0 --- /dev/null +++ b/src/VBox/Runtime/r3/win/process-win.cpp @@ -0,0 +1,2750 @@ +/* $Id: process-win.cpp $ */ +/** @file + * IPRT - Process, Windows. + */ + +/* + * 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_PROCESS +#include <iprt/asm.h> /* hack */ + +#include <iprt/nt/nt-and-windows.h> +#include <Userenv.h> +#include <tlhelp32.h> +#ifndef IPRT_NO_CRT +# include <process.h> +# include <errno.h> +# include <Strsafe.h> +#endif +#include <LsaLookup.h> +#include <Lmcons.h> + +#define _NTDEF_ /* Prevents redefining (P)UNICODE_STRING. */ +#include <Ntsecapi.h> + +#include <iprt/process.h> +#include "internal-r3-win.h" + +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/file.h> +#include <iprt/err.h> +#include <iprt/env.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/once.h> +#include <iprt/path.h> +#include <iprt/pipe.h> +#include <iprt/string.h> +#include <iprt/socket.h> +#include <iprt/utf16.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/* kernel32.dll: */ +//typedef DWORD (WINAPI *PFNWTSGETACTIVECONSOLESESSIONID)(VOID); +typedef HANDLE (WINAPI *PFNCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD); +typedef BOOL (WINAPI *PFNPROCESS32FIRSTW)(HANDLE, LPPROCESSENTRY32W); +typedef BOOL (WINAPI *PFNPROCESS32NEXTW)(HANDLE, LPPROCESSENTRY32W); + +/* psapi.dll: */ +typedef BOOL (WINAPI *PFNENUMPROCESSES)(LPDWORD, DWORD, LPDWORD); +typedef DWORD (WINAPI *PFNGETMODULEBASENAMEW)(HANDLE, HMODULE, LPWSTR, DWORD); + +/* advapi32.dll: */ +typedef BOOL (WINAPI *PFNCREATEPROCESSWITHLOGON)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD, + LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); +typedef NTSTATUS (NTAPI *PFNLSALOOKUPNAMES2)(LSA_HANDLE, ULONG, ULONG, PLSA_UNICODE_STRING, + PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_SID2*); + +/* userenv.dll: */ +typedef BOOL (WINAPI *PFNCREATEENVIRONMENTBLOCK)(LPVOID *, HANDLE, BOOL); +typedef BOOL (WINAPI *PFNPFNDESTROYENVIRONMENTBLOCK)(LPVOID); +typedef BOOL (WINAPI *PFNLOADUSERPROFILEW)(HANDLE, LPPROFILEINFOW); +typedef BOOL (WINAPI *PFNUNLOADUSERPROFILE)(HANDLE, HANDLE); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Init once structure. */ +static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER; +/** Critical section protecting the process array. */ +static RTCRITSECT g_CritSect; +/** The number of processes in the array. */ +static uint32_t g_cProcesses; +/** The current allocation size. */ +static uint32_t g_cProcessesAlloc; +/** Array containing the live or non-reaped child processes. */ +static struct RTPROCWINENTRY +{ + /** The process ID. */ + ULONG_PTR pid; + /** The process handle. */ + HANDLE hProcess; +} *g_paProcesses; + +/** Structure for storing a user's account info. + * Must be free'd with rtProcWinFreeAccountInfo(). */ +typedef struct RTPROCWINACCOUNTINFO +{ + /** User name. */ + PRTUTF16 pwszUserName; + /** Domain this account is tied to. Can be NULL if no domain is being used. */ + PRTUTF16 pwszDomain; +} RTPROCWINACCOUNTINFO, *PRTPROCWINACCOUNTINFO; + +/** @name userenv.dll imports (we don't unload it). + * They're all optional. So in addition to using g_rtProcWinResolveOnce, the + * caller must also check if any of the necessary APIs are NULL pointers. + * @{ */ +/** Init once structure for run-as-user functions we need. */ +static RTONCE g_rtProcWinResolveOnce = RTONCE_INITIALIZER; +/* kernel32.dll: */ +static PFNCREATETOOLHELP32SNAPSHOT g_pfnCreateToolhelp32Snapshot = NULL; +static PFNPROCESS32FIRSTW g_pfnProcess32FirstW = NULL; +static PFNPROCESS32NEXTW g_pfnProcess32NextW = NULL; +/* psapi.dll: */ +static PFNGETMODULEBASENAMEW g_pfnGetModuleBaseNameW = NULL; +static PFNENUMPROCESSES g_pfnEnumProcesses = NULL; +/* advapi32.dll: */ +static PFNCREATEPROCESSWITHLOGON g_pfnCreateProcessWithLogonW = NULL; +static decltype(LogonUserW) *g_pfnLogonUserW = NULL; +static decltype(CreateProcessAsUserW) *g_pfnCreateProcessAsUserW = NULL; +/* user32.dll: */ +static decltype(OpenWindowStationW) *g_pfnOpenWindowStationW = NULL; +static decltype(CloseWindowStation) *g_pfnCloseWindowStation = NULL; +/* userenv.dll: */ +static PFNCREATEENVIRONMENTBLOCK g_pfnCreateEnvironmentBlock = NULL; +static PFNPFNDESTROYENVIRONMENTBLOCK g_pfnDestroyEnvironmentBlock = NULL; +static PFNLOADUSERPROFILEW g_pfnLoadUserProfileW = NULL; +static PFNUNLOADUSERPROFILE g_pfnUnloadUserProfile = NULL; +/** @} */ + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec); +static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, + PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec); + + +/** + * Clean up the globals. + * + * @param enmReason Ignored. + * @param iStatus Ignored. + * @param pvUser Ignored. + */ +static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser) +{ + NOREF(pvUser); NOREF(iStatus); NOREF(enmReason); + + RTCritSectDelete(&g_CritSect); + + size_t i = g_cProcesses; + while (i-- > 0) + { + CloseHandle(g_paProcesses[i].hProcess); + g_paProcesses[i].hProcess = NULL; + } + RTMemFree(g_paProcesses); + + g_paProcesses = NULL; + g_cProcesses = 0; + g_cProcessesAlloc = 0; +} + + +/** + * Initialize the globals. + * + * @returns IPRT status code. + * @param pvUser Ignored. + */ +static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser) +{ + NOREF(pvUser); + + g_cProcesses = 0; + g_cProcessesAlloc = 0; + g_paProcesses = NULL; + int rc = RTCritSectInit(&g_CritSect); + if (RT_SUCCESS(rc)) + { + /** @todo init once, terminate once - this is a generic thing which should + * have some kind of static and simpler setup! */ + rc = RTTermRegisterCallback(rtProcWinTerm, NULL); + if (RT_SUCCESS(rc)) + return rc; + RTCritSectDelete(&g_CritSect); + } + return rc; +} + + +/** + * Gets the process handle for a process from g_paProcesses. + * + * @returns Process handle if found, NULL if not. + * @param pid The process to remove (pid). + */ +static HANDLE rtProcWinFindPid(RTPROCESS pid) +{ + HANDLE hProcess = NULL; + + RTCritSectEnter(&g_CritSect); + uint32_t i = g_cProcesses; + while (i-- > 0) + if (g_paProcesses[i].pid == pid) + { + hProcess = g_paProcesses[i].hProcess; + break; + } + RTCritSectLeave(&g_CritSect); + + return hProcess; +} + + +/** + * Removes a process from g_paProcesses and closes the process handle. + * + * @param pid The process to remove (pid). + */ +static void rtProcWinRemovePid(RTPROCESS pid) +{ + RTCritSectEnter(&g_CritSect); + uint32_t i = g_cProcesses; + while (i-- > 0) + if (g_paProcesses[i].pid == pid) + { + HANDLE hProcess = g_paProcesses[i].hProcess; + + g_cProcesses--; + uint32_t cToMove = g_cProcesses - i; + if (cToMove) + memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0])); + + RTCritSectLeave(&g_CritSect); + CloseHandle(hProcess); + return; + } + RTCritSectLeave(&g_CritSect); +} + + +/** + * Adds a process to g_paProcesses. + * + * @returns IPRT status code. + * @param pid The process id. + * @param hProcess The process handle. + */ +static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess) +{ + RTCritSectEnter(&g_CritSect); + + uint32_t i = g_cProcesses; + if (i >= g_cProcessesAlloc) + { + void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0])); + if (RT_UNLIKELY(!pvNew)) + { + RTCritSectLeave(&g_CritSect); + return VERR_NO_MEMORY; + } + g_paProcesses = (struct RTPROCWINENTRY *)pvNew; + g_cProcessesAlloc = i + 16; + } + + g_paProcesses[i].pid = pid; + g_paProcesses[i].hProcess = hProcess; + g_cProcesses = i + 1; + + RTCritSectLeave(&g_CritSect); + return VINF_SUCCESS; +} + + +/** + * Initialize the import APIs for run-as-user and special environment support. + * + * @returns IPRT status code. + * @param pvUser Ignored. + */ +static DECLCALLBACK(int) rtProcWinResolveOnce(void *pvUser) +{ + int rc; + RTLDRMOD hMod; + RT_NOREF_PV(pvUser); + + /* + * kernel32.dll APIs introduced after NT4. + */ + g_pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)GetProcAddress(g_hModKernel32, "CreateToolhelp32Snapshot"); + g_pfnProcess32FirstW = (PFNPROCESS32FIRSTW )GetProcAddress(g_hModKernel32, "Process32FirstW"); + g_pfnProcess32NextW = (PFNPROCESS32NEXTW )GetProcAddress(g_hModKernel32, "Process32NextW"); + + /* + * psapi.dll APIs, if none of the above are available. + */ + if ( !g_pfnCreateToolhelp32Snapshot + || !g_pfnProcess32FirstW + || !g_pfnProcess32NextW) + { + Assert(!g_pfnCreateToolhelp32Snapshot && !g_pfnProcess32FirstW && !g_pfnProcess32NextW); + + rc = RTLdrLoadSystem("psapi.dll", true /*fNoUnload*/, &hMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hMod, "GetModuleBaseNameW", (void **)&g_pfnGetModuleBaseNameW); + AssertStmt(RT_SUCCESS(rc), g_pfnGetModuleBaseNameW = NULL); + + rc = RTLdrGetSymbol(hMod, "EnumProcesses", (void **)&g_pfnEnumProcesses); + AssertStmt(RT_SUCCESS(rc), g_pfnEnumProcesses = NULL); + + RTLdrClose(hMod); + } + } + + /* + * advapi32.dll APIs. + */ + rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hMod, "CreateProcessWithLogonW", (void **)&g_pfnCreateProcessWithLogonW); + if (RT_FAILURE(rc)) { g_pfnCreateProcessWithLogonW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); } + + rc = RTLdrGetSymbol(hMod, "LogonUserW", (void **)&g_pfnLogonUserW); + if (RT_FAILURE(rc)) { g_pfnLogonUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); } + + rc = RTLdrGetSymbol(hMod, "CreateProcessAsUserW", (void **)&g_pfnCreateProcessAsUserW); + if (RT_FAILURE(rc)) { g_pfnCreateProcessAsUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); } + + RTLdrClose(hMod); + } + + /* + * user32.dll APIs. + */ + rc = RTLdrLoadSystem("user32.dll", true /*fNoUnload*/, &hMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hMod, "OpenWindowStationW", (void **)&g_pfnOpenWindowStationW); + if (RT_FAILURE(rc)) { g_pfnOpenWindowStationW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); } + + rc = RTLdrGetSymbol(hMod, "CloseWindowStation", (void **)&g_pfnCloseWindowStation); + if (RT_FAILURE(rc)) { g_pfnCloseWindowStation = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); } + + RTLdrClose(hMod); + } + + /* + * userenv.dll APIs. + */ + rc = RTLdrLoadSystem("userenv.dll", true /*fNoUnload*/, &hMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hMod, "LoadUserProfileW", (void **)&g_pfnLoadUserProfileW); + if (RT_FAILURE(rc)) { g_pfnLoadUserProfileW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); } + + rc = RTLdrGetSymbol(hMod, "UnloadUserProfile", (void **)&g_pfnUnloadUserProfile); + if (RT_FAILURE(rc)) { g_pfnUnloadUserProfile = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); } + + rc = RTLdrGetSymbol(hMod, "CreateEnvironmentBlock", (void **)&g_pfnCreateEnvironmentBlock); + if (RT_FAILURE(rc)) { g_pfnCreateEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); } + + rc = RTLdrGetSymbol(hMod, "DestroyEnvironmentBlock", (void **)&g_pfnDestroyEnvironmentBlock); + if (RT_FAILURE(rc)) { g_pfnDestroyEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); } + + RTLdrClose(hMod); + } + + return VINF_SUCCESS; +} + + +RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess) +{ + return RTProcCreateEx(pszExec, papszArgs, Env, fFlags, + NULL, NULL, NULL, /* standard handles */ + NULL /*pszAsUser*/, NULL /* pszPassword*/, + NULL /*pvExtraData*/, pProcess); +} + + +/** + * The following NT call is for v3.51 and does the equivalent of: + * DuplicateTokenEx(hSrcToken, MAXIMUM_ALLOWED, NULL, + * SecurityIdentification, TokenPrimary, phToken); + */ +static int rtProcWinDuplicateToken(HANDLE hSrcToken, PHANDLE phToken) +{ + int rc; + if (g_pfnNtDuplicateToken) + { + SECURITY_QUALITY_OF_SERVICE SecQoS; + SecQoS.Length = sizeof(SecQoS); + SecQoS.ImpersonationLevel = SecurityIdentification; + SecQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + SecQoS.EffectiveOnly = FALSE; + + OBJECT_ATTRIBUTES ObjAttr; + InitializeObjectAttributes(&ObjAttr, NULL /*Name*/, 0 /*OBJ_XXX*/, NULL /*Root*/, NULL /*SecDesc*/); + ObjAttr.SecurityQualityOfService = &SecQoS; + + NTSTATUS rcNt = g_pfnNtDuplicateToken(hSrcToken, MAXIMUM_ALLOWED, &ObjAttr, FALSE, TokenPrimary, phToken); + if (NT_SUCCESS(rcNt)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromNtStatus(rcNt); + } + else + rc = VERR_SYMBOL_NOT_FOUND; /** @todo do we really need to duplicate the token? */ + return rc; +} + + +/** + * Get the token assigned to the thread indicated by @a hThread. + * + * Only used when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is in effect and the + * purpose is to get a duplicate the impersonated token of the current thread. + * + * @returns IPRT status code. + * @param hThread The thread handle (current thread). + * @param phToken Where to return the a duplicate of the thread token + * handle on success. (The caller closes it.) + */ +static int rtProcWinGetThreadTokenHandle(HANDLE hThread, PHANDLE phToken) +{ + AssertPtr(phToken); + + int rc; + HANDLE hTokenThread; + if (OpenThreadToken(hThread, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE + | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, + TRUE /* OpenAsSelf - for impersonation at SecurityIdentification level */, + &hTokenThread)) + { + rc = rtProcWinDuplicateToken(hTokenThread, phToken); + CloseHandle(hTokenThread); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + return rc; +} + + +/** + * Get the token assigned the process indicated by @a hProcess. + * + * Only used when pwszUser is NULL and RTPROC_FLAGS_AS_IMPERSONATED_TOKEN isn't + * set. + * + * @returns IPRT status code. + * @param hProcess The process handle (current process). + * @param phToken Where to return the a duplicate of the thread token + * handle on success. (The caller closes it.) + */ +static int rtProcWinGetProcessTokenHandle(HANDLE hProcess, PHANDLE phToken) +{ + AssertPtr(phToken); + + int rc; + HANDLE hTokenProcess; + if (OpenProcessToken(hProcess, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE + | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, + &hTokenProcess)) + { + rc = rtProcWinDuplicateToken(hTokenProcess, phToken); /* not sure if this is strictly necessary */ + CloseHandle(hTokenProcess); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + return rc; +} + + +/** + * Get the process token of the process indicated by @a dwPID if the @a pSid and + * @a idSessionDesired matches. + * + * @returns IPRT status code. + * @param dwPid The process identifier. + * @param pSid The secure identifier of the user. + * @param idDesiredSession The session the process candidate should + * preferably belong to, UINT32_MAX if anything + * goes. + * @param phToken Where to return the a duplicate of the process token + * handle on success. (The caller closes it.) + */ +static int rtProcWinGetProcessTokenHandle(DWORD dwPid, PSID pSid, DWORD idDesiredSession, PHANDLE phToken) +{ + AssertPtr(pSid); + AssertPtr(phToken); + + int rc; + HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPid); + if (hProc != NULL) + { + HANDLE hTokenProc; + if (OpenProcessToken(hProc, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE + | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, + &hTokenProc)) + { + /* + * Query the user SID from the token. + */ + SetLastError(NO_ERROR); + DWORD dwSize = 0; + BOOL fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize); + DWORD dwErr = GetLastError(); + if ( !fRc + && dwErr == ERROR_INSUFFICIENT_BUFFER + && dwSize > 0) + { + PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize); + if (pTokenUser) + { + if (GetTokenInformation(hTokenProc, TokenUser, pTokenUser, dwSize, &dwSize)) + { + /* + * Match token user with the user we're want to create a process as. + */ + if ( IsValidSid(pTokenUser->User.Sid) + && EqualSid(pTokenUser->User.Sid, pSid)) + { + /* + * Do we need to match the session ID? + */ + rc = VINF_SUCCESS; + if (idDesiredSession != UINT32_MAX) + { + DWORD idCurSession = UINT32_MAX; + if (GetTokenInformation(hTokenProc, TokenSessionId, &idCurSession, sizeof(DWORD), &dwSize)) + rc = idDesiredSession == idCurSession ? VINF_SUCCESS : VERR_NOT_FOUND; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + if (RT_SUCCESS(rc)) + { + /* + * Got a match. Duplicate the token. This duplicated token will + * be used for the actual CreateProcessAsUserW() call then. + */ + rc = rtProcWinDuplicateToken(hTokenProc, phToken); + } + } + else + rc = VERR_NOT_FOUND; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + RTMemTmpFree(pTokenUser); + } + else + rc = VERR_NO_MEMORY; + } + else if (fRc || dwErr == NO_ERROR) + rc = VERR_IPE_UNEXPECTED_STATUS; + else + rc = RTErrConvertFromWin32(dwErr); + CloseHandle(hTokenProc); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + CloseHandle(hProc); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + return rc; +} + + +/** + * Fallback method for rtProcWinFindTokenByProcess that uses the older NT4 + * PSAPI.DLL API. + * + * @returns Success indicator. + * @param papszNames The process candidates, in prioritized order. + * @param pSid The secure identifier of the user. + * @param phToken Where to return the token handle - duplicate, + * caller closes it on success. + * + * @remarks NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not + * part of the OS) in order to get a lookup. If we don't have this DLL + * we are not able to get a token and therefore no UI will be visible. + */ +static bool rtProcWinFindTokenByProcessAndPsApi(const char * const *papszNames, PSID pSid, PHANDLE phToken) +{ + /* + * Load PSAPI.DLL and resolve the two symbols we need. + */ + if ( !g_pfnGetModuleBaseNameW + || !g_pfnEnumProcesses) + return false; + + /* + * Get a list of PID. We retry if it looks like there are more PIDs + * to be returned than what we supplied buffer space for. + */ + bool fFound = false; + int rc = VINF_SUCCESS; + DWORD cbPidsAllocated = 4096; + DWORD cbPidsReturned = 0; /* (MSC maybe used uninitialized) */ + DWORD *paPids; + for (;;) + { + paPids = (DWORD *)RTMemTmpAlloc(cbPidsAllocated); + AssertBreakStmt(paPids, rc = VERR_NO_TMP_MEMORY); + cbPidsReturned = 0; + if (!g_pfnEnumProcesses(paPids, cbPidsAllocated, &cbPidsReturned)) + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailedBreak(("%Rrc\n", rc)); + } + if ( cbPidsReturned < cbPidsAllocated + || cbPidsAllocated >= _512K) + break; + RTMemTmpFree(paPids); + cbPidsAllocated *= 2; + } + if (RT_SUCCESS(rc)) + { + /* + * Search for the process. + * + * We ASSUME that the caller won't be specifying any names longer + * than RTPATH_MAX. + */ + PRTUTF16 pwszProcName = (PRTUTF16)RTMemTmpAllocZ(RTPATH_MAX * sizeof(pwszProcName[0])); + if (pwszProcName) + { + for (size_t i = 0; papszNames[i] && !fFound; i++) + { + const DWORD cPids = cbPidsReturned / sizeof(DWORD); + for (DWORD iPid = 0; iPid < cPids && !fFound; iPid++) + { + HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, paPids[iPid]); + if (hProc) + { + *pwszProcName = '\0'; + DWORD cbRet = g_pfnGetModuleBaseNameW(hProc, 0 /*hModule = exe */, pwszProcName, RTPATH_MAX); + if ( cbRet > 0 + && RTUtf16ICmpAscii(pwszProcName, papszNames[i]) == 0 + && RT_SUCCESS(rtProcWinGetProcessTokenHandle(paPids[iPid], pSid, UINT32_MAX, phToken))) + fFound = true; + CloseHandle(hProc); + } + } + } + RTMemTmpFree(pwszProcName); + } + else + rc = VERR_NO_TMP_MEMORY; + } + RTMemTmpFree(paPids); + + return fFound; +} + + +/** + * Finds a one of the processes in @a papszNames running with user @a pSid and possibly + * in the required windows session. Returns a duplicate handle to its token. + * + * @returns Success indicator. + * @param papszNames The process candidates, in prioritized order. + * @param pSid The secure identifier of the user. + * @param idDesiredSession The session the process candidate should + * belong to if possible, UINT32_MAX if anything + * goes. + * @param phToken Where to return the token handle - duplicate, + * caller closes it on success. + */ +static bool rtProcWinFindTokenByProcess(const char * const *papszNames, PSID pSid, uint32_t idDesiredSession, PHANDLE phToken) +{ + AssertPtr(papszNames); + AssertPtr(pSid); + AssertPtr(phToken); + + bool fFound = false; + + /* + * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable + * and reliable. Fallback to EnumProcess on NT4. + */ + bool fFallback = true; + if (g_pfnProcess32NextW && g_pfnProcess32FirstW && g_pfnCreateToolhelp32Snapshot) + { + HANDLE hSnap = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + Assert(hSnap != INVALID_HANDLE_VALUE); + if (hSnap != INVALID_HANDLE_VALUE) + { + fFallback = false; + for (size_t i = 0; papszNames[i] && !fFound; i++) + { + PROCESSENTRY32W ProcEntry; + ProcEntry.dwSize = sizeof(PROCESSENTRY32); + ProcEntry.szExeFile[0] = '\0'; + if (g_pfnProcess32FirstW(hSnap, &ProcEntry)) + { + do + { + if (RTUtf16ICmpAscii(ProcEntry.szExeFile, papszNames[i]) == 0) + { + int rc = rtProcWinGetProcessTokenHandle(ProcEntry.th32ProcessID, pSid, idDesiredSession, phToken); + if (RT_SUCCESS(rc)) + { + fFound = true; + break; + } + } + } while (g_pfnProcess32NextW(hSnap, &ProcEntry)); + } + else + AssertMsgFailed(("dwErr=%u (%x)\n", GetLastError(), GetLastError())); + } + CloseHandle(hSnap); + } + } + + /* If we couldn't take a process snapshot for some reason or another, fall + back on the NT4 compatible API. */ + if (fFallback) + fFound = rtProcWinFindTokenByProcessAndPsApi(papszNames, pSid, phToken); + return fFound; +} + + +/** + * Logs on a specified user and returns its primary token. + * + * @returns IPRT status code. + * @param pwszUser User name. A domain name can be specified (as part of a UPN, User Principal Name), + * e.g. "joedoe@example.com". + * @param pwszPassword Password. + * @param phToken Pointer to store the logon token. + */ +static int rtProcWinUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, HANDLE *phToken) +{ + AssertPtrReturn(pwszUser, VERR_INVALID_POINTER); + AssertPtrReturn(pwszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(phToken, VERR_INVALID_POINTER); + if (!g_pfnLogonUserW) + return VERR_NOT_SUPPORTED; + + /* + * Because we have to deal with http://support.microsoft.com/kb/245683 + * for NULL domain names when running on NT4 here, pass an empty string if so. + * However, passing FQDNs should work! + * + * The SE_TCB_NAME (Policy: Act as part of the operating system) right + * is required on older windows versions (NT4, W2K, possibly XP). + */ + PCRTUTF16 pwszDomainNone = g_enmWinVer < kRTWinOSType_2K ? L"" /* NT4 and older */ : NULL /* Windows 2000 and up */; + BOOL fRc = g_pfnLogonUserW(pwszUser, + /* The domain always is passed as part of the UPN (user name). */ + pwszDomainNone, + pwszPassword, + LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, + phToken); + if (fRc) + return VINF_SUCCESS; + + DWORD dwErr = GetLastError(); + int rc = dwErr == ERROR_PRIVILEGE_NOT_HELD ? VERR_PROC_TCB_PRIV_NOT_HELD : RTErrConvertFromWin32(dwErr); + if (rc == VERR_UNRESOLVED_ERROR) + LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc)); + return rc; +} + + +/** + * Returns the environment to use for the child process. + * + * This implements the RTPROC_FLAGS_ENV_CHANGE_RECORD and environment related + * parts of RTPROC_FLAGS_PROFILE. + * + * @returns IPRT status code. + * @param hToken The user token to use if RTPROC_FLAGS_PROFILE is given. + * The caller must have loaded profile for this. + * @param hEnv The environment passed in by the RTProcCreateEx caller. + * @param fFlags The process creation flags passed in by the + * RTProcCreateEx caller (RTPROC_FLAGS_XXX). + * @param phEnv Where to return the environment to use. This can either + * be a newly created environment block or @a hEnv. In the + * former case, the caller must destroy it. + */ +static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, uint32_t fFlags, PRTENV phEnv) +{ + int rc; + + /* + * Query the environment from the user profile associated with the token if + * the caller has specified it directly or indirectly. + */ + if ( (fFlags & RTPROC_FLAGS_PROFILE) + && ( hEnv == RTENV_DEFAULT + || (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) ) + { + if (g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock) + { + LPVOID pvEnvBlockProfile = NULL; + if (g_pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */)) + { + rc = RTEnvCloneUtf16Block(phEnv, (PCRTUTF16)pvEnvBlockProfile, 0 /*fFlags*/); + if ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) + && RT_SUCCESS(rc) + && hEnv != RTENV_DEFAULT) + { + rc = RTEnvApplyChanges(*phEnv, hEnv); + if (RT_FAILURE(rc)) + RTEnvDestroy(*phEnv); + } + g_pfnDestroyEnvironmentBlock(pvEnvBlockProfile); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_SYMBOL_NOT_FOUND; + } + /* + * We we've got an incoming change record, combine it with the default environment. + */ + else if (hEnv != RTENV_DEFAULT && (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) + { + rc = RTEnvClone(phEnv, RTENV_DEFAULT); + if (RT_SUCCESS(rc)) + { + rc = RTEnvApplyChanges(*phEnv, hEnv); + if (RT_FAILURE(rc)) + RTEnvDestroy(*phEnv); + } + } + /* + * Otherwise we can return the incoming environment directly. + */ + else + { + *phEnv = hEnv; + rc = VINF_SUCCESS; + } + + return rc; +} + + +/** + * Figures which privilege we're missing for success application of + * CreateProcessAsUserW. + * + * @returns IPRT error status. + */ +static int rtProcWinFigureWhichPrivilegeNotHeld2(void) +{ + HANDLE hToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + static struct + { + const char *pszName; + int rc; + } const s_aPrivileges[] = + { + { SE_TCB_NAME, VERR_PROC_TCB_PRIV_NOT_HELD }, + { SE_ASSIGNPRIMARYTOKEN_NAME, VERR_PROC_APT_PRIV_NOT_HELD }, + { SE_INCREASE_QUOTA_NAME, VERR_PROC_IQ_PRIV_NOT_HELD }, + }; + for (uint32_t i = 0; i < RT_ELEMENTS(s_aPrivileges); i++) + { + union + { + TOKEN_PRIVILEGES TokPriv; + char abAlloced[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; + } uNew, uOld; + uNew.TokPriv.PrivilegeCount = 1; + uNew.TokPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + AssertContinue(LookupPrivilegeValue(NULL, s_aPrivileges[i].pszName, &uNew.TokPriv.Privileges[0].Luid)); + uOld = uNew; + SetLastError(NO_ERROR); + DWORD cbActual = RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]); + AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uNew.TokPriv, cbActual, &uOld.TokPriv, &cbActual); + if (GetLastError() != NO_ERROR) + { + CloseHandle(hToken); + return s_aPrivileges[i].rc; + } + if (uOld.TokPriv.Privileges[0].Attributes == 0) + AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uOld.TokPriv, 0, NULL, NULL); + } + AssertFailed(); + CloseHandle(hToken); + } + else + AssertFailed(); + return VERR_PRIVILEGE_NOT_HELD; +} + +#if 0 /* debug code */ + +static char *rtProcWinSidToString(char *psz, PSID pSid) +{ + char *pszRet = psz; + + *psz++ = 'S'; + *psz++ = '-'; + *psz++ = '1'; + *psz++ = '-'; + + PISID pISid = (PISID)pSid; + + psz += RTStrFormatU32(psz, 32, RT_MAKE_U32_FROM_U8(pISid->IdentifierAuthority.Value[5], + pISid->IdentifierAuthority.Value[4], + pISid->IdentifierAuthority.Value[3], + pISid->IdentifierAuthority.Value[2]), + 10, 0, 0, 0); + for (unsigned i = 0; i < pISid->SubAuthorityCount; i++) + { + *psz++ = '-'; + psz += RTStrFormatU32(psz, 32, pISid->SubAuthority[i], 10, 0, 0, 0); + } + *psz++ = '\0'; + return pszRet; +} + +static void rtProcWinLogAcl(PACL pAcl) +{ + if (!pAcl) + RTAssertMsg2("ACL is NULL\n"); + else + { + RTAssertMsg2("AceCount=%d AclSize=%#x AclRevision=%d\n", pAcl->AceCount, pAcl->AclSize, pAcl->AclRevision); + for (uint32_t i = 0; i < pAcl->AceCount; i++) + { + PACE_HEADER pAceHdr = NULL; + if (GetAce(pAcl, i, (PVOID *)&pAceHdr)) + { + RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize); + char szTmp[256]; + if (pAceHdr->AceType == ACCESS_ALLOWED_ACE_TYPE) + RTAssertMsg2(" Mask=%#x %s\n", ((ACCESS_ALLOWED_ACE *)pAceHdr)->Mask, + rtProcWinSidToString(szTmp, &((ACCESS_ALLOWED_ACE *)pAceHdr)->SidStart)); + else + RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x\n", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize); + } + } + } +} + +static bool rtProcWinLogSecAttr(HANDLE hUserObj) +{ + /* + * Get the security descriptor for the user interface object. + */ + uint32_t cbSecDesc = _64K; + PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION; + DWORD cbNeeded; + AssertReturn(pSecDesc, false); + if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded)) + { + RTMemTmpFree(pSecDesc); + AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, false); + cbSecDesc = cbNeeded + 128; + pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + AssertReturn(pSecDesc, false); + if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded)) + { + RTMemTmpFree(pSecDesc); + AssertFailedReturn(false); + } + } + + /* + * Get the discretionary access control list (if we have one). + */ + BOOL fDaclDefaulted; + BOOL fDaclPresent; + PACL pDacl; + if (GetSecurityDescriptorDacl(pSecDesc, &fDaclPresent, &pDacl, &fDaclDefaulted)) + rtProcWinLogAcl(pDacl); + else + RTAssertMsg2("GetSecurityDescriptorDacl failed\n"); + + RTMemFree(pSecDesc); + return true; +} + +#endif /* debug */ + +/** + * Get the user SID from a token. + * + * @returns Pointer to the SID on success. Free by calling RTMemFree. + * @param hToken The token.. + * @param prc Optional return code. + */ +static PSID rtProcWinGetTokenUserSid(HANDLE hToken, int *prc) +{ + int rcIgn; + if (!prc) + prc = &rcIgn; + *prc = VERR_NO_MEMORY; + + /* + * Get the groups associated with the token. We just try a size first then + * reallocates if it's insufficient. + */ + DWORD cbUser = _1K; + PTOKEN_USER pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser); + AssertReturn(pUser, NULL); + DWORD cbNeeded = 0; + if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded)) + { + DWORD dwErr = GetLastError(); + RTMemTmpFree(pUser); + AssertLogRelMsgReturnStmt(dwErr == ERROR_INSUFFICIENT_BUFFER, + ("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr), + *prc = RTErrConvertFromWin32(dwErr), NULL); + cbUser = cbNeeded + 128; + pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser); + AssertReturn(pUser, NULL); + if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded)) + { + dwErr = GetLastError(); + *prc = RTErrConvertFromWin32(dwErr); + RTMemTmpFree(pUser); + AssertLogRelMsgFailedReturn(("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr), NULL); + } + } + + DWORD cbSid = GetLengthSid(pUser->User.Sid); + PSID pSidRet = RTMemDup(pUser->User.Sid, cbSid); + Assert(pSidRet); + RTMemTmpFree(pUser); + *prc = VINF_SUCCESS; + return pSidRet; +} + + +#if 0 /* not used */ +/** + * Get the login SID from a token. + * + * @returns Pointer to the SID on success. Free by calling RTMemFree. + * @param hToken The token.. + */ +static PSID rtProcWinGetTokenLogonSid(HANDLE hToken) +{ + /* + * Get the groups associated with the token. We just try a size first then + * reallocates if it's insufficient. + */ + DWORD cbGroups = _1K; + PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups); + AssertReturn(pGroups, NULL); + DWORD cbNeeded = 0; + if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded)) + { + RTMemTmpFree(pGroups); + AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL); + cbGroups = cbNeeded + 128; + pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups); + AssertReturn(pGroups, NULL); + if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded)) + { + RTMemTmpFree(pGroups); + AssertFailedReturn(NULL); + } + } + + /* + * Locate the logon sid. + */ + PSID pSidRet = NULL; + uint32_t i = pGroups->GroupCount; + while (i-- > 0) + if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) + { + DWORD cbSid = GetLengthSid(pGroups->Groups[i].Sid); + pSidRet = RTMemDup(pGroups->Groups[i].Sid, cbSid); + break; + } + + RTMemTmpFree(pGroups); + Assert(pSidRet); + return pSidRet; +} +#endif /* unused */ + + +/** + * Retrieves the DACL security descriptor of the give GUI object. + * + * @returns Pointer to the security descriptor. + * @param hUserObj The GUI object handle. + * @param pcbSecDesc Where to return the size of the security descriptor. + * @param ppDacl Where to return the DACL pointer. + * @param pfDaclPresent Where to return the DACL-present indicator. + * @param pDaclSizeInfo Where to return the DACL size information. + */ +static PSECURITY_DESCRIPTOR rtProcWinGetUserObjDacl(HANDLE hUserObj, uint32_t *pcbSecDesc, PACL *ppDacl, + BOOL *pfDaclPresent, ACL_SIZE_INFORMATION *pDaclSizeInfo) +{ + /* + * Get the security descriptor for the user interface object. + */ + uint32_t cbSecDesc = _1K; + PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION; + DWORD cbNeeded; + AssertReturn(pSecDesc, NULL); + if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded)) + { + RTMemTmpFree(pSecDesc); + AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL); + cbSecDesc = cbNeeded + 128; + pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + AssertReturn(pSecDesc, NULL); + if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded)) + { + RTMemTmpFree(pSecDesc); + AssertFailedReturn(NULL); + } + } + *pcbSecDesc = cbNeeded; + + /* + * Get the discretionary access control list (if we have one). + */ + BOOL fDaclDefaulted; + if (GetSecurityDescriptorDacl(pSecDesc, pfDaclPresent, ppDacl, &fDaclDefaulted)) + { + RT_ZERO(*pDaclSizeInfo); + pDaclSizeInfo->AclBytesInUse = sizeof(ACL); + if ( !*ppDacl + || GetAclInformation(*ppDacl, pDaclSizeInfo, sizeof(*pDaclSizeInfo), AclSizeInformation)) + return pSecDesc; + AssertFailed(); + } + else + AssertFailed(); + RTMemTmpFree(pSecDesc); + return NULL; +} + + +/** + * Copy ACEs from one ACL to another. + * + * @returns true on success, false on failure. + * @param pDst The destination ACL. + * @param pSrc The source ACL. + * @param cAces The number of ACEs to copy. + */ +static bool rtProcWinCopyAces(PACL pDst, PACL pSrc, uint32_t cAces) +{ + for (uint32_t i = 0; i < cAces; i++) + { + PACE_HEADER pAceHdr; + AssertReturn(GetAce(pSrc, i, (PVOID *)&pAceHdr), false); + AssertReturn(AddAce(pDst, ACL_REVISION, MAXDWORD, pAceHdr, pAceHdr->AceSize), false); + } + return true; +} + + +/** + * Adds an access-allowed access control entry to an ACL. + * + * @returns true on success, false on failure. + * @param pDstAcl The ACL. + * @param fAceFlags The ACE flags. + * @param fMask The ACE access mask. + * @param pSid The SID to go with the ACE. + * @param cbSid The size of the SID. + */ +static bool rtProcWinAddAccessAllowedAce(PACL pDstAcl, uint32_t fAceFlags, uint32_t fMask, PSID pSid, uint32_t cbSid) +{ + struct + { + ACCESS_ALLOWED_ACE Core; + DWORD abPadding[128]; /* More than enough, AFAIK. */ + } AceBuf; + RT_ZERO(AceBuf); + uint32_t const cbAllowedAce = RT_UOFFSETOF(ACCESS_ALLOWED_ACE, SidStart) + cbSid; + AssertReturn(cbAllowedAce <= sizeof(AceBuf), false); + + AceBuf.Core.Header.AceSize = cbAllowedAce; + AceBuf.Core.Header.AceType = ACCESS_ALLOWED_ACE_TYPE; + AceBuf.Core.Header.AceFlags = fAceFlags; + AceBuf.Core.Mask = fMask; + AssertReturn(CopySid(cbSid, &AceBuf.Core.SidStart, pSid), false); + + uint32_t i = pDstAcl->AceCount; + while (i-- > 0) + { + PACE_HEADER pAceHdr; + AssertContinue(GetAce(pDstAcl, i, (PVOID *)&pAceHdr)); + if ( pAceHdr->AceSize == cbAllowedAce + && memcmp(pAceHdr, &AceBuf.Core, cbAllowedAce) == 0) + return true; + + } + AssertMsgReturn(AddAce(pDstAcl, ACL_REVISION, MAXDWORD, &AceBuf.Core, cbAllowedAce), ("%u\n", GetLastError()), false); + return true; +} + + +/** All window station rights we know about */ +#define MY_WINSTATION_ALL_RIGHTS ( WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP \ + | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | WINSTA_READATTRIBUTES \ + | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER ) +/** All desktop rights we know about */ +#define MY_DESKTOP_ALL_RIGHTS ( DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL \ + | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS \ + | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER ) +/** Generic rights. */ +#define MY_GENERIC_ALL_RIGHTS ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL ) + + +/** + * Grants the given SID full access to the given window station. + * + * @returns true on success, false on failure. + * @param hWinStation The window station. + * @param pSid The SID. + */ +static bool rtProcWinAddSidToWinStation(HWINSTA hWinStation, PSID pSid) +{ + bool fRet = false; + + /* + * Get the current DACL. + */ + uint32_t cbSecDesc; + PACL pDacl; + ACL_SIZE_INFORMATION DaclSizeInfo; + BOOL fDaclPresent; + PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hWinStation, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo); + if (pSecDesc) + { + /* + * Create a new DACL. This will contain two extra ACEs. + */ + PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + if ( pNewSecDesc + && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION)) + { + uint32_t const cbSid = GetLengthSid(pSid); + uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 2; + PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl); + if ( pNewDacl + && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION) + && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0)) + { + /* + * Add the two new SID ACEs. + */ + if ( rtProcWinAddAccessAllowedAce(pNewDacl, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE, + MY_GENERIC_ALL_RIGHTS, pSid, cbSid) + && rtProcWinAddAccessAllowedAce(pNewDacl, NO_PROPAGATE_INHERIT_ACE, MY_WINSTATION_ALL_RIGHTS, pSid, cbSid)) + { + /* + * Now mate the new DECL with the security descriptor and set it. + */ + if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/)) + { + SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION; + if (SetUserObjectSecurity(hWinStation, &SecInfo, pNewSecDesc)) + fRet = true; + else + AssertFailed(); + } + else + AssertFailed(); + } + else + AssertFailed(); + } + else + AssertFailed(); + RTMemTmpFree(pNewDacl); + } + else + AssertFailed(); + RTMemTmpFree(pNewSecDesc); + RTMemTmpFree(pSecDesc); + } + return fRet; +} + + +/** + * Grants the given SID full access to the given desktop. + * + * @returns true on success, false on failure. + * @param hDesktop The desktop handle. + * @param pSid The SID. + */ +static bool rtProcWinAddSidToDesktop(HDESK hDesktop, PSID pSid) +{ + bool fRet = false; + + /* + * Get the current DACL. + */ + uint32_t cbSecDesc; + PACL pDacl; + ACL_SIZE_INFORMATION DaclSizeInfo; + BOOL fDaclPresent; + PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hDesktop, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo); + if (pSecDesc) + { + /* + * Create a new DACL. This will contain one extra ACE. + */ + PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc); + if ( pNewSecDesc + && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION)) + { + uint32_t const cbSid = GetLengthSid(pSid); + uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 1; + PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl); + if ( pNewDacl + && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION) + && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0)) + { + /* + * Add the new SID ACE. + */ + if (rtProcWinAddAccessAllowedAce(pNewDacl, 0 /*fAceFlags*/, MY_DESKTOP_ALL_RIGHTS, pSid, cbSid)) + { + /* + * Now mate the new DECL with the security descriptor and set it. + */ + if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/)) + { + SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION; + if (SetUserObjectSecurity(hDesktop, &SecInfo, pNewSecDesc)) + fRet = true; + else + AssertFailed(); + } + else + AssertFailed(); + } + else + AssertFailed(); + } + else + AssertFailed(); + RTMemTmpFree(pNewDacl); + } + else + AssertFailed(); + RTMemTmpFree(pNewSecDesc); + RTMemTmpFree(pSecDesc); + } + return fRet; +} + + +/** + * Preps the window station and desktop for the new app. + * + * EXPERIMENTAL. Thus no return code. + * + * @param hTokenToUse The access token of the new process. + * @param pStartupInfo The startup info (we'll change lpDesktop, maybe). + * @param phWinStationOld Where to return an window station handle to restore. + * Pass this to SetProcessWindowStation if not NULL. + */ +static void rtProcWinStationPrep(HANDLE hTokenToUse, STARTUPINFOW *pStartupInfo, HWINSTA *phWinStationOld) +{ + /** @todo Always mess with the interactive one? Maybe it's not there... */ + *phWinStationOld = GetProcessWindowStation(); + HWINSTA hWinStation0; + if (g_pfnOpenWindowStationW) + hWinStation0 = g_pfnOpenWindowStationW(L"winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC); + else + hWinStation0 = OpenWindowStationA("winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC); /* (for NT3.1) */ + if (hWinStation0) + { + if (SetProcessWindowStation(hWinStation0)) + { + HDESK hDesktop = OpenDesktop("default", 0 /*fFlags*/, FALSE /*fInherit*/, + READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS); + if (hDesktop) + { + /*PSID pSid = rtProcWinGetTokenLogonSid(hTokenToUse); - Better to use the user SID. Avoid overflowing the ACL. */ + PSID pSid = rtProcWinGetTokenUserSid(hTokenToUse, NULL /*prc*/); + if (pSid) + { + if ( rtProcWinAddSidToWinStation(hWinStation0, pSid) + && rtProcWinAddSidToDesktop(hDesktop, pSid)) + { + pStartupInfo->lpDesktop = L"winsta0\\default"; + } + RTMemFree(pSid); + } + CloseDesktop(hDesktop); + } + else + AssertFailed(); + } + else + AssertFailed(); + if (g_pfnCloseWindowStation) + g_pfnCloseWindowStation(hWinStation0); + } + else + AssertFailed(); +} + + +/** + * Extracts the user name + domain from a given UPN (User Principal Name, "joedoe@example.com") or + * Down-Level Logon Name format ("example.com\\joedoe") string. + * + * @return IPRT status code. + * @param pwszString Pointer to string to extract the account info from. + * @param pAccountInfo Where to store the parsed account info. + * Must be free'd with rtProcWinFreeAccountInfo(). + */ +static int rtProcWinParseAccountInfo(PRTUTF16 pwszString, PRTPROCWINACCOUNTINFO pAccountInfo) +{ + AssertPtrReturn(pwszString, VERR_INVALID_POINTER); + AssertPtrReturn(pAccountInfo, VERR_INVALID_POINTER); + + /* + * Note: UPN handling is defined in RFC 822. We only implement very rudimentary parsing for the user + * name and domain fields though. + */ + char *pszString; + int rc = RTUtf16ToUtf8(pwszString, &pszString); + if (RT_SUCCESS(rc)) + { + do + { + /* UPN or FQDN handling needed? */ + /** @todo Add more validation here as needed. Regular expressions would be nice. */ + char *pszDelim = strchr(pszString, '@'); + if (pszDelim) /* UPN name? */ + { + rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszUserName, 0, NULL); + if (RT_FAILURE(rc)) + break; + + rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszDomain, 0, NULL); + if (RT_FAILURE(rc)) + break; + } + else if (pszDelim = strchr(pszString, '\\')) /* FQDN name? */ + { + rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszDomain, 0, NULL); + if (RT_FAILURE(rc)) + break; + + rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszUserName, 0, NULL); + if (RT_FAILURE(rc)) + break; + } + else + rc = VERR_NOT_SUPPORTED; + + } while (0); + + RTStrFree(pszString); + } + +#ifdef DEBUG + LogRelFunc(("Name : %ls\n", pAccountInfo->pwszUserName)); + LogRelFunc(("Domain: %ls\n", pAccountInfo->pwszDomain)); +#endif + + if (RT_FAILURE(rc)) + LogRelFunc(("Parsing \"%ls\" failed with rc=%Rrc\n", pwszString, rc)); + return rc; +} + + +static void rtProcWinFreeAccountInfo(PRTPROCWINACCOUNTINFO pAccountInfo) +{ + if (!pAccountInfo) + return; + + if (pAccountInfo->pwszUserName) + { + RTUtf16Free(pAccountInfo->pwszUserName); + pAccountInfo->pwszUserName = NULL; + } + + if (pAccountInfo->pwszDomain) + { + RTUtf16Free(pAccountInfo->pwszDomain); + pAccountInfo->pwszDomain = NULL; + } +} + + +/** + * Tries to resolve the name of the SID. + * + * @returns IPRT status code. + * @param pSid The SID to resolve. + * @param ppwszName Where to return the name. Use RTUtf16Free to free. + */ +static int rtProcWinSidToName(PSID pSid, PRTUTF16 *ppwszName) +{ + *ppwszName = NULL; + + /* + * Use large initial buffers here to try avoid having to repeat the call. + */ + DWORD cwcAllocated = 512; + while (cwcAllocated < _32K) + { + PRTUTF16 pwszName = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16)); + AssertReturn(pwszName, VERR_NO_UTF16_MEMORY); + PRTUTF16 pwszDomain = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16)); + AssertReturnStmt(pwszDomain, RTUtf16Free(pwszName), VERR_NO_UTF16_MEMORY); + + DWORD cwcName = cwcAllocated; + DWORD cwcDomain = cwcAllocated; + SID_NAME_USE SidNameUse = SidTypeUser; + if (LookupAccountSidW(NULL /*lpSystemName*/, pSid, pwszName, &cwcName, pwszDomain, &cwcDomain, &SidNameUse)) + { + *ppwszName = pwszName; + RTUtf16Free(pwszDomain); /* may need this later. */ + return VINF_SUCCESS; + } + + DWORD const dwErr = GetLastError(); + RTUtf16Free(pwszName); + RTUtf16Free(pwszDomain); + if (dwErr != ERROR_INSUFFICIENT_BUFFER) + return RTErrConvertFromWin32(dwErr); + cwcAllocated = RT_MAX(cwcName, cwcDomain) + 1; + } + + return RTErrConvertFromWin32(ERROR_INSUFFICIENT_BUFFER); +} + + +/** + * Tries to resolve the user name for the token. + * + * @returns IPRT status code. + * @param hToken The token. + * @param ppwszUser Where to return the username. Use RTUtf16Free to free. + */ +static int rtProcWinTokenToUsername(HANDLE hToken, PRTUTF16 *ppwszUser) +{ + int rc = VINF_SUCCESS; + PSID pSid = rtProcWinGetTokenUserSid(hToken, &rc); + if (pSid) + { + rc = rtProcWinSidToName(pSid, ppwszUser); + RTMemFree(pSid); + } + else + *ppwszUser = NULL; + return rc; +} + + +/** + * Method \#2. + * + * @note pwszUser can be NULL when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is set. + */ +static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine, + RTENV hEnv, DWORD dwCreationFlags, + STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, + uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession, + HANDLE hUserToken) +{ + /* + * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE), + * we have to do the following: + * - Check the credentials supplied and get the user SID. + * - If valid get the correct Explorer/VBoxTray instance corresponding to that + * user. This of course is only possible if that user is logged in (over + * physical console or terminal services). + * - If we found the user's Explorer/VBoxTray app, use and modify the token to + * use it in order to allow the newly started process to access the user's + * desktop. If there's no Explorer/VBoxTray app we cannot display the started + * process (but run it without UI). + * + * The following restrictions apply: + * - A process only can show its UI when the user the process should run + * under is logged in (has a desktop). + * - We do not want to display a process of user A run on the desktop + * of user B on multi session systems. + * + * The following rights are needed in order to use LogonUserW and + * CreateProcessAsUserW, so the local policy has to be modified to: + * - SE_TCB_NAME = Act as part of the operating system + * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a (process) token object + * - SE_INCREASE_QUOTA_NAME = Increase quotas + * + * We may fail here with ERROR_PRIVILEGE_NOT_HELD. + */ + DWORD dwErr = NO_ERROR; + HANDLE hTokenLogon = INVALID_HANDLE_VALUE; + int rc = VINF_SUCCESS; + if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED) + hTokenLogon = hUserToken; + else if (fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN) + rc = rtProcWinGetThreadTokenHandle(GetCurrentThread(), &hTokenLogon); + else if (pwszUser == NULL) + rc = rtProcWinGetProcessTokenHandle(GetCurrentProcess(), &hTokenLogon); + else + rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon); + if (RT_SUCCESS(rc)) + { + BOOL fRc; + bool fFound = false; + HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE; + + /* + * If the SERVICE flag is specified, we do something rather ugly to + * make things work at all. We search for a known desktop process + * belonging to the user, grab its token and use it for launching + * the new process. That way the process will have desktop access. + */ + if (fFlags & RTPROC_FLAGS_SERVICE) + { + /* + * For the token search we need a SID. + */ + PSID pSid = rtProcWinGetTokenUserSid(hTokenLogon, &rc); + + /* + * If we got a valid SID, search the running processes. + */ + /* + * If we got a valid SID, search the running processes. + */ + if (pSid) + { + if (IsValidSid(pSid)) + { + /* Array of process names we want to look for. */ + static const char * const s_papszProcNames[] = + { +#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */ + { "VBoxTray.exe" }, +# ifndef IN_GUEST + { "VirtualBox.exe" }, +# endif +#endif + { "explorer.exe" }, + NULL + }; + fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, idDesiredSession, &hTokenUserDesktop); + dwErr = 0; + } + else + { + dwErr = GetLastError(); + LogRelFunc(("SID is invalid: %ld\n", dwErr)); + rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3; + } + + RTMemFree(pSid); + } + } + /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */ + +#if 0 + /* + * If we make LogonUserW to return an impersonation token, enable this + * to convert it into a primary token. + */ + if (!fFound && detect-impersonation-token) + { + HANDLE hNewToken; + if (DuplicateTokenEx(hTokenLogon, MAXIMUM_ALLOWED, NULL /*SecurityAttribs*/, + SecurityIdentification, TokenPrimary, &hNewToken)) + { + CloseHandle(hTokenLogon); + hTokenLogon = hNewToken; + } + else + AssertMsgFailed(("%d\n", GetLastError())); + } +#endif + + if (RT_SUCCESS(rc)) + { + /* + * If we didn't find a matching VBoxTray, just use the token we got + * above from LogonUserW(). This enables us to at least run processes + * with desktop interaction without UI. + */ + HANDLE hTokenToUse = fFound ? hTokenUserDesktop : hTokenLogon; + if ( !(fFlags & RTPROC_FLAGS_PROFILE) + || (g_pfnUnloadUserProfile && g_pfnLoadUserProfileW) ) + { + /* + * Load the profile, if requested. (Must be done prior to creating the enviornment.) + * + * Note! We don't have sufficient rights when impersonating a user, but we can + * ASSUME the user is logged on and has its profile loaded into HKEY_USERS already. + */ + PROFILEINFOW ProfileInfo; + PRTUTF16 pwszUserFree = NULL; + RT_ZERO(ProfileInfo); + /** @todo r=bird: We probably don't need to load anything if pwszUser is NULL... */ + if ((fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN)) == RTPROC_FLAGS_PROFILE) + { + if (!pwszUser) + { + Assert(fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN); + rc = rtProcWinTokenToUsername(hTokenToUse, &pwszUserFree); + pwszUser = pwszUserFree; + } + if (RT_SUCCESS(rc)) + { + ProfileInfo.dwSize = sizeof(ProfileInfo); + ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */ + ProfileInfo.lpUserName = pwszUser; + if (!g_pfnLoadUserProfileW(hTokenToUse, &ProfileInfo)) + rc = RTErrConvertFromWin32(GetLastError()); + } + } + if (RT_SUCCESS(rc)) + { + /* + * Create the environment. + */ + RTENV hEnvFinal; + rc = rtProcWinCreateEnvFromToken(hTokenToUse, hEnv, fFlags, &hEnvFinal); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszzBlock; + rc = RTEnvQueryUtf16Block(hEnvFinal, &pwszzBlock); + if (RT_SUCCESS(rc)) + { + rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec); + if (RT_SUCCESS(rc)) + { + HWINSTA hOldWinStation = NULL; + if ( !fFound + && g_enmWinVer <= kRTWinOSType_NT4) /** @todo test newer versions... */ + rtProcWinStationPrep(hTokenToUse, pStartupInfo, &hOldWinStation); + + /* + * Useful KB articles: + * http://support.microsoft.com/kb/165194/ + * http://support.microsoft.com/kb/184802/ + * http://support.microsoft.com/kb/327618/ + */ + if (g_pfnCreateProcessAsUserW) + { + fRc = g_pfnCreateProcessAsUserW(hTokenToUse, + *ppwszExec, + pwszCmdLine, + NULL, /* pProcessAttributes */ + NULL, /* pThreadAttributes */ + TRUE, /* fInheritHandles */ + dwCreationFlags, + /** @todo Warn about exceeding 8192 bytes + * on XP and up. */ + pwszzBlock, /* lpEnvironment */ + NULL, /* pCurrentDirectory */ + pStartupInfo, + pProcInfo); + if (fRc) + rc = VINF_SUCCESS; + else + { + dwErr = GetLastError(); + if (dwErr == ERROR_PRIVILEGE_NOT_HELD) + rc = rtProcWinFigureWhichPrivilegeNotHeld2(); + else + rc = RTErrConvertFromWin32(dwErr); + } + } + else + rc = VERR_NOT_SUPPORTED; + + if (hOldWinStation) + SetProcessWindowStation(hOldWinStation); + } + RTEnvFreeUtf16Block(pwszzBlock); + } + + if (hEnvFinal != hEnv) + RTEnvDestroy(hEnvFinal); + } + + if ((fFlags & RTPROC_FLAGS_PROFILE) && ProfileInfo.hProfile) + { + fRc = g_pfnUnloadUserProfile(hTokenToUse, ProfileInfo.hProfile); +#ifdef RT_STRICT + if (!fRc) + { + DWORD dwErr2 = GetLastError(); + AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)", + dwErr2, dwErr2, dwErr)); + } +#endif + } + if (pwszUserFree) + RTUtf16Free(pwszUserFree); + } + } + else + rc = VERR_SYMBOL_NOT_FOUND; + } /* Account lookup succeeded? */ + + if (hTokenUserDesktop != INVALID_HANDLE_VALUE) + CloseHandle(hTokenUserDesktop); + if ( !(fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED) + && hTokenLogon != INVALID_HANDLE_VALUE) + CloseHandle(hTokenLogon); + + if (rc == VERR_UNRESOLVED_ERROR) + LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc)); + } + + return rc; +} + + +/** + * Plants a standard handle into a child process on older windows versions. + * + * This is only needed when using CreateProcessWithLogonW on older windows + * versions. It would appear that newer versions of windows does this for us. + * + * @param hSrcHandle The source handle. + * @param hDstProcess The child process handle. + * @param offProcParamMember The offset to RTL_USER_PROCESS_PARAMETERS. + * @param ppvDstProcParamCache Where where cached the address of + * RTL_USER_PROCESS_PARAMETERS in the child. + */ +static void rtProcWinDupStdHandleIntoChild(HANDLE hSrcHandle, HANDLE hDstProcess, uint32_t offProcParamMember, + PVOID *ppvDstProcParamCache) +{ + if (hSrcHandle != NULL && hSrcHandle != INVALID_HANDLE_VALUE) + { + HANDLE hDstHandle; + if (DuplicateHandle(GetCurrentProcess(), hSrcHandle, hDstProcess, &hDstHandle, + 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS)) + { + if (hSrcHandle == hDstHandle) + return; + + if (!*ppvDstProcParamCache) + { + PROCESS_BASIC_INFORMATION BasicInfo; + ULONG cbIgn; + NTSTATUS rcNt = NtQueryInformationProcess(hDstProcess, ProcessBasicInformation, + &BasicInfo, sizeof(BasicInfo), &cbIgn); + if (NT_SUCCESS(rcNt)) + { + SIZE_T cbCopied = 0; + if (!ReadProcessMemory(hDstProcess, + (char *)BasicInfo.PebBaseAddress + RT_UOFFSETOF(PEB_COMMON, ProcessParameters), + ppvDstProcParamCache, sizeof(*ppvDstProcParamCache), &cbCopied)) + { + AssertMsgFailed(("PebBaseAddress=%p %d\n", BasicInfo.PebBaseAddress, GetLastError())); + *ppvDstProcParamCache = NULL; + } + } + else + AssertMsgFailed(("rcNt=%#x\n", rcNt)); + } + if (*ppvDstProcParamCache) + { + if (WriteProcessMemory(hDstProcess, (char *)*ppvDstProcParamCache + offProcParamMember, + &hDstHandle, sizeof(hDstHandle), NULL)) + return; + } + + /* + * Close the handle. + */ + HANDLE hSrcHandle2; + if (DuplicateHandle(hDstProcess, hDstHandle, GetCurrentProcess(), &hSrcHandle2, + 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) + CloseHandle(hSrcHandle2); + else + AssertMsgFailed(("hDstHandle=%p %u\n", hDstHandle, GetLastError())); + } + else + AssertMsg(GetLastError() == ERROR_INVALID_PARAMETER, ("%u\n", GetLastError())); + } +} + + +/** + * Method \#1. + * + * This method requires Windows 2000 or later. It may fail if the process is + * running under the SYSTEM account (like a service, ERROR_ACCESS_DENIED) on + * newer platforms (however, this works on W2K!). + */ +static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine, + RTENV hEnv, DWORD dwCreationFlags, + STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, + uint32_t fFlags, const char *pszExec) +{ + /* The CreateProcessWithLogonW API was introduced with W2K and later. It uses a service + for launching the process. */ + if (!g_pfnCreateProcessWithLogonW) + return VERR_SYMBOL_NOT_FOUND; + + /* + * Create the environment block and find the executable first. + * + * We try to skip this when RTPROC_FLAGS_PROFILE is set so we can sidestep + * potential missing TCB privilege issues when calling UserLogonW. At least + * NT4 and W2K requires the trusted code base (TCB) privilege for logon use. + * Passing pwszzBlock=NULL and LOGON_WITH_PROFILE means the child process + * gets the environment specified by the user profile. + */ + int rc; + PRTUTF16 pwszzBlock = NULL; + + /* Eliminating the path search flags simplifies things a little. */ + if ( (fFlags & RTPROC_FLAGS_SEARCH_PATH) + && (RTPathHasPath(pszExec) || RTPathExists(pszExec))) + fFlags &= ~RTPROC_FLAGS_SEARCH_PATH; + + /* + * No profile is simple, as is a user specified environment (no change record). + */ + if ( !(fFlags & RTPROC_FLAGS_PROFILE) + || ( !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) + && hEnv != RTENV_DEFAULT)) + rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, ppwszExec); + /* + * Default profile environment without changes or path searching we leave + * to the service that implements the API. + */ + else if ( hEnv == RTENV_DEFAULT + && !(fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_SEARCH_PATH))) + { + pwszzBlock = NULL; + rc = VINF_SUCCESS; + } + /* + * Otherwise, we need to get the user profile environment. + */ + else + { + RTENV hEnvToUse = NIL_RTENV; + HANDLE hTokenLogon = INVALID_HANDLE_VALUE; + rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon); + if (RT_SUCCESS(rc)) + { + /* CreateEnvFromToken docs says we should load the profile, though + we haven't observed any difference when not doing it. Maybe it's + only an issue with roaming profiles or something similar... */ + PROFILEINFOW ProfileInfo; + RT_ZERO(ProfileInfo); + ProfileInfo.dwSize = sizeof(ProfileInfo); + ProfileInfo.lpUserName = pwszUser; + ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */ + + if (g_pfnLoadUserProfileW(hTokenLogon, &ProfileInfo)) + { + /* + * Do what we need to do. Don't keep any temp environment object. + */ + rc = rtProcWinCreateEnvFromToken(hTokenLogon, hEnv, fFlags, &hEnvToUse); + if (RT_SUCCESS(rc)) + { + rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec); + if (RT_SUCCESS(rc)) + rc = RTEnvQueryUtf16Block(hEnvToUse, &pwszzBlock); + if (hEnvToUse != hEnv) + RTEnvDestroy(hEnvToUse); + } + + if (!g_pfnUnloadUserProfile(hTokenLogon, ProfileInfo.hProfile)) + AssertFailed(); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + if (hTokenLogon != INVALID_HANDLE_VALUE) + CloseHandle(hTokenLogon); + } + } + if (RT_SUCCESS(rc)) + { + /* + * Create the process. + */ + Assert(!(dwCreationFlags & CREATE_SUSPENDED)); + bool const fCreatedSuspended = g_enmWinVer < kRTWinOSType_XP; + BOOL fRc = g_pfnCreateProcessWithLogonW(pwszUser, + NULL, /* lpDomain*/ + pwszPassword, + fFlags & RTPROC_FLAGS_PROFILE ? 1 /*LOGON_WITH_PROFILE*/ : 0, + *ppwszExec, + pwszCmdLine, + dwCreationFlags | (fCreatedSuspended ? CREATE_SUSPENDED : 0), + pwszzBlock, + NULL, /* pCurrentDirectory */ + pStartupInfo, + pProcInfo); + if (fRc) + { + if (!fCreatedSuspended) + rc = VINF_SUCCESS; + else + { + /* + * Duplicate standard handles into the child process, we ignore failures here as it's + * legal to have bad standard handle values and we cannot dup console I/O handles.* + */ + PVOID pvDstProcParamCache = NULL; + rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdInput, pProcInfo->hProcess, + RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardInput), &pvDstProcParamCache); + rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdOutput, pProcInfo->hProcess, + RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardOutput), &pvDstProcParamCache); + rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdError, pProcInfo->hProcess, + RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardError), &pvDstProcParamCache); + + if (ResumeThread(pProcInfo->hThread) != ~(DWORD)0) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + if (RT_FAILURE(rc)) + { + TerminateProcess(pProcInfo->hProcess, 127); + CloseHandle(pProcInfo->hThread); + CloseHandle(pProcInfo->hProcess); + } + } + } + else + { + DWORD dwErr = GetLastError(); + rc = RTErrConvertFromWin32(dwErr); + if (rc == VERR_UNRESOLVED_ERROR) + LogRelFunc(("CreateProcessWithLogonW failed: dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc)); + } + if (pwszzBlock) + RTEnvFreeUtf16Block(pwszzBlock); + } + return rc; +} + + +static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine, + RTENV hEnv, DWORD dwCreationFlags, + STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, + uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession, + HANDLE hUserToken) +{ + /* + * If we run as a service CreateProcessWithLogon will fail, so don't even + * try it (because of Local System context). If we got an impersonated token + * we should use, we also have to have to skip over this approach. + * Note! This method is very slow on W2K. + */ + if (!(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED))) + { + AssertPtr(pwszUser); + int rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, ppwszExec, pwszCmdLine, + hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec); + if (RT_SUCCESS(rc)) + return rc; + } + return rtProcWinCreateAsUser2(pwszUser, pwszPassword, ppwszExec, pwszCmdLine, hEnv, dwCreationFlags, + pStartupInfo, pProcInfo, fFlags, pszExec, idDesiredSession, hUserToken); +} + + +/** + * RTPathTraverseList callback used by rtProcWinFindExe to locate the + * executable. + */ +static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2) +{ + const char *pszExec = (const char *)pvUser1; + char *pszRealExec = (char *)pvUser2; + int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX, RTPATH_STR_F_STYLE_HOST); + if (RT_FAILURE(rc)) + return rc; + if (RTFileExists(pszRealExec)) + return VINF_SUCCESS; + return VERR_TRY_AGAIN; +} + + +/** + * Locate the executable file if necessary. + * + * @returns IPRT status code. + * @param pszExec The UTF-8 executable string passed in by the user. + * @param fFlags The process creation flags pass in by the user. + * @param hEnv The environment to get the path variabel from. + * @param ppwszExec Pointer to the variable pointing to the UTF-16 + * converted string. If we find something, the current + * pointer will be free (RTUtf16Free) and + * replaced by a new one. + */ +static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec) +{ + /* + * Return immediately if we're not asked to search, or if the file has a + * path already or if it actually exists in the current directory. + */ + if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH) + || RTPathHavePath(pszExec) + || RTPathExists(pszExec) ) + return VINF_SUCCESS; + + /* + * Search the Path or PATH variable for the file. + */ + char *pszPath; + if (RTEnvExistEx(hEnv, "PATH")) + pszPath = RTEnvDupEx(hEnv, "PATH"); + else if (RTEnvExistEx(hEnv, "Path")) + pszPath = RTEnvDupEx(hEnv, "Path"); + else + return VERR_FILE_NOT_FOUND; + + char szRealExec[RTPATH_MAX]; + int rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]); + RTStrFree(pszPath); + if (RT_SUCCESS(rc)) + { + /* + * Replace the executable string. + */ + RTPathWinFree(*ppwszExec); + *ppwszExec = NULL; + rc = RTPathWinFromUtf8(ppwszExec, szRealExec, 0 /*fFlags*/); + } + else if (rc == VERR_END_OF_STRING) + rc = VERR_FILE_NOT_FOUND; + return rc; +} + + +/** + * Creates the UTF-16 environment block and, if necessary, find the executable. + * + * @returns IPRT status code. + * @param fFlags The process creation flags pass in by the user. + * @param hEnv The environment handle passed by the user. + * @param pszExec See rtProcWinFindExe. + * @param ppwszzBlock Where RTEnvQueryUtf16Block returns the block. + * @param ppwszExec See rtProcWinFindExe. + */ +static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, + PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec) +{ + int rc; + + /* + * In most cases, we just need to convert the incoming enviornment to a + * UTF-16 environment block. + */ + RTENV hEnvToUse = NIL_RTENV; /* (MSC maybe used uninitialized) */ + if ( !(fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD)) + || (hEnv == RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_PROFILE)) + || (hEnv != RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) ) + { + hEnvToUse = hEnv; + rc = VINF_SUCCESS; + } + else if (fFlags & RTPROC_FLAGS_PROFILE) + { + /* + * We need to get the profile environment for the current user. + */ + Assert((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT); + AssertReturn(g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock, VERR_SYMBOL_NOT_FOUND); + AssertReturn(g_pfnLoadUserProfileW && g_pfnUnloadUserProfile, VERR_SYMBOL_NOT_FOUND); + HANDLE hToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken)) + { + rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse); + CloseHandle(hToken); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + /* + * Apply hEnv as a change record on top of the default environment. + */ + Assert(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD); + rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); + if (RT_SUCCESS(rc)) + { + rc = RTEnvApplyChanges(hEnvToUse, hEnv); + if (RT_FAILURE(rc)) + RTEnvDestroy(hEnvToUse); + } + } + if (RT_SUCCESS(rc)) + { + /* + * Query the UTF-16 environment block and locate the executable (if needed). + */ + rc = RTEnvQueryUtf16Block(hEnvToUse, ppwszzBlock); + if (RT_SUCCESS(rc)) + rc = rtProcWinFindExe(fFlags, hEnvToUse, pszExec, ppwszExec); + + if (hEnvToUse != hEnv) + RTEnvDestroy(hEnvToUse); + } + + return rc; +} + + +RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, + PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, + const char *pszPassword, void *pvExtraData, PRTPROCESS phProcess) +{ + /* + * Input validation + */ + AssertPtrReturn(pszExec, VERR_INVALID_POINTER); + AssertReturn(*pszExec, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER); + AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER); + AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER); + AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER); + AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER); + + /* Extra data: */ + uint32_t idDesiredSession = UINT32_MAX; + if ( (fFlags & (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE)) + == (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE)) + { + AssertPtrReturn(pvExtraData, VERR_INVALID_POINTER); + idDesiredSession = *(uint32_t *)pvExtraData; + } + else + AssertReturn(!(fFlags & RTPROC_FLAGS_DESIRED_SESSION_ID), VERR_INVALID_FLAGS); + + HANDLE hUserToken = NULL; + if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED) + hUserToken = *(HANDLE *)pvExtraData; + + /* + * Initialize the globals. + */ + int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL); + AssertRCReturn(rc, rc); + if ( pszAsUser + || (fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN + | RTPROC_FLAGS_TOKEN_SUPPLIED))) + { + rc = RTOnce(&g_rtProcWinResolveOnce, rtProcWinResolveOnce, NULL); + AssertRCReturn(rc, rc); + } + + /* + * Get the file descriptors for the handles we've been passed. + * + * It seems there is no point in trying to convince a child process's CRT + * that any of the standard file handles is non-TEXT. So, we don't... + */ + STARTUPINFOW StartupInfo; + RT_ZERO(StartupInfo); + StartupInfo.cb = sizeof(StartupInfo); + StartupInfo.dwFlags = STARTF_USESTDHANDLES; +#if 1 /* The CRT should keep the standard handles up to date. */ + StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); +#else + StartupInfo.hStdInput = _get_osfhandle(0); + StartupInfo.hStdOutput = _get_osfhandle(1); + StartupInfo.hStdError = _get_osfhandle(2); +#endif + /* If we want to have a hidden process (e.g. not visible to + * to the user) use the STARTUPINFO flags. */ + if (fFlags & RTPROC_FLAGS_HIDDEN) + { + StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + StartupInfo.wShowWindow = SW_HIDE; + } + + PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr }; + HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError }; + DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff }; + HANDLE ahStdDups[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; + for (int i = 0; i < 3; i++) + { + if (paHandles[i]) + { + AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER); + switch (paHandles[i]->enmType) + { + case RTHANDLETYPE_FILE: + { + HANDLE hNativeFile = paHandles[i]->u.hFile != NIL_RTFILE + ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile) + : INVALID_HANDLE_VALUE; + if ( hNativeFile == *aphStds[i] + && g_enmWinVer == kRTWinOSType_NT310) + continue; + *aphStds[i] = hNativeFile; + break; + } + + case RTHANDLETYPE_PIPE: + *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE + ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe) + : INVALID_HANDLE_VALUE; + if ( g_enmWinVer == kRTWinOSType_NT310 + && *aphStds[i] == INVALID_HANDLE_VALUE) + { + AssertMsgReturn(RTPipeGetCreationInheritability(paHandles[i]->u.hPipe), ("%Rrc %p\n", rc, *aphStds[i]), + VERR_INVALID_STATE); + continue; + } + break; + + case RTHANDLETYPE_SOCKET: + *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET + ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket) + : INVALID_HANDLE_VALUE; + break; + + default: + AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER); + } + + /* Get the inheritability of the handle. */ + if (*aphStds[i] != INVALID_HANDLE_VALUE) + { + if (!g_pfnGetHandleInformation) + afInhStds[i] = 0; /* No handle info on NT 3.1, so ASSUME it is not inheritable. */ + else if (!g_pfnGetHandleInformation(*aphStds[i], &afInhStds[i])) + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailedReturn(("%Rrc aphStds[%d] => %p paHandles[%d]={%d,%p}\n", + rc, i, *aphStds[i], i, paHandles[i]->enmType, paHandles[i]->u.uInt), + rc); + } + } + } + } + + /* + * Set the inheritability any handles we're handing the child. + * + * Note! On NT 3.1 there is no SetHandleInformation, so we have to duplicate + * the handles to make sure they are inherited by the child. + */ + rc = VINF_SUCCESS; + for (int i = 0; i < 3; i++) + if ( (afInhStds[i] != 0xffffffff) + && !(afInhStds[i] & HANDLE_FLAG_INHERIT)) + { + if (!g_pfnSetHandleInformation) + { + if (DuplicateHandle(GetCurrentProcess(), *aphStds[i], GetCurrentProcess(), &ahStdDups[i], + i == 0 ? GENERIC_READ : GENERIC_WRITE, TRUE /*fInheritHandle*/, DUPLICATE_SAME_ACCESS)) + *aphStds[i] = ahStdDups[i]; + else + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i])); + } + } + else if (!g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) + { + rc = RTErrConvertFromWin32(GetLastError()); + if (rc == VERR_INVALID_FUNCTION && g_enmWinVer == kRTWinOSType_NT310) + rc = VINF_SUCCESS; + else + AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i])); + } + } + + /* + * Create the command line and convert the executable name. + */ + PRTUTF16 pwszCmdLine = NULL; /* Shut up, MSC! */ + if (RT_SUCCESS(rc)) + rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, + !(fFlags & RTPROC_FLAGS_UNQUOTED_ARGS) + ? RTGETOPTARGV_CNV_QUOTE_MS_CRT : RTGETOPTARGV_CNV_UNQUOTED); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszExec; + rc = RTPathWinFromUtf8(&pwszExec, pszExec, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * Get going... + */ + PROCESS_INFORMATION ProcInfo; + RT_ZERO(ProcInfo); + DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; + if (fFlags & RTPROC_FLAGS_DETACHED) + dwCreationFlags |= DETACHED_PROCESS; + if (fFlags & RTPROC_FLAGS_NO_WINDOW) + dwCreationFlags |= CREATE_NO_WINDOW; + + /* + * Only use the normal CreateProcess stuff if we have no user name + * and we are not running from a (Windows) service. Otherwise use + * the more advanced version in rtProcWinCreateAsUser(). + */ + if ( pszAsUser == NULL + && !(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED))) + { + /* Create the environment block first. */ + PRTUTF16 pwszzBlock; + rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, &pwszExec); + if (RT_SUCCESS(rc)) + { + if (CreateProcessW(pwszExec, + pwszCmdLine, + NULL, /* pProcessAttributes */ + NULL, /* pThreadAttributes */ + TRUE, /* fInheritHandles */ + dwCreationFlags, + pwszzBlock, + NULL, /* pCurrentDirectory */ + &StartupInfo, + &ProcInfo)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + RTEnvFreeUtf16Block(pwszzBlock); + } + } + else + { + /* + * Convert the additional parameters and use a helper + * function to do the actual work. + */ + PRTUTF16 pwszUser = NULL; + if (pszAsUser) + rc = RTStrToUtf16(pszAsUser, &pwszUser); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszPassword; + rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword); + if (RT_SUCCESS(rc)) + { + rc = rtProcWinCreateAsUser(pwszUser, pwszPassword, &pwszExec, pwszCmdLine, hEnv, dwCreationFlags, + &StartupInfo, &ProcInfo, fFlags, pszExec, idDesiredSession, + hUserToken); + + if (pwszPassword && *pwszPassword) + RTMemWipeThoroughly(pwszPassword, RTUtf16Len(pwszPassword), 5); + RTUtf16Free(pwszPassword); + } + RTUtf16Free(pwszUser); + } + } + if (RT_SUCCESS(rc)) + { + CloseHandle(ProcInfo.hThread); + if (phProcess) + { + /* + * Add the process to the child process list so RTProcWait can reuse and close + * the process handle, unless, of course, the caller has no intention waiting. + */ + if (!(fFlags & RTPROC_FLAGS_NO_WAIT)) + rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess); + else + CloseHandle(ProcInfo.hProcess); + *phProcess = ProcInfo.dwProcessId; + } + else + CloseHandle(ProcInfo.hProcess); + rc = VINF_SUCCESS; + } + RTPathWinFree(pwszExec); + } + RTUtf16Free(pwszCmdLine); + } + + if (g_pfnSetHandleInformation) + { + /* Undo any handle inherit changes. */ + for (int i = 0; i < 3; i++) + if ( (afInhStds[i] != 0xffffffff) + && !(afInhStds[i] & HANDLE_FLAG_INHERIT)) + { + if ( !g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0) + && ( GetLastError() != ERROR_INVALID_FUNCTION + || g_enmWinVer != kRTWinOSType_NT310) ) + AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i])); + } + } + else + { + /* Close handles duplicated for correct inheritance. */ + for (int i = 0; i < 3; i++) + if (ahStdDups[i] != INVALID_HANDLE_VALUE) + CloseHandle(ahStdDups[i]); + } + + return rc; +} + + + +RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus) +{ + AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER); + int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL); + AssertRCReturn(rc, rc); + + /* + * Try find the process among the ones we've spawned, otherwise, attempt + * opening the specified process. + */ + HANDLE hOpenedProc = NULL; + HANDLE hProcess = rtProcWinFindPid(Process); + if (hProcess == NULL) + { + hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process); + if (hProcess == NULL) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_INVALID_PARAMETER) + return VERR_PROCESS_NOT_FOUND; + return RTErrConvertFromWin32(dwErr); + } + } + + /* + * Wait for it to terminate. + */ + DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0; + DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE); + while (WaitRc == WAIT_IO_COMPLETION) + WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE); + switch (WaitRc) + { + /* + * It has terminated. + */ + case WAIT_OBJECT_0: + { + DWORD dwExitCode; + if (GetExitCodeProcess(hProcess, &dwExitCode)) + { + /** @todo the exit code can be special statuses. */ + if (pProcStatus) + { + pProcStatus->enmReason = RTPROCEXITREASON_NORMAL; + pProcStatus->iStatus = (int)dwExitCode; + } + if (hOpenedProc == NULL) + rtProcWinRemovePid(Process); + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + break; + } + + /* + * It hasn't terminated just yet. + */ + case WAIT_TIMEOUT: + rc = VERR_PROCESS_RUNNING; + break; + + /* + * Something went wrong... + */ + case WAIT_FAILED: + rc = RTErrConvertFromWin32(GetLastError()); + break; + + case WAIT_ABANDONED: + AssertFailed(); + rc = VERR_GENERAL_FAILURE; + break; + + default: + AssertMsgFailed(("WaitRc=%RU32\n", WaitRc)); + rc = VERR_GENERAL_FAILURE; + break; + } + + if (hOpenedProc != NULL) + CloseHandle(hOpenedProc); + return rc; +} + + +RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus) +{ + /** @todo this isn't quite right. */ + return RTProcWait(Process, fFlags, pProcStatus); +} + + +RTR3DECL(int) RTProcTerminate(RTPROCESS Process) +{ + if (Process == NIL_RTPROCESS) + return VINF_SUCCESS; + + int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL); + AssertRCReturn(rc, rc); + + /* + * Try find the process among the ones we've spawned, otherwise, attempt + * opening the specified process. + */ + HANDLE hProcess = rtProcWinFindPid(Process); + if (hProcess != NULL) + { + if (!TerminateProcess(hProcess, 127)) + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process); + if (hProcess != NULL) + { + BOOL fRc = TerminateProcess(hProcess, 127); + DWORD dwErr = GetLastError(); + CloseHandle(hProcess); + if (!fRc) + rc = RTErrConvertFromWin32(dwErr); + } + } + return rc; +} + + +RTR3DECL(uint64_t) RTProcGetAffinityMask(void) +{ + DWORD_PTR dwProcessAffinityMask = 0xffffffff; + DWORD_PTR dwSystemAffinityMask; + + BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask); + Assert(fRc); NOREF(fRc); + + return dwProcessAffinityMask; +} + + +RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser, size_t *pcbUser) +{ + AssertReturn( (pszUser && cbUser > 0) + || (!pszUser && !cbUser), VERR_INVALID_PARAMETER); + AssertReturn(pcbUser || pszUser, VERR_INVALID_PARAMETER); + + int rc; + if ( hProcess == NIL_RTPROCESS + || hProcess == RTProcSelf()) + { + RTUTF16 wszUsername[UNLEN + 1]; + DWORD cwcUsername = RT_ELEMENTS(wszUsername); + if (GetUserNameW(&wszUsername[0], &cwcUsername)) + { + if (pszUser) + { + rc = RTUtf16ToUtf8Ex(wszUsername, cwcUsername, &pszUser, cbUser, pcbUser); + if (pcbUser) + *pcbUser += 1; + } + else + { + *pcbUser = RTUtf16CalcUtf8Len(wszUsername) + 1; + rc = VERR_BUFFER_OVERFLOW; + } + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_NOT_SUPPORTED; + return rc; +} + + +RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser) +{ + AssertPtrReturn(ppszUser, VERR_INVALID_POINTER); + int rc; + if ( hProcess == NIL_RTPROCESS + || hProcess == RTProcSelf()) + { + RTUTF16 wszUsername[UNLEN + 1]; + DWORD cwcUsername = RT_ELEMENTS(wszUsername); + if (GetUserNameW(&wszUsername[0], &cwcUsername)) + rc = RTUtf16ToUtf8(wszUsername, ppszUser); + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_NOT_SUPPORTED; + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp new file mode 100644 index 00000000..f0f478f6 --- /dev/null +++ b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp @@ -0,0 +1,72 @@ +/* $Id: rtProcInitExePath-win.cpp $ */ +/** @file + * IPRT - rtProcInitName, Windows. + */ + +/* + * 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_PROCESS +#include <iprt/win/windows.h> + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/path.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include "internal/process.h" + + +DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath) +{ + /* + * Query the image name from the dynamic linker, convert and return it. + */ + WCHAR wsz[RTPATH_MAX]; + HMODULE hExe = GetModuleHandle(NULL); + if (GetModuleFileNameW(hExe, wsz, RTPATH_MAX)) + { + int rc = RTUtf16ToUtf8Ex(wsz, RTSTR_MAX, &pszPath, cchPath, NULL); + AssertRCReturn(rc, rc); + return VINF_SUCCESS; + } + + DWORD err = GetLastError(); + int rc = RTErrConvertFromWin32(err); + AssertMsgFailed(("%Rrc %d\n", rc, err)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/sched-win.cpp b/src/VBox/Runtime/r3/win/sched-win.cpp new file mode 100644 index 00000000..7b8397e0 --- /dev/null +++ b/src/VBox/Runtime/r3/win/sched-win.cpp @@ -0,0 +1,333 @@ +/* $Id: sched-win.cpp $ */ +/** @file + * IPRT - Scheduling, Win32. + */ + +/* + * 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 + */ + +/** @def WIN32_SCHED_ENABLED + * Enables the priority scheme. */ +#define WIN32_SCHED_ENABLED + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_THREAD +#include <iprt/win/windows.h> + +#include <iprt/thread.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include "internal/sched.h" +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Configuration of one priority. + */ +typedef struct +{ + /** The priority. */ + RTPROCPRIORITY enmPriority; + /** The name of this priority. */ + const char *pszName; + /** The Win32 process priority class. If ANY_PROCESS_PRIORITY_CLASS the + * process priority class is left unchanged. */ + DWORD dwProcessPriorityClass; + /** Array scheduler attributes corresponding to each of the thread types. */ + struct + { + /** For sanity include the array index. */ + RTTHREADTYPE enmType; + /** The Win32 thread priority. */ + int iThreadPriority; + } aTypes[RTTHREADTYPE_END]; +} PROCPRIORITY; + +/** Matches any process priority class. */ +#define ANY_PROCESS_PRIORITY_CLASS (~0U) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Array of static priority configurations. + */ +static const PROCPRIORITY g_aPriorities[] = +{ + { + RTPROCPRIORITY_FLAT, "Flat", ANY_PROCESS_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL } + } + }, + { + RTPROCPRIORITY_LOW, "Low - Below Normal", BELOW_NORMAL_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + }, + { + RTPROCPRIORITY_LOW, "Low", ANY_PROCESS_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL } + } + }, + { + RTPROCPRIORITY_NORMAL, "Normal - Normal", NORMAL_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + }, + { + RTPROCPRIORITY_NORMAL, "Normal", ANY_PROCESS_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + }, + { + RTPROCPRIORITY_HIGH, "High - High", HIGH_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + }, + { + RTPROCPRIORITY_HIGH, "High - Above Normal", ABOVE_NORMAL_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + }, + { + RTPROCPRIORITY_HIGH, "High", ANY_PROCESS_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } + } +}; + +/** + * The dynamic default priority configuration. + * + * This can be recalulated at runtime depending on what the + * system allow us to do. Presently we don't do this as it's + * generally not a bit issue on Win32 hosts. + */ +static PROCPRIORITY g_aDefaultPriority = +{ + RTPROCPRIORITY_LOW, "Default", ANY_PROCESS_PRIORITY_CLASS, + { + { RTTHREADTYPE_INVALID, ~0 }, + { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST }, + { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL }, + { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL }, + { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL }, + { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST }, + { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST } + } +}; + + +/** Pointer to the current priority configuration. */ +static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority; + + +/** + * 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); RT_NOREF_PV(enmType); + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority) +{ + Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); RT_NOREF_PV(enmPriority); + + if (enmPriority == RTPROCPRIORITY_DEFAULT) + { + g_pProcessPriority = &g_aDefaultPriority; + return VINF_SUCCESS; + } + + for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++) + if ( g_aPriorities[i].enmPriority == enmPriority + && g_aPriorities[i].dwProcessPriorityClass == ANY_PROCESS_PRIORITY_CLASS) + { + g_pProcessPriority = &g_aPriorities[i]; + return VINF_SUCCESS; + } + + AssertFailedReturn(VERR_INTERNAL_ERROR); +} + + +/** + * Gets the win32 thread handle. + * + * @returns Valid win32 handle for the specified thread. + * @param pThread The thread. + */ +DECLINLINE(HANDLE) rtThreadNativeGetHandle(PRTTHREADINT pThread) +{ + if ((uintptr_t)pThread->Core.Key == GetCurrentThreadId()) + return GetCurrentThread(); + return (HANDLE)pThread->hThread; +} + + +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); + AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType, + ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType)); + +#ifdef WIN32_SCHED_ENABLED + HANDLE hThread = rtThreadNativeGetHandle(pThread); + if ( hThread == NULL /* No handle for alien threads. */ + || SetThreadPriority(hThread, g_pProcessPriority->aTypes[enmType].iThreadPriority)) + return VINF_SUCCESS; + + DWORD dwLastError = GetLastError(); + int rc = RTErrConvertFromWin32(dwLastError); + AssertMsgFailed(("SetThreadPriority(%p, %d) failed, dwLastError=%d rc=%Rrc\n", + rtThreadNativeGetHandle(pThread), g_pProcessPriority->aTypes[enmType].iThreadPriority, dwLastError, rc)); + return rc; +#else + return VINF_SUCCESS; +#endif +} + diff --git a/src/VBox/Runtime/r3/win/semevent-win.cpp b/src/VBox/Runtime/r3/win/semevent-win.cpp new file mode 100644 index 00000000..be086941 --- /dev/null +++ b/src/VBox/Runtime/r3/win/semevent-win.cpp @@ -0,0 +1,315 @@ +/* $Id: semevent-win.cpp $ */ +/** @file + * IPRT - Event Semaphore, Windows. + */ + +/* + * 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_SEMAPHORE +#include <iprt/win/windows.h> + +#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/mem.h" +#include "internal/strict.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +struct RTSEMEVENTINTERNAL +{ + /** Magic value (RTSEMEVENT_MAGIC). */ + uint32_t u32Magic; + /** The event handle. */ + HANDLE hev; +#ifdef RTSEMEVENT_STRICT + /** Signallers. */ + RTLOCKVALRECSHRD Signallers; + /** Indicates that lock validation should be performed. */ + bool volatile fEverHadSignallers; +#endif + /** The creation flags. */ + uint32_t fFlags; +}; + + + +RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) +{ + return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); + Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); + + struct RTSEMEVENTINTERNAL *pThis; + if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)) + pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(*pThis)); + else + pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + /* + * Create the semaphore. + * (Auto reset, not signaled, private event object.) + */ + pThis->hev = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */ + { + pThis->u32Magic = RTSEMEVENT_MAGIC; + pThis->fFlags = fFlags; +#ifdef RTSEMEVENT_STRICT + if (!pszNameFmt) + { + static uint32_t volatile s_iSemEventAnon = 0; + RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL), + "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1); + } + else + { + va_list va; + va_start(va, pszNameFmt); + RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL), + pszNameFmt, va); + va_end(va); + } + pThis->fEverHadSignallers = false; +#else + RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt); +#endif + + *phEventSem = pThis; + return VINF_SUCCESS; + } + + DWORD dwErr = GetLastError(); + if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)) + RTMemFree(pThis); + else + rtMemBaseFree(pThis); + return RTErrConvertFromWin32(dwErr); +} + + +RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) +{ + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + if (pThis == NIL_RTSEMEVENT) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE); + + /* + * Invalidate the handle and close the semaphore. + */ + int rc = VINF_SUCCESS; + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC, RTSEMEVENT_MAGIC), VERR_INVALID_HANDLE); + if (CloseHandle(pThis->hev)) + { +#ifdef RTSEMEVENT_STRICT + RTLockValidatorRecSharedDelete(&pThis->Signallers); +#endif + if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)) + RTMemFree(pThis); + else + rtMemBaseFree(pThis); + } + else + { + DWORD dwErr = GetLastError(); + rc = RTErrConvertFromWin32(dwErr); + AssertMsgFailed(("Destroy hEventSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc)); + /* Leak it. */ + } + + return rc; +} + + +RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE); + +#ifdef RTSEMEVENT_STRICT + if (pThis->fEverHadSignallers) + { + int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD); + if (RT_FAILURE(rc9)) + return rc9; + } +#endif + + /* + * Signal the object. + */ + if (SetEvent(pThis->hev)) + return VINF_SUCCESS; + DWORD dwErr = GetLastError(); + AssertMsgFailed(("Signaling hEventSem %p failed, lasterr=%d\n", pThis, dwErr)); + return RTErrConvertFromWin32(dwErr); +} + + +/** Goto avoidance. */ +DECL_FORCE_INLINE(int) rtSemEventWaitHandleStatus(struct RTSEMEVENTINTERNAL *pThis, DWORD rc) +{ + switch (rc) + { + case WAIT_OBJECT_0: return VINF_SUCCESS; + case WAIT_TIMEOUT: return VERR_TIMEOUT; + case WAIT_IO_COMPLETION: return VERR_INTERRUPTED; + case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED; + default: + AssertMsgFailed(("%u\n", rc)); + case WAIT_FAILED: + { + int rc2 = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError())); + if (rc2) + return rc2; + + AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2)); + RT_NOREF_PV(pThis); + return VERR_INTERNAL_ERROR; + } + } +} + + +#undef RTSemEventWaitNoResume +RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies) +{ + /* + * Validate input. + */ + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE); + + /* + * Wait for condition. + */ +#ifdef RTSEMEVENT_STRICT + RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) + ? RTThreadSelfAutoAdopt() + : RTThreadSelf(); + if (pThis->fEverHadSignallers) + { + DWORD rc = WaitForSingleObjectEx(pThis->hev, + 0 /*Timeout*/, + TRUE /*fAlertable*/); + if (rc != WAIT_TIMEOUT || cMillies == 0) + return rtSemEventWaitHandleStatus(pThis, rc); + int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false, + cMillies, RTTHREADSTATE_EVENT, true); + if (RT_FAILURE(rc9)) + return rc9; + } +#else + RTTHREAD hThreadSelf = RTThreadSelf(); +#endif + RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true); + DWORD rc = WaitForSingleObjectEx(pThis->hev, + cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies, + TRUE /*fAlertable*/); + RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT); + return rtSemEventWaitHandleStatus(pThis, rc); +} + + +RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL); +#else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +#endif +} + + +RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL); +#else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +#endif +} + + +RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTINTERNAL *pThis = hEventSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC); + + RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread); +#else + RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread); +#endif +} + diff --git a/src/VBox/Runtime/r3/win/semeventmulti-win.cpp b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp new file mode 100644 index 00000000..ed37f656 --- /dev/null +++ b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp @@ -0,0 +1,389 @@ +/* $Id: semeventmulti-win.cpp $ */ +/** @file + * IPRT - Multiple Release Event Semaphore, Windows. + * + * @remarks This file is identical to semevent-win.cpp except for the 2nd + * CreateEvent parameter, the reset function and the "Multi" infix. + */ + +/* + * 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_SEMAPHORE +#include <iprt/win/windows.h> + +#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 <iprt/time.h> +#include "internal/magics.h" +#include "internal/strict.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +struct RTSEMEVENTMULTIINTERNAL +{ + /** Magic value (RTSEMEVENTMULTI_MAGIC). */ + uint32_t u32Magic; + /** The event handle. */ + HANDLE hev; +#ifdef RTSEMEVENT_STRICT + /** Signallers. */ + RTLOCKVALRECSHRD Signallers; + /** Indicates that lock validation should be performed. */ + bool volatile fEverHadSignallers; +#endif +}; + + + +RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem) +{ + return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass, + const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + + struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + /* + * Create the semaphore. + * (Manual reset, not signaled, private event object.) + */ + pThis->hev = CreateEvent(NULL, TRUE, FALSE, NULL); + if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */ + { + pThis->u32Magic = RTSEMEVENTMULTI_MAGIC; +#ifdef RTSEMEVENT_STRICT + if (!pszNameFmt) + { + static uint32_t volatile s_iSemEventMultiAnon = 0; + RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), + "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1); + } + else + { + va_list va; + va_start(va, pszNameFmt); + RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis, + true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), + pszNameFmt, va); + va_end(va); + } + pThis->fEverHadSignallers = false; +#else + RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt); +#endif + + *phEventMultiSem = pThis; + return VINF_SUCCESS; + } + + DWORD dwErr = GetLastError(); + RTMemFree(pThis); + return RTErrConvertFromWin32(dwErr); +} + + +RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem) +{ + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + if (pThis == NIL_RTSEMEVENT) /* don't bitch */ + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE); + + /* + * Invalidate the handle and close the semaphore. + */ + int rc = VINF_SUCCESS; + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC, RTSEMEVENTMULTI_MAGIC), VERR_INVALID_HANDLE); + if (CloseHandle(pThis->hev)) + { +#ifdef RTSEMEVENT_STRICT + RTLockValidatorRecSharedDelete(&pThis->Signallers); +#endif + RTMemFree(pThis); + } + else + { + DWORD dwErr = GetLastError(); + rc = RTErrConvertFromWin32(dwErr); + AssertMsgFailed(("Destroy hEventMultiSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc)); + /* Leak it. */ + } + + return rc; +} + + +RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE); + +#ifdef RTSEMEVENT_STRICT + if (pThis->fEverHadSignallers) + { + int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD); + if (RT_FAILURE(rc9)) + return rc9; + } +#endif + + /* + * Signal the object. + */ + if (SetEvent(pThis->hev)) + return VINF_SUCCESS; + DWORD dwErr = GetLastError(); + AssertMsgFailed(("Signaling hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr)); + return RTErrConvertFromWin32(dwErr); +} + + +RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE); + + /* + * Reset the object. + */ + if (ResetEvent(pThis->hev)) + return VINF_SUCCESS; + DWORD dwErr = GetLastError(); + AssertMsgFailed(("Resetting hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr)); + return RTErrConvertFromWin32(dwErr); +} + + +/** Goto avoidance. */ +DECL_FORCE_INLINE(int) +rtSemEventWaitHandleStatus(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, DWORD rc) +{ + switch (rc) + { + case WAIT_OBJECT_0: return VINF_SUCCESS; + case WAIT_TIMEOUT: return VERR_TIMEOUT; + case WAIT_IO_COMPLETION: return fFlags & RTSEMWAIT_FLAGS_RESUME ? VERR_TIMEOUT : VERR_INTERRUPTED; + case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED; + default: + AssertMsgFailed(("%u\n", rc)); + case WAIT_FAILED: + { + int rc2 = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError())); + if (rc2) + return rc2; + + AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2)); + RT_NOREF_PV(pThis); + return VERR_INTERNAL_ERROR; + } + } +} + + +DECLINLINE(int) rtSemEventMultiWinWait(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + /* + * Validate input. + */ + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); + + /* + * Convert the timeout to a millisecond count. + */ + uint64_t uAbsDeadline; + DWORD dwMsTimeout; + if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) + { + dwMsTimeout = INFINITE; + uAbsDeadline = UINT64_MAX; + } + else + { + if (fFlags & RTSEMWAIT_FLAGS_NANOSECS) + uTimeout = uTimeout < UINT64_MAX - UINT32_C(1000000) / 2 + ? (uTimeout + UINT32_C(1000000) / 2) / UINT32_C(1000000) + : UINT64_MAX / UINT32_C(1000000); + if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE) + { + uAbsDeadline = uTimeout; + uint64_t u64Now = RTTimeSystemMilliTS(); + if (u64Now < uTimeout) + uTimeout -= u64Now; + else + uTimeout = 0; + } + else if (fFlags & RTSEMWAIT_FLAGS_RESUME) + uAbsDeadline = RTTimeSystemMilliTS() + uTimeout; + else + uAbsDeadline = UINT64_MAX; + + dwMsTimeout = uTimeout < UINT32_MAX + ? (DWORD)uTimeout + : INFINITE; + } + + /* + * Do the wait. + */ + DWORD rc; +#ifdef RTSEMEVENT_STRICT + RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt(); + if (pThis->fEverHadSignallers) + { + do + rc = WaitForSingleObjectEx(pThis->hev, 0 /*Timeout*/, TRUE /*fAlertable*/); + while (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME)); + if (rc != WAIT_TIMEOUT || dwMsTimeout == 0) + return rtSemEventWaitHandleStatus(pThis, fFlags, rc); + int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false, + dwMsTimeout, RTTHREADSTATE_EVENT_MULTI, true); + if (RT_FAILURE(rc9)) + return rc9; + } +#else + RTTHREAD hThreadSelf = RTThreadSelf(); + RT_NOREF_PV(pSrcPos); +#endif + RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true); + rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/); + if (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME)) + { + while ( rc == WAIT_IO_COMPLETION + && RTTimeSystemMilliTS() < uAbsDeadline) + rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/); + + } + RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI); + return rtSemEventWaitHandleStatus(pThis, fFlags, rc); +} + + + +#undef RTSemEventMultiWaitEx +RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +#endif +} + + +RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +} + + + +RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL); +#else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +#endif +} + + +RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + ASMAtomicWriteBool(&pThis->fEverHadSignallers, true); + RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL); +#else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +#endif +} + + +RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread) +{ +#ifdef RTSEMEVENT_STRICT + struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem; + AssertPtrReturnVoid(pThis); + AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC); + + RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread); +#else + RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread); +#endif +} + diff --git a/src/VBox/Runtime/r3/win/semmutex-win.cpp b/src/VBox/Runtime/r3/win/semmutex-win.cpp new file mode 100644 index 00000000..34aa8bc5 --- /dev/null +++ b/src/VBox/Runtime/r3/win/semmutex-win.cpp @@ -0,0 +1,356 @@ +/* $Id: semmutex-win.cpp $ */ +/** @file + * IPRT - Mutex Semaphores, Windows. + */ + +/* + * 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_SEMAPHORE +#include <iprt/win/windows.h> + +#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" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Posix internal representation of a Mutex semaphore. */ +struct RTSEMMUTEXINTERNAL +{ + /** Magic value (RTSEMMUTEX_MAGIC). */ + uint32_t u32Magic; + /** Recursion count. */ + uint32_t volatile cRecursions; + /** The owner thread. */ + RTNATIVETHREAD volatile hNativeOwner; + /** The mutex handle. */ + HANDLE hMtx; +#ifdef RTSEMMUTEX_STRICT + /** Lock validator record associated with this mutex. */ + RTLOCKVALRECEXCL ValidatorRec; +#endif +}; + + + +#undef RTSemMutexCreate +RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem) +{ + return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL); +} + + +RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags, + RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...) +{ + AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + + /* + * Create the semaphore. + */ + int rc; + HANDLE hMtx = CreateMutex(NULL, FALSE, NULL); + if (hMtx) + { + RTSEMMUTEXINTERNAL *pThis = (RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMMUTEX_MAGIC; + pThis->hMtx = hMtx; + pThis->hNativeOwner = NIL_RTNATIVETHREAD; + pThis->cRecursions = 0; +#ifdef RTSEMMUTEX_STRICT + if (!pszNameFmt) + { + static uint32_t volatile s_iMutexAnon = 0; + RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis, + !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), + "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1); + } + else + { + va_list va; + va_start(va, pszNameFmt); + RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis, + !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va); + va_end(va); + } +#else + RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt); +#endif + *phMutexSem = pThis; + return VINF_SUCCESS; + } + + rc = VERR_NO_MEMORY; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + return rc; +} + + +RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem) +{ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + if (pThis == NIL_RTSEMMUTEX) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Close semaphore handle. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE); + HANDLE hMtx = pThis->hMtx; + ASMAtomicWritePtr(&pThis->hMtx, INVALID_HANDLE_VALUE); + + int rc = VINF_SUCCESS; + if (!CloseHandle(hMtx)) + { + rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("%p rc=%d lasterr=%d\n", pThis->hMtx, rc, GetLastError())); + } + +#ifdef RTSEMMUTEX_STRICT + RTLockValidatorRecExclDelete(&pThis->ValidatorRec); +#endif + RTMemFree(pThis); + return rc; +} + + +RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass) +{ +#ifdef RTSEMMUTEX_STRICT + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID); + + return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass); +#else + RT_NOREF_PV(hMutexSem); RT_NOREF_PV(uSubClass); + return RTLOCKVAL_SUB_CLASS_INVALID; +#endif +} + + +/** + * Internal worker for RTSemMutexRequestNoResume and it's debug companion. + * + * @returns Same as RTSEmMutexRequestNoResume + * @param hMutexSem The mutex handle. + * @param cMillies The number of milliseconds to wait. + * @param pSrcPos The source position of the caller. + */ +DECL_FORCE_INLINE(int) rtSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos) +{ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Check for recursive entry. + */ + RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf(); + RTNATIVETHREAD hNativeOwner; + ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner); + if (hNativeOwner == hNativeSelf) + { +#ifdef RTSEMMUTEX_STRICT + int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos); + if (RT_FAILURE(rc9)) + return rc9; +#endif + ASMAtomicIncU32(&pThis->cRecursions); + return VINF_SUCCESS; + } + + /* + * Lock mutex semaphore. + */ + RTTHREAD hThreadSelf = NIL_RTTHREAD; + if (cMillies > 0) + { +#ifdef RTSEMMUTEX_STRICT + hThreadSelf = RTThreadSelfAutoAdopt(); + int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true, + cMillies, RTTHREADSTATE_MUTEX, true); + if (RT_FAILURE(rc9)) + return rc9; +#else + hThreadSelf = RTThreadSelf(); + RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true); + RT_NOREF_PV(pSrcPos); +#endif + } + DWORD rc = WaitForSingleObjectEx(pThis->hMtx, + cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies, + TRUE /*fAlertable*/); + RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX); + switch (rc) + { + case WAIT_OBJECT_0: +#ifdef RTSEMMUTEX_STRICT + RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true); +#endif + ASMAtomicWriteHandle(&pThis->hNativeOwner, hNativeSelf); + ASMAtomicWriteU32(&pThis->cRecursions, 1); + return VINF_SUCCESS; + + case WAIT_TIMEOUT: return VERR_TIMEOUT; + case WAIT_IO_COMPLETION: return VERR_INTERRUPTED; + case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED; + default: + AssertMsgFailed(("%u\n", rc)); + case WAIT_FAILED: + { + int rc2 = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d lasterr=%d\n", hMutexSem, rc, GetLastError())); + if (rc2 != VINF_SUCCESS) + return rc2; + + AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2)); + return VERR_INTERNAL_ERROR; + } + } +} + + +#undef RTSemMutexRequestNoResume +RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ +#ifndef RTSEMMUTEX_STRICT + return rtSemMutexRequestNoResume(hMutexSem, cMillies, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos); +#endif +} + + +RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos); +} + + +RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem) +{ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE); + + /* + * Check ownership and recursions. + */ + RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf(); + RTNATIVETHREAD hNativeOwner; + ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner); + if (RT_UNLIKELY(hNativeOwner != hNativeSelf)) + { + AssertMsgFailed(("Not owner of mutex %p!! hNativeSelf=%RTntrd Owner=%RTntrd cRecursions=%d\n", + pThis, hNativeSelf, hNativeOwner, pThis->cRecursions)); + return VERR_NOT_OWNER; + } + if (pThis->cRecursions > 1) + { +#ifdef RTSEMMUTEX_STRICT + int rc9 = RTLockValidatorRecExclUnwind(&pThis->ValidatorRec); + if (RT_FAILURE(rc9)) + return rc9; +#endif + ASMAtomicDecU32(&pThis->cRecursions); + return VINF_SUCCESS; + } + + /* + * Unlock mutex semaphore. + */ +#ifdef RTSEMMUTEX_STRICT + int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, false); + if (RT_FAILURE(rc9)) + return rc9; +#endif + ASMAtomicWriteU32(&pThis->cRecursions, 0); + ASMAtomicWriteHandle(&pThis->hNativeOwner, NIL_RTNATIVETHREAD); + + if (ReleaseMutex(pThis->hMtx)) + return VINF_SUCCESS; + + int rc = RTErrConvertFromWin32(GetLastError()); + AssertMsgFailed(("%p/%p, rc=%Rrc lasterr=%d\n", pThis, pThis->hMtx, rc, GetLastError())); + return rc; +} + + +RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem) +{ + /* + * Validate. + */ + RTSEMMUTEXINTERNAL *pThis = hMutexSem; + AssertPtrReturn(pThis, false); + AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false); + + RTNATIVETHREAD hNativeOwner; + ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner); + return hNativeOwner != NIL_RTNATIVETHREAD; +} + diff --git a/src/VBox/Runtime/r3/win/serialport-win.cpp b/src/VBox/Runtime/r3/win/serialport-win.cpp new file mode 100644 index 00000000..0f6f04c2 --- /dev/null +++ b/src/VBox/Runtime/r3/win/serialport-win.cpp @@ -0,0 +1,1056 @@ +/* $Id: serialport-win.cpp $ */ +/** @file + * IPRT - Serial Port API, Windows Implementation. + */ + +/* + * Copyright (C) 2017-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/serialport.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/cdefs.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include "internal/magics.h" + +#include <iprt/win/windows.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Internal serial port state. + */ +typedef struct RTSERIALPORTINTERNAL +{ + /** Magic value (RTSERIALPORT_MAGIC). */ + uint32_t u32Magic; + /** Flags given while opening the serial port. */ + uint32_t fOpenFlags; + /** The device handle. */ + HANDLE hDev; + /** The overlapped write structure. */ + OVERLAPPED OverlappedWrite; + /** The overlapped read structure. */ + OVERLAPPED OverlappedRead; + /** The overlapped I/O structure when waiting on events. */ + OVERLAPPED OverlappedEvt; + /** The event handle to wait on for the overlapped event operations of the device. */ + HANDLE hEvtDev; + /** The event handle to wait on for the overlapped write operations of the device. */ + HANDLE hEvtWrite; + /** The event handle to wait on for the overlapped read operations of the device. */ + HANDLE hEvtRead; + /** The event handle to wait on for waking up waiting threads externally. */ + HANDLE hEvtIntr; + /** Events currently waited for. */ + uint32_t fEvtMask; + /** Event mask as received by WaitCommEvent(). */ + DWORD dwEventMask; + /** Flag whether a write is currently pending. */ + bool fWritePending; + /** Event query pending. */ + bool fEvtQueryPending; + /** Bounce buffer for writes. */ + uint8_t *pbBounceBuf; + /** Amount of used buffer space. */ + size_t cbBounceBufUsed; + /** Amount of allocated buffer space. */ + size_t cbBounceBufAlloc; + /** The current active port config. */ + DCB PortCfg; +} RTSERIALPORTINTERNAL; +/** Pointer to the internal serial port state. */ +typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL; + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The pipe buffer size we prefer. */ +#define RTSERIALPORT_NT_SIZE _32K + + + +/********************************************************************************************************************************* +* Global variables * +*********************************************************************************************************************************/ + + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Updatest the current event mask to wait for. + * + * @returns IPRT status code. + * @param pThis The internal serial port instance data. + * @param fEvtMask The new event mask to change to. + */ +static int rtSerialPortWinUpdateEvtMask(PRTSERIALPORTINTERNAL pThis, uint32_t fEvtMask) +{ + DWORD dwEvtMask = EV_ERR; + + if (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX) + dwEvtMask |= EV_RXCHAR; + if (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX) + dwEvtMask |= EV_TXEMPTY; + if (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED) + dwEvtMask |= EV_BREAK; + if (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED) + dwEvtMask |= EV_CTS | EV_DSR | EV_RING | EV_RLSD; + + int rc = VINF_SUCCESS; + if (!SetCommMask(pThis->hDev, dwEvtMask)) + rc = RTErrConvertFromWin32(GetLastError()); + else + pThis->fEvtMask = fEvtMask; + + return rc; +} + + +/** + * Tries to set the default config on the given serial port. + * + * @returns IPRT status code. + * @param pThis The internal serial port instance data. + */ +static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis) +{ + if (!PurgeComm(pThis->hDev, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR)) + return RTErrConvertFromWin32(GetLastError()); + + pThis->PortCfg.DCBlength = sizeof(pThis->PortCfg); + if (!GetCommState(pThis->hDev, &pThis->PortCfg)) + return RTErrConvertFromWin32(GetLastError()); + + pThis->PortCfg.BaudRate = CBR_9600; + pThis->PortCfg.fBinary = TRUE; + pThis->PortCfg.fParity = TRUE; + pThis->PortCfg.fDtrControl = DTR_CONTROL_DISABLE; + pThis->PortCfg.ByteSize = 8; + pThis->PortCfg.Parity = NOPARITY; + pThis->PortCfg.fOutxCtsFlow = FALSE; + pThis->PortCfg.fOutxDsrFlow = FALSE; + pThis->PortCfg.fDsrSensitivity = FALSE; + pThis->PortCfg.fTXContinueOnXoff = TRUE; + pThis->PortCfg.fOutX = FALSE; + pThis->PortCfg.fInX = FALSE; + pThis->PortCfg.fErrorChar = FALSE; + pThis->PortCfg.fNull = FALSE; + pThis->PortCfg.fRtsControl = RTS_CONTROL_DISABLE; + pThis->PortCfg.fAbortOnError = FALSE; + pThis->PortCfg.wReserved = 0; + pThis->PortCfg.XonLim = 5; + pThis->PortCfg.XoffLim = 5; + + int rc = VINF_SUCCESS; + if (!SetCommState(pThis->hDev, &pThis->PortCfg)) + rc = RTErrConvertFromWin32(GetLastError()); + + if (RT_SUCCESS(rc)) + { + /* + * Set timeouts for non blocking mode. + * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts . + */ + COMMTIMEOUTS ComTimeouts; + RT_ZERO(ComTimeouts); + ComTimeouts.ReadIntervalTimeout = MAXDWORD; + if (!SetCommTimeouts(pThis->hDev, &ComTimeouts)) + rc = RTErrConvertFromWin32(GetLastError()); + } + + return rc; +} + + +/** + * Common worker for handling I/O completion. + * + * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + */ +static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis) +{ + int rc = VINF_SUCCESS; + DWORD dwRc = WaitForSingleObject(pThis->OverlappedWrite.hEvent, 0); + if (dwRc == WAIT_OBJECT_0) + { + DWORD cbWritten = 0; + if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE)) + { + for (;;) + { + if (cbWritten >= pThis->cbBounceBufUsed) + { + pThis->fWritePending = false; + rc = VINF_SUCCESS; + break; + } + + /* resubmit the remainder of the buffer - can this actually happen? */ + memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten); + rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE); + if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, + &cbWritten, &pThis->OverlappedWrite)) + { + if (GetLastError() == ERROR_IO_PENDING) + rc = VINF_TRY_AGAIN; + else + { + pThis->fWritePending = false; + rc = RTErrConvertFromWin32(GetLastError()); + } + break; + } + Assert(cbWritten > 0); + } + } + else + { + pThis->fWritePending = false; + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else if (dwRc == WAIT_TIMEOUT) + rc = VINF_TRY_AGAIN; + else + { + pThis->fWritePending = false; + if (dwRc == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + return rc; +} + + +/** + * Processes the received Windows comm events and converts them to our format. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + * @param dwEventMask Event mask received. + * @param pfEvtsRecv Where to store the converted events. + */ +static int rtSerialPortEvtFlagsProcess(PRTSERIALPORTINTERNAL pThis, DWORD dwEventMask, uint32_t *pfEvtsRecv) +{ + int rc = VINF_SUCCESS; + + if (dwEventMask & EV_RXCHAR) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX; + if (dwEventMask & EV_TXEMPTY) + { + if (pThis->fWritePending) + { + rc = rtSerialPortWriteCheckCompletion(pThis); + if (rc == VINF_SUCCESS) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX; + else + rc = VINF_SUCCESS; + } + else + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX; + } + if (dwEventMask & EV_BREAK) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED; + if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD)) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED; + + return rc; +} + + +/** + * The internal comm event wait worker. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + * @param msTimeout The timeout to wait for. + */ +static int rtSerialPortEvtWaitWorker(PRTSERIALPORTINTERNAL pThis, RTMSINTERVAL msTimeout) +{ + int rc = VINF_SUCCESS; + HANDLE ahWait[2]; + ahWait[0] = pThis->hEvtDev; + ahWait[1] = pThis->hEvtIntr; + + DWORD dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout); + if (dwRet == WAIT_TIMEOUT) + rc = VERR_TIMEOUT; + else if (dwRet == WAIT_FAILED) + rc = RTErrConvertFromWin32(GetLastError()); + else if (dwRet == WAIT_OBJECT_0) + rc = VINF_SUCCESS; + else + { + Assert(dwRet == WAIT_OBJECT_0 + 1); + rc = VERR_INTERRUPTED; + } + + return rc; +} + + +RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags) +{ + AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER); + AssertReturn(RT_VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE), + VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSERIALPORT_MAGIC; + pThis->fOpenFlags = fFlags; + pThis->fEvtMask = 0; + pThis->fWritePending = false; + pThis->fEvtQueryPending = false; + pThis->pbBounceBuf = NULL; + pThis->cbBounceBufUsed = 0; + pThis->cbBounceBufAlloc = 0; + RT_ZERO(pThis->OverlappedEvt); + RT_ZERO(pThis->OverlappedWrite); + RT_ZERO(pThis->OverlappedRead); + pThis->hEvtDev = CreateEvent(NULL, TRUE, FALSE, NULL); + if (pThis->hEvtDev) + { + pThis->OverlappedEvt.hEvent = pThis->hEvtDev, + pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pThis->hEvtIntr) + { + pThis->hEvtWrite = CreateEvent(NULL, TRUE, TRUE, NULL); + if (pThis->hEvtWrite) + { + pThis->OverlappedWrite.hEvent = pThis->hEvtWrite; + pThis->hEvtRead = CreateEvent(NULL, TRUE, TRUE, NULL); + if (pThis->hEvtRead) + { + pThis->OverlappedRead.hEvent = pThis->hEvtRead; + DWORD fWinFlags = 0; + + if (fFlags & RTSERIALPORT_OPEN_F_WRITE) + fWinFlags |= GENERIC_WRITE; + if (fFlags & RTSERIALPORT_OPEN_F_READ) + fWinFlags |= GENERIC_READ; + + pThis->hDev = CreateFile(pszPortAddress, + fWinFlags, + 0, /* Must be opened with exclusive access. */ + NULL, /* No SECURITY_ATTRIBUTES structure. */ + OPEN_EXISTING, /* Must use OPEN_EXISTING. */ + FILE_FLAG_OVERLAPPED, /* Overlapped I/O. */ + NULL); + if (pThis->hDev) + { + rc = rtSerialPortSetDefaultCfg(pThis); + if (RT_SUCCESS(rc)) + { + *phSerialPort = pThis; + return rc; + } + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + CloseHandle(pThis->hEvtRead); + } + + CloseHandle(pThis->hEvtWrite); + } + + CloseHandle(pThis->hEvtIntr); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + CloseHandle(pThis->hEvtDev); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + if (pThis == NIL_RTSERIALPORT) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + /* + * Do the cleanup. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE); + + if (pThis->fWritePending) + rtSerialPortWriteCheckCompletion(pThis); + + CloseHandle(pThis->hDev); + CloseHandle(pThis->hEvtDev); + CloseHandle(pThis->hEvtWrite); + CloseHandle(pThis->hEvtRead); + CloseHandle(pThis->hEvtIntr); + pThis->hDev = NULL; + pThis->hEvtDev = NULL; + pThis->hEvtWrite = NULL; + pThis->hEvtRead = NULL; + pThis->hEvtIntr = NULL; + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, -1); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1); + + return (RTHCINTPTR)pThis->hDev; +} + + +RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER); + + /* + * Kick of an overlapped read. + */ + int rc = VINF_SUCCESS; + uint8_t *pbBuf = (uint8_t *)pvBuf; + + while ( cbToRead > 0 + && RT_SUCCESS(rc)) + { + BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc); + DWORD cbRead = 0; + if (ReadFile(pThis->hDev, pbBuf, + cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0, + &cbRead, &pThis->OverlappedRead)) + { + if (pcbRead) + { + *pcbRead = cbRead; + break; + } + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + DWORD dwWait = WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE); + if (dwWait == WAIT_OBJECT_0) + { + if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/)) + { + if (pcbRead) + { + *pcbRead = cbRead; + break; + } + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + Assert(dwWait == WAIT_FAILED); + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + if (RT_SUCCESS(rc)) + { + cbToRead -= cbRead; + pbBuf += cbRead; + } + } + + return rc; +} + + +RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbRead, VERR_INVALID_POINTER); + + *pcbRead = 0; + + /* Check whether there is data waiting in the input queue. */ + int rc = VINF_SUCCESS; + COMSTAT ComStat; RT_ZERO(ComStat); + if (ClearCommError(pThis->hDev, NULL, &ComStat)) + { + if (ComStat.cbInQue > 0) + { + DWORD dwToRead = RT_MIN(ComStat.cbInQue, (DWORD)cbToRead); + /* Kick of an overlapped read. It should return immediately */ + BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc); + DWORD cbRead = 0; + if ( cbToRead == 0 + || ReadFile(pThis->hDev, pvBuf, dwToRead, + &cbRead, &pThis->OverlappedRead)) + *pcbRead = cbRead; + else if (GetLastError() == ERROR_IO_PENDING) + { + /* This shouldn't actually happen, so turn this into a synchronous read. */ + if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/)) + *pcbRead = cbRead; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VINF_TRY_AGAIN; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; +} + + +RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER); + + /* If I/O is pending, check if it has completed. */ + int rc = VINF_SUCCESS; + if (pThis->fWritePending) + rc = rtSerialPortWriteCheckCompletion(pThis); + if (rc == VINF_SUCCESS) + { + const uint8_t *pbBuf = (const uint8_t *)pvBuf; + + while ( cbToWrite > 0 + && RT_SUCCESS(rc)) + { + BOOL fSucc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc); + DWORD cbWritten = 0; + if (WriteFile(pThis->hDev, pbBuf, + cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0, + &cbWritten, &pThis->OverlappedWrite)) + { + if (pcbWritten) + { + *pcbWritten = cbWritten; + break; + } + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + DWORD dwWait = WaitForSingleObject(pThis->OverlappedWrite.hEvent, INFINITE); + if (dwWait == WAIT_OBJECT_0) + { + if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE /*fWait*/)) + { + if (pcbWritten) + { + *pcbWritten = cbWritten; + break; + } + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + { + Assert(dwWait == WAIT_FAILED); + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + if (RT_SUCCESS(rc)) + { + cbToWrite -= cbWritten; + pbBuf += cbWritten; + } + } + } + + return rc; +} + + +RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); + + /* If I/O is pending, check if it has completed. */ + int rc = VINF_SUCCESS; + if (pThis->fWritePending) + rc = rtSerialPortWriteCheckCompletion(pThis); + if (rc == VINF_SUCCESS) + { + Assert(!pThis->fWritePending); + + /* Do the bounce buffering. */ + if ( pThis->cbBounceBufAlloc < cbToWrite + && pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE) + { + if (cbToWrite > RTSERIALPORT_NT_SIZE) + cbToWrite = RTSERIALPORT_NT_SIZE; + void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K)); + if (pv) + { + pThis->pbBounceBuf = (uint8_t *)pv; + pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K); + } + else + rc = VERR_NO_MEMORY; + } + else if (cbToWrite > RTSERIALPORT_NT_SIZE) + cbToWrite = RTSERIALPORT_NT_SIZE; + if (RT_SUCCESS(rc) && cbToWrite) + { + memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite); + pThis->cbBounceBufUsed = (uint32_t)cbToWrite; + + /* Submit the write. */ + rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE); + DWORD cbWritten = 0; + if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, + &cbWritten, &pThis->OverlappedWrite)) + { + *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */ + rc = VINF_SUCCESS; + } + else if (GetLastError() == ERROR_IO_PENDING) + { + *pcbWritten = cbToWrite; + pThis->fWritePending = true; + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (RT_SUCCESS(rc)) + *pcbWritten = 0; + } + else if (RT_SUCCESS(rc)) + *pcbWritten = 0; + + return rc; +} + + +RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + pCfg->uBaudRate = pThis->PortCfg.BaudRate; + switch (pThis->PortCfg.Parity) + { + case NOPARITY: + pCfg->enmParity = RTSERIALPORTPARITY_NONE; + break; + case EVENPARITY: + pCfg->enmParity = RTSERIALPORTPARITY_EVEN; + break; + case ODDPARITY: + pCfg->enmParity = RTSERIALPORTPARITY_ODD; + break; + case MARKPARITY: + pCfg->enmParity = RTSERIALPORTPARITY_MARK; + break; + case SPACEPARITY: + pCfg->enmParity = RTSERIALPORTPARITY_SPACE; + break; + default: + AssertFailed(); + return VERR_INTERNAL_ERROR; + } + + switch (pThis->PortCfg.ByteSize) + { + case 5: + pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS; + break; + case 6: + pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS; + break; + case 7: + pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS; + break; + case 8: + pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS; + break; + default: + AssertFailed(); + return VERR_INTERNAL_ERROR; + } + + switch (pThis->PortCfg.StopBits) + { + case ONESTOPBIT: + pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE; + break; + case ONE5STOPBITS: + pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE; + break; + case TWOSTOPBITS: + pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO; + break; + default: + AssertFailed(); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + RT_NOREF(pErrInfo); + + DCB DcbNew; + memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew)); + DcbNew.BaudRate = pCfg->uBaudRate; + + switch (pCfg->enmParity) + { + case RTSERIALPORTPARITY_NONE: + DcbNew.Parity = NOPARITY; + break; + case RTSERIALPORTPARITY_EVEN: + DcbNew.Parity = EVENPARITY; + break; + case RTSERIALPORTPARITY_ODD: + DcbNew.Parity = ODDPARITY; + break; + case RTSERIALPORTPARITY_MARK: + DcbNew.Parity = MARKPARITY; + break; + case RTSERIALPORTPARITY_SPACE: + DcbNew.Parity = SPACEPARITY; + break; + default: + AssertFailedReturn(VERR_INVALID_PARAMETER); + } + + switch (pCfg->enmDataBitCount) + { + case RTSERIALPORTDATABITS_5BITS: + DcbNew.ByteSize = 5; + break; + case RTSERIALPORTDATABITS_6BITS: + DcbNew.ByteSize = 6; + break; + case RTSERIALPORTDATABITS_7BITS: + DcbNew.ByteSize = 7; + break; + case RTSERIALPORTDATABITS_8BITS: + DcbNew.ByteSize = 8; + break; + default: + AssertFailedReturn(VERR_INVALID_PARAMETER); + } + + switch (pCfg->enmStopBitCount) + { + case RTSERIALPORTSTOPBITS_ONE: + DcbNew.StopBits = ONESTOPBIT; + break; + case RTSERIALPORTSTOPBITS_ONEPOINTFIVE: + AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER); + DcbNew.StopBits = ONE5STOPBITS; + break; + case RTSERIALPORTSTOPBITS_TWO: + AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER); + DcbNew.StopBits = TWOSTOPBITS; + break; + default: + AssertFailedReturn(VERR_INVALID_PARAMETER); + } + + int rc = VINF_SUCCESS; + if (!SetCommState(pThis->hDev, &DcbNew)) + rc = RTErrConvertFromWin32(GetLastError()); + else + memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew)); + + return rc; +} + + +RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv, + RTMSINTERVAL msTimeout) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER); + AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER); + + *pfEvtsRecv = 0; + + int rc = VINF_SUCCESS; + if (fEvtMask != pThis->fEvtMask) + { + rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask); + if ( RT_SUCCESS(rc) + && pThis->fEvtQueryPending) + { + /* + * Setting a new event mask lets the WaitCommEvent() call finish immediately, + * so clean up and process any events here. + */ + rc = rtSerialPortEvtWaitWorker(pThis, 1); + AssertRC(rc); + + if (pThis->dwEventMask != 0) + { + pThis->fEvtQueryPending = false; + return rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv); + } + } + } + + /* + * EV_RXCHAR is triggered only if a byte is received after the event mask is set, + * not if there is already something in the input buffer. Thatswhy we check the input + * buffer for any stored data and the output buffer whether it is empty and return + * the appropriate flags. + */ + if (RT_SUCCESS(rc)) + { + COMSTAT ComStat; RT_ZERO(ComStat); + if (!ClearCommError(pThis->hDev, NULL, &ComStat)) + return RTErrConvertFromWin32(GetLastError()); + + /* Check whether data is already waiting in the input buffer. */ + if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX) + && ComStat.cbInQue > 0) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX; + + /* Check whether the output buffer is empty. */ + if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX) + && ComStat.cbOutQue == 0) + *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX; + + /* Return if there is at least one event. */ + if (*pfEvtsRecv != 0) + return VINF_SUCCESS; + } + + if (RT_SUCCESS(rc)) + { + /* Set up a new event wait if there is none pending. */ + if (!pThis->fEvtQueryPending) + { + RT_ZERO(pThis->OverlappedEvt); + pThis->OverlappedEvt.hEvent = pThis->hEvtDev; + + pThis->dwEventMask = 0; + pThis->fEvtQueryPending = true; + if (!WaitCommEvent(pThis->hDev, &pThis->dwEventMask, &pThis->OverlappedEvt)) + { + DWORD dwRet = GetLastError(); + if (dwRet == ERROR_IO_PENDING) + rc = VINF_SUCCESS; + else + { + rc = RTErrConvertFromWin32(GetLastError()); + pThis->fEvtQueryPending = false; + } + } + else + pThis->fEvtQueryPending = false; + } + + if ( RT_SUCCESS(rc) + && pThis->fEvtQueryPending) + rc = rtSerialPortEvtWaitWorker(pThis, msTimeout); + + if (RT_SUCCESS(rc)) + { + pThis->fEvtQueryPending = false; + rc = rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv); + } + } + + return rc; +} + + +RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + if (!SetEvent(pThis->hEvtIntr)) + return RTErrConvertFromWin32(GetLastError()); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + BOOL fSucc = FALSE; + if (fSet) + fSucc = SetCommBreak(pThis->hDev); + else + fSucc = ClearCommBreak(pThis->hDev); + + int rc = VINF_SUCCESS; + if (!fSucc) + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; +} + + +RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + + BOOL fSucc = TRUE; + if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR) + fSucc = EscapeCommFunction(pThis->hDev, SETDTR); + if ( fSucc + && (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)) + fSucc = EscapeCommFunction(pThis->hDev, SETRTS); + + if ( fSucc + && (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR)) + fSucc = EscapeCommFunction(pThis->hDev, CLRDTR); + if ( fSucc + && (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS)) + fSucc = EscapeCommFunction(pThis->hDev, CLRRTS); + + int rc = VINF_SUCCESS; + if (!fSucc) + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; +} + + +RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines) +{ + PRTSERIALPORTINTERNAL pThis = hSerialPort; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER); + + *pfStsLines = 0; + + int rc = VINF_SUCCESS; + DWORD fStsLinesQueried = 0; + + /* Get the new state */ + if (GetCommModemStatus(pThis->hDev, &fStsLinesQueried)) + { + *pfStsLines |= (fStsLinesQueried & MS_RLSD_ON) ? RTSERIALPORT_STS_LINE_DCD : 0; + *pfStsLines |= (fStsLinesQueried & MS_RING_ON) ? RTSERIALPORT_STS_LINE_RI : 0; + *pfStsLines |= (fStsLinesQueried & MS_DSR_ON) ? RTSERIALPORT_STS_LINE_DSR : 0; + *pfStsLines |= (fStsLinesQueried & MS_CTS_ON) ? RTSERIALPORT_STS_LINE_CTS : 0; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/shmem-win.cpp b/src/VBox/Runtime/r3/win/shmem-win.cpp new file mode 100644 index 00000000..b97fc796 --- /dev/null +++ b/src/VBox/Runtime/r3/win/shmem-win.cpp @@ -0,0 +1,473 @@ +/* $Id: shmem-win.cpp $ */ +/** @file + * IPRT - Named shared memory object, Windows Implementation. + */ + +/* + * Copyright (C) 2018-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/shmem.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/cdefs.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include "internal/magics.h" +#include "internal/path.h" +#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */ + +/* + * Define values ourselves in case the compiling host is too old. + * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga + * for when these were introduced. + */ +#ifndef PAGE_EXECUTE_READ +# define PAGE_EXECUTE_READ 0x20 +#endif +#ifndef PAGE_EXECUTE_READWRITE +# define PAGE_EXECUTE_READWRITE 0x40 +#endif +#ifndef PAGE_EXECUTE_WRITECOPY +# define PAGE_EXECUTE_WRITECOPY 0x80 +#endif +#ifndef FILE_MAP_EXECUTE +# define FILE_MAP_EXECUTE 0x20 +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Shared memory object mapping descriptor. + */ +typedef struct RTSHMEMMAPPINGDESC +{ + /** Number of references held to this mapping, 0 if the descriptor is free. */ + volatile uint32_t cMappings; + /** Pointer to the region mapping. */ + void *pvMapping; + /** Start offset */ + size_t offRegion; + /** Size of the region. */ + size_t cbRegion; + /** Access flags for this region .*/ + uint32_t fFlags; +} RTSHMEMMAPPINGDESC; +/** Pointer to a shared memory object mapping descriptor. */ +typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC; +/** Pointer to a constant shared memory object mapping descriptor. */ +typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC; + + +/** + * Internal shared memory object state. + */ +typedef struct RTSHMEMINT +{ + /** Magic value (RTSHMEM_MAGIC). */ + uint32_t u32Magic; + /** Flag whether this instance created the named shared memory object. */ + bool fCreate; + /** Handle to the underlying mapping object. */ + HANDLE hShmObj; + /** Size of the mapping object in bytes. */ + size_t cbMax; + /** Overall number of mappings active for this shared memory object. */ + volatile uint32_t cMappings; + /** Maximum number of mapping descriptors allocated. */ + uint32_t cMappingDescsMax; + /** Number of mapping descriptors used. */ + volatile uint32_t cMappingDescsUsed; + /** Array of mapping descriptors - variable in size. */ + RTSHMEMMAPPINGDESC aMappingDescs[1]; +} RTSHMEMINT; +/** Pointer to the internal shared memory object state. */ +typedef RTSHMEMINT *PRTSHMEMINT; + + + + +/** + * Returns a mapping descriptor matching the given region properties or NULL if none was found. + * + * @returns Pointer to the matching mapping descriptor or NULL if not found. + * @param pThis Pointer to the shared memory object instance. + * @param offRegion Offset into the shared memory object to start mapping at. + * @param cbRegion Size of the region to map. + * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines. + */ +DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags) +{ + for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++) + { + if ( pThis->aMappingDescs[i].offRegion == offRegion + && pThis->aMappingDescs[i].cbRegion == cbRegion + && pThis->aMappingDescs[i].fFlags == fFlags) + return &pThis->aMappingDescs[i]; + } + + return NULL; +} + + +RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint) +{ + AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszName, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE); + AssertReturn(cbMax > 0 || !(fFlags & RTSHMEM_O_F_CREATE), VERR_NOT_SUPPORTED); + + if (fFlags & RTSHMEM_O_F_TRUNCATE) + return VERR_NOT_SUPPORTED; + + /* + * The executable access was introduced with Windows XP SP2 and Windows Server 2003 SP1, + * PAGE_EXECUTE_WRITECOPY was not available until Windows Vista SP1. + * Allow execute mappings only starting from Windows 7 to keep the checks simple here (lazy coder). + */ + if ( (fFlags & RTSHMEM_O_F_MAYBE_EXEC) + && g_enmWinVer < kRTWinOSType_7) + return VERR_NOT_SUPPORTED; + + cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint; + int rc = VINF_SUCCESS; + PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint])); + if (RT_LIKELY(pThis)) + { + pThis->u32Magic = RTSHMEM_MAGIC; + /*pThis->fCreate = false; */ + /*pThis->cMappings = 0; */ + pThis->cMappingDescsMax = cMappingsHint; + /*pThis->cMappingDescsUsed = 0; */ + /* Construct the filename, always use the local namespace, global requires special privileges. */ + char szName[RTPATH_MAX]; + ssize_t cch = RTStrPrintf2(&szName[0], sizeof(szName), "Local\\%s", pszName); + if (cch > 0) + { + PRTUTF16 pwszName = NULL; + rc = RTStrToUtf16Ex(&szName[0], RTSTR_MAX, &pwszName, 0, NULL); + if (RT_SUCCESS(rc)) + { + if (fFlags & RTSHMEM_O_F_CREATE) + { +#if HC_ARCH_BITS == 64 + DWORD dwSzMaxHigh = cbMax >> 32; +#elif HC_ARCH_BITS == 32 + DWORD dwSzMaxHigh = 0; +#else +# error "Port me" +#endif + DWORD dwSzMaxLow = cbMax & UINT32_C(0xffffffff); + DWORD fProt = 0; + + if (fFlags & RTSHMEM_O_F_MAYBE_EXEC) + { + if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ) + fProt |= PAGE_EXECUTE_READ; + else + fProt |= PAGE_EXECUTE_READWRITE; + } + else + { + if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ) + fProt |= PAGE_READONLY; + else + fProt |= PAGE_READWRITE; + } + pThis->hShmObj = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, fProt, + dwSzMaxHigh, dwSzMaxLow, pwszName); + } + else + { + DWORD fProt = SECTION_QUERY; + if (fFlags & RTSHMEM_O_F_MAYBE_EXEC) + fProt |= FILE_MAP_EXECUTE; + if (fFlags & RTSHMEM_O_F_READ) + fProt |= FILE_MAP_READ; + if (fFlags & RTSHMEM_O_F_WRITE) + fProt |= FILE_MAP_WRITE; + + pThis->hShmObj = OpenFileMappingW(fProt, FALSE, pwszName); + } + RTUtf16Free(pwszName); + if (pThis->hShmObj != NULL) + { + *phShMem = pThis; + return VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + } + else + rc = VERR_BUFFER_OVERFLOW; + + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTShMemClose(RTSHMEM hShMem) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->cMappings, VERR_INVALID_STATE); + + int rc = VINF_SUCCESS; + if (CloseHandle(pThis->hShmObj)) + { + pThis->u32Magic = RTSHMEM_MAGIC_DEAD; + RTMemFree(pThis); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + return rc; +} + + +RTDECL(int) RTShMemDelete(const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(*pszName != '\0', VERR_INVALID_PARAMETER); + + return VERR_NOT_SUPPORTED; +} + + +RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, 0); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0); + + return pThis->cMappings; +} + + +RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE); + AssertReturn(!pThis->cMappings, VERR_INVALID_STATE); + AssertReturn(cbMem, VERR_NOT_SUPPORTED); + + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + SECTION_BASIC_INFORMATION SecInf; + SIZE_T cbRet; + NTSTATUS rcNt = NtQuerySection(pThis->hShmObj, SectionBasicInformation, &SecInf, sizeof(SecInf), &cbRet); + if (NT_SUCCESS(rcNt)) + { + AssertReturn(cbRet == sizeof(SecInf), VERR_INTERNAL_ERROR); +#if HC_ARCH_BITS == 32 + AssertReturn(SecInf.MaximumSize.HighPart == 0, VERR_INTERNAL_ERROR_2); + *pcbMem = SecInf.MaximumSize.LowPart; +#elif HC_ARCH_BITS == 64 + *pcbMem = SecInf.MaximumSize.QuadPart; +#else +# error "Port me" +#endif + } + else + rc = RTErrConvertFromNtStatus(rcNt); + + return rc; +} + + +RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(ppv, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER); + + /* See comment in RTShMemOpen(). */ + if ( (fFlags & RTSHMEM_MAP_F_EXEC) + && g_enmWinVer < kRTWinOSType_7) + return VERR_NOT_SUPPORTED; + + /* Try to find a mapping with compatible parameters first. */ + PRTSHMEMMAPPINGDESC pMappingDesc = NULL; + for (uint32_t iTry = 0; iTry < 10; iTry++) + { + pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags); + if (!pMappingDesc) + break; + + /* Increase the mapping count and check that the region is still accessible by us. */ + if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1 + && pMappingDesc->offRegion == offRegion + && pMappingDesc->cbRegion == cbRegion + && pMappingDesc->fFlags == fFlags) + break; + /* Mapping was freed inbetween, next round. */ + } + + int rc = VINF_SUCCESS; + if (!pMappingDesc) + { + /* Find an empty region descriptor and map the region. */ + for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++) + { + if (!pThis->aMappingDescs[i].cMappings) + { + pMappingDesc = &pThis->aMappingDescs[i]; + + /* Try to grab this one. */ + if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1) + break; + + /* Somebody raced us, drop reference and continue. */ + ASMAtomicDecU32(&pMappingDesc->cMappings); + pMappingDesc = NULL; + } + } + + if (RT_LIKELY(pMappingDesc)) + { + /* Try to map it. */ + DWORD fProt = 0; + DWORD offLow = offRegion & UINT32_C(0xffffffff); +#if HC_ARCH_BITS == 64 + DWORD offHigh = offRegion >> 32; +#elif HC_ARCH_BITS == 32 + DWORD offHigh = 0; +#else +# error "Port me" +#endif + if (fFlags & RTSHMEM_MAP_F_READ) + fProt |= FILE_MAP_READ; + if (fFlags & RTSHMEM_MAP_F_WRITE) + fProt |= FILE_MAP_WRITE; + if (fFlags & RTSHMEM_MAP_F_EXEC) + fProt |= FILE_MAP_EXECUTE; + if (fFlags & RTSHMEM_MAP_F_COW) + fProt |= FILE_MAP_COPY; + + void *pv = MapViewOfFile(pThis->hShmObj, fProt, offHigh, offLow, cbRegion); + if (pv != NULL) + { + pMappingDesc->pvMapping = pv; + pMappingDesc->offRegion = offRegion; + pMappingDesc->cbRegion = cbRegion; + pMappingDesc->fFlags = fFlags; + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + ASMAtomicDecU32(&pMappingDesc->cMappings); + } + } + else + rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED; + } + + if (RT_SUCCESS(rc)) + { + *ppv = pMappingDesc->pvMapping; + ASMAtomicIncU32(&pThis->cMappings); + } + + return rc; +} + + +RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv) +{ + PRTSHMEMINT pThis = hShMem; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pv, VERR_INVALID_PARAMETER); + + /* Find the mapping descriptor by the given region address. */ + PRTSHMEMMAPPINGDESC pMappingDesc = NULL; + for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++) + { + if (pThis->aMappingDescs[i].pvMapping == pv) + { + pMappingDesc = &pThis->aMappingDescs[i]; + break; + } + } + + AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + if (!ASMAtomicDecU32(&pMappingDesc->cMappings)) + { + /* Last mapping of this region was unmapped, so do the real unmapping now. */ + if (UnmapViewOfFile(pv)) + { + ASMAtomicDecU32(&pThis->cMappingDescsUsed); + ASMAtomicDecU32(&pThis->cMappings); + } + else + { + ASMAtomicIncU32(&pMappingDesc->cMappings); + rc = RTErrConvertFromWin32(GetLastError()); + } + } + + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/symlink-win.cpp b/src/VBox/Runtime/r3/win/symlink-win.cpp new file mode 100644 index 00000000..a0c5f1e2 --- /dev/null +++ b/src/VBox/Runtime/r3/win/symlink-win.cpp @@ -0,0 +1,367 @@ +/* $Id: symlink-win.cpp $ */ +/** @file + * IPRT - Symbolic Links, Windows. + */ + +/* + * 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_SYMLINK +#include <iprt/win/windows.h> + +#include <iprt/symlink.h> +#include "internal-r3-win.h" + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/path.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include "internal/path.h" + + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct MY_REPARSE_DATA_BUFFER +{ + ULONG ReparseTag; +#define MY_IO_REPARSE_TAG_SYMLINK 0xa000000c +#define MY_IO_REPARSE_TAG_MOUNT_POINT 0xa0000003 + + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; +#define MY_SYMLINK_FLAG_RELATIVE 1 + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} MY_REPARSE_DATA_BUFFER; +#define MY_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +RTDECL(bool) RTSymlinkExists(const char *pszSymlink) +{ + bool fRc = false; + RTFSOBJINFO ObjInfo; + int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); + if (RT_SUCCESS(rc)) + fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode); + + LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc)); + return fRc; +} + + +RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink) +{ + bool fRc = false; + RTFSOBJINFO ObjInfo; + int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK); + if (RT_SUCCESS(rc)) + { + fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode); + if (fRc) + { + rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); + fRc = !RT_SUCCESS_NP(rc); + } + } + + LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc)); + return fRc; +} + + +RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate) +{ + /* + * Validate the input. + */ + AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER); + AssertPtrReturn(pszTarget, VERR_INVALID_POINTER); + RT_NOREF_PV(fCreate); + + /* + * Resolve the API. + */ + typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD); + static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL; + static bool s_fTried = FALSE; + if (!s_fTried) + { + PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(g_hModKernel32, "CreateSymbolicLinkW"); + if (pfn) + s_pfnCreateSymbolicLinkW = pfn; + s_fTried = true; + } + if (!s_pfnCreateSymbolicLinkW) + { + LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n", + pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate)); + return VERR_NOT_SUPPORTED; + } + + /* + * Convert the paths. + */ + PRTUTF16 pwszNativeSymlink; + int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszNativeTarget; + rc = RTPathWinFromUtf8(&pwszNativeTarget, pszTarget, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* The link target path must use backslashes to work reliably. */ + RTUTF16 wc; + PRTUTF16 pwsz = pwszNativeTarget; + while ((wc = *pwsz) != '\0') + { + if (wc == '/') + *pwsz = '\\'; + pwsz++; + } + + /* + * Massage the target path, determin the link type. + */ + size_t cchTarget = strlen(pszTarget); + size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget); +#if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */ + if ( cchTarget > RT_MIN(cchVolSpecTarget, 1) + && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) + { + size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget); + size_t offFromEnd = 1; + while ( offFromEnd < cchTarget + && cchTarget - offFromEnd >= cchVolSpecTarget + && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd])) + { + Assert(offFromEnd < cwcNativeTarget); + pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0; + offFromEnd++; + } + } +#endif + + if (enmType == RTSYMLINKTYPE_UNKNOWN) + { + if ( cchTarget > cchVolSpecTarget + && RTPATH_IS_SLASH(pszTarget[cchTarget - 1])) + enmType = RTSYMLINKTYPE_DIR; + else if (cchVolSpecTarget) + { + /** @todo this is subject to sharing violations. */ + DWORD dwAttr = GetFileAttributesW(pwszNativeTarget); + if ( dwAttr != INVALID_FILE_ATTRIBUTES + && (dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + enmType = RTSYMLINKTYPE_DIR; + } + else + { + /** @todo Join the symlink directory with the target and + * look up the attributes on that. -lazy bird. */ + } + } + + /* + * Create the link. + */ + if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + + RTPathWinFree(pwszNativeTarget); + } + RTPathWinFree(pwszNativeSymlink); + } + + LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc)); + return rc; +} + + +RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete) +{ + RT_NOREF_PV(fDelete); + + /* + * Convert the path. + */ + PRTUTF16 pwszNativeSymlink; + int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * We have to use different APIs depending on whether this is a + * directory or file link. This means we're subject to one more race + * than on posix at the moment. We could probably avoid this though, + * if we wanted to go talk with the native API layer below Win32... + */ + DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink); + if (dwAttr != INVALID_FILE_ATTRIBUTES) + { + if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) + { + BOOL fRc; + if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) + fRc = RemoveDirectoryW(pwszNativeSymlink); + else + fRc = DeleteFileW(pwszNativeSymlink); + if (fRc) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_NOT_SYMLINK; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + RTPathWinFree(pwszNativeSymlink); + } + + LogFlow(("RTSymlinkDelete(%p={%s}, %#x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc)); + return rc; +} + + +RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead) +{ + RT_NOREF_PV(fRead); + + char *pszMyTarget; + int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget); + if (RT_SUCCESS(rc)) + { + rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget); + RTStrFree(pszMyTarget); + } + LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc)); + return rc; +} + + +RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget) +{ + AssertPtr(ppszTarget); + PRTUTF16 pwszNativeSymlink; + int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + HANDLE hSymlink = CreateFileW(pwszNativeSymlink, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (hSymlink != INVALID_HANDLE_VALUE) + { + DWORD cbReturned = 0; + union + { + MY_REPARSE_DATA_BUFFER Buf; + uint8_t abBuf[16*_1K + sizeof(WCHAR)]; + } u; + if (DeviceIoControl(hSymlink, + MY_FSCTL_GET_REPARSE_POINT, + NULL /*pInBuffer */, + 0 /*cbInBuffer */, + &u.Buf, + sizeof(u) - sizeof(WCHAR), + &cbReturned, + NULL /*pOverlapped*/)) + { + if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK) + { + PWCHAR pwszTarget = &u.Buf.SymbolicLinkReparseBuffer.PathBuffer[0]; + pwszTarget += u.Buf.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2; + pwszTarget[u.Buf.SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0; + if ( !(u.Buf.SymbolicLinkReparseBuffer.Flags & MY_SYMLINK_FLAG_RELATIVE) + && pwszTarget[0] == '\\' + && pwszTarget[1] == '?' + && pwszTarget[2] == '?' + && pwszTarget[3] == '\\' + && pwszTarget[4] != 0 + ) + pwszTarget += 4; + rc = RTUtf16ToUtf8(pwszTarget, ppszTarget); + } + else + rc = VERR_NOT_SYMLINK; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + CloseHandle(hSymlink); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + RTPathWinFree(pwszNativeSymlink); + } + + if (RT_SUCCESS(rc)) + LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget)); + else + LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp new file mode 100644 index 00000000..30088667 --- /dev/null +++ b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp @@ -0,0 +1,68 @@ +/* $Id: system-get-nt-xxx-win.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 "internal/iprt.h" +#include <iprt/win/windows.h> + +#include "internal-r3-win.h" +#include <iprt/system.h> +#include <iprt/assert.h> + + +RTDECL(uint32_t) RTSystemGetNtBuildNo(void) +{ + Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0); + return g_WinOsInfoEx.dwBuildNumber; +} + + +RTDECL(uint64_t) RTSystemGetNtVersion(void) +{ + Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0); + return RTSYSTEM_MAKE_NT_VERSION(g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber); +} + + +RTDECL(uint8_t) RTSystemGetNtProductType(void) +{ + Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0); + return g_WinOsInfoEx.wProductType; /* It's a byte, not a word as 'w' normally indicates. (Baka Maikurosofuto!) */ +} + diff --git a/src/VBox/Runtime/r3/win/thread-win.cpp b/src/VBox/Runtime/r3/win/thread-win.cpp new file mode 100644 index 00000000..80bd3879 --- /dev/null +++ b/src/VBox/Runtime/r3/win/thread-win.cpp @@ -0,0 +1,588 @@ +/* $Id: thread-win.cpp $ */ +/** @file + * IPRT - Threads, Windows. + */ + +/* + * 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/nt/nt-and-windows.h> + +#ifndef IPRT_NO_CRT +# include <errno.h> +# include <process.h> +#endif + +#include <iprt/thread.h> +#include "internal/iprt.h" + +#include <iprt/asm-amd64-x86.h> +#include <iprt/assert.h> +#include <iprt/cpuset.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include "internal/thread.h" +#include "internal-r3-win.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** SetThreadDescription */ +typedef HRESULT (WINAPI *PFNSETTHREADDESCRIPTION)(HANDLE hThread, WCHAR *pwszName); /* Since W10 1607 */ + +/** CoInitializeEx */ +typedef HRESULT (WINAPI *PFNCOINITIALIZEEX)(LPVOID, DWORD); +/** CoUninitialize */ +typedef void (WINAPI *PFNCOUNINITIALIZE)(void); +/** OleUninitialize */ +typedef void (WINAPI *PFNOLEUNINITIALIZE)(void); + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The TLS index allocated for storing the RTTHREADINT pointer. */ +static DWORD g_dwSelfTLS = TLS_OUT_OF_INDEXES; +/** Pointer to SetThreadDescription (KERNEL32.DLL) if available. */ +static PFNSETTHREADDESCRIPTION g_pfnSetThreadDescription = NULL; + +/** Pointer to CoInitializeEx (OLE32.DLL / combase.dll) if available. */ +static PFNCOINITIALIZEEX volatile g_pfnCoInitializeEx = NULL; +/** Pointer to CoUninitialize (OLE32.DLL / combase.dll) if available. */ +static PFNCOUNINITIALIZE volatile g_pfnCoUninitialize = NULL; +/** Pointer to OleUninitialize (OLE32.DLL / combase.dll) if available. */ +static PFNOLEUNINITIALIZE volatile g_pfnOleUninitialize = NULL; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName); +DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread); + + +DECLHIDDEN(int) rtThreadNativeInit(void) +{ + g_dwSelfTLS = TlsAlloc(); + if (g_dwSelfTLS == TLS_OUT_OF_INDEXES) + return VERR_NO_TLS_FOR_SELF; + + g_pfnSetThreadDescription = (PFNSETTHREADDESCRIPTION)GetProcAddress(g_hModKernel32, "SetThreadDescription"); + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void) +{ + /* nothing to do here. */ +} + + +DECLHIDDEN(void) rtThreadNativeDetach(void) +{ + /* + * Deal with alien threads. + */ + PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS); + if ( pThread + && (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN)) + { + rtThreadTerminate(pThread, 0); + TlsSetValue(g_dwSelfTLS, NULL); + } +} + + +DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread) +{ + if (pThread == (PRTTHREADINT)TlsGetValue(g_dwSelfTLS)) + TlsSetValue(g_dwSelfTLS, NULL); + + if ((HANDLE)pThread->hThread != INVALID_HANDLE_VALUE) + { + CloseHandle((HANDLE)pThread->hThread); + pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE; + } +} + + +DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread) +{ + if (!TlsSetValue(g_dwSelfTLS, pThread)) + return VERR_FAILED_TO_SET_SELF_TLS; + rtThreadWinSetThreadName(pThread, GetCurrentThreadId()); + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtThreadNativeInformDebugger(PRTTHREADINT pThread) +{ + rtThreadWinTellDebuggerThreadName((uint32_t)(uintptr_t)pThread->Core.Key, pThread->szName); +} + + +/** + * Communicates the thread name to the debugger, if we're begin debugged that + * is. + * + * See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for debugger + * interface details. + * + * @param idThread The thread ID. UINT32_MAX for current thread. + * @param pszName The name. + */ +static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName) +{ + struct + { + uint32_t uType; + const char *pszName; + uint32_t idThread; + uint32_t fFlags; + } Pkg = { 0x1000, pszName, idThread, 0 }; + __try + { + RaiseException(0x406d1388, 0, sizeof(Pkg)/sizeof(ULONG_PTR), (ULONG_PTR *)&Pkg); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + + } +} + + +/** + * Sets the thread name as best as we can. + */ +DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread) +{ + if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent()) + rtThreadWinTellDebuggerThreadName(idThread, &pThread->szName[0]); + + /* The SetThreadDescription API introduced in windows 10 1607 / server 2016 + allows setting the thread name while the debugger isn't attached. Works + with WinDbgX, VisualStudio 2017 v15.6+, and presumeably some recent windbg + version. */ + if (g_pfnSetThreadDescription) + { + /* The name should be ASCII, so we just need to expand 'char' to 'WCHAR'. */ + WCHAR wszName[RTTHREAD_NAME_LEN]; + for (size_t i = 0; i < RTTHREAD_NAME_LEN; i++) + wszName[i] = pThread->szName[i]; + + HRESULT hrc = g_pfnSetThreadDescription(GetCurrentThread(), wszName); + Assert(SUCCEEDED(hrc)); RT_NOREF(hrc); + } +} + + +/** + * Bitch about dangling COM and OLE references, dispose of them + * afterwards so we don't end up deadlocked somewhere below + * OLE32!DllMain. + */ +static void rtThreadNativeUninitComAndOle(void) +{ +#if 1 /* experimental code */ + /* + * Read the counters. + */ + struct MySOleTlsData + { + void *apvReserved0[2]; /**< x86=0x00 W7/64=0x00 */ + DWORD adwReserved0[3]; /**< x86=0x08 W7/64=0x10 */ + void *apvReserved1[1]; /**< x86=0x14 W7/64=0x20 */ + DWORD cComInits; /**< x86=0x18 W7/64=0x28 */ + DWORD cOleInits; /**< x86=0x1c W7/64=0x2c */ + DWORD dwReserved1; /**< x86=0x20 W7/64=0x30 */ + void *apvReserved2[4]; /**< x86=0x24 W7/64=0x38 */ + DWORD adwReserved2[1]; /**< x86=0x34 W7/64=0x58 */ + void *pvCurrentCtx; /**< x86=0x38 W7/64=0x60 */ + IUnknown *pCallState; /**< x86=0x3c W7/64=0x68 */ + } *pOleTlsData = NULL; /* outside the try/except for debugging */ + DWORD cComInits = 0; + DWORD cOleInits = 0; + __try + { + void *pvTeb = NtCurrentTeb(); +# ifdef RT_ARCH_AMD64 + pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x1758); /*TEB.ReservedForOle*/ +# elif RT_ARCH_X86 + pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x0f80); /*TEB.ReservedForOle*/ +# else +# error "Port me!" +# endif + if (pOleTlsData) + { + cComInits = pOleTlsData->cComInits; + cOleInits = pOleTlsData->cOleInits; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + AssertFailedReturnVoid(); + } + + /* + * Assert sanity. If any of these breaks, the structure layout above is + * probably not correct any longer. + */ + AssertMsgReturnVoid(cComInits < 1000, ("%u (%#x)\n", cComInits, cComInits)); + AssertMsgReturnVoid(cOleInits < 1000, ("%u (%#x)\n", cOleInits, cOleInits)); + AssertMsgReturnVoid(cComInits >= cOleInits, ("cComInits=%#x cOleInits=%#x\n", cComInits, cOleInits)); + + /* + * Do the uninitializing. + */ + if (cComInits) + { + AssertMsgFailed(("cComInits=%u (%#x) cOleInits=%u (%#x) - dangling COM/OLE inits!\n", + cComInits, cComInits, cOleInits, cOleInits)); + + PFNOLEUNINITIALIZE pfnOleUninitialize = g_pfnOleUninitialize; + PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize; + if (pfnCoUninitialize && pfnOleUninitialize) + { /* likely */ } + else + { + HMODULE hOle32 = GetModuleHandle("ole32.dll"); + AssertReturnVoid(hOle32 != NULL); + + pfnOleUninitialize = (PFNOLEUNINITIALIZE)GetProcAddress(hOle32, "OleUninitialize"); + AssertReturnVoid(pfnOleUninitialize); + + pfnCoUninitialize = (PFNCOUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize"); + AssertReturnVoid(pfnCoUninitialize); + } + + while (cOleInits-- > 0) + { + pfnOleUninitialize(); + cComInits--; + } + + while (cComInits-- > 0) + pfnCoUninitialize(); + } +#endif +} + + +/** + * Implements the RTTHREADFLAGS_COM_MTA and RTTHREADFLAGS_COM_STA flags. + * + * @returns true if COM uninitialization should be done, false if not. + * @param fFlags The thread flags. + */ +static bool rtThreadNativeWinCoInitialize(unsigned fFlags) +{ + /* + * Resolve the ole32 init and uninit functions dynamically. + */ + PFNCOINITIALIZEEX pfnCoInitializeEx = g_pfnCoInitializeEx; + PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize; + if (pfnCoInitializeEx && pfnCoUninitialize) + { /* likely */ } + else + { + RTLDRMOD hModOle32 = NIL_RTLDRMOD; + int rc = RTLdrLoadSystem("ole32.dll", true /*fNoUnload*/, &hModOle32); + AssertRCReturn(rc, false); + + PFNOLEUNINITIALIZE pfnOleUninitialize; + pfnOleUninitialize = (PFNOLEUNINITIALIZE)RTLdrGetFunction(hModOle32, "OleUninitialize"); + pfnCoUninitialize = (PFNCOUNINITIALIZE )RTLdrGetFunction(hModOle32, "CoUninitialize"); + pfnCoInitializeEx = (PFNCOINITIALIZEEX )RTLdrGetFunction(hModOle32, "CoInitializeEx"); + + RTLdrClose(hModOle32); + AssertReturn(pfnCoInitializeEx && pfnCoUninitialize, false); + + if (pfnOleUninitialize && !g_pfnOleUninitialize) + g_pfnOleUninitialize = pfnOleUninitialize; + g_pfnCoInitializeEx = pfnCoInitializeEx; + g_pfnCoUninitialize = pfnCoUninitialize; + } + + /* + * Do the initializating. + */ + DWORD fComInit; + if (fFlags & RTTHREADFLAGS_COM_MTA) + fComInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE; + else + fComInit = COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY; + HRESULT hrc = pfnCoInitializeEx(NULL, fComInit); + AssertMsg(SUCCEEDED(hrc), ("%Rhrc fComInit=%#x\n", hrc, fComInit)); + return SUCCEEDED(hrc); +} + + +/** + * Wrapper which unpacks the param stuff and calls thread function. + */ +#ifndef IPRT_NO_CRT +static unsigned __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF +#else +static DWORD __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF +#endif +{ + DWORD dwThreadId = GetCurrentThreadId(); + PRTTHREADINT pThread = (PRTTHREADINT)pvArgs; + + if (!TlsSetValue(g_dwSelfTLS, pThread)) + AssertReleaseMsgFailed(("failed to set self TLS. lasterr=%d thread '%s'\n", GetLastError(), pThread->szName)); + rtThreadWinSetThreadName(pThread, dwThreadId); + + bool fUninitCom = (pThread->fFlags & (RTTHREADFLAGS_COM_MTA | RTTHREADFLAGS_COM_STA)) != 0; + if (fUninitCom) + fUninitCom = rtThreadNativeWinCoInitialize(pThread->fFlags); + + int rc = rtThreadMain(pThread, dwThreadId, &pThread->szName[0]); + + TlsSetValue(g_dwSelfTLS, NULL); /* rtThreadMain already released the structure. */ + + if (fUninitCom && g_pfnCoUninitialize) + g_pfnCoUninitialize(); + + rtThreadNativeUninitComAndOle(); +#ifndef IPRT_NO_CRT + _endthreadex(rc); + return rc; /* not reached */ +#else + for (;;) + ExitThread(rc); +#endif +} + + +DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread) +{ + AssertReturn(pThread->cbStack < ~(unsigned)0, VERR_INVALID_PARAMETER); + + /* + * If a stack size is given, make sure it's not a multiple of 64KB so that we + * get one or more pages for overflow protection. (ASSUMES 64KB alloc align.) + */ + unsigned cbStack = (unsigned)pThread->cbStack; + if (cbStack > 0 && RT_ALIGN_T(cbStack, _64K, unsigned) == cbStack) + cbStack += PAGE_SIZE; + + /* + * Create the thread. + */ + pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE; +#ifndef IPRT_NO_CRT + unsigned uThreadId = 0; + uintptr_t hThread = _beginthreadex(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &uThreadId); + if (hThread != 0 && hThread != ~0U) + { + pThread->hThread = hThread; + *pNativeThread = uThreadId; + return VINF_SUCCESS; + } + return RTErrConvertFromErrno(errno); +#else + DWORD idThread = 0; + HANDLE hThread = CreateThread(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &idThread); + if (hThread != NULL) + { + pThread->hThread = (uintptr_t)hThread; + *pNativeThread = idThread; + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +#endif +} + + +DECLHIDDEN(bool) rtThreadNativeIsAliveKludge(PRTTHREADINT pThread) +{ + PPEB_COMMON pPeb = NtCurrentPeb(); + if (!pPeb || !pPeb->Ldr || !pPeb->Ldr->ShutdownInProgress) + return true; + DWORD rcWait = WaitForSingleObject((HANDLE)pThread->hThread, 0); + return rcWait != WAIT_OBJECT_0; +} + + +RTDECL(RTTHREAD) RTThreadSelf(void) +{ + PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS); + /** @todo import alien threads ? */ + return pThread; +} + + +#if 0 /* noone is using this ... */ +/** + * Returns the processor number the current thread was running on during this call + * + * @returns processor nr + */ +static int rtThreadGetCurrentProcessorNumber(void) +{ + static bool fInitialized = false; + static DWORD (WINAPI *pfnGetCurrentProcessorNumber)(void) = NULL; + if (!fInitialized) + { + HMODULE hmodKernel32 = GetModuleHandle("kernel32.dll"); + if (hmodKernel32) + pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)(void))GetProcAddress(hmodKernel32, "GetCurrentProcessorNumber"); + fInitialized = true; + } + if (pfnGetCurrentProcessorNumber) + return pfnGetCurrentProcessorNumber(); + return -1; +} +#endif + + +RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet) +{ + /* The affinity functionality was added in NT 3.50, so we resolve the APIs + dynamically to be able to run on NT 3.1. */ + if (g_pfnSetThreadAffinityMask) + { + DWORD_PTR fNewMask = pCpuSet ? RTCpuSetToU64(pCpuSet) : ~(DWORD_PTR)0; + DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(GetCurrentThread(), fNewMask); + if (dwRet) + return VINF_SUCCESS; + + int iLastError = GetLastError(); + AssertMsgFailed(("SetThreadAffinityMask failed, LastError=%d\n", iLastError)); + return RTErrConvertFromWin32(iLastError); + } + return VERR_NOT_SUPPORTED; +} + + +RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet) +{ + /* The affinity functionality was added in NT 3.50, so we resolve the APIs + dynamically to be able to run on NT 3.1. */ + if ( g_pfnSetThreadAffinityMask + && g_pfnGetProcessAffinityMask) + { + /* + * Haven't found no query api, but the set api returns the old mask, so let's use that. + */ + DWORD_PTR dwIgnored; + DWORD_PTR dwProcAff = 0; + if (g_pfnGetProcessAffinityMask(GetCurrentProcess(), &dwProcAff, &dwIgnored)) + { + HANDLE hThread = GetCurrentThread(); + DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(hThread, dwProcAff); + if (dwRet) + { + DWORD_PTR dwSet = g_pfnSetThreadAffinityMask(hThread, dwRet); + Assert(dwSet == dwProcAff); NOREF(dwRet); + + RTCpuSetFromU64(pCpuSet, (uint64_t)dwSet); + return VINF_SUCCESS; + } + } + + int iLastError = GetLastError(); + AssertMsgFailed(("SetThreadAffinityMask or GetProcessAffinityMask failed, LastError=%d\n", iLastError)); + return RTErrConvertFromWin32(iLastError); + } + return VERR_NOT_SUPPORTED; +} + + +RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime) +{ + uint64_t u64CreationTime, u64ExitTime, u64KernelTime, u64UserTime; + + if (GetThreadTimes(GetCurrentThread(), (LPFILETIME)&u64CreationTime, (LPFILETIME)&u64ExitTime, (LPFILETIME)&u64KernelTime, (LPFILETIME)&u64UserTime)) + { + *pKernelTime = u64KernelTime / 10000; /* GetThreadTimes returns time in 100 ns units */ + *pUserTime = u64UserTime / 10000; /* GetThreadTimes returns time in 100 ns units */ + return VINF_SUCCESS; + } + + int iLastError = GetLastError(); + AssertMsgFailed(("GetThreadTimes failed, LastError=%d\n", iLastError)); + return RTErrConvertFromWin32(iLastError); +} + + +/** + * Gets the native thread handle for a IPRT thread. + * + * @returns The thread handle. INVALID_HANDLE_VALUE on failure. + * @param hThread The IPRT thread handle. + * + * @note Windows only. + * @note Only valid after parent returns from the thread creation call. + */ +RTDECL(uintptr_t) RTThreadGetNativeHandle(RTTHREAD hThread) +{ + PRTTHREADINT pThread = rtThreadGet(hThread); + if (pThread) + { + uintptr_t hHandle = pThread->hThread; + rtThreadRelease(pThread); + return hHandle; + } + return (uintptr_t)INVALID_HANDLE_VALUE; +} +RT_EXPORT_SYMBOL(RTThreadGetNativeHandle); + + +RTDECL(int) RTThreadPoke(RTTHREAD hThread) +{ + AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER); + if (g_pfnNtAlertThread) + { + PRTTHREADINT pThread = rtThreadGet(hThread); + AssertReturn(pThread, VERR_INVALID_HANDLE); + + NTSTATUS rcNt = g_pfnNtAlertThread((HANDLE)pThread->hThread); + + rtThreadRelease(pThread); + if (NT_SUCCESS(rcNt)) + return VINF_SUCCESS; + return RTErrConvertFromNtStatus(rcNt); + } + return VERR_NOT_IMPLEMENTED; +} + diff --git a/src/VBox/Runtime/r3/win/thread2-win.cpp b/src/VBox/Runtime/r3/win/thread2-win.cpp new file mode 100644 index 00000000..f9874c96 --- /dev/null +++ b/src/VBox/Runtime/r3/win/thread2-win.cpp @@ -0,0 +1,84 @@ +/* $Id: thread2-win.cpp $ */ +/** @file + * IPRT - Threads part 2, Windows. + */ + +/* + * 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/win/windows.h> + +#include <iprt/thread.h> +#include "internal/iprt.h" + +#include <iprt/asm-amd64-x86.h> +#include <iprt/errcore.h> +#include <iprt/log.h> +#include "internal/thread.h" + + +RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void) +{ + return (RTNATIVETHREAD)GetCurrentThreadId(); +} + + +RTR3DECL(int) RTThreadSleep(RTMSINTERVAL cMillies) +{ + LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies)); + Sleep(cMillies); + LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", VINF_SUCCESS, cMillies)); + return VINF_SUCCESS; +} + + +RTR3DECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies) +{ + Sleep(cMillies); + return VINF_SUCCESS; +} + + +RTR3DECL(bool) RTThreadYield(void) +{ + uint64_t u64TS = ASMReadTSC(); + Sleep(0); + u64TS = ASMReadTSC() - u64TS; + bool fRc = u64TS > 1500; + LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS)); + return fRc; +} + diff --git a/src/VBox/Runtime/r3/win/time-win.cpp b/src/VBox/Runtime/r3/win/time-win.cpp new file mode 100644 index 00000000..09e42d8e --- /dev/null +++ b/src/VBox/Runtime/r3/win/time-win.cpp @@ -0,0 +1,228 @@ +/* $Id: time-win.cpp $ */ +/** @file + * IPRT - Time, Windows. + */ + +/* + * 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_TIME +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/time.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include "internal/time.h" +#include "internal-r3-win.h" + +/* + * Note! The selected time source be the exact same one as we use in kernel land! + */ +//#define USE_TICK_COUNT +//#define USE_PERFORMANCE_COUNTER +//# define USE_FILE_TIME +//#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64) +# define USE_INTERRUPT_TIME +//#else +//# define USE_TICK_COUNT +//#endif + + + +DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void) +{ +#if defined USE_TICK_COUNT + /* + * This would work if it didn't flip over every 49 (or so) days. + */ + return (uint64_t)GetTickCount() * RT_NS_1MS_64; + +#elif defined USE_PERFORMANCE_COUNTER + /* + * Slow and not derived from InterruptTime. + */ + static LARGE_INTEGER llFreq; + static unsigned uMult; + if (!llFreq.QuadPart) + { + if (!QueryPerformanceFrequency(&llFreq)) + return (uint64_t)GetTickCount() * RT_NS_1MS_64; + llFreq.QuadPart /= 1000; + uMult = 1000000; /* no math genius, but this seemed to help avoiding floating point. */ + } + + LARGE_INTEGER ll; + if (QueryPerformanceCounter(&ll)) + return (ll.QuadPart * uMult) / llFreq.QuadPart; + return (uint64_t)GetTickCount() * RT_NS_1MS_64; + +#elif defined USE_FILE_TIME + /* + * This is SystemTime not InterruptTime. + */ + uint64_t u64; /* manual say larger integer, should be safe to assume it's the same. */ + GetSystemTimeAsFileTime((LPFILETIME)&u64); + return u64 * 100; + +#elif defined USE_INTERRUPT_TIME + /* + * Use interrupt time if we can (not possible on NT 3.1). + * Note! We cannot entirely depend on g_enmWinVer here as we're likely to + * get called before IPRT is initialized. Ditto g_hModNtDll. + */ + static PFNRTLGETINTERRUPTTIMEPRECISE s_pfnRtlGetInterruptTimePrecise = NULL; + static int volatile s_iCanUseUserSharedData = -1; + int iCanUseUserSharedData = s_iCanUseUserSharedData; + if (iCanUseUserSharedData != -1) + { /* likely */ } + else + { + /* We may be called before g_enmWinVer has been initialized. */ + if (g_enmWinVer != kRTWinOSType_UNKNOWN) + iCanUseUserSharedData = g_enmWinVer > kRTWinOSType_NT310; + else + { + DWORD dwVer = GetVersion(); + iCanUseUserSharedData = (dwVer & 0xff) != 3 || ((dwVer >> 8) & 0xff) >= 50; + } + if (iCanUseUserSharedData != 0) + { + FARPROC pfn = GetProcAddress(g_hModNtDll ? g_hModNtDll : GetModuleHandleW(L"ntdll"), "RtlGetInterruptTimePrecise"); + if (pfn != NULL) + { + ASMAtomicWritePtr(&s_pfnRtlGetInterruptTimePrecise, pfn); + iCanUseUserSharedData = 42; + } + } + s_iCanUseUserSharedData = iCanUseUserSharedData; + } + + if (iCanUseUserSharedData != 0) + { + LARGE_INTEGER Time; + if (iCanUseUserSharedData == 42) + { + uint64_t iIgnored; + Time.QuadPart = s_pfnRtlGetInterruptTimePrecise(&iIgnored); + } + else + { + PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA; + do + { + Time.HighPart = pUserSharedData->InterruptTime.High1Time; + Time.LowPart = pUserSharedData->InterruptTime.LowPart; + } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart); + } + + return (uint64_t)Time.QuadPart * 100; + } + + return (uint64_t)GetTickCount() * RT_NS_1MS_64; + +#else +# error "Must select a method bright guy!" +#endif +} + + +RTDECL(uint64_t) RTTimeSystemNanoTS(void) +{ + return rtTimeGetSystemNanoTS(); +} + + +RTDECL(uint64_t) RTTimeSystemMilliTS(void) +{ + return rtTimeGetSystemNanoTS() / RT_NS_1MS; +} + + +RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime) +{ + uint64_t u64; + AssertCompile(sizeof(u64) == sizeof(FILETIME)); + if (g_pfnGetSystemTimeAsFileTime) + g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64); + else + { + SYSTEMTIME SysTime = {0}; + GetSystemTime(&SysTime); + BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64); + Assert(fRet); RT_NOREF(fRet); + } + return RTTimeSpecSetNtTime(pTime, u64); + +} + + +RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime) +{ + uint64_t u64Local; + if (g_pfnGetSystemTimeAsFileTime) + { + uint64_t u64; + AssertCompile(sizeof(u64) == sizeof(FILETIME)); + g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64); + if (!FileTimeToLocalFileTime((FILETIME const *)&u64, (LPFILETIME)&u64Local)) + u64Local = u64; + } + else + { + SYSTEMTIME SysTime = {0}; + GetLocalTime(&SysTime); + BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64Local); + Assert(fRet); RT_NOREF(fRet); + } + return RTTimeSpecSetNtTime(pTime, u64Local); +} + + +RTDECL(int64_t) RTTimeLocalDeltaNano(void) +{ + /* + * UTC = local + Tzi.Bias; + * The bias is given in minutes. + */ + TIME_ZONE_INFORMATION Tzi; + Tzi.Bias = 0; + if (GetTimeZoneInformation(&Tzi) != TIME_ZONE_ID_INVALID) + return -(int64_t)Tzi.Bias * 60 * RT_NS_1SEC_64; + return 0; +} + diff --git a/src/VBox/Runtime/r3/win/time2-win.cpp b/src/VBox/Runtime/r3/win/time2-win.cpp new file mode 100644 index 00000000..d4f0db06 --- /dev/null +++ b/src/VBox/Runtime/r3/win/time2-win.cpp @@ -0,0 +1,164 @@ +/* $Id: time2-win.cpp $ */ +/** @file + * IPRT - Time, Windows. + */ + +/* + * 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_TIME +#include <iprt/win/windows.h> + +#include <iprt/time.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include "internal/time.h" + +#include "internal-r3-win.h" + + +RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime) +{ + FILETIME FileTime; + SYSTEMTIME SysTime; + if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTime, &FileTime), &SysTime)) + { + if (SetSystemTime(&SysTime)) + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +} + + +RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec) +{ + RTTIMESPEC LocalTime; + if (g_pfnSystemTimeToTzSpecificLocalTime) + { + /* + * FileTimeToLocalFileTime does not do the right thing, so we'll have + * to convert to system time and SystemTimeToTzSpecificLocalTime instead. + * + * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus + * we have to do the offUTC calculation using milliseconds and adjust + * u32Nanosecons by sub milliseconds digits. + */ + SYSTEMTIME SystemTimeIn; + FILETIME FileTime; + if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn)) + { + SYSTEMTIME SystemTimeOut; + if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut)) + { + if (SystemTimeToFileTime(&SystemTimeOut, &FileTime)) + { + RTTimeSpecSetNtFileTime(&LocalTime, &FileTime); + pTime = RTTimeExplode(pTime, &LocalTime); + if (pTime) + { + pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL; + pTime->offUTC = (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) / RT_MS_1MIN; + pTime->u32Nanosecond += RTTimeSpecGetNano(pTimeSpec) % RT_NS_1MS; + } + return pTime; + } + } + } + } + + /* + * The fallback is to use the current offset. + * (A better fallback would be to use the offset of the same time of the year.) + */ + LocalTime = *pTimeSpec; + int64_t cNsUtcOffset = RTTimeLocalDeltaNano(); + 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; +} + + +/** + * Gets the delta between UTC and local time at the given time. + * + * @code + * RTTIMESPEC LocalTime; + * RTTimeNow(&LocalTime); + * RTTimeSpecAddNano(&LocalTime, RTTimeLocalDeltaNanoFor(&LocalTime)); + * @endcode + * + * @param pTimeSpec The time spec giving the time to get the delta for. + * @returns Returns the nanosecond delta between UTC and local time. + */ +RTDECL(int64_t) RTTimeLocalDeltaNanoFor(PCRTTIMESPEC pTimeSpec) +{ + RTTIMESPEC LocalTime; + if (g_pfnSystemTimeToTzSpecificLocalTime) + { + /* + * FileTimeToLocalFileTime does not do the right thing, so we'll have + * to convert to system time and SystemTimeToTzSpecificLocalTime instead. + * + * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus + * we have to do the offUTC calculation using milliseconds and adjust + * u32Nanosecons by sub milliseconds digits. + */ + SYSTEMTIME SystemTimeIn; + FILETIME FileTime; + if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn)) + { + SYSTEMTIME SystemTimeOut; + if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut)) + { + if (SystemTimeToFileTime(&SystemTimeOut, &FileTime)) + { + RTTimeSpecSetNtFileTime(&LocalTime, &FileTime); + + return (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) * RT_NS_1MS; + } + } + } + } + + return RTTimeLocalDeltaNano(); +} + diff --git a/src/VBox/Runtime/r3/win/timer-win.cpp b/src/VBox/Runtime/r3/win/timer-win.cpp new file mode 100644 index 00000000..f8d4a520 --- /dev/null +++ b/src/VBox/Runtime/r3/win/timer-win.cpp @@ -0,0 +1,520 @@ +/* $Id: timer-win.cpp $ */ +/** @file + * IPRT - Timer. + */ + +/* + * 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_TIMER +#define _WIN32_WINNT 0x0500 +#include <iprt/win/windows.h> + +#include <iprt/timer.h> +#ifdef USE_CATCH_UP +# include <iprt/time.h> +#endif +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/thread.h> +#include <iprt/log.h> +#include <iprt/asm.h> +#include <iprt/semaphore.h> +#include <iprt/err.h> +#include "internal/magics.h" +#include "internal-r3-win.h" + + +/** Define the flag for creating a manual reset timer if not available in the SDK we are compiling with. */ +#ifndef CREATE_WAITABLE_TIMER_MANUAL_RESET +# define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x00000001 +#endif +/** Define the flag for high resolution timers, available since Windows 10 RS4 if not available. */ +#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +# define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002 +#endif + + +RT_C_DECLS_BEGIN +/* from sysinternals. */ +NTSYSAPI LONG NTAPI NtSetTimerResolution(IN ULONG DesiredResolution, IN BOOLEAN SetResolution, OUT PULONG CurrentResolution); +NTSYSAPI LONG NTAPI NtQueryTimerResolution(OUT PULONG MaximumResolution, OUT PULONG MinimumResolution, OUT PULONG CurrentResolution); +RT_C_DECLS_END + + +/********************************************************************************************************************************* +* 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. */ + bool volatile fSuspended; + /** Flag indicating that the timer has been destroyed. */ + bool volatile fDestroyed; + /** User argument. */ + void *pvUser; + /** Callback. */ + PFNRTTIMER pfnTimer; + /** The current tick. */ + uint64_t iTick; + /** The timer interval. 0 if one-shot. */ + uint64_t u64NanoInterval; + /** The first shot interval. 0 if ASAP. */ + uint64_t volatile u64NanoFirst; + /** Time handle. */ + HANDLE hTimer; + /** USE_CATCH_UP: ns time of the next tick. + * !USE_CATCH_UP: -uMilliesInterval * 10000 */ + LARGE_INTEGER llNext; + /** The thread handle of the timer thread. */ + RTTHREAD Thread; + /** Event semaphore on which the thread is blocked. */ + RTSEMEVENT Event; + /** The error/status of the timer. + * Initially -1, set to 0 when the timer have been successfully started, and + * to errno on failure in starting the timer. */ + volatile int iError; +} RTTIMER; + + + +/** + * Timer thread. + */ +static DECLCALLBACK(int) rttimerCallback(RTTHREAD hThreadSelf, void *pvArg) +{ + PRTTIMER pTimer = (PRTTIMER)(void *)pvArg; + Assert(pTimer->u32Magic == RTTIMER_MAGIC); + + /* + * Bounce our priority up quite a bit. + */ + if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) + { + int rc = GetLastError(); + AssertMsgFailed(("Failed to set priority class lasterror %d.\n", rc)); + pTimer->iError = RTErrConvertFromWin32(rc); + RTThreadUserSignal(hThreadSelf); + return rc; + } + + /* + * The work loop. + */ + RTThreadUserSignal(hThreadSelf); + + while ( !pTimer->fDestroyed + && pTimer->u32Magic == RTTIMER_MAGIC) + { + /* + * Wait for a start or destroy event. + */ + if (pTimer->fSuspended) + { + int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT); + if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED) + { + AssertRC(rc); + if (pTimer->fDestroyed) + continue; + RTThreadSleep(1000); /* Don't cause trouble! */ + } + if ( pTimer->fSuspended + || pTimer->fDestroyed) + continue; + } + + /* + * Start the waitable timer. + */ + pTimer->llNext.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100; + LARGE_INTEGER ll; + if (pTimer->u64NanoFirst) + { + GetSystemTimeAsFileTime((LPFILETIME)&ll); + ll.QuadPart += pTimer->u64NanoFirst / 100; + pTimer->u64NanoFirst = 0; + } + else + ll.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100; + if (!SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE)) + { + ASMAtomicXchgBool(&pTimer->fSuspended, true); + int rc = GetLastError(); + AssertMsgFailed(("Failed to set timer, lasterr %d.\n", rc)); + pTimer->iError = RTErrConvertFromWin32(rc); + RTThreadUserSignal(hThreadSelf); + continue; /* back to suspended mode. */ + } + pTimer->iError = 0; + RTThreadUserSignal(hThreadSelf); + + /* + * Timer Service Loop. + */ + do + { + int rc = WaitForSingleObjectEx(pTimer->hTimer, INFINITE, FALSE); + if (pTimer->u32Magic != RTTIMER_MAGIC) + break; + if (rc == WAIT_OBJECT_0) + { + /* + * Callback the handler. + */ + pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick); + + /* + * Rearm the timer handler. + */ + ll = pTimer->llNext; + BOOL fRc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE); + AssertMsg(fRc || pTimer->u32Magic != RTTIMER_MAGIC, ("last error %d\n", GetLastError())); NOREF(fRc); + } + else + { + /* + * We failed during wait, so just signal the destructor and exit. + */ + int rc2 = GetLastError(); + RTThreadUserSignal(hThreadSelf); + AssertMsgFailed(("Wait on hTimer failed, rc=%d lasterr=%d\n", rc, rc2)); NOREF(rc2); + return -1; + } + } while (RT_LIKELY( !pTimer->fSuspended + && !pTimer->fDestroyed + && pTimer->u32Magic == RTTIMER_MAGIC)); + + /* + * Disable the timer. + */ + int rc = CancelWaitableTimer (pTimer->hTimer); RT_NOREF(rc); + AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError())); + + /* + * ACK any pending suspend request. + */ + if (!pTimer->fDestroyed) + { + pTimer->iError = 0; + RTThreadUserSignal(hThreadSelf); + } + } + + /* + * Exit. + */ + pTimer->iError = 0; + RTThreadUserSignal(hThreadSelf); + return VINF_SUCCESS; +} + + +/** + * Tries to set the NT timer resolution to a value matching the given timer interval. + * + * @returns IPRT status code. + * @param u64NanoInterval The timer interval in nano seconds. + */ +static int rtTimerNtSetTimerResolution(uint64_t u64NanoInterval) +{ + /* + * On windows we'll have to set the timer resolution before + * we start the timer. + */ + ULONG ulMax = UINT32_MAX; + ULONG ulMin = UINT32_MAX; + ULONG ulCur = UINT32_MAX; + ULONG ulReq = (ULONG)(u64NanoInterval / 100); + NtQueryTimerResolution(&ulMax, &ulMin, &ulCur); + Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur)); + if (ulCur > ulMin && ulCur > ulReq) + { + ulReq = RT_MIN(ulMin, ulReq); + if (NtSetTimerResolution(ulReq, TRUE, &ulCur) >= 0) + Log(("Changed timer resolution to %lu*100ns.\n", ulReq)); + else if (NtSetTimerResolution(10000, TRUE, &ulCur) >= 0) + Log(("Changed timer resolution to 1ms.\n")); + else if (NtSetTimerResolution(20000, TRUE, &ulCur) >= 0) + Log(("Changed timer resolution to 2ms.\n")); + else if (NtSetTimerResolution(40000, TRUE, &ulCur) >= 0) + Log(("Changed timer resolution to 4ms.\n")); + else if (ulMin <= 50000 && NtSetTimerResolution(ulMin, TRUE, &ulCur) >= 0) + Log(("Changed timer resolution to %lu *100ns.\n", ulMin)); + else + { + AssertMsgFailed(("Failed to configure timer resolution!\n")); + return VERR_INTERNAL_ERROR; + } + } + + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser) +{ + /* + * We don't support the fancy MP features. + */ + if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) + return VERR_NOT_SUPPORTED; + + /* + * Create new timer. + */ + int rc = VERR_IPE_UNINITIALIZED_STATUS; + PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer)); + if (pTimer) + { + pTimer->u32Magic = RTTIMER_MAGIC; + pTimer->fSuspended = true; + pTimer->fDestroyed = false; + pTimer->Thread = NIL_RTTHREAD; + pTimer->pfnTimer = pfnTimer; + pTimer->pvUser = pvUser; + pTimer->u64NanoInterval = u64NanoInterval; + + rc = RTSemEventCreate(&pTimer->Event); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + /* + * Create Win32 waitable timer. + * We will first try the undocumented CREATE_WAITABLE_TIMER_HIGH_RESOLUTION which + * exists since some Windows 10 version (RS4). If this fails we resort to the old + * method of setting the timer resolution before creating a timer which will probably + * not give us the accuracy for intervals below the system tick resolution. + */ + pTimer->iError = 0; + if (g_pfnCreateWaitableTimerExW) + pTimer->hTimer = g_pfnCreateWaitableTimerExW(NULL, NULL, + CREATE_WAITABLE_TIMER_MANUAL_RESET | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, + TIMER_ALL_ACCESS); + if (!pTimer->hTimer) + { + rc = rtTimerNtSetTimerResolution(u64NanoInterval); + if (RT_SUCCESS(rc)) + pTimer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + } + + if (pTimer->hTimer) + { + /* + * Kick off the timer thread. + */ + rc = RTThreadCreate(&pTimer->Thread, rttimerCallback, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer"); + if (RT_SUCCESS(rc)) + { + /* + * Wait for the timer to successfully create the timer + * If we don't get a response in 10 secs, then we assume we're screwed. + */ + rc = RTThreadUserWait(pTimer->Thread, 10000); + if (RT_SUCCESS(rc)) + { + rc = pTimer->iError; + if (RT_SUCCESS(rc)) + { + *ppTimer = pTimer; + return VINF_SUCCESS; + } + } + + /* bail out */ + ASMAtomicXchgBool(&pTimer->fDestroyed, true); + ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC); + RTThreadWait(pTimer->Thread, 45*1000, NULL); + CancelWaitableTimer(pTimer->hTimer); + } + CloseHandle(pTimer->hTimer); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + RTSemEventDestroy(pTimer->Event); + pTimer->Event = NIL_RTSEMEVENT; + } + + RTMemFree(pTimer); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer) +{ + /* NULL is ok. */ + if (!pTimer) + return VINF_SUCCESS; + + int rc = VINF_SUCCESS; + AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC); + AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR); + + /* + * Signal that we want the thread to exit. + */ + ASMAtomicWriteBool(&pTimer->fDestroyed, true); + ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC); + + /* + * Suspend the timer if it's running. + */ + if (!pTimer->fSuspended) + { + LARGE_INTEGER ll = {0}; + ll.LowPart = 100; + rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE); + AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError())); + } + + rc = RTSemEventSignal(pTimer->Event); + AssertRC(rc); + + /* + * Wait for the thread to exit. + * And if it don't wanna exit, we'll get kill it. + */ + rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL); + if (RT_FAILURE(rc)) + TerminateThread((HANDLE)RTThreadGetNative(pTimer->Thread), UINT32_MAX); + + /* + * Free resource. + */ + rc = CloseHandle(pTimer->hTimer); + AssertMsg(rc, ("CloseHandle lasterr=%d\n", GetLastError())); + + RTSemEventDestroy(pTimer->Event); + pTimer->Event = NIL_RTSEMEVENT; + + RTMemFree(pTimer); + return rc; +} + + +RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First) +{ + /* + * Validate input. + */ + AssertPtrReturn(pTimer, VERR_INVALID_POINTER); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC); + AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR); + + RTThreadUserReset(pTimer->Thread); + + /* + * Already running? + */ + if (!ASMAtomicXchgBool(&pTimer->fSuspended, false)) + return VERR_TIMER_ACTIVE; + LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval)); + + /* + * Tell the thread to start servicing the timer. + * Wait for it to ACK the request to avoid reset races. + */ + ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First); + ASMAtomicUoWriteU64(&pTimer->iTick, 0); + int rc = RTSemEventSignal(pTimer->Event); + if (RT_SUCCESS(rc)) + { + rc = RTThreadUserWait(pTimer->Thread, 45*1000); + AssertRC(rc); + RTThreadUserReset(pTimer->Thread); + } + else + AssertRC(rc); + + if (RT_FAILURE(rc)) + ASMAtomicXchgBool(&pTimer->fSuspended, true); + return rc; +} + + +RTDECL(int) RTTimerStop(PRTTIMER pTimer) +{ + /* + * Validate input. + */ + AssertPtrReturn(pTimer, VERR_INVALID_POINTER); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC); + + RTThreadUserReset(pTimer->Thread); + + /* + * Already running? + */ + if (ASMAtomicXchgBool(&pTimer->fSuspended, true)) + return VERR_TIMER_SUSPENDED; + LogFlow(("RTTimerStop: pTimer=%p\n", pTimer)); + + /* + * Tell the thread to stop servicing the timer. + */ + int rc = VINF_SUCCESS; + if (RTThreadSelf() != pTimer->Thread) + { + LARGE_INTEGER ll = {0}; + ll.LowPart = 100; + rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE); + AssertMsg(rc, ("SetWaitableTimer lasterr=%d\n", GetLastError())); + rc = RTThreadUserWait(pTimer->Thread, 45*1000); + AssertRC(rc); + RTThreadUserReset(pTimer->Thread); + } + + return rc; +} + + +RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval) +{ + AssertPtrReturn(pTimer, VERR_INVALID_POINTER); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC); + NOREF(u64NanoInterval); + return VERR_NOT_SUPPORTED; +} diff --git a/src/VBox/Runtime/r3/win/tls-win.cpp b/src/VBox/Runtime/r3/win/tls-win.cpp new file mode 100644 index 00000000..5145a266 --- /dev/null +++ b/src/VBox/Runtime/r3/win/tls-win.cpp @@ -0,0 +1,230 @@ +/* $Id: tls-win.cpp $ */ +/** @file + * IPRT - Thread Local Storage (TLS), Win32. + */ + +/* + * 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/win/windows.h> + +#include <iprt/thread.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/errcore.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/once.h> +#include "internal/thread.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RTTLSWINDTOR +{ + RTLISTNODE ListEntry; + DWORD iTls; + PFNRTTLSDTOR pfnDestructor; +} RTTLSWINDTOR; +typedef RTTLSWINDTOR *PRTTLSWINDTOR; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Init once for the list and critical section. */ +static RTONCE g_Once = RTONCE_INITIALIZER; +/** Critical section protecting the TLS destructor list. */ +static RTCRITSECTRW g_CritSect; +/** List of TLS destrictors (RTTLSWINDTOR). */ +static RTLISTANCHOR g_TlsDtorHead; +/** Number of desturctors in the list (helps putting of initialization). */ +static uint32_t volatile g_cTlsDtors = 0; + + +/** + * @callback_method_impl{FNRTONCE} + */ +static DECLCALLBACK(int32_t) rtTlsWinInitLock(void *pvUser) +{ + RT_NOREF(pvUser); + RTListInit(&g_TlsDtorHead); + return RTCritSectRwInit(&g_CritSect); +} + + +RTR3DECL(RTTLS) RTTlsAlloc(void) +{ + AssertCompile(sizeof(RTTLS) >= sizeof(DWORD)); + DWORD iTls = TlsAlloc(); + return iTls != TLS_OUT_OF_INDEXES ? (RTTLS)iTls : NIL_RTTLS; +} + + +RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor) +{ + int rc; + if (!pfnDestructor) + { + DWORD iTls = TlsAlloc(); + if (iTls != TLS_OUT_OF_INDEXES) + { + Assert((RTTLS)iTls != NIL_RTTLS); + *piTls = (RTTLS)iTls; + Assert((DWORD)*piTls == iTls); + rc = VINF_SUCCESS; + } + else + rc = VERR_NO_MEMORY; + } + else + { + rc = RTOnce(&g_Once, rtTlsWinInitLock, NULL); + if (RT_SUCCESS(rc)) + { + PRTTLSWINDTOR pDtor = (PRTTLSWINDTOR)RTMemAlloc(sizeof(*pDtor)); + if (pDtor) + { + DWORD iTls = TlsAlloc(); + if (iTls != TLS_OUT_OF_INDEXES) + { + Assert((RTTLS)iTls != NIL_RTTLS); + *piTls = (RTTLS)iTls; + Assert((DWORD)*piTls == iTls); + + /* + * Add the destructor to the list. We keep it sorted. + */ + pDtor->iTls = iTls; + pDtor->pfnDestructor = pfnDestructor; + RTCritSectRwEnterExcl(&g_CritSect); + RTListAppend(&g_TlsDtorHead, &pDtor->ListEntry); + ASMAtomicIncU32(&g_cTlsDtors); + RTCritSectRwLeaveExcl(&g_CritSect); + + rc = VINF_SUCCESS; + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_NO_MEMORY; + } + } + return rc; +} + + +RTR3DECL(int) RTTlsFree(RTTLS iTls) +{ + if (iTls == NIL_RTTLS) + return VINF_SUCCESS; + if (TlsFree((DWORD)iTls)) + { + if (ASMAtomicReadU32(&g_cTlsDtors) > 0) + { + RTCritSectRwEnterExcl(&g_CritSect); + PRTTLSWINDTOR pDtor; + RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry) + { + if (pDtor->iTls == (DWORD)iTls) + { + RTListNodeRemove(&pDtor->ListEntry); + ASMAtomicDecU32(&g_cTlsDtors); + RTMemFree(pDtor); + break; + } + } + RTCritSectRwLeaveExcl(&g_CritSect); + } + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +} + + +RTR3DECL(void *) RTTlsGet(RTTLS iTls) +{ + return TlsGetValue((DWORD)iTls); +} + + +RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue) +{ + void *pv = TlsGetValue((DWORD)iTls); + if (pv) + { + *ppvValue = pv; + return VINF_SUCCESS; + } + + /* TlsGetValue always updates last error */ + *ppvValue = NULL; + return RTErrConvertFromWin32(GetLastError()); +} + + +RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue) +{ + if (TlsSetValue((DWORD)iTls, pvValue)) + return VINF_SUCCESS; + return RTErrConvertFromWin32(GetLastError()); +} + + +/** + * Called by dllmain-win.cpp when a thread detaches. + */ +DECLHIDDEN(void) rtThreadWinTlsDestruction(void) +{ + if (ASMAtomicReadU32(&g_cTlsDtors) > 0) + { + RTCritSectRwEnterShared(&g_CritSect); + PRTTLSWINDTOR pDtor; + RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry) + { + void *pvValue = TlsGetValue(pDtor->iTls); + if (pvValue != NULL) + { + pDtor->pfnDestructor(pvValue); + TlsSetValue(pDtor->iTls, NULL); + } + } + RTCritSectRwLeaveShared(&g_CritSect); + } +} + diff --git a/src/VBox/Runtime/r3/win/tpm-win.cpp b/src/VBox/Runtime/r3/win/tpm-win.cpp new file mode 100644 index 00000000..3ee0e46b --- /dev/null +++ b/src/VBox/Runtime/r3/win/tpm-win.cpp @@ -0,0 +1,307 @@ +/* $Id: tpm-win.cpp $ */ +/** @file + * IPRT - Trusted Platform Module (TPM) access, Windows variant. + */ + +/* + * 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 * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DEFAULT +#include <iprt/tpm.h> + +#include <iprt/assertcompile.h> +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/once.h> + +#include "internal-r3-win.h" + +#include <iprt/win/windows.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* tbs.dll: */ +typedef struct TBS_CONTEXT_PARAMS2 +{ + UINT32 version; + union + { + struct + { + UINT32 requestRaw: 1; + UINT32 includeTpm12: 1; + UINT32 includeTpm20: 1; + } Fields; + + UINT32 asUINT32; + } u; +} TBS_CONTEXT_PARAMS2; + +typedef struct TBS_DEVICE_INFO +{ + UINT32 structVersion; + UINT32 tpmVersion; + UINT32 tpmInterfaceType; + UINT32 tpmImpRevision; +} TBS_DEVICE_INFO; + +#define TPM_VERSION_12 1 +#define TPM_VERSION_20 2 + +#define TBS_SUCCESS S_OK +#define TBS_COMMAND_PRIORITY_NORMAL 200 + +typedef UINT32 TBS_RESULT; +typedef void *TBS_HCONTEXT; +typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CREATE)(const TBS_CONTEXT_PARAMS2 *, TBS_HCONTEXT *); +typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CLOSE)(TBS_HCONTEXT); +typedef TBS_RESULT (WINAPI *PFNTBSI_GET_DEVICE_INFO)(UINT32, TBS_DEVICE_INFO *); +typedef TBS_RESULT (WINAPI *PFNTBSI_CANCEL_COMMANDS)(TBS_HCONTEXT); +typedef TBS_RESULT (WINAPI *PFNTBSI_SUBMIT_COMMANDS)(TBS_HCONTEXT, UINT32, UINT32, const BYTE *, UINT32, PBYTE, PUINT32); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Internal TPM instance data. + */ +typedef struct RTTPMINT +{ + /** Handle to the TPM context. */ + TBS_HCONTEXT hCtx; + /** The deduced TPM version. */ + RTTPMVERSION enmTpmVers; +} RTTPMINT; +/** Pointer to the internal TPM instance data. */ +typedef RTTPMINT *PRTTPMINT; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Init once structure. */ +static RTONCE g_rtTpmWinInitOnce = RTONCE_INITIALIZER; +/* tbs.dll: */ +static PFNTBSI_CONTEXT_CREATE g_pfnTbsiContextCreate = NULL; +static PFNTBSI_CONTEXT_CLOSE g_pfnTbsiContextClose = NULL; +static PFNTBSI_GET_DEVICE_INFO g_pfnTbsiGetDeviceInfo = NULL; +static PFNTBSI_CANCEL_COMMANDS g_pfnTbsiCancelCommands = NULL; +static PFNTBSI_SUBMIT_COMMANDS g_pfnTbsiSubmitCommands = NULL; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Initialize the globals. + * + * @returns IPRT status code. + * @param pvUser Ignored. + */ +static DECLCALLBACK(int32_t) rtTpmWinInitOnce(void *pvUser) +{ + RT_NOREF(pvUser); + RTLDRMOD hMod; + + int rc = RTLdrLoadSystem("tbs.dll", true /*fNoUnload*/, &hMod); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hMod, "Tbsi_Context_Create", (void **)&g_pfnTbsiContextCreate); + if (RT_FAILURE(rc)) return rc; + + rc = RTLdrGetSymbol(hMod, "Tbsip_Context_Close", (void **)&g_pfnTbsiContextClose); + if (RT_FAILURE(rc)) return rc; + + rc = RTLdrGetSymbol(hMod, "Tbsip_Cancel_Commands", (void **)&g_pfnTbsiCancelCommands); + if (RT_FAILURE(rc)) return rc; + + rc = RTLdrGetSymbol(hMod, "Tbsip_Submit_Command", (void **)&g_pfnTbsiSubmitCommands); + if (RT_FAILURE(rc)) return rc; + + rc = RTLdrGetSymbol(hMod, "Tbsi_GetDeviceInfo", (void **)&g_pfnTbsiGetDeviceInfo); + if (RT_FAILURE(rc)) { g_pfnTbsiGetDeviceInfo = NULL; Assert(g_enmWinVer < kRTWinOSType_8); } + + RTLdrClose(hMod); + } + + return rc; +} + + +RTDECL(int) RTTpmOpen(PRTTPM phTpm, uint32_t idTpm) +{ + AssertPtrReturn(phTpm, VERR_INVALID_POINTER); + if (idTpm == RTTPM_ID_DEFAULT) + idTpm = 0; + + AssertReturn(idTpm == 0, VERR_NOT_SUPPORTED); + + /* + * Initialize the globals. + */ + int rc = RTOnce(&g_rtTpmWinInitOnce, rtTpmWinInitOnce, NULL); + AssertRCReturn(rc, rc); + + PRTTPMINT pThis = (PRTTPMINT)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + TBS_CONTEXT_PARAMS2 CtxParams; RT_ZERO(CtxParams); + + CtxParams.version = TPM_VERSION_12; + if (g_pfnTbsiGetDeviceInfo) + { + /* TPM2 support is available starting with Win8 which has Tbsi_GetDeviceInfo available. */ + TBS_DEVICE_INFO DevInfo; RT_ZERO(DevInfo); + + DevInfo.structVersion = TPM_VERSION_20; + TBS_RESULT rcTbs = g_pfnTbsiGetDeviceInfo(sizeof(DevInfo), &DevInfo); + if (rcTbs == TBS_SUCCESS) + { + CtxParams.version = TPM_VERSION_20; + if (DevInfo.tpmVersion == TPM_VERSION_20) + { + pThis->enmTpmVers = RTTPMVERSION_2_0; + CtxParams.u.Fields.includeTpm20 = 1; + } + else + { + Assert(DevInfo.tpmVersion == TPM_VERSION_12); + pThis->enmTpmVers = RTTPMVERSION_1_2; + CtxParams.u.Fields.includeTpm12 = 1; + } + } + else + rc = VERR_NOT_FOUND; + } + else + pThis->enmTpmVers = RTTPMVERSION_1_2; + + if (RT_SUCCESS(rc)) + { + TBS_RESULT rcTbs = g_pfnTbsiContextCreate(&CtxParams, &pThis->hCtx); + if (rcTbs == TBS_SUCCESS) + { + *phTpm = pThis; + return VINF_SUCCESS; + } + else + rc = VERR_NOT_FOUND; + } + + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTTpmClose(RTTPM hTpm) +{ + PRTTPMINT pThis = hTpm; + + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + TBS_RESULT rcTbs = g_pfnTbsiContextClose(pThis->hCtx); + Assert(rcTbs == TBS_SUCCESS); RT_NOREF(rcTbs); + + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(RTTPMVERSION) RTTpmGetVersion(RTTPM hTpm) +{ + PRTTPMINT pThis = hTpm; + + AssertPtrReturn(pThis, RTTPMVERSION_INVALID); + return pThis->enmTpmVers; +} + + +RTDECL(uint32_t) RTTpmGetLocalityMax(RTTPM hTpm) +{ + RT_NOREF(hTpm); + return 0; /* Only TPM locality 0 is supported. */ +} + + +RTDECL(int) RTTpmReqCancel(RTTPM hTpm) +{ + PRTTPMINT pThis = hTpm; + + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + + TBS_RESULT rcTbs = g_pfnTbsiCancelCommands(pThis->hCtx); + if (rcTbs != TBS_SUCCESS) + return VERR_DEV_IO_ERROR; + + return VINF_SUCCESS; +} + + +RTDECL(int) RTTpmReqExec(RTTPM hTpm, uint8_t bLoc, const void *pvReq, size_t cbReq, + void *pvResp, size_t cbRespMax, size_t *pcbResp) +{ + PRTTPMINT pThis = hTpm; + + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertPtrReturn(pvReq, VERR_INVALID_POINTER); + AssertPtrReturn(pvResp, VERR_INVALID_POINTER); + AssertReturn(cbReq && cbRespMax, VERR_INVALID_PARAMETER); + AssertReturn(cbReq == (UINT32)cbReq && cbRespMax == (UINT32)cbRespMax, VERR_BUFFER_OVERFLOW); + AssertReturn(bLoc == 0, VERR_NOT_SUPPORTED); /* TBS doesn't support another locality than 0. */ + + UINT32 cbResult = (UINT32)cbRespMax; + TBS_RESULT rcTbs = g_pfnTbsiSubmitCommands(pThis->hCtx, 0 /*Locality*/, TBS_COMMAND_PRIORITY_NORMAL, + (const BYTE *)pvReq, (UINT32)cbReq, (BYTE *)pvResp, &cbResult); + if (rcTbs == TBS_SUCCESS) + { + if (pcbResp) + *pcbResp = cbResult; + return VINF_SUCCESS; + } + + return VERR_DEV_IO_ERROR; +} + diff --git a/src/VBox/Runtime/r3/win/utf16locale-win.cpp b/src/VBox/Runtime/r3/win/utf16locale-win.cpp new file mode 100644 index 00000000..d282572a --- /dev/null +++ b/src/VBox/Runtime/r3/win/utf16locale-win.cpp @@ -0,0 +1,58 @@ +/* $Id: utf16locale-win.cpp $ */ +/** @file + * IPRT - UTF-16 Locale Specific Manipulation, Win32. + */ + +/* + * 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/win/windows.h> + +#include <iprt/utf16.h> + + +RTDECL(int) RTUtf16LocaleICmp(PCRTUTF16 pusz1, PCRTUTF16 pusz2) +{ + if (pusz1 == pusz2) + return 0; + if (pusz1 == NULL) + return -1; + if (pusz2 == NULL) + return 1; + + return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pusz1, -1, pusz2, -1) - 2; +} + diff --git a/src/VBox/Runtime/r3/win/utf8-win.cpp b/src/VBox/Runtime/r3/win/utf8-win.cpp new file mode 100644 index 00000000..7defa137 --- /dev/null +++ b/src/VBox/Runtime/r3/win/utf8-win.cpp @@ -0,0 +1,204 @@ +/* $Id: utf8-win.cpp $ */ +/** @file + * IPRT - UTF8 helpers. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_UTF8 +#include <iprt/win/windows.h> + +#include <iprt/string.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/utf16.h> + + + +RTR3DECL(int) RTStrUtf8ToCurrentCPTag(char **ppszString, const char *pszString, const char *pszTag) +{ + return RTStrUtf8ToCurrentCPExTag(ppszString, pszString, RTSTR_MAX, pszTag); +} + + +RTR3DECL(int) RTStrUtf8ToCurrentCPExTag(char **ppszString, const char *pszString, size_t cchString, const char *pszTag) +{ + Assert(ppszString); + Assert(pszString); + *ppszString = NULL; + + /* + * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed. + * Same goes for empty strings. + */ + if ( cchString == 0 + || *pszString == '\0') + return RTStrDupNExTag(ppszString, pszString, 0, pszTag); + if (GetACP() == CP_UTF8) + { + int rc = RTStrValidateEncodingEx(pszString, cchString, 0); + AssertRCReturn(rc, rc); + return RTStrDupNExTag(ppszString, pszString, cchString, pszTag); + } + + /* + * Convert to wide char first. + */ + PRTUTF16 pwszString = NULL; + int rc = RTStrToUtf16Ex(pszString, cchString, &pwszString, 0, NULL); + if (RT_FAILURE(rc)) + return rc; + + /* + * First calc result string length. + */ + int cbResult = WideCharToMultiByte(CP_ACP, 0, pwszString, -1, NULL, 0, NULL, NULL); + if (cbResult > 0) + { + /* + * Alloc space for result buffer. + */ + LPSTR lpString = (LPSTR)RTMemTmpAllocTag(cbResult, pszTag); + if (lpString) + { + /* + * Do the translation. + */ + if (WideCharToMultiByte(CP_ACP, 0, pwszString, -1, lpString, cbResult, NULL, NULL) > 0) + { + /* ok */ + *ppszString = lpString; + RTMemTmpFree(pwszString); + return VINF_SUCCESS; + } + + /* translation error */ + int iLastErr = GetLastError(); + AssertMsgFailed(("Unicode to ACP translation failed. lasterr=%d\n", iLastErr)); + rc = RTErrConvertFromWin32(iLastErr); + } + else + rc = VERR_NO_TMP_MEMORY; + RTMemTmpFree(lpString); + } + else + { + /* translation error */ + int iLastErr = GetLastError(); + AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr)); + rc = RTErrConvertFromWin32(iLastErr); + } + RTMemTmpFree(pwszString); + return rc; +} + +static int rtStrCPToUtf8Tag(char **ppszString, const char *pszString, uint32_t uCodePage, const char *pszTag) +{ + Assert(ppszString); + Assert(pszString); + *ppszString = NULL; + + /* + * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed. + * Same goes for empty strings. + */ + if (*pszString == '\0') + return RTStrDupExTag(ppszString, pszString, pszTag); + if (GetACP() == CP_UTF8) + { + int rc = RTStrValidateEncoding(pszString); + AssertRCReturn(rc, rc); + return RTStrDupExTag(ppszString, pszString, pszTag); + } + + /** @todo is there a quicker way? Currently: ACP -> UTF-16 -> UTF-8 */ + + /* + * First calc result string length. + */ + int rc; + int cwc = MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, NULL, 0); + if (cwc > 0) + { + /* + * Alloc space for result buffer. + */ + PRTUTF16 pwszString = (PRTUTF16)RTMemTmpAlloc(cwc * sizeof(RTUTF16)); + if (pwszString) + { + /* + * Do the translation. + */ + if (MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, pwszString, cwc) > 0) + { + /* + * Now we got UTF-16, convert it to UTF-8 + */ + rc = RTUtf16ToUtf8(pwszString, ppszString); + RTMemTmpFree(pwszString); + return rc; + } + RTMemTmpFree(pwszString); + /* translation error */ + int iLastErr = GetLastError(); + AssertMsgFailed(("ACP to Unicode translation failed. lasterr=%d\n", iLastErr)); + rc = RTErrConvertFromWin32(iLastErr); + } + else + rc = VERR_NO_TMP_MEMORY; + } + else + { + /* translation error */ + int iLastErr = GetLastError(); + AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr)); + rc = RTErrConvertFromWin32(iLastErr); + } + return rc; +} + + +RTR3DECL(int) RTStrCurrentCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag) +{ + return rtStrCPToUtf8Tag(ppszString, pszString, CP_ACP, pszTag); +} + + +RTR3DECL(int) RTStrConsoleCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag) +{ + return rtStrCPToUtf8Tag(ppszString, pszString, GetConsoleCP(), pszTag); +} diff --git a/src/VBox/Runtime/r3/win/uuid-win.cpp b/src/VBox/Runtime/r3/win/uuid-win.cpp new file mode 100644 index 00000000..eebeb0c0 --- /dev/null +++ b/src/VBox/Runtime/r3/win/uuid-win.cpp @@ -0,0 +1,195 @@ +/* $Id: uuid-win.cpp $ */ +/** @file + * IPRT - UUID, Windows 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_UUID +#include <iprt/win/windows.h> + +#include <iprt/uuid.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/errcore.h> + + +RTDECL(int) RTUuidClear(PRTUUID pUuid) +{ + /* check params */ + AssertPtrReturn(pUuid, VERR_INVALID_POINTER); + + return RTErrConvertFromWin32(UuidCreateNil((UUID *)pUuid)); +} + + +RTDECL(bool) RTUuidIsNull(PCRTUUID pUuid) +{ + /* check params */ + AssertPtrReturn(pUuid, true); + + RPC_STATUS status; + return !!UuidIsNil((UUID *)pUuid, &status); +} + + +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); + + /* + * Hand the rest to the Windows API. + */ + RPC_STATUS status; + return UuidCompare((UUID *)pUuid1, (UUID *)pUuid2, &status); +} + + +RTDECL(int) RTUuidCompareStr(PCRTUUID pUuid1, const char *pszString2) +{ + /* check params */ + AssertPtrReturn(pUuid1, -1); + AssertPtrReturn(pszString2, 1); + + /* + * Try convert the string to a UUID and then compare the two. + */ + RTUUID Uuid2; + int rc = RTUuidFromStr(&Uuid2, pszString2); + AssertRCReturn(rc, 1); + + return RTUuidCompare(pUuid1, &Uuid2); +} + + +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); +} + + +RTDECL(int) RTUuidToStr(PCRTUUID pUuid, char *pszString, size_t cchString) +{ + /* check params */ + AssertPtrReturn(pUuid, VERR_INVALID_POINTER); + AssertPtrReturn(pszString, VERR_INVALID_POINTER); + AssertReturn(cchString >= RTUUID_STR_LENGTH, VERR_INVALID_PARAMETER); + + /* + * Try convert it. + * + * The API allocates a new string buffer for us, so we can do our own + * buffer overflow handling. + */ + RPC_STATUS Status; + unsigned char *pszTmpStr = NULL; +#ifdef RPC_UNICODE_SUPPORTED + /* always use ASCII version! */ + Status = UuidToStringA((UUID *)pUuid, &pszTmpStr); +#else + Status = UuidToString((UUID *)pUuid, &pszTmpStr); +#endif + if (Status != RPC_S_OK) + return RTErrConvertFromWin32(Status); + + /* copy it. */ + int rc = VINF_SUCCESS; + size_t cchTmpStr = strlen((char *)pszTmpStr); + if (cchTmpStr < cchString) + memcpy(pszString, pszTmpStr, cchTmpStr + 1); + else + { + AssertFailed(); + rc = ERROR_BUFFER_OVERFLOW; + } + + /* free buffer */ +#ifdef RPC_UNICODE_SUPPORTED + /* always use ASCII version! */ + RpcStringFreeA(&pszTmpStr); +#else + RpcStringFree(&pszTmpStr); +#endif + + /* all done */ + return rc; +} + + +RTDECL(int) RTUuidFromStr(PRTUUID pUuid, const char *pszString) +{ + /* check params */ + AssertPtrReturn(pUuid, VERR_INVALID_POINTER); + AssertPtrReturn(pszString, VERR_INVALID_POINTER); + + RPC_STATUS rc; +#ifdef RPC_UNICODE_SUPPORTED + /* always use ASCII version! */ + rc = UuidFromStringA((unsigned char *)pszString, (UUID *)pUuid); +#else + rc = UuidFromString((unsigned char *)pszString, (UUID *)pUuid); +#endif + + return RTErrConvertFromWin32(rc); +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h new file mode 100644 index 00000000..1708ee52 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h @@ -0,0 +1,43 @@ + +COMMENT("XP SP2 / W2K3 SP1 / VISTA") +MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4) +MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4) +COMMENT("XP") +MAKE_IMPORT_ENTRY(5,1, CreateIoCompletionPort, 16) +MAKE_IMPORT_ENTRY(5,1, GetQueuedCompletionStatus, 20) +MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16) +MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20) +MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4) +MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4) +MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4) +MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8) +MAKE_IMPORT_ENTRY(5,1, PostQueuedCompletionStatus, 16) +MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4) +COMMENT("W2K") +MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0) +MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28) +MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12) +MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16) +COMMENT("NT 4 SP4+") +MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16) +COMMENT("NT 4 SP3+") +MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8) +COMMENT("NT 4") +MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4) +MAKE_IMPORT_ENTRY(4,0, CancelIo, 4) +COMMENT("NT 3.51") +MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0) +MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4) +COMMENT("NT 3.50") +MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4) +MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4) +MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0) +MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4) +MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16) +MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8) +MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8) +MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8) +MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12) +MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8) +MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12) + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h new file mode 100644 index 00000000..a65e6392 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h @@ -0,0 +1,42 @@ +MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4) +MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4) +MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4) +MAKE_IMPORT_ENTRY(5,1, GetModuleHandleExW, 12) +MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16) +MAKE_IMPORT_ENTRY(5,0, SetFilePointerEx, 20) +MAKE_IMPORT_ENTRY(5,0, GetFileSizeEx, 8) +MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8) +MAKE_IMPORT_ENTRY(4,0, FindFirstFileExW, 24) +MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4) +MAKE_IMPORT_ENTRY(4,0, CancelIo, 4) +MAKE_IMPORT_ENTRY(3,51, FreeLibraryAndExitThread, 8) +MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0) +MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4) +MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesW, 8) +MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4) +MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4) +MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0) +MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4) +MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8) +MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8) +MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12) +MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8) +MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12) + +COMMENT("Not needed (usually)") +MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16) +MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20) +MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4) +MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4) +MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8) +MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4) +MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0) +MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28) +MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12) +MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16) +MAKE_IMPORT_ENTRY(3,51, PostQueuedCompletionStatus, 16) +MAKE_IMPORT_ENTRY(3,51, CreateIoCompletionPort, 16) +MAKE_IMPORT_ENTRY(3,51, GetQueuedCompletionStatus, 20) +MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16) +MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8) + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm new file mode 100644 index 00000000..eb1449c3 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm @@ -0,0 +1,56 @@ +; $Id: vcc-fakes-kernel32-A.asm $ +;; @file +; IPRT - Wrappers for kernel32 APIs missing in NT4 and earlier. +; + +; +; 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 +; + + +%include "vcc-fakes.mac" + +%define FAKE_MODULE_NAME kernel32 + +BEGINDATA +GLOBALNAME vcc100_kernel32_fakes_asm + +%ifdef VCC_FAKES_TARGET_VCC100 + %include "vcc-fakes-kernel32-100.h" +%elifdef VCC_FAKES_TARGET_VCC140 + %include "vcc-fakes-kernel32-141.h" +%elifdef VCC_FAKES_TARGET_VCC141 + %include "vcc-fakes-kernel32-141.h" +%elifdef VCC_FAKES_TARGET_VCC142 + %include "vcc-fakes-kernel32-141.h" +%else + %error "PORT ME!" +%endif + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp new file mode 100644 index 00000000..697f22ca --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp @@ -0,0 +1,891 @@ +/* $Id: vcc-fakes-kernel32.cpp $ */ +/** @file + * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP. + */ + +/* + * 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/cdefs.h> +#include <iprt/types.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#ifdef DEBUG +# include <stdio.h> /* _snprintf */ +#endif + +#ifndef RT_ARCH_X86 +# error "This code is X86 only" +#endif + +#include <iprt/nt/nt-and-windows.h> + +#include "vcc-fakes.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#ifndef HEAP_STANDARD +# define HEAP_STANDARD 0 +#endif + + +/** Declare a kernel32 API. + * @note We are not exporting them as that causes duplicate symbol troubles in + * the OpenGL bits. */ +#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI + +#if defined(WDK_NTDDI_VERSION) && defined(NTDDI_WIN10) +# if WDK_NTDDI_VERSION >= NTDDI_WIN10 /* In Windows 10 SDK the 'Sequence' field has been renamed to 'CpuId'. */ +# define SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID +# endif +#endif + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static volatile bool g_fInitialized = false; +#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) DECLARE_FUNCTION_POINTER(a_Name, a_cb) +#ifdef VCC_FAKES_TARGET_VCC100 +# include "vcc-fakes-kernel32-100.h" +#elif defined(VCC_FAKES_TARGET_VCC140) +# include "vcc-fakes-kernel32-141.h" +#elif defined(VCC_FAKES_TARGET_VCC141) +# include "vcc-fakes-kernel32-141.h" +#elif defined(VCC_FAKES_TARGET_VCC142) +# include "vcc-fakes-kernel32-141.h" +#else +# error "Port me!" +#endif + + +static BOOL FakeSetLastErrorFromNtStatus(NTSTATUS rcNt) +{ + DWORD dwErr; + switch (rcNt) + { + case STATUS_INVALID_PARAMETER: + case STATUS_INVALID_PARAMETER_1: + case STATUS_INVALID_PARAMETER_2: + case STATUS_INVALID_PARAMETER_3: + case STATUS_INVALID_PARAMETER_4: + case STATUS_INVALID_PARAMETER_5: + case STATUS_INVALID_PARAMETER_6: + case STATUS_INVALID_PARAMETER_7: + case STATUS_INVALID_PARAMETER_8: + case STATUS_INVALID_PARAMETER_9: + case STATUS_INVALID_PARAMETER_10: + case STATUS_INVALID_PARAMETER_11: + case STATUS_INVALID_PARAMETER_12: + dwErr = ERROR_INVALID_PARAMETER; + break; + + case STATUS_INVALID_HANDLE: + dwErr = ERROR_INVALID_HANDLE; + break; + + case STATUS_ACCESS_DENIED: + dwErr = ERROR_ACCESS_DENIED; + break; + + default: + dwErr = ERROR_INVALID_PARAMETER; + break; + } + SetLastError(dwErr); + return FALSE; +} + + + +DECL_KERNEL32(PVOID) Fake_DecodePointer(PVOID pvEncoded) +{ + return pvEncoded; +} + + +DECL_KERNEL32(PVOID) Fake_EncodePointer(PVOID pvNative) +{ + return pvNative; +} + + +DECL_KERNEL32(BOOL) Fake_InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION pCritSect, DWORD cSpin) +{ + RT_NOREF(cSpin); + InitializeCriticalSection(pCritSect); + return TRUE; +} + + +DECL_KERNEL32(HANDLE) Fake_CreateIoCompletionPort(HANDLE hFile, HANDLE hExistingCompletionPort, ULONG_PTR uCompletionKey, + DWORD cConcurrentThreads) +{ + RT_NOREF(hFile, hExistingCompletionPort, uCompletionKey, cConcurrentThreads); + SetLastError(ERROR_NOT_SUPPORTED); + return NULL; +} + + +DECL_KERNEL32(BOOL) Fake_GetQueuedCompletionStatus(HANDLE hCompletionPort, PDWORD_PTR pcbTransfered, PULONG_PTR puCompletionKey, + LPOVERLAPPED *ppOverlapped, DWORD cMs) +{ + RT_NOREF(hCompletionPort, pcbTransfered, puCompletionKey, ppOverlapped, cMs); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_PostQueuedCompletionStatus(HANDLE hCompletionPort, DWORD cbTransfered, ULONG_PTR uCompletionKey, + LPOVERLAPPED pOverlapped) +{ + RT_NOREF(hCompletionPort, cbTransfered, uCompletionKey, pOverlapped); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_HeapSetInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass, PVOID pvBuf, SIZE_T cbBuf) +{ + RT_NOREF(hHeap); + if (enmInfoClass == HeapCompatibilityInformation) + { + if ( cbBuf != sizeof(ULONG) + || !pvBuf + || *(PULONG)pvBuf == HEAP_STANDARD + ) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + return TRUE; + } + + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_HeapQueryInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass, + PVOID pvBuf, SIZE_T cbBuf, PSIZE_T pcbRet) +{ + RT_NOREF(hHeap); + if (enmInfoClass == HeapCompatibilityInformation) + { + *pcbRet = sizeof(ULONG); + if (cbBuf < sizeof(ULONG) || !pvBuf) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + *(PULONG)pvBuf = HEAP_STANDARD; + return TRUE; + } + + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; +} + + +/* These are used by INTEL\mt_obj\Timer.obj: */ + +DECL_KERNEL32(HANDLE) Fake_CreateTimerQueue(void) +{ + SetLastError(ERROR_NOT_SUPPORTED); + return NULL; +} + +DECL_KERNEL32(BOOL) Fake_CreateTimerQueueTimer(PHANDLE phTimer, HANDLE hTimerQueue, WAITORTIMERCALLBACK pfnCallback, PVOID pvUser, + DWORD msDueTime, DWORD msPeriod, ULONG fFlags) +{ + NOREF(phTimer); NOREF(hTimerQueue); NOREF(pfnCallback); NOREF(pvUser); NOREF(msDueTime); NOREF(msPeriod); NOREF(fFlags); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + +DECL_KERNEL32(BOOL) Fake_DeleteTimerQueueTimer(HANDLE hTimerQueue, HANDLE hTimer, HANDLE hEvtCompletion) +{ + NOREF(hTimerQueue); NOREF(hTimer); NOREF(hEvtCompletion); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + +/* This is used by several APIs. */ + +DECL_KERNEL32(VOID) Fake_InitializeSListHead(PSLIST_HEADER pHead) +{ + pHead->Alignment = 0; +} + + +DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedFlushSList(PSLIST_HEADER pHead) +{ + PSLIST_ENTRY pRet = NULL; + if (pHead->Next.Next) + { + for (;;) + { + SLIST_HEADER OldHead = *pHead; + SLIST_HEADER NewHead; + NewHead.Alignment = 0; +#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID + NewHead.CpuId = OldHead.CpuId + 1; +#else + NewHead.Sequence = OldHead.Sequence + 1; +#endif + if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment)) + { + pRet = OldHead.Next.Next; + break; + } + } + } + return pRet; +} + +DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPopEntrySList(PSLIST_HEADER pHead) +{ + PSLIST_ENTRY pRet = NULL; + for (;;) + { + SLIST_HEADER OldHead = *pHead; + pRet = OldHead.Next.Next; + if (pRet) + { + SLIST_HEADER NewHead; + __try + { + NewHead.Next.Next = pRet->Next; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + continue; + } + NewHead.Depth = OldHead.Depth - 1; +#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID + NewHead.CpuId = OldHead.CpuId + 1; +#else + NewHead.Sequence = OldHead.Sequence + 1; +#endif + if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment)) + break; + } + else + break; + } + return pRet; +} + +DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPushEntrySList(PSLIST_HEADER pHead, PSLIST_ENTRY pEntry) +{ + PSLIST_ENTRY pRet = NULL; + for (;;) + { + SLIST_HEADER OldHead = *pHead; + pRet = OldHead.Next.Next; + pEntry->Next = pRet; + SLIST_HEADER NewHead; + NewHead.Next.Next = pEntry; + NewHead.Depth = OldHead.Depth + 1; +#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID + NewHead.CpuId = OldHead.CpuId + 1; +#else + NewHead.Sequence = OldHead.Sequence + 1; +#endif + if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment)) + break; + } + return pRet; +} + +DECL_KERNEL32(WORD) Fake_QueryDepthSList(PSLIST_HEADER pHead) +{ + return pHead->Depth; +} + + +/* curl drags these in: */ +DECL_KERNEL32(BOOL) Fake_VerifyVersionInfoA(LPOSVERSIONINFOEXA pInfo, DWORD fTypeMask, DWORDLONG fConditionMask) +{ + OSVERSIONINFOEXA VerInfo; + RT_ZERO(VerInfo); + VerInfo.dwOSVersionInfoSize = sizeof(VerInfo); + if (!GetVersionExA((OSVERSIONINFO *)&VerInfo)) + { + RT_ZERO(VerInfo); + VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + BOOL fRet = GetVersionExA((OSVERSIONINFO *)&VerInfo); + if (fRet) + { /* likely */ } + else + { + MY_ASSERT(false, "VerifyVersionInfoA: #1"); + return FALSE; + } + } + + BOOL fRet = TRUE; + for (unsigned i = 0; i < 8 && fRet; i++) + if (fTypeMask & RT_BIT_32(i)) + { + uint32_t uLeft, uRight; + switch (RT_BIT_32(i)) + { +#define MY_CASE(a_Num, a_Member) case a_Num: uLeft = VerInfo.a_Member; uRight = pInfo->a_Member; break + MY_CASE(VER_MINORVERSION, dwMinorVersion); + MY_CASE(VER_MAJORVERSION, dwMajorVersion); + MY_CASE(VER_BUILDNUMBER, dwBuildNumber); + MY_CASE(VER_PLATFORMID, dwPlatformId); + MY_CASE(VER_SERVICEPACKMINOR, wServicePackMinor); + MY_CASE(VER_SERVICEPACKMAJOR, wServicePackMinor); + MY_CASE(VER_SUITENAME, wSuiteMask); + MY_CASE(VER_PRODUCT_TYPE, wProductType); +#undef MY_CASE + default: uLeft = uRight = 0; MY_ASSERT(false, "VerifyVersionInfoA: #2"); + } + switch ((uint8_t)(fConditionMask >> (i*8))) + { + case VER_EQUAL: fRet = uLeft == uRight; break; + case VER_GREATER: fRet = uLeft > uRight; break; + case VER_GREATER_EQUAL: fRet = uLeft >= uRight; break; + case VER_LESS: fRet = uLeft < uRight; break; + case VER_LESS_EQUAL: fRet = uLeft <= uRight; break; + case VER_AND: fRet = (uLeft & uRight) == uRight; break; + case VER_OR: fRet = (uLeft & uRight) != 0; break; + default: fRet = FALSE; MY_ASSERT(false, "VerifyVersionInfoA: #3"); break; + } + } + + return fRet; +} + + +DECL_KERNEL32(ULONGLONG) Fake_VerSetConditionMask(ULONGLONG fConditionMask, DWORD fTypeMask, BYTE bOperator) +{ + for (unsigned i = 0; i < 8; i++) + if (fTypeMask & RT_BIT_32(i)) + { + uint64_t fMask = UINT64_C(0xff) << (i*8); + fConditionMask &= ~fMask; + fConditionMask |= (uint64_t)bOperator << (i*8); + + } + return fConditionMask; +} + + +#if VCC_FAKES_TARGET >= 140 +/** @since 5.0 (windows 2000) */ +DECL_KERNEL32(BOOL) Fake_GetModuleHandleExW(DWORD dwFlags, LPCWSTR pwszModuleName, HMODULE *phModule) +{ + HMODULE hmod; + if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) + { + /** @todo search the loader list. */ + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; + } + else + { + hmod = GetModuleHandleW(pwszModuleName); + if (!hmod) + return FALSE; + } + + /* + * Get references to the module. + */ + if ( !(dwFlags & (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_PIN)) + && GetModuleHandleW(NULL) != hmod) + { + WCHAR wszModule[MAX_PATH]; + if (GetModuleFileNameW(hmod, wszModule, RT_ELEMENTS(wszModule)) > 0) + { + if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) + { + uint32_t cRefs = 32; + while (cRefs-- > 0) + LoadLibraryW(wszModule); + } + else if (!LoadLibraryW(wszModule)) + return FALSE; + } + } + + *phModule = hmod; + return TRUE; +} +#endif /* VCC_FAKES_TARGET >= 141 */ + + +#if VCC_FAKES_TARGET >= 140 +/** @since 5.0 (windows 2000) */ +DECL_KERNEL32(BOOL) Fake_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offDistanceToMove, + PLARGE_INTEGER pNewFilePointer, DWORD dwMoveMethod) +{ + NTSTATUS rcNt; + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + + FILE_POSITION_INFORMATION PosInfo; + switch (dwMoveMethod) + { + case FILE_BEGIN: + PosInfo.CurrentByteOffset = offDistanceToMove; + break; + + case FILE_CURRENT: + PosInfo.CurrentByteOffset.QuadPart = INT64_MAX; + rcNt = NtQueryInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation); + if (NT_SUCCESS(rcNt)) + { + PosInfo.CurrentByteOffset.QuadPart += offDistanceToMove.QuadPart; + break; + } + return FakeSetLastErrorFromNtStatus(rcNt); + + case FILE_END: + { + FILE_STANDARD_INFO StdInfo = {{0}}; + rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation); + if (NT_SUCCESS(rcNt)) + { + PosInfo.CurrentByteOffset.QuadPart = offDistanceToMove.QuadPart + StdInfo.EndOfFile.QuadPart; + break; + } + return FakeSetLastErrorFromNtStatus(rcNt); + } + + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + rcNt = NtSetInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation); + if (NT_SUCCESS(rcNt)) + { + if (pNewFilePointer) + *pNewFilePointer = PosInfo.CurrentByteOffset; + return TRUE; + } + return FakeSetLastErrorFromNtStatus(rcNt); +} +#endif /* VCC_FAKES_TARGET >= 140 */ + + +#if VCC_FAKES_TARGET >= 140 +/** @since 5.0 (windows 2000) */ +DECL_KERNEL32(BOOL) Fake_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile) +{ + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + FILE_STANDARD_INFO StdInfo = {{0}}; + NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation); + if (NT_SUCCESS(rcNt)) + { + *pcbFile = StdInfo.EndOfFile; + return TRUE; + } + return FakeSetLastErrorFromNtStatus(rcNt); +} +#endif /* VCC_FAKES_TARGET >= 140 */ + + + + +/* + * NT 3.51 stuff. + */ + +#if VCC_FAKES_TARGET >= 140 +/** @since 4.0 */ +DECL_KERNEL32(HANDLE) Fake_FindFirstFileExW(LPCWSTR pwszFileName, FINDEX_INFO_LEVELS enmInfoLevel, LPVOID pvFindFileData, + FINDEX_SEARCH_OPS enmSearchOp, LPVOID pvSearchFilter, DWORD dwAdditionalFlags) +{ + // STL: FindFirstFileExW(, FindExInfoBasic, , FindExSearchNameMatch, NULL, 0); + // CRT/_findfile: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0); + // CRT/argv_wildcards: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0); + MY_ASSERT_STMT_RETURN(dwAdditionalFlags == 0, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE); + MY_ASSERT_STMT_RETURN(pvSearchFilter == NULL, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE); + MY_ASSERT_STMT_RETURN(enmSearchOp == FindExSearchNameMatch, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE); + MY_ASSERT_STMT_RETURN(enmInfoLevel == FindExInfoStandard || enmInfoLevel == FindExInfoBasic, + SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE); + + return FindFirstFileW(pwszFileName, (WIN32_FIND_DATAW *)pvFindFileData); +} +#endif /* VCC_FAKES_TARGET >= 140 */ + + +DECL_KERNEL32(BOOL) Fake_IsProcessorFeaturePresent(DWORD enmProcessorFeature) +{ + /* Could make more of an effort here... */ + RT_NOREF(enmProcessorFeature); + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_CancelIo(HANDLE hHandle) +{ + /* All NT versions the NTDLL API this corresponds to. */ + RESOLVE_NTDLL_API(NtCancelIoFile); + if (pfnNtCancelIoFile) + { + IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; + NTSTATUS rcNt = pfnNtCancelIoFile(hHandle, &Ios); + if (RT_SUCCESS(rcNt)) + return TRUE; + if (rcNt == STATUS_INVALID_HANDLE) + SetLastError(ERROR_INVALID_HANDLE); + else + SetLastError(ERROR_INVALID_FUNCTION); + } + else + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + + +/* + * NT 3.50 stuff. + */ + +#if VCC_FAKES_TARGET >= 140 +/** @since 3.51 */ +DECL_KERNEL32(VOID) Fake_FreeLibraryAndExitThread(HMODULE hLibModule, DWORD dwExitCode) +{ + if (hLibModule) + FreeModule(hLibModule); + ExitThread(dwExitCode); +} +#endif /* VCC_FAKES_TARGET >= 140 */ + + +DECL_KERNEL32(BOOL) Fake_IsDebuggerPresent(VOID) +{ + /* Fallback: */ + return FALSE; +} + + +DECL_KERNEL32(VOID) Fake_GetSystemTimeAsFileTime(LPFILETIME pTime) +{ + DWORD dwVersion = GetVersion(); + if ( (dwVersion & 0xff) > 3 + || ( (dwVersion & 0xff) == 3 + && ((dwVersion >> 8) & 0xff) >= 50) ) + { + PKUSER_SHARED_DATA pUsd = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA; + + /* use interrupt time */ + LARGE_INTEGER Time; + do + { + Time.HighPart = pUsd->SystemTime.High1Time; + Time.LowPart = pUsd->SystemTime.LowPart; + } while (pUsd->SystemTime.High2Time != Time.HighPart); + + pTime->dwHighDateTime = Time.HighPart; + pTime->dwLowDateTime = Time.LowPart; + } + else + { + /* NT 3.1 didn't have a KUSER_SHARED_DATA nor a GetSystemTimeAsFileTime export. */ + SYSTEMTIME SystemTime; + GetSystemTime(&SystemTime); + BOOL fRet = SystemTimeToFileTime(&SystemTime, pTime); + if (fRet) + { /* likely */ } + else + { + MY_ASSERT(false, "GetSystemTimeAsFileTime: #2"); + pTime->dwHighDateTime = 0; + pTime->dwLowDateTime = 0; + } + } +} + + +/* + * NT 3.1 stuff. + */ + +DECL_KERNEL32(BOOL) Fake_GetVersionExA(LPOSVERSIONINFOA pInfo) +{ + DWORD dwVersion = GetVersion(); + + /* Common fields: */ + pInfo->dwMajorVersion = dwVersion & 0xff; + pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff; + if (!(dwVersion & RT_BIT_32(31))) + pInfo->dwBuildNumber = dwVersion >> 16; + else + pInfo->dwBuildNumber = 511; + pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT; +/** @todo get CSD from registry. */ + pInfo->szCSDVersion[0] = '\0'; + + /* OSVERSIONINFOEX fields: */ + if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo))) + { + LPOSVERSIONINFOEXA pInfoEx = (LPOSVERSIONINFOEXA)pInfo; + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wServicePackMinor)) + { + pInfoEx->wServicePackMajor = 0; + pInfoEx->wServicePackMinor = 0; + } + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wSuiteMask)) + pInfoEx->wSuiteMask = 0; + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType)) + pInfoEx->wProductType = VER_NT_WORKSTATION; + if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType)) + pInfoEx->wReserved = 0; + } + + return TRUE; +} + + +DECL_KERNEL32(BOOL) Fake_GetVersionExW(LPOSVERSIONINFOW pInfo) +{ + DWORD dwVersion = GetVersion(); + + /* Common fields: */ + pInfo->dwMajorVersion = dwVersion & 0xff; + pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff; + if (!(dwVersion & RT_BIT_32(31))) + pInfo->dwBuildNumber = dwVersion >> 16; + else + pInfo->dwBuildNumber = 511; + pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT; +/** @todo get CSD from registry. */ + pInfo->szCSDVersion[0] = '\0'; + + /* OSVERSIONINFOEX fields: */ + if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo))) + { + LPOSVERSIONINFOEXW pInfoEx = (LPOSVERSIONINFOEXW)pInfo; + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wServicePackMinor)) + { + pInfoEx->wServicePackMajor = 0; + pInfoEx->wServicePackMinor = 0; + } + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wSuiteMask)) + pInfoEx->wSuiteMask = 0; + if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType)) + pInfoEx->wProductType = VER_NT_WORKSTATION; + if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType)) + pInfoEx->wReserved = 0; + } + + return TRUE; +} + + +DECL_KERNEL32(LPWCH) Fake_GetEnvironmentStringsW(void) +{ + /* + * Environment is ANSI in NT 3.1. We should only be here for NT 3.1. + * For now, don't try do a perfect job converting it, just do it. + */ + char *pszzEnv = (char *)RTNtCurrentPeb()->ProcessParameters->Environment; + size_t offEnv = 0; + while (pszzEnv[offEnv] != '\0') + { + size_t cchLen = strlen(&pszzEnv[offEnv]); + offEnv += cchLen + 1; + } + size_t const cchEnv = offEnv + 1; + + PRTUTF16 pwszzEnv = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0, cchEnv * sizeof(RTUTF16)); + if (!pwszzEnv) + return NULL; + for (offEnv = 0; offEnv < cchEnv; offEnv++) + { + unsigned char ch = pwszzEnv[offEnv]; + if (!(ch & 0x80)) + pwszzEnv[offEnv] = ch; + else + pwszzEnv[offEnv] = '_'; + } + return pwszzEnv; +} + + +DECL_KERNEL32(BOOL) Fake_FreeEnvironmentStringsW(LPWCH pwszzEnv) +{ + if (pwszzEnv) + HeapFree(GetProcessHeap(), 0, pwszzEnv); + return TRUE; +} + + +DECL_KERNEL32(int) Fake_GetLocaleInfoA(LCID idLocale, LCTYPE enmType, LPSTR pData, int cchData) +{ + NOREF(idLocale); NOREF(enmType); NOREF(pData); NOREF(cchData); + //MY_ASSERT(false, "GetLocaleInfoA: idLocale=%#x enmType=%#x cchData=%#x", idLocale, enmType, cchData); + MY_ASSERT(false, "GetLocaleInfoA"); + SetLastError(ERROR_NOT_SUPPORTED); + return 0; +} + + +#if VCC_FAKES_TARGET >= 140 +/** @since 3.51 */ +DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesW(LOCALE_ENUMPROCW pfnLocaleEnum, DWORD dwFlags) +{ + RT_NOREF(pfnLocaleEnum, dwFlags); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} +#endif /* VCC_FAKES_TARGET >= 140 */ + + +DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesA(LOCALE_ENUMPROCA pfnCallback, DWORD fFlags) +{ + NOREF(pfnCallback); NOREF(fFlags); + //MY_ASSERT(false, "EnumSystemLocalesA: pfnCallback=%p fFlags=%#x", pfnCallback, fFlags); + MY_ASSERT(false, "EnumSystemLocalesA"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_IsValidLocale(LCID idLocale, DWORD fFlags) +{ + NOREF(idLocale); NOREF(fFlags); + //MY_ASSERT(false, "IsValidLocale: idLocale fFlags=%#x", idLocale, fFlags); + MY_ASSERT(false, "IsValidLocale"); + SetLastError(ERROR_NOT_SUPPORTED); + return FALSE; +} + + +DECL_KERNEL32(DWORD_PTR) Fake_SetThreadAffinityMask(HANDLE hThread, DWORD_PTR fAffinityMask) +{ + SYSTEM_INFO SysInfo; + GetSystemInfo(&SysInfo); + //MY_ASSERT(false, "SetThreadAffinityMask: hThread=%p fAffinityMask=%p SysInfo.dwActiveProcessorMask=%p", + // hThread, fAffinityMask, SysInfo.dwActiveProcessorMask); + MY_ASSERT(false, "SetThreadAffinityMask"); + if ( SysInfo.dwActiveProcessorMask == fAffinityMask + || fAffinityMask == ~(DWORD_PTR)0) + return fAffinityMask; + + SetLastError(ERROR_NOT_SUPPORTED); + RT_NOREF(hThread); + return 0; +} + + +DECL_KERNEL32(BOOL) Fake_GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR pfProcessAffinityMask, PDWORD_PTR pfSystemAffinityMask) +{ + SYSTEM_INFO SysInfo; + GetSystemInfo(&SysInfo); + //MY_ASSERT(false, "GetProcessAffinityMask: SysInfo.dwActiveProcessorMask=%p", SysInfo.dwActiveProcessorMask); + MY_ASSERT(false, "GetProcessAffinityMask"); + if (pfProcessAffinityMask) + *pfProcessAffinityMask = SysInfo.dwActiveProcessorMask; + if (pfSystemAffinityMask) + *pfSystemAffinityMask = SysInfo.dwActiveProcessorMask; + RT_NOREF(hProcess); + return TRUE; +} + + +DECL_KERNEL32(BOOL) Fake_GetHandleInformation(HANDLE hObject, DWORD *pfFlags) +{ + OBJECT_HANDLE_FLAG_INFORMATION Info = { 0, 0 }; + DWORD cbRet = sizeof(Info); + NTSTATUS rcNt = NtQueryObject(hObject, ObjectHandleFlagInformation, &Info, sizeof(Info), &cbRet); + if (NT_SUCCESS(rcNt)) + { + *pfFlags = (Info.Inherit ? HANDLE_FLAG_INHERIT : 0) + | (Info.ProtectFromClose ? HANDLE_FLAG_PROTECT_FROM_CLOSE : 0); + return TRUE; + } + *pfFlags = 0; + //MY_ASSERT(rcNt == STATUS_INVALID_HANDLE, "rcNt=%#x", rcNt); + MY_ASSERT(rcNt == STATUS_INVALID_HANDLE || rcNt == STATUS_INVALID_INFO_CLASS, "GetHandleInformation"); + SetLastError(rcNt == STATUS_INVALID_HANDLE ? ERROR_INVALID_HANDLE : ERROR_INVALID_FUNCTION); /* see also process-win.cpp */ + return FALSE; +} + + +DECL_KERNEL32(BOOL) Fake_SetHandleInformation(HANDLE hObject, DWORD fMask, DWORD fFlags) +{ + NOREF(hObject); NOREF(fMask); NOREF(fFlags); + SetLastError(ERROR_INVALID_FUNCTION); + return FALSE; +} + + + +/** + * Resolves all the APIs ones and for all, updating the fake IAT entries. + */ +DECLASM(void) FakeResolve_kernel32(void) +{ + CURRENT_VERSION_VARIABLE(); + + HMODULE hmod = GetModuleHandleW(L"kernel32"); + MY_ASSERT(hmod != NULL, "kernel32"); + +#undef MAKE_IMPORT_ENTRY +#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb) +#ifdef VCC_FAKES_TARGET_VCC100 +# include "vcc-fakes-kernel32-100.h" +#elif defined(VCC_FAKES_TARGET_VCC140) +# include "vcc-fakes-kernel32-141.h" +#elif defined(VCC_FAKES_TARGET_VCC141) +# include "vcc-fakes-kernel32-141.h" +#elif defined(VCC_FAKES_TARGET_VCC142) +# include "vcc-fakes-kernel32-141.h" +#else +# error "Port me!" +#endif + + g_fInitialized = true; +} + + +/* Dummy to force dragging in this object in the link, so the linker + won't accidentally use the symbols from kernel32. */ +extern "C" int vcc100_kernel32_fakes_cpp(void) +{ + return 42; +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp new file mode 100644 index 00000000..9aaf2d4d --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp @@ -0,0 +1,93 @@ +/* $Id: vcc-fakes-msvcrt.cpp $ */ +/** @file + * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP. + */ + +/* + * 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/assert.h> +#include <stdlib.h> + + +#ifndef RT_ARCH_X86 +# error "This code is X86 only" +#endif + + + +/* This one is static in libcmt, fortunately no rocket science. */ +extern "C" void __cdecl my_initterm(PFNRT *papfnStart, PFNRT *papfnEnd) +{ + for (; (uintptr_t)papfnStart < (uintptr_t)papfnEnd; papfnStart++) + if (*papfnStart) + (*papfnStart)(); +} + +extern "C" PFNRT __cdecl my_dllonexit(PFNRT pfnToAdd, PFNRT **ppapfnStart, PFNRT **ppapfnEnd) +{ + /* This is _very_ crude, but it'll probably do for our purposes... */ + size_t cItems = *ppapfnEnd - *ppapfnStart; + *ppapfnStart = (PFNRT *)realloc(*ppapfnStart, (cItems + 1) * sizeof(**ppapfnStart)); + (*ppapfnStart)[cItems] = pfnToAdd; + *ppapfnEnd = &(*ppapfnStart)[cItems + 1]; + return pfnToAdd; +} + +extern "C" int _newmode; +extern "C" int __cdecl __setargv(void); +extern "C" int __cdecl _setargv(void); + +extern "C" int __cdecl my_getmainargs(int *pcArgs, char ***ppapszArgs, char ***ppapszEnv, int fDoWildcardExp, int *pfNewMode) +{ + _newmode = *pfNewMode; + + Assert(!fDoWildcardExp); + int rc = _setargv(); + if (rc >= 0) + { + *pcArgs = __argc; + *ppapszArgs = __argv; + *ppapszEnv = _environ; + } + return rc; +} + +extern "C" void __cdecl my_setusermatherr(PFNRT pfnIgnore) +{ + RT_NOREF(pfnIgnore); + /* pure stub. */ +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm new file mode 100644 index 00000000..4eb482b2 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm @@ -0,0 +1,57 @@ +; $Id: vcc-fakes-ntdll-A.asm $ +;; @file +; IPRT - Wrappers for ntdll APIs misisng NT4. +; + +; +; 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 +; + +%include "iprt/asmdefs.mac" + +%ifndef RT_ARCH_X86 + %error "This is x86 only code. +%endif + + +%macro MAKE_IMPORT_ENTRY 2 +extern _ %+ %1 %+ @ %+ %2 +global __imp__ %+ %1 %+ @ %+ %2 +__imp__ %+ %1 %+ @ %+ %2: + dd _ %+ %1 %+ @ %+ %2 + +%endmacro + + +BEGINDATA +GLOBALNAME vcc100_ntdll_fakes_asm + +MAKE_IMPORT_ENTRY RtlGetLastWin32Error, 0 + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp new file mode 100644 index 00000000..9aa5dd01 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp @@ -0,0 +1,84 @@ +/* $Id: vcc-fakes-ntdll.cpp $ */ +/** @file + * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - NTDLL.DLL. + */ + +/* + * 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/cdefs.h> +#include <iprt/types.h> + +#ifndef RT_ARCH_X86 +# error "This code is X86 only" +#endif + +#include <iprt/win/windows.h> + + + +/** Dynamically resolves an kernel32 API. */ +#define RESOLVE_ME(ApiNm) \ + static bool volatile s_fInitialized = false; \ + static decltype(ApiNm) *s_pfnApi = NULL; \ + decltype(ApiNm) *pfnApi; \ + if (s_fInitialized) \ + pfnApi = s_pfnApi; \ + else \ + { \ + pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"ntdll.dll"), #ApiNm); \ + s_pfnApi = pfnApi; \ + s_fInitialized = true; \ + } do {} while (0) \ + + +extern "C" +__declspec(dllexport) +ULONG WINAPI RtlGetLastWin32Error(VOID) +{ + RESOLVE_ME(RtlGetLastWin32Error); + if (pfnApi) + return pfnApi(); + return GetLastError(); +} + + +/* Dummy to force dragging in this object in the link, so the linker + won't accidentally use the symbols from kernel32. */ +extern "C" int vcc100_ntdll_fakes_cpp(void) +{ + return 42; +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm new file mode 100644 index 00000000..ddf7491b --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm @@ -0,0 +1,59 @@ +; $Id: vcc-fakes-shell32-A.asm $ +;; @file +; IPRT - Wrappers for shell32 APIs missing in NT 4 and earlier. +; + +; +; 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 +; + + +%include "iprt/asmdefs.mac" + +%ifndef RT_ARCH_X86 + %error "This is x86 only code. +%endif + + +%macro MAKE_IMPORT_ENTRY 2 +extern _ %+ %1 %+ @ %+ %2 +global __imp__ %+ %1 %+ @ %+ %2 +__imp__ %+ %1 %+ @ %+ %2: + dd _ %+ %1 %+ @ %+ %2 + +%endmacro + + +BEGINDATA +GLOBALNAME vcc100_shell32_fakes_asm + +; NT 3.1 +MAKE_IMPORT_ENTRY CommandLineToArgvW, 8 + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp new file mode 100644 index 00000000..bd82befa --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp @@ -0,0 +1,103 @@ +/* $Id: vcc-fakes-shell32.cpp $ */ +/** @file + * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP. + */ + +/* + * 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 RT_NO_STRICT /* Minimal deps so that it works on NT 3.51 too. */ +#include <iprt/types.h> +#include <iprt/assert.h> +#include <iprt/string.h> + +#ifndef RT_ARCH_X86 +# error "This code is X86 only" +#endif + +#define CommandLineToArgvW Ignore_CommandLineToArgvW + +#include <iprt/nt/nt-and-windows.h> + +#undef CommandLineToArgvW + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Dynamically resolves an kernel32 API. */ +#define RESOLVE_ME(ApiNm) \ + static decltype(ShellExecuteW) * volatile s_pfnInitialized = NULL; \ + static decltype(ApiNm) *s_pfnApi = NULL; \ + decltype(ApiNm) *pfnApi; \ + if (s_pfnInitialized) \ + pfnApi = s_pfnApi; \ + else \ + { \ + pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"shell32"), #ApiNm); \ + s_pfnApi = pfnApi; \ + s_pfnInitialized = ShellExecuteW; /* ensures shell32 is loaded */ \ + } do {} while (0) + + +/** Declare a shell32 API. + * @note We are not exporting them as that causes duplicate symbol troubles in + * the OpenGL bits. */ +#define DECL_SHELL32(a_Type) extern "C" a_Type WINAPI + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + + +DECL_SHELL32(LPWSTR *) CommandLineToArgvW(LPCWSTR pwszCmdLine, int *pcArgs) +{ + RESOLVE_ME(CommandLineToArgvW); + if (pfnApi) + return pfnApi(pwszCmdLine, pcArgs); + + *pcArgs = 0; + return NULL; +} + + +/* Dummy to force dragging in this object in the link, so the linker + won't accidentally use the symbols from shell32. */ +extern "C" int vcc100_shell32_fakes_cpp(void) +{ + return 42; +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm new file mode 100644 index 00000000..2158917a --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm @@ -0,0 +1,58 @@ +; $Id: vcc-fakes-ws2_32-A.asm $ +;; @file +; IPRT - Wrappers for ws2_32 APIs misisng NT4. +; + +; +; 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 +; + +%include "iprt/asmdefs.mac" + +%ifndef RT_ARCH_X86 + %error "This is x86 only code. +%endif + + +%macro MAKE_IMPORT_ENTRY 2 +extern _ %+ %1 %+ @ %+ %2 +global __imp__ %+ %1 %+ @ %+ %2 +__imp__ %+ %1 %+ @ %+ %2: + dd _ %+ %1 %+ @ %+ %2 + +%endmacro + + +BEGINDATA +GLOBALNAME vcc100_ws2_32_fakes_asm + +MAKE_IMPORT_ENTRY getaddrinfo, 16 +MAKE_IMPORT_ENTRY freeaddrinfo, 4 + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp new file mode 100644 index 00000000..b12f253e --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp @@ -0,0 +1,113 @@ +/* $Id: vcc-fakes-ws2_32.cpp $ */ +/** @file + * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - WS2_32.DLL. + */ + +/* + * 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/cdefs.h> +#include <iprt/types.h> +#include <iprt/asm.h> +#include <iprt/string.h> + +#ifndef RT_ARCH_X86 +# error "This code is X86 only" +#endif + +#define getaddrinfo Ignore_getaddrinfo +#define freeaddrinfo Ignore_freeaddrinfo + +#include <iprt/win/winsock2.h> +#include <iprt/win/ws2tcpip.h> + +#undef getaddrinfo +#undef freeaddrinfo + + +/** Dynamically resolves an kernel32 API. */ +#define RESOLVE_ME(ApiNm) \ + static bool volatile s_fInitialized = false; \ + static decltype(ApiNm) *s_pfnApi = NULL; \ + decltype(ApiNm) *pfnApi; \ + if (s_fInitialized) \ + pfnApi = s_pfnApi; \ + else \ + { \ + pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"wc2_32.dll"), #ApiNm); \ + s_pfnApi = pfnApi; \ + s_fInitialized = true; \ + } do {} while (0) \ + + +extern "C" +__declspec(dllexport) +int WINAPI getaddrinfo(const char *pszNodeName, const char *pszServiceName, const struct addrinfo *pHints, + struct addrinfo **ppResults) +{ + RESOLVE_ME(getaddrinfo); + if (pfnApi) + return pfnApi(pszNodeName, pszServiceName, pHints, ppResults); + + /** @todo fallback */ + WSASetLastError(WSAEAFNOSUPPORT); + return WSAEAFNOSUPPORT; +} + + + +extern "C" +__declspec(dllexport) +void WINAPI freeaddrinfo(struct addrinfo *pResults) +{ + RESOLVE_ME(freeaddrinfo); + if (pfnApi) + pfnApi(pResults); + else + { + Assert(!pResults); + /** @todo fallback */ + } +} + + + +/* Dummy to force dragging in this object in the link, so the linker + won't accidentally use the symbols from kernel32. */ +extern "C" int vcc100_ws2_32_fakes_cpp(void) +{ + return 42; +} + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.h b/src/VBox/Runtime/r3/win/vcc-fakes.h new file mode 100644 index 00000000..6e72e2b8 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes.h @@ -0,0 +1,155 @@ +/* $Id: vcc-fakes.h $ */ +/** @file + * IPRT - Common macros for the Visual C++ 2010+ CRT import fakes. + */ + +/* + * 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 + */ + +#ifndef IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h +#define IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#ifdef RT_STRICT +# include <stdio.h> /* _snprintf */ +#endif + + +/** @def MY_ASSERT + * We use a special assertion macro here to avoid dragging in IPRT bits in + * places which cannot handle it (direct GA 3D bits or something like that). + * + * Turns out snprintf is off limits too. Needs Locale info and runs out of + * stack + */ +#ifdef RT_STRICT +# if 1 +# define MY_ASSERT(a_Expr, g_szMsg) \ + do { \ + if ((a_Expr)) \ + { /*likely*/ } \ + else \ + { \ + OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \ + OutputDebugStringA("Assertion message: " g_szMsg "\n"); \ + RT_BREAKPOINT(); \ + } \ + } while (0) +# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \ + do { \ + if (a_Expr) \ + { /* likely */ } \ + else \ + { \ + OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \ + RT_BREAKPOINT(); \ + a_Stmt; \ + return (a_rc); \ + } \ + } while (0) +# else +# define MY_ASSERT(a_Expr, ...) \ + do { \ + if ((a_Expr)) \ + { /*likely*/ } \ + else \ + { \ + char szTmp[256]; \ + _snprintf(szTmp, sizeof(szTmp), "Assertion failed on line %u in '%s': %s", __LINE__, __PRETTY_FUNCTION__, #a_Expr); \ + OutputDebugStringA(szTmp); \ + _snprintf(szTmp, sizeof(szTmp), __VA_ARGS__); \ + OutputDebugStringA(szTmp); \ + RT_BREAKPOINT(); \ + } \ + } while (0) +# endif +#else +# define MY_ASSERT(a_Expr, ...) do { } while (0) +# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \ + do { if (a_Expr) { /* likely */ } else { a_Stmt; return (a_rc); }} while (0) +#endif + + +/** Dynamically resolves an NTDLL API we need. */ +#define RESOLVE_NTDLL_API(ApiNm) \ + static bool volatile s_fInitialized##ApiNm = false; \ + static decltype(ApiNm) *s_pfn##ApiNm = NULL; \ + decltype(ApiNm) *pfn##ApiNm; \ + if (s_fInitialized##ApiNm) \ + pfn##ApiNm = s_pfn##ApiNm; \ + else \ + { \ + pfn##ApiNm = (decltype(pfn##ApiNm))GetProcAddress(GetModuleHandleW(L"ntdll"), #ApiNm); \ + s_pfn##ApiNm = pfn##ApiNm; \ + s_fInitialized##ApiNm = true; \ + } do {} while (0) + + +/** Declare a kernel32 API. + * @note We are not exporting them as that causes duplicate symbol troubles in + * the OpenGL bits. */ +#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI + + +/** Ignore comments. */ +#define COMMENT(a_Text) + +/** Used for MAKE_IMPORT_ENTRY when declaring external g_pfnXxxx variables. */ +#define DECLARE_FUNCTION_POINTER(a_Name, a_cb) extern "C" decltype(a_Name) *RT_CONCAT(g_pfn, a_Name); + +/** Used in the InitFakes method to decl are uCurVersion used by assertion. */ +#ifdef RT_STRICT +# define CURRENT_VERSION_VARIABLE() \ + unsigned uCurVersion = GetVersion(); \ + uCurVersion = ((uCurVersion & 0xff) << 8) | ((uCurVersion >> 8) & 0xff) +#else +# define CURRENT_VERSION_VARIABLE() (void)0 +#endif + + +/** Used for MAKE_IMPORT_ENTRY when resolving an import. */ +#define RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb) \ + do { \ + FARPROC pfnApi = GetProcAddress(hmod, #a_Name); \ + if (pfnApi) \ + RT_CONCAT(g_pfn, a_Name) = (decltype(a_Name) *)pfnApi; \ + else \ + { \ + MY_ASSERT(uCurVersion < (((a_uMajorVer) << 8) | (a_uMinorVer)), #a_Name); \ + RT_CONCAT(g_pfn, a_Name) = RT_CONCAT(Fake_,a_Name); \ + } \ + } while (0); + + +#endif /* !IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h */ + diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.mac b/src/VBox/Runtime/r3/win/vcc-fakes.mac new file mode 100644 index 00000000..37c51543 --- /dev/null +++ b/src/VBox/Runtime/r3/win/vcc-fakes.mac @@ -0,0 +1,76 @@ +; $Id: vcc-fakes.mac $ +;; @file +; IPRT - Common macros for the Visual C++ 2010+ CRT import fakes. +; + +; +; 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 +; + + +%include "iprt/asmdefs.mac" + +%ifndef RT_ARCH_X86 + %error "This is x86 only code. +%endif + + +;; +; Fakes an API import table entry. +; +; @param 1 The function name +; @param 2 Number of bytes of parameters for x86. +%macro MAKE_IMPORT_ENTRY_INTERNAL 2 + +BEGINDATA +extern _FakeResolve_ %+ FAKE_MODULE_NAME + +extern _Fake_ %+ %1 %+ @ %+ %2 +global __imp__ %+ %1 %+ @ %+ %2 ; The import address table (IAT) entry name. +__imp__ %+ %1 %+ @ %+ %2: +global _g_pfn %+ %1 ; C accessible label. +_g_pfn %+ %1: + dd FakeLazyInit_ %+ %1 + +BEGINCODE +FakeLazyInit_ %+ %1: + pusha + call _FakeResolve_ %+ FAKE_MODULE_NAME + popa +global _ %+ %1 %+ @ %+ %2 ; The imported function stub. +_ %+ %1 %+ @ %+ %2: + jmp [_g_pfn %+ %1] + + +%endmacro + +%define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cbParams) MAKE_IMPORT_ENTRY_INTERNAL a_Name, a_cbParams +%define COMMENT(a_Text) + |