diff options
Diffstat (limited to '')
51 files changed, 18830 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk new file mode 100644 index 00000000..eead95e5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk @@ -0,0 +1,254 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the common guest addition code library. +# + +# +# 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 +# + +SUB_DEPTH = ../../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# Target config. +# +if defined(VBOX_WITH_ADDITION_DRIVERS) && !defined(VBOX_ONLY_VALIDATIONKIT) + LIBRARIES += \ + VBoxGuestR0Lib \ + VBoxGuestR0LibBase +endif +LIBRARIES += \ + VBoxGuestR3Lib \ + VBoxGuestR3LibShared +ifndef VBOX_ONLY_VALIDATIONKIT + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd) + ifndef VBOX_USE_SYSTEM_XORG_HEADERS + LIBRARIES += \ + VBoxGuestR3LibXFree86 + endif + endif + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) + LIBRARIES += \ + VBoxGuestR3LibXOrg + endif +endif +LIBRARIES.win.amd64 += VBoxGuestR3Lib-x86 VBoxGuestR3LibShared-x86 + + +# +# VBoxGuestR0Lib +# +VBoxGuestR0Lib_TEMPLATE = VBoxGuestR0DrvLib +VBoxGuestR0Lib_DEFS = VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxGuestR0Lib_INCS = \ + $(VBoxGuestR0Lib_0_OUTDIR) +VBoxGuestR0Lib_SOURCES = \ + VBoxGuestR0LibInit.cpp \ + VBoxGuestR0LibPhysHeap.cpp \ + VBoxGuestR0LibGenericRequest.cpp \ + VBoxGuestR0LibVMMDev.cpp \ + VBoxGuestR0LibHGCM.cpp \ + VbglR0CanUsePhysPageList.cpp \ + \ + VBoxGuestR0LibIdc.cpp \ + VBoxGuestR0LibSharedFolders.c \ + VBoxGuestR0LibCrOgl.cpp \ + VBoxGuestR0LibMouse.cpp +VBoxGuestR0Lib_SOURCES.os2 = VBoxGuestR0LibIdc-os2.cpp +VBoxGuestR0Lib_SOURCES.solaris = VBoxGuestR0LibIdc-solaris.cpp +VBoxGuestR0Lib_SOURCES.win = VBoxGuestR0LibIdc-win.cpp +ifn1of ($(KBUILD_TARGET), os2 solaris win) + VBoxGuestR0Lib_SOURCES += VBoxGuestR0LibIdc-unix.cpp +endif + +# +# VBoxGuestR0LibBase +# +VBoxGuestR0LibBase_TEMPLATE = VBoxGuestR0DrvLib +VBoxGuestR0LibBase_DEFS = VBOX_WITH_HGCM VBGL_VBOXGUEST \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxGuestR0LibBase_INCS = $(VBoxGuestR0Lib_INCS) +VBoxGuestR0LibBase_INCS.win = $(VBoxGuestR0Lib_INCS.win) +VBoxGuestR0LibBase_SOURCES = \ + VBoxGuestR0LibInit.cpp \ + VBoxGuestR0LibPhysHeap.cpp \ + VBoxGuestR0LibGenericRequest.cpp \ + VBoxGuestR0LibVMMDev.cpp \ + VBoxGuestR0LibHGCMInternal.cpp \ + VbglR0CanUsePhysPageList.cpp + +# +# VBoxGuestR3Lib +# +VBoxGuestR3Lib_TEMPLATE := VBoxGuestR3Lib +VBoxGuestR3Lib_DEFS = \ + VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD),VBOX_WITH_SHARED_CLIPBOARD,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) \ + $(if $(VBOX_WITH_SHARED_FOLDERS),VBOX_WITH_SHARED_FOLDERS,) \ + $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,) +VBoxGuestR3Lib_SOURCES = \ + VBoxGuestR3Lib.cpp \ + VBoxGuestR3LibAdditions.cpp \ + VBoxGuestR3LibAutoLogon.cpp \ + VBoxGuestR3LibBalloon.cpp \ + VBoxGuestR3LibCoreDump.cpp \ + VBoxGuestR3LibCpuHotPlug.cpp \ + VBoxGuestR3LibCredentials.cpp \ + VBoxGuestR3LibDrmClient.cpp \ + VBoxGuestR3LibEvent.cpp \ + VBoxGuestR3LibGuestUser.cpp \ + VBoxGuestR3LibGR.cpp \ + VBoxGuestR3LibHGCM.cpp \ + VBoxGuestR3LibHostChannel.cpp \ + VBoxGuestR3LibLog.cpp \ + VBoxGuestR3LibMisc.cpp \ + VBoxGuestR3LibStat.cpp \ + VBoxGuestR3LibTime.cpp \ + VBoxGuestR3LibModule.cpp \ + VBoxGuestR3LibPidFile.cpp \ + VBoxGuestR3LibVrdp.cpp \ + VBoxGuestR3LibMouse.cpp \ + VBoxGuestR3LibSeamless.cpp \ + VBoxGuestR3LibVideo.cpp +ifneq ($(KBUILD_TARGET),win) + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibDaemonize.cpp +endif +ifdef VBOX_WITH_GUEST_PROPS + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibGuestProp.cpp \ + VBoxGuestR3LibHostVersion.cpp +endif +ifdef VBOX_WITH_SHARED_CLIPBOARD + VBoxGuestR3Lib_DEFS += VBOX_WITH_SHARED_CLIPBOARD_GUEST + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibClipboard.cpp + ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + VBoxGuestR3Lib_SOURCES += \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardMIME.cpp + endif +endif +ifdef VBOX_WITH_SHARED_FOLDERS + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibSharedFolders.cpp +endif +ifdef VBOX_WITH_GUEST_CONTROL + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibGuestCtrl.cpp +endif +ifdef VBOX_WITH_DRAG_AND_DROP + VBoxGuestR3Lib_DEFS += \ + VBOX_WITH_DRAG_AND_DROP \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) + VBoxGuestR3Lib_SOURCES += \ + VBoxGuestR3LibDragAndDrop.cpp +endif + +VBoxGuestR3LibAdditions.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV) + +# +# VBoxGuestR3LibShared - a PIC variant of VBoxGuestR3Lib for linking into .so/.dll/.dylib. +# +VBoxGuestR3LibShared_TEMPLATE := VBoxGuestR3Dll +VBoxGuestR3LibShared_DEFS := $(VBoxGuestR3Lib_DEFS) +VBoxGuestR3LibShared_SOURCES := $(VBoxGuestR3Lib_SOURCES) +VBoxGuestR3LibShared_INST := $(INST_ADDITIONS_LIB) + + +# +# VBoxGuestR3Lib-x86 - an x86 (32-bit) variant of VBoxGuestR3Lib for 64-bit Windows. +# +VBoxGuestR3Lib-x86_EXTENDS := VBoxGuestR3Lib +VBoxGuestR3Lib-x86_BLD_TRG_ARCH := x86 + + +# +# VBoxGuestR3LibShared-x86 - an x86 (32-bit) variant of VBoxGuestR3LibShared for 64-bit Windows. +# +VBoxGuestR3LibShared-x86_EXTENDS := VBoxGuestR3LibShared +VBoxGuestR3LibShared-x86_BLD_TRG_ARCH := x86 + + +# +# VBoxGuestR3LibXFree86 - a reduced version of the guest library which uses +# the X server runtime instead of IPRT, for use with old servers where the +# C library is not available. +# +VBoxGuestR3LibXFree86_TEMPLATE = VBoxGuestR3XFree86Lib +VBoxGuestR3LibXFree86_DEFS = \ + VBOX_WITH_HGCM \ + VBOX_VBGLR3_XFREE86 \ + RTMEM_NO_WRAP_TO_EF_APIS \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) +VBoxGuestR3LibXFree86_SOURCES = \ + VBoxGuestR3Lib.cpp \ + VBoxGuestR3LibGR.cpp \ + $(if $(VBOX_WITH_GUEST_PROPS),VBoxGuestR3LibGuestProp.cpp,) \ + VBoxGuestR3LibMouse.cpp \ + VBoxGuestR3LibMisc.cpp \ + VBoxGuestR3LibSeamless.cpp \ + VBoxGuestR3LibVideo.cpp \ + VBoxGuestR3LibRuntimeXF86.cpp +VBoxGuestR3LibXFree86_INCS = \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/Xserver \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11 + +# +# VBoxGuestR3LibXOrg - a reduced version of the guest library which uses +# the C server runtime instead of IPRT. +# +VBoxGuestR3LibXOrg_TEMPLATE = VBoxGuestR3XOrgLib +VBoxGuestR3LibXOrg_DEFS = \ + VBOX_WITH_HGCM \ + VBOX_VBGLR3_XORG \ + RTMEM_NO_WRAP_TO_EF_APIS \ + IN_RT_STATIC \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \ + $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) +VBoxGuestR3LibXOrg_SOURCES = $(VBoxGuestR3LibXFree86_SOURCES) + +VBoxGuestR3LibRuntimeXF86.cpp_CXXFLAGS = -Wno-shadow + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp new file mode 100644 index 00000000..a6638dc0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp @@ -0,0 +1,134 @@ +/* $Id: VBoxGuestR0LibCrOgl.cpp $ */ +/** @file + * VBoxGuestLib - Ring-3 Support Library for VirtualBox guest additions, Chromium OpenGL Service. + */ + +/* + * Copyright (C) 2012-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include "VBoxGuestR0LibInternal.h" + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +DECLR0VBGL(int) VbglR0CrCtlCreate(VBGLCRCTLHANDLE *phCtl) +{ + int rc; + + if (phCtl) + { + struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc(); + if (pHandleData) + { + rc = VbglR0IdcOpen(&pHandleData->IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/); + if (RT_SUCCESS(rc)) + { + *phCtl = pHandleData; + return VINF_SUCCESS; + } + + vbglR0HGCMHandleFree(pHandleData); + } + else + rc = VERR_NO_MEMORY; + + *phCtl = NULL; + } + else + rc = VERR_INVALID_PARAMETER; + + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlDestroy(VBGLCRCTLHANDLE hCtl) +{ + VbglR0IdcClose(&hCtl->IdcHandle); + + vbglR0HGCMHandleFree(hCtl); + + return VINF_SUCCESS; +} + +DECLR0VBGL(int) VbglR0CrCtlConConnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID *pidClient) +{ + VBGLIOCHGCMCONNECT info; + int rc; + + if (!hCtl || !pidClient) + return VERR_INVALID_PARAMETER; + + RT_ZERO(info); + VBGLREQHDR_INIT(&info.Hdr, HGCM_CONNECT); + info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + RTStrCopy(info.u.In.Loc.u.host.achName, sizeof(info.u.In.Loc.u.host.achName), "VBoxSharedCrOpenGL"); + rc = VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &info.Hdr, sizeof(info)); + if (RT_SUCCESS(rc)) + { + Assert(info.u.Out.idClient); + *pidClient = info.u.Out.idClient; + return rc; + } + + AssertRC(rc); + *pidClient = 0; + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlConDisconnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID idClient) +{ + VBGLIOCHGCMDISCONNECT info; + VBGLREQHDR_INIT(&info.Hdr, HGCM_DISCONNECT); + info.u.In.idClient = idClient; + return VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &info.Hdr, sizeof(info)); +} + +DECLR0VBGL(int) VbglR0CrCtlConCallRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); +} + +DECLR0VBGL(int) VbglR0CrCtlConCall(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + int rc = VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); + if (RT_SUCCESS(rc)) + rc = pCallInfo->Hdr.rc; + return rc; +} + +DECLR0VBGL(int) VbglR0CrCtlConCallUserDataRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo) +{ + return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbCallInfo), &pCallInfo->Hdr, cbCallInfo); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp new file mode 100644 index 00000000..72518a3e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp @@ -0,0 +1,183 @@ +/* $Id: VBoxGuestR0LibGenericRequest.cpp $ */ +/** @file + * VBoxGuestLibR0 - Generic VMMDev request management. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <VBox/err.h> + + +DECLR0VBGL(int) VbglGR0Verify(const VMMDevRequestHeader *pReq, size_t cbReq) +{ + size_t cbReqExpected; + + if (RT_UNLIKELY(!pReq || cbReq < sizeof(VMMDevRequestHeader))) + { + dprintf(("VbglGR0Verify: Invalid parameter: pReq = %p, cbReq = %zu\n", pReq, cbReq)); + return VERR_INVALID_PARAMETER; + } + + if (RT_UNLIKELY(pReq->size > cbReq)) + { + dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq)); + return VERR_INVALID_PARAMETER; + } + + /* The request size must correspond to the request type. */ + cbReqExpected = vmmdevGetRequestSize(pReq->requestType); + if (RT_UNLIKELY(cbReq < cbReqExpected)) + { + dprintf(("VbglGR0Verify: buffer size %zu < expected size %zu\n", cbReq, cbReqExpected)); + return VERR_INVALID_PARAMETER; + } + + if (cbReqExpected == cbReq) + { + /* + * This is most likely a fixed size request, and in this case the + * request size must be also equal to the expected size. + */ + if (RT_UNLIKELY(pReq->size != cbReqExpected)) + { + dprintf(("VbglGR0Verify: request size %u != expected size %zu\n", pReq->size, cbReqExpected)); + return VERR_INVALID_PARAMETER; + } + + return VINF_SUCCESS; + } + + /* + * This can be a variable size request. Check the request type and limit the size + * to VMMDEV_MAX_VMMDEVREQ_SIZE, which is max size supported by the host. + * + * Note: Keep this list sorted for easier human lookup! + */ + if ( pReq->requestType == VMMDevReq_ChangeMemBalloon + || pReq->requestType == VMMDevReq_GetDisplayChangeRequestMulti +#ifdef VBOX_WITH_64_BITS_GUESTS + || pReq->requestType == VMMDevReq_HGCMCall64 +#endif + || pReq->requestType == VMMDevReq_HGCMCall32 + || pReq->requestType == VMMDevReq_RegisterSharedModule + || pReq->requestType == VMMDevReq_ReportGuestUserState + || pReq->requestType == VMMDevReq_LogString + || pReq->requestType == VMMDevReq_SetPointerShape + || pReq->requestType == VMMDevReq_VideoSetVisibleRegion + || pReq->requestType == VMMDevReq_VideoUpdateMonitorPositions) + { + if (RT_UNLIKELY(cbReq > VMMDEV_MAX_VMMDEVREQ_SIZE)) + { + dprintf(("VbglGR0Verify: VMMDevReq_LogString: buffer size %zu too big\n", cbReq)); + return VERR_BUFFER_OVERFLOW; /** @todo is this error code ok? */ + } + } + else + { + dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq)); + return VERR_IO_BAD_LENGTH; /** @todo is this error code ok? */ + } + + return VINF_SUCCESS; +} + +DECLR0VBGL(int) VbglR0GRAlloc(VMMDevRequestHeader **ppReq, size_t cbReq, VMMDevRequestType enmReqType) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + { + if ( ppReq + && cbReq >= sizeof(VMMDevRequestHeader) + && cbReq == (uint32_t)cbReq) + { + VMMDevRequestHeader *pReq = (VMMDevRequestHeader *)VbglR0PhysHeapAlloc((uint32_t)cbReq); + AssertMsgReturn(pReq, ("VbglR0GRAlloc: no memory (cbReq=%u)\n", cbReq), VERR_NO_MEMORY); + memset(pReq, 0xAA, cbReq); + + pReq->size = (uint32_t)cbReq; + pReq->version = VMMDEV_REQUEST_HEADER_VERSION; + pReq->requestType = enmReqType; + pReq->rc = VERR_GENERAL_FAILURE; + pReq->reserved1 = 0; +#ifdef VBGL_VBOXGUEST + pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV +#else + pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV_OTHER +#endif + + | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN; + *ppReq = pReq; + rc = VINF_SUCCESS; + } + else + { + dprintf(("VbglR0GRAlloc: Invalid parameter: ppReq=%p cbReq=%u\n", ppReq, cbReq)); + rc = VERR_INVALID_PARAMETER; + } + } + return rc; +} + +DECLR0VBGL(int) VbglR0GRPerform(VMMDevRequestHeader *pReq) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + { + if (pReq) + { + RTCCPHYS PhysAddr = VbglR0PhysHeapGetPhysAddr(pReq); + if ( PhysAddr != 0 + && PhysAddr < _4G) /* Port IO is 32 bit. */ + { + ASMOutU32(g_vbgldata.portVMMDev + VMMDEV_PORT_OFF_REQUEST, (uint32_t)PhysAddr); + /* Make the compiler aware that the host has changed memory. */ + ASMCompilerBarrier(); + rc = pReq->rc; + } + else + rc = VERR_VBGL_INVALID_ADDR; + } + else + rc = VERR_INVALID_PARAMETER; + } + return rc; +} + +DECLR0VBGL(void) VbglR0GRFree(VMMDevRequestHeader *pReq) +{ + int rc = vbglR0Enter(); + if (RT_SUCCESS(rc)) + VbglR0PhysHeapFree(pReq); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp new file mode 100644 index 00000000..e2e28ebb --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp @@ -0,0 +1,240 @@ +/* $Id: VBoxGuestR0LibHGCM.cpp $ */ +/** @file + * VBoxGuestLib - Host-Guest Communication Manager, ring-0 client drivers. + * + * These public functions can be only used by other drivers. They all + * do an IOCTL to VBoxGuest via IDC. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/assert.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <VBox/err.h> + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBGL_HGCM_ASSERT_MSG AssertReleaseMsg + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * Fast heap for HGCM handles data. + * @{ + */ +static RTSEMFASTMUTEX g_hMtxHGCMHandleData; +static struct VBGLHGCMHANDLEDATA g_aHGCMHandleData[64]; +/** @} */ + + +/** + * Initializes the HGCM VBGL bits. + * + * @return VBox status code. + */ +DECLR0VBGL(int) VbglR0HGCMInit(void) +{ + AssertReturn(g_hMtxHGCMHandleData == NIL_RTSEMFASTMUTEX, VINF_ALREADY_INITIALIZED); + return RTSemFastMutexCreate(&g_hMtxHGCMHandleData); +} + +/** + * Initializes the HGCM VBGL bits. + * + * @return VBox status code. + */ +DECLR0VBGL(int) VbglR0HGCMTerminate(void) +{ + RTSemFastMutexDestroy(g_hMtxHGCMHandleData); + g_hMtxHGCMHandleData = NIL_RTSEMFASTMUTEX; + + return VINF_SUCCESS; +} + +DECLINLINE(int) vbglR0HandleHeapEnter(void) +{ + int rc = RTSemFastMutexRequest(g_hMtxHGCMHandleData); + + VBGL_HGCM_ASSERT_MSG(RT_SUCCESS(rc), ("Failed to request handle heap mutex, rc = %Rrc\n", rc)); + + return rc; +} + +DECLINLINE(void) vbglR0HandleHeapLeave(void) +{ + RTSemFastMutexRelease(g_hMtxHGCMHandleData); +} + +struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void) +{ + struct VBGLHGCMHANDLEDATA *p = NULL; + int rc = vbglR0HandleHeapEnter(); + if (RT_SUCCESS(rc)) + { + uint32_t i; + + /* Simple linear search in array. This will be called not so often, only connect/disconnect. */ + /** @todo bitmap for faster search and other obvious optimizations. */ + for (i = 0; i < RT_ELEMENTS(g_aHGCMHandleData); i++) + { + if (!g_aHGCMHandleData[i].fAllocated) + { + p = &g_aHGCMHandleData[i]; + p->fAllocated = 1; + break; + } + } + + vbglR0HandleHeapLeave(); + + VBGL_HGCM_ASSERT_MSG(p != NULL, ("Not enough HGCM handles.\n")); + } + return p; +} + +void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle) +{ + if (pHandle) + { + int rc = vbglR0HandleHeapEnter(); + if (RT_SUCCESS(rc)) + { + VBGL_HGCM_ASSERT_MSG(pHandle->fAllocated, ("Freeing not allocated handle.\n")); + + RT_ZERO(*pHandle); + vbglR0HandleHeapLeave(); + } + } +} + +DECLR0VBGL(int) VbglR0HGCMConnect(VBGLHGCMHANDLE *pHandle, const char *pszServiceName, HGCMCLIENTID *pidClient) +{ + int rc; + if (pHandle && pszServiceName && pidClient) + { + struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc(); + if (pHandleData) + { + rc = VbglR0IdcOpen(&pHandleData->IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/); + if (RT_SUCCESS(rc)) + { + VBGLIOCHGCMCONNECT Info; + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT); + Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName); + if (RT_SUCCESS(rc)) + { + rc = VbglR0IdcCall(&pHandleData->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + { + *pidClient = Info.u.Out.idClient; + *pHandle = pHandleData; + return rc; + } + } + + VbglR0IdcClose(&pHandleData->IdcHandle); + } + + vbglR0HGCMHandleFree(pHandleData); + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMDisconnect(VBGLHGCMHANDLE handle, HGCMCLIENTID idClient) +{ + int rc; + VBGLIOCHGCMDISCONNECT Info; + + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT); + Info.u.In.idClient = idClient; + rc = VbglR0IdcCall(&handle->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info)); + + VbglR0IdcClose(&handle->IdcHandle); + + vbglR0HGCMHandleFree(handle); + + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMCallRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter), + ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms, + sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL))); + + return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbData), &pData->Hdr, cbData); +} + +DECLR0VBGL(int) VbglR0HGCMCall(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + int rc = VbglR0HGCMCallRaw(handle, pData, cbData); + if (RT_SUCCESS(rc)) + rc = pData->Hdr.rc; + return rc; +} + +DECLR0VBGL(int) VbglR0HGCMCallUserDataRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData) +{ + VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter), + ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms, + sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL))); + + return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbData), &pData->Hdr, cbData); +} + + +DECLR0VBGL(int) VbglR0HGCMFastCall(VBGLHGCMHANDLE hHandle, PVBGLIOCIDCHGCMFASTCALL pCallReq, uint32_t cbCallReq) +{ + /* pCallReq->Hdr.rc and pCallReq->HgcmCallReq.header.header.rc; are not used by this IDC. */ + return VbglR0IdcCallRaw(&hHandle->IdcHandle, VBGL_IOCTL_IDC_HGCM_FAST_CALL, &pCallReq->Hdr, cbCallReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp new file mode 100644 index 00000000..2a237db1 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp @@ -0,0 +1,1175 @@ +/* $Id: VBoxGuestR0LibHGCMInternal.cpp $ */ +/** @file + * VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_HGCM + +#include "VBoxGuestR0LibInternal.h" +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include <VBox/err.h> + +#ifndef VBGL_VBOXGUEST +# error "This file should only be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The max parameter buffer size for a user request. */ +#define VBGLR0_MAX_HGCM_USER_PARM (24*_1M) +/** The max parameter buffer size for a kernel request. */ +#define VBGLR0_MAX_HGCM_KERNEL_PARM (16*_1M) +/** The max embedded buffer size. */ +#define VBGLR0_MAX_HGCM_EMBEDDED_BUFFER _64K + +#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) +/** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted + * side effects. + * Darwin 32bit & 64bit also needs this because of 4GB/4GB user/kernel space. */ +# define USE_BOUNCE_BUFFERS +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Lock info structure used by VbglR0HGCMInternalCall and its helpers. + */ +struct VbglR0ParmInfo +{ + uint32_t cLockBufs; + struct + { + uint32_t iParm; + RTR0MEMOBJ hObj; +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf; +#endif + } aLockBufs[10]; +}; + + + +/* These functions can be only used by VBoxGuest. */ + +DECLR0VBGL(int) VbglR0HGCMInternalConnect(HGCMServiceLocation const *pLoc, uint32_t fRequestor, HGCMCLIENTID *pidClient, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + int rc; + if ( RT_VALID_PTR(pLoc) + && RT_VALID_PTR(pidClient) + && RT_VALID_PTR(pfnAsyncCallback)) + { + /* Allocate request */ + VMMDevHGCMConnect *pHGCMConnect = NULL; + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMConnect, sizeof(VMMDevHGCMConnect), VMMDevReq_HGCMConnect); + if (RT_SUCCESS(rc)) + { + /* Initialize request memory */ + pHGCMConnect->header.header.fRequestor = fRequestor; + + pHGCMConnect->header.fu32Flags = 0; + + memcpy(&pHGCMConnect->loc, pLoc, sizeof(pHGCMConnect->loc)); + pHGCMConnect->u32ClientID = 0; + + /* Issue request */ + rc = VbglR0GRPerform (&pHGCMConnect->header.header); + if (RT_SUCCESS(rc)) + { + /* Check if host decides to process the request asynchronously. */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + /* Wait for request completion interrupt notification from host */ + pfnAsyncCallback(&pHGCMConnect->header, pvAsyncData, u32AsyncData); + } + + rc = pHGCMConnect->header.result; + if (RT_SUCCESS(rc)) + *pidClient = pHGCMConnect->u32ClientID; + } + VbglR0GRFree(&pHGCMConnect->header.header); + } + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + + +DECLR0VBGL(int) VbglR0HGCMInternalDisconnect(HGCMCLIENTID idClient, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + int rc; + if ( idClient != 0 + && pfnAsyncCallback) + { + /* Allocate request */ + VMMDevHGCMDisconnect *pHGCMDisconnect = NULL; + rc = VbglR0GRAlloc ((VMMDevRequestHeader **)&pHGCMDisconnect, sizeof (VMMDevHGCMDisconnect), VMMDevReq_HGCMDisconnect); + if (RT_SUCCESS(rc)) + { + /* Initialize request memory */ + pHGCMDisconnect->header.header.fRequestor = fRequestor; + + pHGCMDisconnect->header.fu32Flags = 0; + + pHGCMDisconnect->u32ClientID = idClient; + + /* Issue request */ + rc = VbglR0GRPerform(&pHGCMDisconnect->header.header); + if (RT_SUCCESS(rc)) + { + /* Check if host decides to process the request asynchronously. */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + /* Wait for request completion interrupt notification from host */ + pfnAsyncCallback(&pHGCMDisconnect->header, pvAsyncData, u32AsyncData); + } + + rc = pHGCMDisconnect->header.result; + } + + VbglR0GRFree(&pHGCMDisconnect->header.header); + } + } + else + rc = VERR_INVALID_PARAMETER; + return rc; +} + + +/** + * Preprocesses the HGCM call, validating and locking/buffering parameters. + * + * @returns VBox status code. + * + * @param pCallInfo The call info. + * @param cbCallInfo The size of the call info structure. + * @param fIsUser Is it a user request or kernel request. + * @param pcbExtra Where to return the extra request space needed for + * physical page lists. + */ +static int vbglR0HGCMInternalPreprocessCall(PCVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, + bool fIsUser, struct VbglR0ParmInfo *pParmInfo, size_t *pcbExtra) +{ + HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + uint32_t const cParms = pCallInfo->cParms; + uint32_t iParm; + uint32_t cb; + + /* + * Lock down the any linear buffers so we can get their addresses + * and figure out how much extra storage we need for page lists. + * + * Note! With kernel mode users we can be assertive. For user mode users + * we should just (debug) log it and fail without any fanfare. + */ + *pcbExtra = 0; + pParmInfo->cLockBufs = 0; + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++) + { + switch (pSrcParm->type) + { + case VMMDevHGCMParmType_32bit: + Log4(("GstHGCMCall: parm=%u type=32bit: %#010x\n", iParm, pSrcParm->u.value32)); + break; + + case VMMDevHGCMParmType_64bit: + Log4(("GstHGCMCall: parm=%u type=64bit: %#018RX64\n", iParm, pSrcParm->u.value64)); + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + if (fIsUser) + return VERR_INVALID_PARAMETER; + cb = pSrcParm->u.PageList.size; + if (cb) + { + uint32_t off = pSrcParm->u.PageList.offset; + HGCMPageListInfo *pPgLst; + uint32_t cPages; + uint32_t u32; + + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter) + && off <= cbCallInfo - sizeof(HGCMPageListInfo), + ("offset=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + + pPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + off); + cPages = pPgLst->cPages; + u32 = RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]) + off; + AssertMsgReturn(u32 <= cbCallInfo, + ("u32=%#x (cPages=%#x offset=%#x) cbCallInfo=%#x\n", u32, cPages, off, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn(pPgLst->offFirstPage < PAGE_SIZE, ("#x\n", pPgLst->offFirstPage), VERR_INVALID_PARAMETER); + u32 = RT_ALIGN_32(pPgLst->offFirstPage + cb, PAGE_SIZE) >> PAGE_SHIFT; + AssertMsgReturn(cPages == u32, ("cPages=%#x u32=%#x\n", cPages, u32), VERR_INVALID_PARAMETER); + AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pPgLst->flags), ("%#x\n", pPgLst->flags), VERR_INVALID_PARAMETER); + Log4(("GstHGCMCall: parm=%u type=pglst: cb=%#010x cPgs=%u offPg0=%#x flags=%#x\n", + iParm, cb, cPages, pPgLst->offFirstPage, pPgLst->flags)); + u32 = cPages; + while (u32-- > 0) + { + Log4(("GstHGCMCall: pg#%u=%RHp\n", u32, pPgLst->aPages[u32])); + AssertMsgReturn(!(pPgLst->aPages[u32] & (PAGE_OFFSET_MASK | UINT64_C(0xfff0000000000000))), + ("pg#%u=%RHp\n", u32, pPgLst->aPages[u32]), + VERR_INVALID_PARAMETER); + } + + *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[pPgLst->cPages]); + } + else + Log4(("GstHGCMCall: parm=%u type=pglst: cb=0\n", iParm)); + break; + + case VMMDevHGCMParmType_Embedded: + if (fIsUser) /// @todo relax this. + return VERR_INVALID_PARAMETER; + cb = pSrcParm->u.Embedded.cbData; + if (cb) + { + uint32_t off = pSrcParm->u.Embedded.offData; + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_EMBEDDED_BUFFER, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_EMBEDDED_BUFFER), + VERR_INVALID_PARAMETER); + AssertMsgReturn(cb <= cbCallInfo - cParms * sizeof(HGCMFunctionParameter), + ("cb=%#x cParms=%#x cbCallInfo=%3x\n", cb, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter) + && off <= cbCallInfo - cb, + ("offData=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo), + VERR_INVALID_PARAMETER); + AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pSrcParm->u.Embedded.fFlags), + ("%#x\n", pSrcParm->u.Embedded.fFlags), VERR_INVALID_PARAMETER); + + *pcbExtra += RT_ALIGN_32(cb, 8); + } + else + Log4(("GstHGCMCall: parm=%u type=embed: cb=0\n", iParm)); + break; + + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (fIsUser) + return VERR_INVALID_PARAMETER; + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + cb = pSrcParm->u.Pointer.size; + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + if (cb != 0) + Log4(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr)); + else + Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type)); + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + cb = pSrcParm->u.Pointer.size; + if (cb != 0) + { +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf = NULL; +#endif + uint32_t iLockBuf = pParmInfo->cLockBufs; + RTR0MEMOBJ hObj; + int rc; + uint32_t fAccess = pSrcParm->type == VMMDevHGCMParmType_LinAddr_In + || pSrcParm->type == VMMDevHGCMParmType_LinAddr_Locked_In + ? RTMEM_PROT_READ + : RTMEM_PROT_READ | RTMEM_PROT_WRITE; + + AssertReturn(iLockBuf < RT_ELEMENTS(pParmInfo->aLockBufs), VERR_INVALID_PARAMETER); + if (!fIsUser) + { + AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM), + VERR_OUT_OF_RANGE); + rc = RTR0MemObjLockKernel(&hObj, (void *)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess); + if (RT_FAILURE(rc)) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockKernel(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked kernel -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + } + else if (cb > VBGLR0_MAX_HGCM_USER_PARM) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u pv=%p cb=%#x > %#x -> out of range\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, + cb, VBGLR0_MAX_HGCM_USER_PARM)); + return VERR_OUT_OF_RANGE; + } + else + { +#ifndef USE_BOUNCE_BUFFERS + rc = RTR0MemObjLockUser(&hObj, (RTR3PTR)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess, NIL_RTR0PROCESS); + if (RT_FAILURE(rc)) + { + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockUser(,%p,%#x,nil) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked user -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + +#else /* USE_BOUNCE_BUFFERS */ + /* + * This is a bit massive, but we don't want to waste a + * whole page for a 3 byte string buffer (guest props). + * + * The threshold is ASSUMING sizeof(RTMEMHDR) == 16 and + * the system is using some power of two allocator. + */ + /** @todo A more efficient strategy would be to combine buffers. However it + * is probably going to be more massive than the current code, so + * it can wait till later. */ + bool fCopyIn = pSrcParm->type != VMMDevHGCMParmType_LinAddr_Out + && pSrcParm->type != VMMDevHGCMParmType_LinAddr_Locked_Out; + if (cb <= PAGE_SIZE / 2 - 16) + { + pvSmallBuf = fCopyIn ? RTMemTmpAlloc(cb) : RTMemTmpAllocZ(cb); + if (RT_UNLIKELY(!pvSmallBuf)) + return VERR_NO_MEMORY; + if (fCopyIn) + { + rc = RTR0MemUserCopyFrom(pvSmallBuf, pSrcParm->u.Pointer.u.linearAddr, cb); + if (RT_FAILURE(rc)) + { + RTMemTmpFree(pvSmallBuf); + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, + pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + } + rc = RTR0MemObjLockKernel(&hObj, pvSmallBuf, cb, fAccess); + if (RT_FAILURE(rc)) + { + RTMemTmpFree(pvSmallBuf); + Log(("GstHGCMCall: RTR0MemObjLockKernel failed for small buffer: rc=%Rrc pvSmallBuf=%p cb=%#x\n", + rc, pvSmallBuf, cb)); + return rc; + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p small buffer %p -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, pvSmallBuf, hObj)); + } + else + { + rc = RTR0MemObjAllocPage(&hObj, cb, false /*fExecutable*/); + if (RT_FAILURE(rc)) + return rc; + if (!fCopyIn) + memset(RTR0MemObjAddress(hObj), '\0', cb); + else + { + rc = RTR0MemUserCopyFrom(RTR0MemObjAddress(hObj), pSrcParm->u.Pointer.u.linearAddr, cb); + if (RT_FAILURE(rc)) + { + RTR0MemObjFree(hObj, false /*fFreeMappings*/); + Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n", + pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, + pSrcParm->u.Pointer.u.linearAddr, cb, rc)); + return rc; + } + } + Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p big buffer -> %p\n", + iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj)); + } +#endif /* USE_BOUNCE_BUFFERS */ + } + + pParmInfo->aLockBufs[iLockBuf].iParm = iParm; + pParmInfo->aLockBufs[iLockBuf].hObj = hObj; +#ifdef USE_BOUNCE_BUFFERS + pParmInfo->aLockBufs[iLockBuf].pvSmallBuf = pvSmallBuf; +#endif + pParmInfo->cLockBufs = iLockBuf + 1; + + if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false)) + { + size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT; + *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + } + else + Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type)); + break; + + default: + return VERR_INVALID_PARAMETER; + } + } + + return VINF_SUCCESS; +} + + +/** + * Translates locked linear address to the normal type. + * The locked types are only for the guest side and not handled by the host. + * + * @returns normal linear address type. + * @param enmType The type. + */ +static HGCMFunctionParameterType vbglR0HGCMInternalConvertLinAddrType(HGCMFunctionParameterType enmType) +{ + switch (enmType) + { + case VMMDevHGCMParmType_LinAddr_Locked_In: + return VMMDevHGCMParmType_LinAddr_In; + case VMMDevHGCMParmType_LinAddr_Locked_Out: + return VMMDevHGCMParmType_LinAddr_Out; + case VMMDevHGCMParmType_LinAddr_Locked: + return VMMDevHGCMParmType_LinAddr; + default: + return enmType; + } +} + + +/** + * Translates linear address types to page list direction flags. + * + * @returns page list flags. + * @param enmType The type. + */ +static uint32_t vbglR0HGCMInternalLinAddrTypeToPageListFlags(HGCMFunctionParameterType enmType) +{ + switch (enmType) + { + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Locked_In: + return VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + return VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + + default: AssertFailed(); RT_FALL_THRU(); + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_Locked: + return VBOX_HGCM_F_PARM_DIRECTION_BOTH; + } +} + + +/** + * Initializes the call request that we're sending to the host. + * + * @returns VBox status code. + * + * @param pCallInfo The call info. + * @param cbCallInfo The size of the call info structure. + * @param fRequestor VMMDEV_REQUESTOR_XXX. + * @param fIsUser Is it a user request or kernel request. + * @param pcbExtra Where to return the extra request space needed for + * physical page lists. + */ +static void vbglR0HGCMInternalInitCall(VMMDevHGCMCall *pHGCMCall, PCVBGLIOCHGCMCALL pCallInfo, + uint32_t cbCallInfo, uint32_t fRequestor, bool fIsUser, struct VbglR0ParmInfo *pParmInfo) +{ + HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + HGCMFunctionParameter *pDstParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); + uint32_t const cParms = pCallInfo->cParms; + uint32_t offExtra = (uint32_t)((uintptr_t)(pDstParm + cParms) - (uintptr_t)pHGCMCall); + uint32_t iLockBuf = 0; + uint32_t iParm; + RT_NOREF1(cbCallInfo); +#ifndef USE_BOUNCE_BUFFERS + RT_NOREF1(fIsUser); +#endif + + /* + * The call request headers. + */ + pHGCMCall->header.header.fRequestor = !fIsUser || (fRequestor & VMMDEV_REQUESTOR_USERMODE) ? fRequestor + : VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_USR_NOT_GIVEN + | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN | VMMDEV_REQUESTOR_CON_DONT_KNOW; + + pHGCMCall->header.fu32Flags = 0; + pHGCMCall->header.result = VINF_SUCCESS; + + pHGCMCall->u32ClientID = pCallInfo->u32ClientID; + pHGCMCall->u32Function = pCallInfo->u32Function; + pHGCMCall->cParms = cParms; + + /* + * The parameters. + */ + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++) + { + switch (pSrcParm->type) + { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *pDstParm = *pSrcParm; + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + pDstParm->type = pSrcParm->type; + pDstParm->u.PageList.size = pSrcParm->u.PageList.size; + if (pSrcParm->u.PageList.size) + { + HGCMPageListInfo const *pSrcPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + pSrcParm->u.PageList.offset); + HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra); + uint32_t const cPages = pSrcPgLst->cPages; + uint32_t iPage; + + pDstParm->u.PageList.offset = offExtra; + pDstPgLst->flags = pSrcPgLst->flags; + pDstPgLst->offFirstPage = pSrcPgLst->offFirstPage; + pDstPgLst->cPages = (uint16_t)cPages; + for (iPage = 0; iPage < cPages; iPage++) + pDstPgLst->aPages[iPage] = pSrcPgLst->aPages[iPage]; + + offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + else + pDstParm->u.PageList.offset = 0; /** @todo will fail on the host side now */ + break; + + case VMMDevHGCMParmType_Embedded: + { + uint32_t const cb = pSrcParm->u.Embedded.cbData; + pDstParm->type = VMMDevHGCMParmType_Embedded; + pDstParm->u.Embedded.cbData = cb; + pDstParm->u.Embedded.offData = offExtra; + if (cb > 0) + { + uint8_t *pbDst = (uint8_t *)pHGCMCall + offExtra; + if (pSrcParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST) + { + memcpy(pbDst, (uint8_t const *)pCallInfo + pSrcParm->u.Embedded.offData, cb); + if (RT_ALIGN(cb, 8) != cb) + memset(pbDst + cb, 0, RT_ALIGN(cb, 8) - cb); + } + else + RT_BZERO(pbDst, RT_ALIGN(cb, 8)); + offExtra += RT_ALIGN(cb, 8); + } + break; + } + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + *pDstParm = *pSrcParm; + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + if (pSrcParm->u.Pointer.size != 0) + { +#ifdef USE_BOUNCE_BUFFERS + void *pvSmallBuf = pParmInfo->aLockBufs[iLockBuf].pvSmallBuf; +#endif + RTR0MEMOBJ hObj = pParmInfo->aLockBufs[iLockBuf].hObj; + Assert(iParm == pParmInfo->aLockBufs[iLockBuf].iParm); + + if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false)) + { + HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra); + size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT; + size_t iPage; + + pDstParm->type = VMMDevHGCMParmType_PageList; + pDstParm->u.PageList.size = pSrcParm->u.Pointer.size; + pDstParm->u.PageList.offset = offExtra; + pDstPgLst->flags = vbglR0HGCMInternalLinAddrTypeToPageListFlags(pSrcParm->type); +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + pDstPgLst->offFirstPage = (uintptr_t)pvSmallBuf & PAGE_OFFSET_MASK; + else +#endif + pDstPgLst->offFirstPage = (uint16_t)(pSrcParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK); + pDstPgLst->cPages = (uint16_t)cPages; Assert(pDstPgLst->cPages == cPages); + for (iPage = 0; iPage < cPages; iPage++) + { + pDstPgLst->aPages[iPage] = RTR0MemObjGetPagePhysAddr(hObj, iPage); + Assert(pDstPgLst->aPages[iPage] != NIL_RTHCPHYS); + } + + offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]); + } + else + { + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + pDstParm->u.Pointer.u.linearAddr = pvSmallBuf + ? (uintptr_t)pvSmallBuf + : (uintptr_t)RTR0MemObjAddress(hObj); + else +#endif + pDstParm->u.Pointer.u.linearAddr = pSrcParm->u.Pointer.u.linearAddr; + } + iLockBuf++; + } + else + { + pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type); + pDstParm->u.Pointer.size = 0; + pDstParm->u.Pointer.u.linearAddr = 0; + } + break; + + default: + AssertFailed(); + pDstParm->type = VMMDevHGCMParmType_Invalid; + break; + } + } +} + + +/** + * Performs the call and completion wait. + * + * @returns VBox status code of this operation, not necessarily the call. + * + * @param pHGCMCall The HGCM call info. + * @param pfnAsyncCallback The async callback that will wait for the call + * to complete. + * @param pvAsyncData Argument for the callback. + * @param u32AsyncData Argument for the callback. + * @param pfLeakIt Where to return the leak it / free it, + * indicator. Cancellation fun. + */ +static int vbglR0HGCMInternalDoCall(VMMDevHGCMCall *pHGCMCall, PFNVBGLHGCMCALLBACK pfnAsyncCallback, + void *pvAsyncData, uint32_t u32AsyncData, bool *pfLeakIt) +{ + int rc; + + Log(("calling VbglR0GRPerform\n")); + rc = VbglR0GRPerform(&pHGCMCall->header.header); + Log(("VbglR0GRPerform rc = %Rrc (header rc=%d)\n", rc, pHGCMCall->header.result)); + + /* + * If the call failed, but as a result of the request itself, then pretend + * success. Upper layers will interpret the result code in the packet. + */ + if ( RT_FAILURE(rc) + && rc == pHGCMCall->header.result) + { + Assert(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE); + rc = VINF_SUCCESS; + } + + /* + * Check if host decides to process the request asynchronously, + * if so, we wait for it to complete using the caller supplied callback. + */ + *pfLeakIt = false; + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + Log(("Processing HGCM call asynchronously\n")); + rc = pfnAsyncCallback(&pHGCMCall->header, pvAsyncData, u32AsyncData); + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + { + Assert(!(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED)); + rc = VINF_SUCCESS; + } + else + { + /* + * The request didn't complete in time or the call was interrupted, + * the RC from the callback indicates which. Try cancel the request. + * + * This is a bit messy because we're racing request completion. Sorry. + */ + /** @todo It would be nice if we could use the waiter callback to do further + * waiting in case of a completion race. If it wasn't for WINNT having its own + * version of all that stuff, I would've done it already. */ + VMMDevHGCMCancel2 *pCancelReq; + int rc2 = VbglR0GRAlloc((VMMDevRequestHeader **)&pCancelReq, sizeof(*pCancelReq), VMMDevReq_HGCMCancel2); + if (RT_SUCCESS(rc2)) + { + pCancelReq->physReqToCancel = VbglR0PhysHeapGetPhysAddr(pHGCMCall); + rc2 = VbglR0GRPerform(&pCancelReq->header); + VbglR0GRFree(&pCancelReq->header); + } +#if 1 /** @todo ADDVER: Remove this on next minor version change. */ + if (rc2 == VERR_NOT_IMPLEMENTED) + { + /* host is too old, or we're out of heap. */ + pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + pHGCMCall->header.header.requestType = VMMDevReq_HGCMCancel; + rc2 = VbglR0GRPerform(&pHGCMCall->header.header); + if (rc2 == VERR_INVALID_PARAMETER) + rc2 = VERR_NOT_FOUND; + else if (RT_SUCCESS(rc)) + RTThreadSleep(1); + } +#endif + if (RT_SUCCESS(rc)) rc = VERR_INTERRUPTED; /** @todo weed this out from the WINNT VBoxGuest code. */ + if (RT_SUCCESS(rc2)) + { + Log(("vbglR0HGCMInternalDoCall: successfully cancelled\n")); + pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + } + else + { + /* + * Wait for a bit while the host (hopefully) completes it. + */ + uint64_t u64Start = RTTimeSystemMilliTS(); + uint32_t cMilliesToWait = rc2 == VERR_NOT_FOUND || rc2 == VERR_SEM_DESTROYED ? 500 : 2000; + uint64_t cElapsed = 0; + if (rc2 != VERR_NOT_FOUND) + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("vbglR0HGCMInternalDoCall: Failed to cancel the HGCM call on %Rrc: rc2=%Rrc\n", rc, rc2)); + } + else + Log(("vbglR0HGCMInternalDoCall: Cancel race rc=%Rrc rc2=%Rrc\n", rc, rc2)); + + do + { + ASMCompilerBarrier(); /* paranoia */ + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + break; + RTThreadSleep(1); + cElapsed = RTTimeSystemMilliTS() - u64Start; + } while (cElapsed < cMilliesToWait); + + ASMCompilerBarrier(); /* paranoia^2 */ + if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE) + rc = VINF_SUCCESS; + else + { + LogRel(("vbglR0HGCMInternalDoCall: Leaking %u bytes. Pending call to %u with %u parms. (rc2=%Rrc)\n", + pHGCMCall->header.header.size, pHGCMCall->u32Function, pHGCMCall->cParms, rc2)); + *pfLeakIt = true; + } + Log(("vbglR0HGCMInternalDoCall: Cancel race ended with rc=%Rrc (rc2=%Rrc) after %llu ms\n", rc, rc2, cElapsed)); + } + } + } + + Log(("GstHGCMCall: rc=%Rrc result=%Rrc fu32Flags=%#x fLeakIt=%d\n", + rc, pHGCMCall->header.result, pHGCMCall->header.fu32Flags, *pfLeakIt)); + return rc; +} + + +/** + * Copies the result of the call back to the caller info structure and user + * buffers (if using bounce buffers). + * + * @returns rc, unless RTR0MemUserCopyTo fails. + * @param pCallInfo Call info structure to update. + * @param cbCallInfo The size of the client request. + * @param pHGCMCall HGCM call request. + * @param cbHGCMCall The size of the HGCM call request. + * @param pParmInfo Parameter locking/buffering info. + * @param fIsUser Is it a user (true) or kernel request. + * @param rc The current result code. Passed along to + * preserve informational status codes. + */ +static int vbglR0HGCMInternalCopyBackResult(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, + VMMDevHGCMCall const *pHGCMCall, uint32_t cbHGCMCall, + struct VbglR0ParmInfo *pParmInfo, bool fIsUser, int rc) +{ + HGCMFunctionParameter const *pSrcParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall); + HGCMFunctionParameter *pDstParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo); + uint32_t const cParms = pCallInfo->cParms; +#ifdef USE_BOUNCE_BUFFERS + uint32_t iLockBuf = 0; +#endif + uint32_t iParm; + RT_NOREF1(pParmInfo); +#ifndef USE_BOUNCE_BUFFERS + RT_NOREF1(fIsUser); +#endif + + /* + * The call result. + */ + pCallInfo->Hdr.rc = pHGCMCall->header.result; + + /* + * Copy back parameters. + */ + /** @todo This is assuming user data (pDstParm) is buffered. Not true + * on OS/2, though I'm not sure we care... */ + for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++) + { + switch (pDstParm->type) + { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *pDstParm = *pSrcParm; + break; + + case VMMDevHGCMParmType_PageList: + case VMMDevHGCMParmType_ContiguousPageList: + pDstParm->u.PageList.size = pSrcParm->u.PageList.size; + break; + + case VMMDevHGCMParmType_Embedded: + { + uint32_t const cbDst = pDstParm->u.Embedded.cbData; + uint32_t cbSrc; + pDstParm->u.Embedded.cbData = cbSrc = pSrcParm->u.Embedded.cbData; + if ( cbSrc > 0 + && (pDstParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)) + { + uint32_t const offDst = pDstParm->u.Embedded.offData; + uint32_t const offSrc = pSrcParm->u.Embedded.offData; + + AssertReturn(offDst < cbCallInfo, VERR_INTERNAL_ERROR_2); + AssertReturn(offDst >= sizeof(*pCallInfo) + cParms * sizeof(*pDstParm), VERR_INTERNAL_ERROR_2); + AssertReturn(cbDst <= cbCallInfo - offDst , VERR_INTERNAL_ERROR_2); + + AssertReturn(offSrc < cbCallInfo, VERR_INTERNAL_ERROR_2); + AssertReturn(offSrc >= sizeof(*pHGCMCall) + cParms * sizeof(*pSrcParm), VERR_INTERNAL_ERROR_2); + if (cbSrc <= cbHGCMCall - offSrc) + { /* likely */ } + else + { + /* Special case: Buffer overflow w/ correct size given. */ + AssertReturn(RT_FAILURE_NP(rc), VERR_INTERNAL_ERROR_2); + cbSrc = cbHGCMCall - offSrc; + } + memcpy((uint8_t *)pCallInfo + offDst, (uint8_t const *)pHGCMCall + offSrc, RT_MIN(cbSrc, cbDst)); + } + break; + } + + case VMMDevHGCMParmType_LinAddr_Locked_In: + case VMMDevHGCMParmType_LinAddr_In: +#ifdef USE_BOUNCE_BUFFERS + if ( fIsUser + && iLockBuf < pParmInfo->cLockBufs + && iParm == pParmInfo->aLockBufs[iLockBuf].iParm) + iLockBuf++; +#endif + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + + case VMMDevHGCMParmType_LinAddr_Locked_Out: + case VMMDevHGCMParmType_LinAddr_Locked: + if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true)) + { + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + } + RT_FALL_THRU(); + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + { +#ifdef USE_BOUNCE_BUFFERS + if (fIsUser) + { + size_t cbOut = RT_MIN(pSrcParm->u.Pointer.size, pDstParm->u.Pointer.size); + if (cbOut) + { + int rc2; + Assert(pParmInfo->aLockBufs[iLockBuf].iParm == iParm); + rc2 = RTR0MemUserCopyTo((RTR3PTR)pDstParm->u.Pointer.u.linearAddr, + pParmInfo->aLockBufs[iLockBuf].pvSmallBuf + ? pParmInfo->aLockBufs[iLockBuf].pvSmallBuf + : RTR0MemObjAddress(pParmInfo->aLockBufs[iLockBuf].hObj), + cbOut); + if (RT_FAILURE(rc2)) + return rc2; + iLockBuf++; + } + else if ( iLockBuf < pParmInfo->cLockBufs + && iParm == pParmInfo->aLockBufs[iLockBuf].iParm) + iLockBuf++; + } +#endif + pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size; + break; + } + + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_4; + break; + } + } + +#ifdef USE_BOUNCE_BUFFERS + Assert(!fIsUser || pParmInfo->cLockBufs == iLockBuf); +#endif + return rc; +} + + +DECLR0VBGL(int) VbglR0HGCMInternalCall(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + bool fIsUser = (fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_USER; + struct VbglR0ParmInfo ParmInfo; + size_t cbExtra; + int rc; + + /* + * Basic validation. + */ + AssertMsgReturn( !pCallInfo + || !pfnAsyncCallback + || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS + || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK), + ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags), + VERR_INVALID_PARAMETER); + AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL) + || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter), + VERR_INVALID_PARAMETER); + + Log(("GstHGCMCall: u32ClientID=%#x u32Function=%u cParms=%u cbCallInfo=%#x fFlags=%#x\n", + pCallInfo->u32ClientID, pCallInfo->u32ClientID, pCallInfo->u32Function, pCallInfo->cParms, cbCallInfo, fFlags)); + + /* + * Validate, lock and buffer the parameters for the call. + * This will calculate the amount of extra space for physical page list. + */ + rc = vbglR0HGCMInternalPreprocessCall(pCallInfo, cbCallInfo, fIsUser, &ParmInfo, &cbExtra); + if (RT_SUCCESS(rc)) + { + /* + * Allocate the request buffer and recreate the call request. + */ + VMMDevHGCMCall *pHGCMCall; + uint32_t const cbHGCMCall = sizeof(VMMDevHGCMCall) + pCallInfo->cParms * sizeof(HGCMFunctionParameter) + (uint32_t)cbExtra; + rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMCall, cbHGCMCall, VMMDevReq_HGCMCall); + if (RT_SUCCESS(rc)) + { + bool fLeakIt; + vbglR0HGCMInternalInitCall(pHGCMCall, pCallInfo, cbCallInfo, fRequestor, fIsUser, &ParmInfo); + + /* + * Perform the call. + */ + rc = vbglR0HGCMInternalDoCall(pHGCMCall, pfnAsyncCallback, pvAsyncData, u32AsyncData, &fLeakIt); + if (RT_SUCCESS(rc)) + { + /* + * Copy back the result (parameters and buffers that changed). + */ + rc = vbglR0HGCMInternalCopyBackResult(pCallInfo, cbCallInfo, pHGCMCall, cbHGCMCall, &ParmInfo, fIsUser, rc); + } + else + { + if ( rc != VERR_INTERRUPTED + && rc != VERR_TIMEOUT) + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalDoCall failed. rc=%Rrc\n", rc)); + } + } + + if (!fLeakIt) + VbglR0GRFree(&pHGCMCall->header.header); + } + } + else + LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalPreprocessCall failed. rc=%Rrc\n", rc)); + + /* + * Release locks and free bounce buffers. + */ + if (ParmInfo.cLockBufs) + while (ParmInfo.cLockBufs-- > 0) + { + RTR0MemObjFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].hObj, false /*fFreeMappings*/); +#ifdef USE_BOUNCE_BUFFERS + RTMemTmpFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].pvSmallBuf); +#endif + } + + return rc; +} + + +#if ARCH_BITS == 64 +DECLR0VBGL(int) VbglR0HGCMInternalCall32(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor, + PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData) +{ + PVBGLIOCHGCMCALL pCallInfo64 = NULL; + HGCMFunctionParameter *pParm64 = NULL; + HGCMFunctionParameter32 *pParm32 = NULL; + uint32_t cParms = 0; + uint32_t iParm = 0; + int rc = VINF_SUCCESS; + + /* + * Input validation. + */ + AssertMsgReturn( !pCallInfo + || !pfnAsyncCallback + || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS + || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK), + ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags), + VERR_INVALID_PARAMETER); + AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL) + || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter32), + VERR_INVALID_PARAMETER); + + /* This Assert does not work on Solaris/Windows 64/32 mixed mode, not sure why, skipping for now */ +#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_WINDOWS) + AssertReturn((fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_KERNEL, VERR_WRONG_ORDER); +#endif + + cParms = pCallInfo->cParms; + Log(("VbglR0HGCMInternalCall32: cParms=%d, u32Function=%d, fFlags=%#x\n", cParms, pCallInfo->u32Function, fFlags)); + + /* + * The simple approach, allocate a temporary request and convert the parameters. + */ + pCallInfo64 = (PVBGLIOCHGCMCALL)RTMemTmpAllocZ(sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter)); + if (!pCallInfo64) + return VERR_NO_TMP_MEMORY; + + *pCallInfo64 = *pCallInfo; + pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo); + pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64); + for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++) + { + switch (pParm32->type) + { + case VMMDevHGCMParmType_32bit: + pParm64->type = VMMDevHGCMParmType_32bit; + pParm64->u.value32 = pParm32->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + pParm64->type = VMMDevHGCMParmType_64bit; + pParm64->u.value64 = pParm32->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + pParm64->type = pParm32->type; + pParm64->u.Pointer.size = pParm32->u.Pointer.size; + pParm64->u.Pointer.u.linearAddr = pParm32->u.Pointer.u.linearAddr; + break; + + default: + rc = VERR_INVALID_PARAMETER; + LogRel(("VbglR0HGCMInternalCall32: pParm32 type %#x invalid.\n", pParm32->type)); + break; + } + if (RT_FAILURE(rc)) + break; + } + if (RT_SUCCESS(rc)) + { + rc = VbglR0HGCMInternalCall(pCallInfo64, sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter), fFlags, + fRequestor, pfnAsyncCallback, pvAsyncData, u32AsyncData); + + if (RT_SUCCESS(rc)) + { + *pCallInfo = *pCallInfo64; + + /* + * Copy back. + */ + pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo); + pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64); + for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++) + { + switch (pParm64->type) + { + case VMMDevHGCMParmType_32bit: + pParm32->u.value32 = pParm64->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + pParm32->u.value64 = pParm64->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + pParm32->u.Pointer.size = pParm64->u.Pointer.size; + break; + + default: + LogRel(("VbglR0HGCMInternalCall32: failed invalid pParm32 type %d\n", pParm32->type)); + rc = VERR_INTERNAL_ERROR_3; + break; + } + } + } + else + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall32: VbglR0HGCMInternalCall failed. rc=%Rrc\n", rc)); + } + } + else + { + static unsigned s_cErrors = 0; + if (s_cErrors++ < 32) + LogRel(("VbglR0HGCMInternalCall32: failed. rc=%Rrc\n", rc)); + } + + RTMemTmpFree(pCallInfo64); + return rc; +} +#endif /* ARCH_BITS == 64 */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp new file mode 100644 index 00000000..828dea2e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp @@ -0,0 +1,85 @@ +/* $Id: VBoxGuestR0LibIdc-os2.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, OS/2 specific. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <VBox/err.h> +#include <VBox/log.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +RT_C_DECLS_BEGIN /* for watcom */ +/* This is defined in some assembly file. The AttachDD operation + is done in the driver init code. */ +extern VBGLOS2ATTACHDD g_VBoxGuestIDC; +RT_C_DECLS_END + + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + /* + * Just check whether the connection was made or not. + */ + if ( g_VBoxGuestIDC.u32Version == VBGL_IOC_VERSION + && RT_VALID_PTR(g_VBoxGuestIDC.u32Session) + && RT_VALID_PTR(g_VBoxGuestIDC.pfnServiceEP)) + return g_VBoxGuestIDC.pfnServiceEP(g_VBoxGuestIDC.u32Session, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); + Log(("vbglDriverOpen: failed\n")); + RT_NOREF(pHandle); + return VERR_FILE_NOT_FOUND; +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, uReq, pReqHdr, cbReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp new file mode 100644 index 00000000..760f060c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp @@ -0,0 +1,97 @@ +/* $Id: VBoxGuestR0LibIdc-solaris.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Solaris specific. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <sys/conf.h> +#include <sys/sunldi.h> +#include <sys/file.h> +#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */ +#include "VBoxGuestR0LibInternal.h" +#include <VBox/err.h> + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + ldi_handle_t hDev = NULL; + ldi_ident_t hIdent = ldi_ident_from_anon(); + int rc = ldi_open_by_name((char *)VBOXGUEST_DEVICE_NAME, FREAD, kcred, &hDev, hIdent); + ldi_ident_release(hIdent); + if (rc == 0) + { + pHandle->s.hDev = hDev; + rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + return VINF_SUCCESS; + ldi_close(hDev, FREAD, kcred); + } + else + rc = VERR_OPEN_FAILED; + pHandle->s.hDev = NULL; + return rc; +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + int rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + { + ldi_close(pHandle->s.hDev, FREAD, kcred); + pHandle->s.hDev = NULL; + } + return rc; +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ +#if 0 + return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq); +#else + int iIgn; + int rc = ldi_ioctl(pHandle->s.hDev, uReq, (intptr_t)pReqHdr, FKIOCTL | FNATIVE, kcred, &iIgn); + if (rc == 0) + return VINF_SUCCESS; + return RTErrConvertFromErrno(rc); +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp new file mode 100644 index 00000000..8aefb725 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp @@ -0,0 +1,64 @@ +/* $Id: VBoxGuestR0LibIdc-unix.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, UNIX-like OSes. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + RT_NOREF(pHandle); + return VBoxGuestIDC(NULL /*pvSession*/, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + return VBoxGuestIDC(pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq)); +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp new file mode 100644 index 00000000..395667e0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp @@ -0,0 +1,208 @@ +/* $Id: VBoxGuestR0LibIdc-win.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Windows specific. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/nt/nt.h> +#include "VBoxGuestR0LibInternal.h" +#include <VBox/VBoxGuest.h> +#include <iprt/errcore.h> +#include <VBox/log.h> + + +/** + * Internal I/O Control call worker. + * + * @returns VBox status code. + * @param pDeviceObject The device object to call. + * @param pFileObject The file object for the connection. + * @param uReq The request. + * @param pReq The request packet. + */ +static int vbglR0IdcNtCallInternal(PDEVICE_OBJECT pDeviceObject, PFILE_OBJECT pFileObject, uint32_t uReq, PVBGLREQHDR pReq) +{ + int rc; + NTSTATUS rcNt; + + /* + * Build the request. + * + * We want to avoid double buffering of the request, therefore we don't + * specify any request pointers or sizes when asking the kernel to build + * the IRP for us, but instead do that part our selves. + * + * See https://www.osr.com/blog/2018/02/14/beware-iobuilddeviceiocontrolrequest/ + * for how fun this is when we're not at IRQL PASSIVE (HACK ALERT futher down). + * Ran into this little issue when LoadLibraryEx on a .NET DLL using the + * LOAD_LIBRARY_AS_DATAFILE and LOAD_LIBRARY_AS_IMAGE_RESOURCE flags. + */ + KEVENT Event; + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + IO_STATUS_BLOCK IoStatusBlock = RTNT_IO_STATUS_BLOCK_INITIALIZER; +#if 0 + PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */ + pDeviceObject, + pReq, /* InputBuffer */ + pReq->cbIn, /* InputBufferLength */ + pReq, /* OutputBuffer */ + pReq->cbOut, /* OutputBufferLength */ + TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */ + &Event, /* Event */ + &IoStatusBlock); /* IoStatusBlock */ +#else + PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */ + pDeviceObject, + NULL, /* InputBuffer */ + 0, /* InputBufferLength */ + NULL, /* OutputBuffer */ + 0, /* OutputBufferLength */ + TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */ + &Event, /* Event */ + &IoStatusBlock); /* IoStatusBlock */ +#endif + if (pIrp) + { +#if 0 + IoGetNextIrpStackLocation(pIrp)->FileObject = pFileObject; +#else + //w2k3sp1+: if (!KeAreAllApcsDisabled()) + //w2k3sp1+: pIrp->Flags |= IRP_SYNCHRONOUS_API; + //w2k3sp1+: else + { + /* HACK ALERT! Causes IoCompleteRequest to update UserIosb and free the IRP w/o any APC happening. */ + pIrp->Flags |= IRP_SYNCHRONOUS_API | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO; + KIRQL bIrqlSaved; + KeRaiseIrql(APC_LEVEL, &bIrqlSaved); + RemoveEntryList(&pIrp->ThreadListEntry); + InitializeListHead(&pIrp->ThreadListEntry); + KeLowerIrql(bIrqlSaved); + } + pIrp->UserBuffer = pReq; + pIrp->AssociatedIrp.SystemBuffer = pReq; + PIO_STACK_LOCATION pStack = IoGetNextIrpStackLocation(pIrp); + pStack->FileObject = pFileObject; + pStack->Parameters.DeviceIoControl.OutputBufferLength = pReq->cbOut; + pStack->Parameters.DeviceIoControl.InputBufferLength = pReq->cbIn; +#endif + + /* + * Call the driver, wait for an async request to complete (should never happen). + */ + rcNt = IoCallDriver(pDeviceObject, pIrp); + if (rcNt == STATUS_PENDING) + rcNt = KeWaitForSingleObject(&Event, /* Object */ + Executive, /* WaitReason */ + KernelMode, /* WaitMode */ + FALSE, /* Alertable */ + NULL); /* TimeOut */ + if (NT_SUCCESS(rcNt)) + rcNt = IoStatusBlock.Status; + if (NT_SUCCESS(rcNt)) + rc = pReq->rc; + else + rc = RTErrConvertFromNtStatus(rcNt); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq) +{ + PDEVICE_OBJECT pDeviceObject = NULL; + PFILE_OBJECT pFileObject = NULL; + UNICODE_STRING wszDeviceName; + NTSTATUS rcNt; + int rc; + + /* + * Get the device object pointer. + */ + RtlInitUnicodeString(&wszDeviceName, VBOXGUEST_DEVICE_NAME_NT); + rcNt = IoGetDeviceObjectPointer(&wszDeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject); + if (NT_SUCCESS(rcNt)) + { + /* + * Make the connection call. + */ + rc = vbglR0IdcNtCallInternal(pDeviceObject, pFileObject, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + { + pHandle->s.pDeviceObject = pDeviceObject; + pHandle->s.pFileObject = pFileObject; + return rc; + } + + /* only the file object. */ + ObDereferenceObject(pFileObject); + } + else + rc = RTErrConvertFromNtStatus(rcNt); + + pHandle->s.pDeviceObject = NULL; + pHandle->s.pFileObject = NULL; + return rc; +} + + +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq) +{ + PFILE_OBJECT pFileObject = pHandle->s.pFileObject; + int rc = vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pFileObject, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr); + if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc)) + { + pHandle->s.pDeviceObject = NULL; + pHandle->s.pFileObject = NULL; + ObDereferenceObject(pFileObject); + } + + return rc; +} + + +/** + * Makes an IDC call, returning only the I/O control status code. + * + * @returns VBox status code (the I/O control failure status). + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + NOREF(cbReq); + return vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pHandle->s.pFileObject, (uint32_t)uReq, pReqHdr); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp new file mode 100644 index 00000000..c0b0ac53 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp @@ -0,0 +1,205 @@ +/* $Id: VBoxGuestR0LibIdc.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#include <iprt/errcore.h> +#include <VBox/VBoxGuest.h> +/*#include <iprt/asm.h>*/ + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/*static PVBGLIDCHANDLE volatile g_pMainHandle = NULL;*/ + + +/** + * Opens the IDC interface of the support driver. + * + * This will perform basic version negotiations and fail if the + * minimum requirements aren't met. + * + * @returns VBox status code. + * @param pHandle The handle structure (output). + * @param uReqVersion The requested version. Pass 0 for default. + * @param uMinVersion The minimum required version. Pass 0 for default. + * @param puSessionVersion Where to store the session version. Optional. + * @param puDriverVersion Where to store the session version. Optional. + * @param puDriverRevision Where to store the SVN revision of the driver. Optional. + */ +DECLR0VBGL(int) VbglR0IdcOpen(PVBGLIDCHANDLE pHandle, uint32_t uReqVersion, uint32_t uMinVersion, + uint32_t *puSessionVersion, uint32_t *puDriverVersion, uint32_t *puDriverRevision) +{ + unsigned uDefaultMinVersion; + VBGLIOCIDCCONNECT Req; + int rc; + + /* + * Validate and set failure return values. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + pHandle->s.pvSession = NULL; + + AssertPtrNullReturn(puSessionVersion, VERR_INVALID_POINTER); + if (puSessionVersion) + *puSessionVersion = 0; + + AssertPtrNullReturn(puDriverVersion, VERR_INVALID_POINTER); + if (puDriverVersion) + *puDriverVersion = 0; + + AssertPtrNullReturn(puDriverRevision, VERR_INVALID_POINTER); + if (puDriverRevision) + *puDriverRevision = 0; + + AssertReturn(!uMinVersion || (uMinVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER); + AssertReturn(!uReqVersion || (uReqVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER); + + /* + * Handle default version input and enforce minimum requirements made + * by this library. + * + * The clients will pass defaults (0), and only in the case that some + * special API feature was just added will they set an actual version. + * So, this is the place where can easily enforce a minimum IDC version + * on bugs and similar. It corresponds a bit to what SUPR3Init is + * responsible for. + */ + uDefaultMinVersion = VBGL_IOC_VERSION & UINT32_C(0xffff0000); + if (!uMinVersion || uMinVersion < uDefaultMinVersion) + uMinVersion = uDefaultMinVersion; + if (!uReqVersion || uReqVersion < uDefaultMinVersion) + uReqVersion = uDefaultMinVersion; + + /* + * Setup the connect request packet and call the OS specific function. + */ + VBGLREQHDR_INIT(&Req.Hdr, IDC_CONNECT); + Req.u.In.u32MagicCookie = VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE; + Req.u.In.uMinVersion = uMinVersion; + Req.u.In.uReqVersion = uReqVersion; + Req.u.In.uReserved = 0; + rc = vbglR0IdcNativeOpen(pHandle, &Req); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + { + pHandle->s.pvSession = Req.u.Out.pvSession; + if (puSessionVersion) + *puSessionVersion = Req.u.Out.uSessionVersion; + if (puDriverVersion) + *puDriverVersion = Req.u.Out.uDriverVersion; + if (puDriverRevision) + *puDriverRevision = Req.u.Out.uDriverRevision; + + /* + * We don't really trust anyone, make sure the returned + * session and version values actually makes sense. + */ + if ( RT_VALID_PTR(Req.u.Out.pvSession) + && Req.u.Out.uSessionVersion >= uMinVersion + && (Req.u.Out.uSessionVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000))) + { + /*ASMAtomicCmpXchgPtr(&g_pMainHandle, pHandle, NULL);*/ + return rc; + } + + AssertMsgFailed(("pSession=%p uSessionVersion=0x%x (r%u)\n", Req.u.Out.pvSession, Req.u.Out.uSessionVersion, Req.u.Out.uDriverRevision)); + rc = VERR_VERSION_MISMATCH; + VbglR0IdcClose(pHandle); + } + + return rc; +} + + +/** + * Closes a IDC connection established by VbglR0IdcOpen. + * + * @returns VBox status code. + * @param pHandle The IDC handle. + */ +DECLR0VBGL(int) VbglR0IdcClose(PVBGLIDCHANDLE pHandle) +{ + VBGLIOCIDCDISCONNECT Req; + int rc; + + /* + * Catch closed handles and check that the session is valid. + */ + AssertPtrReturn(pHandle, VERR_INVALID_POINTER); + if (!pHandle->s.pvSession) + return VERR_INVALID_HANDLE; + AssertPtrReturn(pHandle->s.pvSession, VERR_INVALID_HANDLE); + + /* + * Create the request and hand it to the OS specific code. + */ + VBGLREQHDR_INIT(&Req.Hdr, IDC_DISCONNECT); + Req.u.In.pvSession = pHandle->s.pvSession; + rc = vbglR0IdcNativeClose(pHandle, &Req); + if (RT_SUCCESS(rc)) + rc = Req.Hdr.rc; + if (RT_SUCCESS(rc)) + { + pHandle->s.pvSession = NULL; + /*ASMAtomicCmpXchgPtr(&g_pMainHandle, NULL, pHandle);*/ + } + return rc; +} + + +/** + * Makes an IDC call, returning the request status. + * + * @returns VBox status code. Request status if the I/O control succeeds, + * otherwise the I/O control failure status. + * @param pHandle The IDC handle. + * @param uReq The request number. + * @param pReqHdr The request header. + * @param cbReq The request size. + */ +DECLR0VBGL(int) VbglR0IdcCall(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq) +{ + int rc = VbglR0IdcCallRaw(pHandle, uReq, pReqHdr, cbReq); + if (RT_SUCCESS(rc)) + rc = pReqHdr->rc; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp new file mode 100644 index 00000000..0ed6c35f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp @@ -0,0 +1,333 @@ +/* $Id: VBoxGuestR0LibInit.cpp $ */ +/** @file + * VBoxGuestLibR0 - Library initialization. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/semaphore.h> +#include <VBox/err.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The global VBGL instance data. */ +VBGLDATA g_vbgldata; + + +/** + * Used by vbglR0QueryDriverInfo and VbglInit to try get the host feature mask + * and version information (g_vbgldata::hostVersion). + * + * This was first implemented by the host in 3.1 and we quietly ignore failures + * for that reason. + */ +static void vbglR0QueryHostVersion(void) +{ + VMMDevReqHostVersion *pReq; + int rc = VbglR0GRAlloc((VMMDevRequestHeader **) &pReq, sizeof (*pReq), VMMDevReq_GetHostVersion); + if (RT_SUCCESS(rc)) + { + rc = VbglR0GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + { + g_vbgldata.hostVersion = *pReq; + Log(("vbglR0QueryHostVersion: %u.%u.%ur%u %#x\n", + pReq->major, pReq->minor, pReq->build, pReq->revision, pReq->features)); + } + + VbglR0GRFree(&pReq->header); + } +} + + +#ifndef VBGL_VBOXGUEST +/** + * The guest library uses lazy initialization for VMMDev port and memory, + * because these values are provided by the VBoxGuest driver and it might + * be loaded later than other drivers. + * + * The VbglEnter checks the current library status, tries to retrieve these + * values and fails if they are unavailable. + */ +static int vbglR0QueryDriverInfo(void) +{ +# ifdef VBGLDATA_USE_FAST_MUTEX + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxIdcSetup); +# else + int rc = RTSemMutexRequest(g_vbgldata.hMtxIdcSetup, RT_INDEFINITE_WAIT); +# endif + if (RT_SUCCESS(rc)) + { + if (g_vbgldata.status == VbglStatusReady) + { /* likely */ } + else + { + rc = VbglR0IdcOpen(&g_vbgldata.IdcHandle, + VBGL_IOC_VERSION /*uReqVersion*/, + VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/, + NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*puDriverRevision*/); + if (RT_SUCCESS(rc)) + { + /* + * Try query the port info. + */ + VBGLIOCGETVMMDEVIOINFO PortInfo; + RT_ZERO(PortInfo); + VBGLREQHDR_INIT(&PortInfo.Hdr, GET_VMMDEV_IO_INFO); + rc = VbglR0IdcCall(&g_vbgldata.IdcHandle, VBGL_IOCTL_GET_VMMDEV_IO_INFO, &PortInfo.Hdr, sizeof(PortInfo)); + if (RT_SUCCESS(rc)) + { + dprintf(("Port I/O = 0x%04x, MMIO = %p\n", PortInfo.u.Out.IoPort, PortInfo.u.Out.pvVmmDevMapping)); + + g_vbgldata.portVMMDev = PortInfo.u.Out.IoPort; + g_vbgldata.pVMMDevMemory = (VMMDevMemory *)PortInfo.u.Out.pvVmmDevMapping; + g_vbgldata.status = VbglStatusReady; + + vbglR0QueryHostVersion(); + } + } + + dprintf(("vbglQueryDriverInfo rc = %Rrc\n", rc)); + } + +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexRelease(g_vbgldata.hMtxIdcSetup); +# else + RTSemMutexRelease(g_vbgldata.hMtxIdcSetup); +# endif + } + return rc; +} +#endif /* !VBGL_VBOXGUEST */ + +/** + * Checks if VBGL has been initialized. + * + * The client library, this will lazily complete the initialization. + * + * @return VINF_SUCCESS or VERR_VBGL_NOT_INITIALIZED. + */ +int vbglR0Enter(void) +{ + if (g_vbgldata.status == VbglStatusReady) + return VINF_SUCCESS; + +#ifndef VBGL_VBOXGUEST + if (g_vbgldata.status == VbglStatusInitializing) + { + vbglR0QueryDriverInfo(); + if (g_vbgldata.status == VbglStatusReady) + return VINF_SUCCESS; + } +#endif + return VERR_VBGL_NOT_INITIALIZED; +} + + +static int vbglR0InitCommon(void) +{ + int rc; + + RT_ZERO(g_vbgldata); + g_vbgldata.status = VbglStatusInitializing; + + rc = VbglR0PhysHeapInit(); + if (RT_SUCCESS(rc)) + { + dprintf(("vbglR0InitCommon: returns rc = %d\n", rc)); + return rc; + } + + LogRel(("vbglR0InitCommon: VbglR0PhysHeapInit failed: rc=%Rrc\n", rc)); + g_vbgldata.status = VbglStatusNotInitialized; + return rc; +} + + +static void vbglR0TerminateCommon(void) +{ + VbglR0PhysHeapTerminate(); + g_vbgldata.status = VbglStatusNotInitialized; +} + +#ifdef VBGL_VBOXGUEST + +DECLR0VBGL(int) VbglR0InitPrimary(RTIOPORT portVMMDev, VMMDevMemory *pVMMDevMemory, uint32_t *pfFeatures) +{ + int rc; + +# ifdef RT_OS_WINDOWS /** @todo r=bird: this doesn't make sense. Is there something special going on on windows? */ + dprintf(("vbglInit: starts g_vbgldata.status %d\n", g_vbgldata.status)); + + if ( g_vbgldata.status == VbglStatusInitializing + || g_vbgldata.status == VbglStatusReady) + { + /* Initialization is already in process. */ + return VINF_SUCCESS; + } +# else + dprintf(("vbglInit: starts\n")); +# endif + + rc = vbglR0InitCommon(); + if (RT_SUCCESS(rc)) + { + g_vbgldata.portVMMDev = portVMMDev; + g_vbgldata.pVMMDevMemory = pVMMDevMemory; + g_vbgldata.status = VbglStatusReady; + + vbglR0QueryHostVersion(); + *pfFeatures = g_vbgldata.hostVersion.features; + return VINF_SUCCESS; + } + + g_vbgldata.status = VbglStatusNotInitialized; + return rc; +} + +DECLR0VBGL(void) VbglR0TerminatePrimary(void) +{ + vbglR0TerminateCommon(); +} + + +#else /* !VBGL_VBOXGUEST */ + +DECLR0VBGL(int) VbglR0InitClient(void) +{ + int rc; + + /** @todo r=bird: explain why we need to be doing this, please... */ + if ( g_vbgldata.status == VbglStatusInitializing + || g_vbgldata.status == VbglStatusReady) + { + /* Initialization is already in process. */ + return VINF_SUCCESS; + } + + rc = vbglR0InitCommon(); + if (RT_SUCCESS(rc)) + { +# ifdef VBGLDATA_USE_FAST_MUTEX + rc = RTSemFastMutexCreate(&g_vbgldata.hMtxIdcSetup); +# else + rc = RTSemMutexCreate(&g_vbgldata.hMtxIdcSetup); +# endif + if (RT_SUCCESS(rc)) + { + /* Try to obtain VMMDev port via IOCTL to VBoxGuest main driver. */ + vbglR0QueryDriverInfo(); + +# ifdef VBOX_WITH_HGCM + rc = VbglR0HGCMInit(); +# endif + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX; +# else + RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX; +# endif + } + vbglR0TerminateCommon(); + } + + return rc; +} + +DECLR0VBGL(void) VbglR0TerminateClient(void) +{ +# ifdef VBOX_WITH_HGCM + VbglR0HGCMTerminate(); +# endif + + /* driver open could fail, which does not prevent VbglInit from succeeding, + * close the driver only if it is opened */ + VbglR0IdcClose(&g_vbgldata.IdcHandle); +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX; +# else + RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup); + g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX; +# endif + + /* note: do vbglR0TerminateCommon as a last step since it zeroez up the g_vbgldata + * conceptually, doing vbglR0TerminateCommon last is correct + * since this is the reverse order to how init is done */ + vbglR0TerminateCommon(); +} + + +int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle) +{ + if (g_vbgldata.status == VbglStatusReady) + { /* likely */ } + else + { + vbglR0QueryDriverInfo(); + if (g_vbgldata.status != VbglStatusReady) + { + *ppIdcHandle = NULL; + return VERR_TRY_AGAIN; + } + } + + *ppIdcHandle = &g_vbgldata.IdcHandle; + return VINF_SUCCESS; +} + + +DECLR0VBGL(int) VbglR0QueryHostFeatures(uint32_t *pfHostFeatures) +{ + if (g_vbgldata.status == VbglStatusReady) + *pfHostFeatures = g_vbgldata.hostVersion.features; + else + { + int rc = vbglR0QueryDriverInfo(); + if (g_vbgldata.status != VbglStatusReady) + return rc; + *pfHostFeatures = g_vbgldata.hostVersion.features; + } + + return VINF_SUCCESS; +} + +#endif /* !VBGL_VBOXGUEST */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h new file mode 100644 index 00000000..81657a8e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h @@ -0,0 +1,212 @@ +/* $Id: VBoxGuestR0LibInternal.h $ */ +/** @file + * VBoxGuestLibR0 - Internal header. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h +#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* + * Define the private IDC handle structure before we include the VBoxGuestLib.h header. + */ +#include <iprt/types.h> +#include <iprt/assert.h> +RT_C_DECLS_BEGIN + +# ifndef VBGL_VBOXGUEST +/** + * The hidden part of VBGLIDCHANDLE. + */ +struct VBGLIDCHANDLEPRIVATE +{ + /** Pointer to the session handle. */ + void *pvSession; +# if defined(RT_OS_WINDOWS) && (defined(IPRT_INCLUDED_nt_ntddk_h) || defined(IPRT_INCLUDED_nt_nt_h)) + /** Pointer to the NT device object. */ + PDEVICE_OBJECT pDeviceObject; + /** Pointer to the NT file object. */ + PFILE_OBJECT pFileObject; +# elif defined(RT_OS_SOLARIS) && defined(_SYS_SUNLDI_H) + /** LDI device handle to keep the device attached. */ + ldi_handle_t hDev; +# endif +}; +/** Indicate that the VBGLIDCHANDLEPRIVATE structure is present. */ +# define VBGLIDCHANDLEPRIVATE_DECLARED 1 +#endif + +#include <VBox/VMMDev.h> +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> + +#ifdef VBGLIDCHANDLEPRIVATE_DECLARED +AssertCompile(RT_SIZEOFMEMB(VBGLIDCHANDLE, apvPadding) >= sizeof(struct VBGLIDCHANDLEPRIVATE)); +#endif + + +/* + * Native IDC functions. + */ +int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq); +int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq); + + +/* + * Deprecated logging macro + */ +#include <VBox/log.h> +#ifdef RT_OS_WINDOWS /** @todo dprintf() -> Log() */ +# if (defined(DEBUG) && !defined(NO_LOGGING)) || defined(LOG_ENABLED) +# define dprintf(a) RTLogBackdoorPrintf a +# else +# define dprintf(a) do {} while (0) +# endif +#else +# define dprintf(a) Log(a) +#endif + +/* + * Lazy bird: OS/2 doesn't currently implement the RTSemMutex API in ring-0, so + * use a fast mutex instead. Unlike Windows, the OS/2 implementation + * doesn't have any nasty side effects on IRQL-like context properties, so the + * fast mutexes on OS/2 are identical to normal mutexes except for the missing + * timeout aspec. Fortunately we don't need timeouts here. + */ +#ifdef RT_OS_OS2 +# define VBGLDATA_USE_FAST_MUTEX +#endif + +struct VBGLPHYSHEAPBLOCK; +typedef struct VBGLPHYSHEAPBLOCK VBGLPHYSHEAPBLOCK; +struct VBGLPHYSHEAPFREEBLOCK; +typedef struct VBGLPHYSHEAPFREEBLOCK VBGLPHYSHEAPFREEBLOCK; +struct VBGLPHYSHEAPCHUNK; +typedef struct VBGLPHYSHEAPCHUNK VBGLPHYSHEAPCHUNK; + +enum VbglLibStatus +{ + VbglStatusNotInitialized = 0, + VbglStatusInitializing, + VbglStatusReady +}; + +/** + * Global VBGL ring-0 data. + * Lives in VBoxGuestR0LibInit.cpp. + */ +typedef struct VBGLDATA +{ + enum VbglLibStatus status; + + RTIOPORT portVMMDev; + + VMMDevMemory *pVMMDevMemory; + + /** Physical memory heap data. + * @{ + */ + RTSEMFASTMUTEX hMtxHeap; + /** Head of the block list (both free and allocated blocks). + * This runs parallel to the chunk list and is sorted by address within each + * chunk. This allows merging with blocks both after and before when + * freeing. */ + VBGLPHYSHEAPBLOCK *pBlockHead; + /** Head of the free list. + * This is not sorted and more on the most recently freed approach. */ + VBGLPHYSHEAPFREEBLOCK *pFreeHead; + /** Number of block of any kind. */ + int32_t cBlocks; + /** Number of free blocks. */ + int32_t cFreeBlocks; + /** Head of the chunk list. */ + VBGLPHYSHEAPCHUNK *pChunkHead; + /** @} */ + + /** + * The host version data. + */ + VMMDevReqHostVersion hostVersion; + + +#ifndef VBGL_VBOXGUEST + /** The IDC handle. This is used for talking to the main driver. */ + VBGLIDCHANDLE IdcHandle; + /** Mutex used to serialize IDC setup. */ +# ifdef VBGLDATA_USE_FAST_MUTEX + RTSEMFASTMUTEX hMtxIdcSetup; +# else + RTSEMMUTEX hMtxIdcSetup; +# endif +#endif +} VBGLDATA; + + +extern VBGLDATA g_vbgldata; + +/** + * Internal macro for checking whether we can pass physical page lists to the + * host. + * + * ASSUMES that vbglR0Enter has been called already. + * + * @param a_fLocked For the windows shared folders workarounds. + * + * @remarks Disabled the PageList feature for locked memory on Windows, + * because a new MDL is created by VBGL to get the page addresses + * and the pages from the MDL are marked as dirty when they should not. + */ +#if defined(RT_OS_WINDOWS) +# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ + ( !(a_fLocked) && (g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) +#else +# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ + ( !!(g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) +#endif + +int vbglR0Enter (void); + +#ifdef VBOX_WITH_HGCM +struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void); +void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle); +#endif /* VBOX_WITH_HGCM */ + +#ifndef VBGL_VBOXGUEST +/** + * Get the IDC handle to the main VBoxGuest driver. + * @returns VERR_TRY_AGAIN if the main driver has not yet been loaded. + */ +int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle); +#endif + +RT_C_DECLS_END + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp new file mode 100644 index 00000000..a0c9e1c5 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp @@ -0,0 +1,130 @@ +/* $Id: VBoxGuestR0LibMouse.cpp $ */ +/** @file + * VBoxGuestLibR0 - Mouse Integration. + */ + +/* + * Copyright (C) 2012-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/** + * Sets the function which is called back on each mouse pointer event. Only + * one callback can be active at once, so if you need several for any reason + * you must multiplex yourself. Call backs can be disabled by passing NULL + * as the function pointer. + * + * @remarks Ring-0. + * @returns iprt status code. + * @returns VERR_TRY_AGAIN if the main guest driver hasn't finished + * initialising. + * + * @param pfnNotify the function to call back. NULL to disable call backs. + * @param pvUser user supplied data/cookie to be passed to the function. + */ +DECLR0VBGL(int) VbglR0SetMouseNotifyCallback(PFNVBOXGUESTMOUSENOTIFY pfnNotify, void *pvUser) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VBGLIOCSETMOUSENOTIFYCALLBACK NotifyCallback; + VBGLREQHDR_INIT(&NotifyCallback.Hdr, SET_MOUSE_NOTIFY_CALLBACK); + NotifyCallback.u.In.pfnNotify = pfnNotify; + NotifyCallback.u.In.pvUser = pvUser; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK, &NotifyCallback.Hdr, sizeof(NotifyCallback)); + } + return rc; +} + + +/** + * Retrieve mouse coordinates and features from the host. + * + * @remarks Ring-0. + * @returns VBox status code. + * + * @param pfFeatures Where to store the mouse features. + * @param px Where to store the X co-ordinate. + * @param py Where to store the Y co-ordinate. + */ +DECLR0VBGL(int) VbglR0GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VMMDevReqMouseStatus Req; + VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetMouseStatus); + Req.mouseFeatures = 0; + Req.pointerXPos = 0; + Req.pointerYPos = 0; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_VMMDEV_REQUEST(sizeof(Req)), (PVBGLREQHDR)&Req.header, sizeof(Req)); + if (RT_SUCCESS(rc)) + { + if (pfFeatures) + *pfFeatures = Req.mouseFeatures; + if (px) + *px = Req.pointerXPos; + if (py) + *py = Req.pointerYPos; + } + } + return rc; +} + + +/** + * Send mouse features to the host. + * + * @remarks Ring-0. + * @returns VBox status code. + * + * @param fFeatures Supported mouse pointer features. The main guest driver + * will mediate different callers and show the host any + * feature enabled by any guest caller. + */ +DECLR0VBGL(int) VbglR0SetMouseStatus(uint32_t fFeatures) +{ + PVBGLIDCHANDLE pIdcHandle; + int rc = vbglR0QueryIdcHandle(&pIdcHandle); + if (RT_SUCCESS(rc)) + { + VBGLIOCSETMOUSESTATUS Req; + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fFeatures; + rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req)); + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp new file mode 100644 index 00000000..b99f51ad --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp @@ -0,0 +1,1197 @@ +/* $Id: VBoxGuestR0LibPhysHeap.cpp $ */ +/** @file + * VBoxGuestLibR0 - Physical memory heap. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** @page pg_vbglr0_phys_heap VBoxGuestLibR0 - Physical memory heap. + * + * Traditional heap implementation keeping all blocks in a ordered list and + * keeping free blocks on additional list via pointers in the user area. This + * is similar to @ref grp_rt_heap_simple "RTHeapSimple" and + * @ref grp_rt_heap_offset "RTHeapOffset" in IPRT, except that this code handles + * mutiple chunks and has a physical address associated with each chunk and + * block. The alignment is fixed (VBGL_PH_ALLOC_ALIGN). + * + * When allocating memory, a free block is found that satisfies the request, + * extending the heap with another chunk if needed. The block is split if it's + * too large, and the tail end is put on the free list. + * + * When freeing memory, the block being freed is put back on the free list and + * we use the block list to check whether it can be merged with adjacent blocks. + * + * @note The original code managed the blocks in two separate lists for free and + * allocated blocks, which had the disadvantage only allowing merging with + * the block after the block being freed. On the plus side, it had the + * potential for slightly better locality when examining the free list, + * since the next pointer and block size members were closer to one + * another. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/semaphore.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Enables heap dumping. */ +#if defined(DOXYGEN_RUNNING) || 0 +# define VBGL_PH_DUMPHEAP +#endif + +#ifdef VBGL_PH_DUMPHEAP +# define VBGL_PH_DPRINTF(a) RTAssertMsg2Weak a +#else +# define VBGL_PH_DPRINTF(a) do { } while (0) +#endif + +/** Heap chunk signature */ +#define VBGL_PH_CHUNKSIGNATURE UINT32_C(0xADDCCCCC) +/** Heap chunk allocation unit */ +#define VBGL_PH_CHUNKSIZE (0x10000) + +/** Heap block signature */ +#define VBGL_PH_BLOCKSIGNATURE UINT32_C(0xADDBBBBB) + +/** The allocation block alignment. + * + * This cannot be larger than VBGLPHYSHEAPBLOCK. + */ +#define VBGL_PH_ALLOC_ALIGN (sizeof(void *)) + +/** Max number of free nodes to search before just using the best fit. + * + * This is used to limit the free list walking during allocation and just get + * on with the job. A low number should reduce the cache trashing at the + * possible cost of heap fragmentation. + * + * Picked 16 after comparing the tstVbglR0PhysHeap-1 results w/ uRandSeed=42 for + * different max values. + */ +#define VBGL_PH_MAX_FREE_SEARCH 16 + +/** Threshold to stop the block search if a free block is at least this much too big. + * + * May cause more fragmation (depending on usage pattern), but should speed up + * allocation and hopefully reduce cache trashing. + * + * Since we merge adjacent free blocks when we can, free blocks should typically + * be a lot larger that what's requested. So, it is probably a good idea to + * just chop up a large block rather than keep searching for a perfect-ish + * match. + * + * Undefine this to disable this trick. + */ +#if defined(DOXYGEN_RUNNING) || 1 +# define VBGL_PH_STOP_SEARCH_AT_EXCESS _4K +#endif + +/** Threshold at which to split out a tail free block when allocating. + * + * The value gives the amount of user space, i.e. excluding the header. + * + * Using 32 bytes based on VMMDev.h request sizes. The smallest requests are 24 + * bytes, i.e. only the header, at least 4 of these. There are at least 10 with + * size 28 bytes and at least 11 with size 32 bytes. So, 32 bytes would fit + * some 25 requests out of about 60, which is reasonable. + */ +#define VBGL_PH_MIN_SPLIT_FREE_BLOCK 32 + + +/** The smallest amount of user data that can be allocated. + * + * This is to ensure that the block can be converted into a + * VBGLPHYSHEAPFREEBLOCK structure when freed. This must be smaller or equal + * to VBGL_PH_MIN_SPLIT_FREE_BLOCK. + */ +#define VBGL_PH_SMALLEST_ALLOC_SIZE 16 + +/** The maximum allocation request size. */ +#define VBGL_PH_LARGEST_ALLOC_SIZE RT_ALIGN_32( _128M \ + - sizeof(VBGLPHYSHEAPBLOCK) \ + - sizeof(VBGLPHYSHEAPCHUNK) \ + - VBGL_PH_ALLOC_ALIGN, \ + VBGL_PH_ALLOC_ALIGN) + +/** + * Whether to use the RTR0MemObjAllocCont API or RTMemContAlloc for + * allocating chunks. + * + * This can be enabled on hosts where RTMemContAlloc is more complicated than + * RTR0MemObjAllocCont. This can also be done if we wish to save code space, as + * the latter is typically always dragged into the link on guests where the + * linker cannot eliminiate functions within objects. Only drawback is that + * RTR0MemObjAllocCont requires another heap allocation for the handle. + */ +#if defined(DOXYGEN_RUNNING) || (!defined(IN_TESTCASE) && 0) +# define VBGL_PH_USE_MEMOBJ +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * A heap block (within a chunk). + * + * This is used to track a part of a heap chunk that's either free or + * allocated. The VBGLPHYSHEAPBLOCK::fAllocated member indicates which it is. + */ +struct VBGLPHYSHEAPBLOCK +{ + /** Magic value (VBGL_PH_BLOCKSIGNATURE). */ + uint32_t u32Signature; + + /** Size of user data in the block. Does not include this block header. */ + uint32_t cbUser : 31; + /** The top bit indicates whether it's allocated or free. */ + uint32_t fAllocated : 1; + + /** Pointer to the next block on the list. */ + VBGLPHYSHEAPBLOCK *pNext; + /** Pointer to the previous block on the list. */ + VBGLPHYSHEAPBLOCK *pPrev; + /** Pointer back to the chunk. */ + VBGLPHYSHEAPCHUNK *pChunk; +}; + +/** + * A free block. + */ +struct VBGLPHYSHEAPFREEBLOCK +{ + /** Core block data. */ + VBGLPHYSHEAPBLOCK Core; + /** Pointer to the next free list entry. */ + VBGLPHYSHEAPFREEBLOCK *pNextFree; + /** Pointer to the previous free list entry. */ + VBGLPHYSHEAPFREEBLOCK *pPrevFree; +}; +AssertCompile(VBGL_PH_SMALLEST_ALLOC_SIZE >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK)); +AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK)); +AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= VBGL_PH_SMALLEST_ALLOC_SIZE); + +/** + * A chunk of memory used by the heap for sub-allocations. + * + * There is a list of these. + */ +struct VBGLPHYSHEAPCHUNK +{ + /** Magic value (VBGL_PH_CHUNKSIGNATURE). */ + uint32_t u32Signature; + + /** Size of the chunk. Includes the chunk header. */ + uint32_t cbChunk; + + /** Physical address of the chunk (contiguous). */ + uint32_t physAddr; + +#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32 + uint32_t uPadding1; +#endif + + /** Number of block of any kind. */ + int32_t cBlocks; + /** Number of free blocks. */ + int32_t cFreeBlocks; + + /** Pointer to the next chunk. */ + VBGLPHYSHEAPCHUNK *pNext; + /** Pointer to the previous chunk. */ + VBGLPHYSHEAPCHUNK *pPrev; + +#if defined(VBGL_PH_USE_MEMOBJ) + /** The allocation handle. */ + RTR0MEMOBJ hMemObj; +#endif + +#if ARCH_BITS == 64 + /** Pad the size up to 64 bytes. */ +# ifdef VBGL_PH_USE_MEMOBJ + uintptr_t auPadding2[2]; +# else + uintptr_t auPadding2[3]; +# endif +#endif +}; +#if ARCH_BITS == 64 +AssertCompileSize(VBGLPHYSHEAPCHUNK, 64); +#endif + + +/** + * Debug function that dumps the heap. + */ +#ifndef VBGL_PH_DUMPHEAP +# define dumpheap(pszWhere) do { } while (0) +#else +static void dumpheap(const char *pszWhere) +{ + VBGL_PH_DPRINTF(("VBGL_PH dump at '%s'\n", pszWhere)); + + VBGL_PH_DPRINTF(("Chunks:\n")); + for (VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead; pChunk; pChunk = pChunk->pNext) + VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, cBlocks = %8d, cFreeBlocks=%8d, phys = %08X\n", + pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbChunk, + pChunk->cBlocks, pChunk->cFreeBlocks, pChunk->physAddr)); + + VBGL_PH_DPRINTF(("Allocated blocks:\n")); + for (VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pBlockHead; pBlock; pBlock = pBlock->pNext) + VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, size = %05x, sign = %08X, %s, pChunk = %p\n", + pBlock, pBlock->pNext, pBlock->pPrev, pBlock->cbUser, + pBlock->u32Signature, pBlock->fAllocated ? "allocated" : " free", pBlock->pChunk)); + + VBGL_PH_DPRINTF(("Free blocks:\n")); + for (VBGLPHYSHEAPFREEBLOCK *pBlock = g_vbgldata.pFreeHead; pBlock; pBlock = pBlock->pNextFree) + VBGL_PH_DPRINTF(("%p: pNextFree = %p, pPrevFree = %p, size = %05x, sign = %08X, pChunk = %p%s\n", + pBlock, pBlock->pNextFree, pBlock->pPrevFree, pBlock->Core.cbUser, + pBlock->Core.u32Signature, pBlock->Core.pChunk, + !pBlock->Core.fAllocated ? "" : " !!allocated-block-on-freelist!!")); + + VBGL_PH_DPRINTF(("VBGL_PH dump at '%s' done\n", pszWhere)); +} +#endif + + +/** + * Initialize a free block + */ +static void vbglPhysHeapInitFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbUser) +{ + Assert(pBlock != NULL); + Assert(pChunk != NULL); + + pBlock->Core.u32Signature = VBGL_PH_BLOCKSIGNATURE; + pBlock->Core.cbUser = cbUser; + pBlock->Core.fAllocated = false; + pBlock->Core.pNext = NULL; + pBlock->Core.pPrev = NULL; + pBlock->Core.pChunk = pChunk; + pBlock->pNextFree = NULL; + pBlock->pPrevFree = NULL; +} + + +/** + * Updates block statistics when a block is added. + */ +DECLINLINE(void) vbglPhysHeapStatsBlockAdded(VBGLPHYSHEAPBLOCK *pBlock) +{ + g_vbgldata.cBlocks += 1; + pBlock->pChunk->cBlocks += 1; + AssertMsg((uint32_t)pBlock->pChunk->cBlocks <= pBlock->pChunk->cbChunk / sizeof(VBGLPHYSHEAPFREEBLOCK), + ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks)); +} + + +/** + * Links @a pBlock onto the head of block list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertBlock(VBGLPHYSHEAPBLOCK *pBlock) +{ + AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext)); + AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev)); + + /* inserting to head of list */ + VBGLPHYSHEAPBLOCK *pOldHead = g_vbgldata.pBlockHead; + + pBlock->pNext = pOldHead; + pBlock->pPrev = NULL; + + if (pOldHead) + pOldHead->pPrev = pBlock; + g_vbgldata.pBlockHead = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsBlockAdded(pBlock); +} + + +/** + * Links @a pBlock onto the block list after @a pInsertAfter. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertBlockAfter(VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPBLOCK *pInsertAfter) +{ + AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext)); + AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev)); + + pBlock->pNext = pInsertAfter->pNext; + pBlock->pPrev = pInsertAfter; + + if (pInsertAfter->pNext) + pInsertAfter->pNext->pPrev = pBlock; + + pInsertAfter->pNext = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsBlockAdded(pBlock); +} + + +/** + * Unlinks @a pBlock from the block list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapUnlinkBlock(VBGLPHYSHEAPBLOCK *pBlock) +{ + VBGLPHYSHEAPBLOCK *pOtherBlock = pBlock->pNext; + if (pOtherBlock) + pOtherBlock->pPrev = pBlock->pPrev; + /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */ + + pOtherBlock = pBlock->pPrev; + if (pOtherBlock) + pOtherBlock->pNext = pBlock->pNext; + else + { + Assert(g_vbgldata.pBlockHead == pBlock); + g_vbgldata.pBlockHead = pBlock->pNext; + } + + pBlock->pNext = NULL; + pBlock->pPrev = NULL; + + /* Update the stats: */ + g_vbgldata.cBlocks -= 1; + pBlock->pChunk->cBlocks -= 1; + AssertMsg(pBlock->pChunk->cBlocks >= 0, + ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks)); + Assert(g_vbgldata.cBlocks >= 0); +} + + + +/** + * Updates statistics after adding a free block. + */ +DECLINLINE(void) vbglPhysHeapStatsFreeBlockAdded(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + g_vbgldata.cFreeBlocks += 1; + pBlock->Core.pChunk->cFreeBlocks += 1; +} + + +/** + * Links @a pBlock onto head of the free chain. + * + * This is used during block freeing and when adding a new chunk. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + Assert(!pBlock->Core.fAllocated); + AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree)); + AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree)); + + /* inserting to head of list */ + VBGLPHYSHEAPFREEBLOCK *pOldHead = g_vbgldata.pFreeHead; + + pBlock->pNextFree = pOldHead; + pBlock->pPrevFree = NULL; + + if (pOldHead) + pOldHead->pPrevFree = pBlock; + g_vbgldata.pFreeHead = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsFreeBlockAdded(pBlock); +} + + +/** + * Links @a pBlock after @a pInsertAfter. + * + * This is used when splitting a free block during allocation to preserve the + * place in the free list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapInsertFreeBlockAfter(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPFREEBLOCK *pInsertAfter) +{ + Assert(!pBlock->Core.fAllocated); + AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree)); + AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree)); + + /* inserting after the tiven node */ + pBlock->pNextFree = pInsertAfter->pNextFree; + pBlock->pPrevFree = pInsertAfter; + + if (pInsertAfter->pNextFree) + pInsertAfter->pNextFree->pPrevFree = pBlock; + + pInsertAfter->pNextFree = pBlock; + + /* Update the stats: */ + vbglPhysHeapStatsFreeBlockAdded(pBlock); +} + + +/** + * Unlinks @a pBlock from the free list. + * + * This also update the per-chunk block counts. + */ +static void vbglPhysHeapUnlinkFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock) +{ + Assert(!pBlock->Core.fAllocated); + + VBGLPHYSHEAPFREEBLOCK *pOtherBlock = pBlock->pNextFree; + if (pOtherBlock) + pOtherBlock->pPrevFree = pBlock->pPrevFree; + /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */ + + pOtherBlock = pBlock->pPrevFree; + if (pOtherBlock) + pOtherBlock->pNextFree = pBlock->pNextFree; + else + { + Assert(g_vbgldata.pFreeHead == pBlock); + g_vbgldata.pFreeHead = pBlock->pNextFree; + } + + pBlock->pNextFree = NULL; + pBlock->pPrevFree = NULL; + + /* Update the stats: */ + g_vbgldata.cFreeBlocks -= 1; + pBlock->Core.pChunk->cFreeBlocks -= 1; + AssertMsg(pBlock->Core.pChunk->cFreeBlocks >= 0, + ("pChunk=%p: cbChunk=%#x cFreeBlocks=%d\n", + pBlock->Core.pChunk, pBlock->Core.pChunk->cbChunk, pBlock->Core.pChunk->cFreeBlocks)); + Assert(g_vbgldata.cFreeBlocks >= 0); +} + + +/** + * Allocate another chunk and add it to the heap. + * + * @returns Pointer to the free block in the new chunk on success, NULL on + * allocation failure. + * @param cbMinBlock The size of the user block we need this chunk for. + */ +static VBGLPHYSHEAPFREEBLOCK *vbglPhysHeapChunkAlloc(uint32_t cbMinBlock) +{ + RTCCPHYS PhysAddr = NIL_RTHCPHYS; + VBGLPHYSHEAPCHUNK *pChunk; + uint32_t cbChunk; +#ifdef VBGL_PH_USE_MEMOBJ + RTR0MEMOBJ hMemObj = NIL_RTR0MEMOBJ; + int rc; +#endif + + VBGL_PH_DPRINTF(("Allocating new chunk for %#x byte allocation\n", cbMinBlock)); + AssertReturn(cbMinBlock <= VBGL_PH_LARGEST_ALLOC_SIZE, NULL); /* paranoia */ + + /* + * Compute the size of the new chunk, rounding up to next chunk size, + * which must be power of 2. + * + * Note! Using VBGLPHYSHEAPFREEBLOCK here means the minimum block size is + * 8 or 16 bytes too high, but safer this way since cbMinBlock is + * zero during the init code call. + */ + Assert(RT_IS_POWER_OF_TWO(VBGL_PH_CHUNKSIZE)); + cbChunk = cbMinBlock + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK); + cbChunk = RT_ALIGN_32(cbChunk, VBGL_PH_CHUNKSIZE); + + /* + * This function allocates physical contiguous memory below 4 GB. This 4GB + * limitation stems from using a 32-bit OUT instruction to pass a block + * physical address to the host. + */ +#ifdef VBGL_PH_USE_MEMOBJ + rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/); + pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL); +#else + pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk); +#endif + if (!pChunk) + { + /* If the allocation fail, halv the size till and try again. */ + uint32_t cbMinChunk = RT_MAX(cbMinBlock, PAGE_SIZE / 2) + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK); + cbMinChunk = RT_ALIGN_32(cbMinChunk, PAGE_SIZE); + if (cbChunk > cbMinChunk) + do + { + cbChunk >>= 2; + cbChunk = RT_ALIGN_32(cbChunk, PAGE_SIZE); +#ifdef VBGL_PH_USE_MEMOBJ + rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/); + pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL); +#else + pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk); +#endif + } while (!pChunk && cbChunk > cbMinChunk); + } + if (pChunk) + { + VBGLPHYSHEAPCHUNK *pOldHeadChunk; + VBGLPHYSHEAPFREEBLOCK *pBlock; + AssertRelease(PhysAddr < _4G && PhysAddr + cbChunk <= _4G); + + /* + * Init the new chunk. + */ + pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE; + pChunk->cbChunk = cbChunk; + pChunk->physAddr = (uint32_t)PhysAddr; + pChunk->cBlocks = 0; + pChunk->cFreeBlocks = 0; + pChunk->pNext = NULL; + pChunk->pPrev = NULL; +#ifdef VBGL_PH_USE_MEMOBJ + pChunk->hMemObj = hMemObj; +#endif + + /* Initialize the padding too: */ +#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32 + pChunk->uPadding1 = UINT32_C(0xADDCAAA1); +#endif +#if ARCH_BITS == 64 + pChunk->auPadding2[0] = UINT64_C(0xADDCAAA3ADDCAAA2); + pChunk->auPadding2[1] = UINT64_C(0xADDCAAA5ADDCAAA4); +# ifndef VBGL_PH_USE_MEMOBJ + pChunk->auPadding2[2] = UINT64_C(0xADDCAAA7ADDCAAA6); +# endif +#endif + + /* + * Initialize the free block, which now occupies entire chunk. + */ + pBlock = (VBGLPHYSHEAPFREEBLOCK *)(pChunk + 1); + vbglPhysHeapInitFreeBlock(pBlock, pChunk, cbChunk - sizeof(VBGLPHYSHEAPCHUNK) - sizeof(VBGLPHYSHEAPBLOCK)); + vbglPhysHeapInsertBlock(&pBlock->Core); + vbglPhysHeapInsertFreeBlock(pBlock); + + /* + * Add the chunk to the list. + */ + pOldHeadChunk = g_vbgldata.pChunkHead; + pChunk->pNext = pOldHeadChunk; + if (pOldHeadChunk) + pOldHeadChunk->pPrev = pChunk; + g_vbgldata.pChunkHead = pChunk; + + VBGL_PH_DPRINTF(("Allocated chunk %p LB %#x, block %p LB %#x\n", pChunk, cbChunk, pBlock, pBlock->Core.cbUser)); + return pBlock; + } + LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u (%#x) contiguous bytes.\n", cbChunk, cbChunk)); + return NULL; +} + + +/** + * Deletes a chunk: Unlinking all its blocks and freeing its memory. + */ +static void vbglPhysHeapChunkDelete(VBGLPHYSHEAPCHUNK *pChunk) +{ + uintptr_t uEnd, uCur; + Assert(pChunk != NULL); + AssertMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, ("pChunk->u32Signature = %08X\n", pChunk->u32Signature)); + + VBGL_PH_DPRINTF(("Deleting chunk %p size %x\n", pChunk, pChunk->cbChunk)); + + /* + * First scan the chunk and unlink all blocks from the lists. + * + * Note! We could do this by finding the first and last block list entries + * and just drop the whole chain relating to this chunk, rather than + * doing it one by one. But doing it one by one is simpler and will + * continue to work if the block list ends in an unsorted state. + */ + uEnd = (uintptr_t)pChunk + pChunk->cbChunk; + uCur = (uintptr_t)(pChunk + 1); + + while (uCur < uEnd) + { + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)uCur; + Assert(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE); + Assert(pBlock->pChunk == pChunk); + + uCur += pBlock->cbUser + sizeof(VBGLPHYSHEAPBLOCK); + Assert(uCur == (uintptr_t)pBlock->pNext || uCur >= uEnd); + + if (!pBlock->fAllocated) + vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pBlock); + vbglPhysHeapUnlinkBlock(pBlock); + } + + AssertMsg(uCur == uEnd, ("uCur = %p, uEnd = %p, pChunk->cbChunk = %08X\n", uCur, uEnd, pChunk->cbChunk)); + + /* + * Unlink the chunk from the chunk list. + */ + if (pChunk->pNext) + pChunk->pNext->pPrev = pChunk->pPrev; + /* else: we do not maintain tail pointer. */ + + if (pChunk->pPrev) + pChunk->pPrev->pNext = pChunk->pNext; + else + { + Assert(g_vbgldata.pChunkHead == pChunk); + g_vbgldata.pChunkHead = pChunk->pNext; + } + + /* + * Finally, free the chunk memory. + */ +#ifdef VBGL_PH_USE_MEMOBJ + RTR0MemObjFree(pChunk->hMemObj, true /*fFreeMappings*/); +#else + RTMemContFree(pChunk, pChunk->cbChunk); +#endif +} + + +DECLR0VBGL(void *) VbglR0PhysHeapAlloc(uint32_t cb) +{ + VBGLPHYSHEAPFREEBLOCK *pBlock; + VBGLPHYSHEAPFREEBLOCK *pIter; + int32_t cLeft; +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + uint32_t cbAlwaysSplit; +#endif + int rc; + + /* + * Make sure we don't allocate anything too small to turn into a free node + * and align the size to prevent pointer misalignment and whatnot. + */ + cb = RT_MAX(cb, VBGL_PH_SMALLEST_ALLOC_SIZE); + cb = RT_ALIGN_32(cb, VBGL_PH_ALLOC_ALIGN); + AssertCompile(VBGL_PH_ALLOC_ALIGN <= sizeof(pBlock->Core)); + + rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, NULL); + + dumpheap("pre alloc"); + + /* + * Search the free list. We do this in linear fashion as we don't expect + * there to be many blocks in the heap. + */ +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + cbAlwaysSplit = cb + VBGL_PH_STOP_SEARCH_AT_EXCESS; +#endif + cLeft = VBGL_PH_MAX_FREE_SEARCH; + pBlock = NULL; + if (cb <= PAGE_SIZE / 4 * 3) + { + /* Smaller than 3/4 page: Prefer a free block that can keep the request within a single page, + so HGCM processing in VMMDev can use page locks instead of several reads and writes. */ + VBGLPHYSHEAPFREEBLOCK *pFallback = NULL; + for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--) + { + AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + if (pIter->Core.cbUser >= cb) + { + if (pIter->Core.cbUser == cb) + { + if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb) + { + pBlock = pIter; + break; + } + pFallback = pIter; + } + else + { + if (!pFallback || pIter->Core.cbUser < pFallback->Core.cbUser) + pFallback = pIter; + if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb) + { + if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser) + pBlock = pIter; +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + else if (pIter->Core.cbUser >= cbAlwaysSplit) + { + pBlock = pIter; + break; + } +#endif + } + } + + if (cLeft > 0) + { /* likely */ } + else + break; + } + } + + if (!pBlock) + pBlock = pFallback; + } + else + { + /* Large than 3/4 page: Find closest free list match. */ + for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--) + { + AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + if (pIter->Core.cbUser >= cb) + { + if (pIter->Core.cbUser == cb) + { + /* Exact match - we're done! */ + pBlock = pIter; + break; + } + +#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS + if (pIter->Core.cbUser >= cbAlwaysSplit) + { + /* Really big block - no point continue searching! */ + pBlock = pIter; + break; + } +#endif + /* Looking for a free block with nearest size. */ + if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser) + pBlock = pIter; + + if (cLeft > 0) + { /* likely */ } + else + break; + } + } + } + + if (!pBlock) + { + /* No free blocks, allocate a new chunk, the only free block of the + chunk will be returned. */ + pBlock = vbglPhysHeapChunkAlloc(cb); + } + + if (pBlock) + { + /* We have a free block, either found or allocated. */ + AssertMsg(pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE, + ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->Core.u32Signature)); + AssertMsg(!pBlock->Core.fAllocated, ("pBlock = %p\n", pBlock)); + + /* + * If the block is too large, split off a free block with the unused space. + * + * We do this before unlinking the block so we can preserve the location + * in the free list. + * + * Note! We cannot split off and return the tail end here, because that may + * violate the same page requirements for requests smaller than 3/4 page. + */ + AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(*pBlock) - sizeof(pBlock->Core)); + if (pBlock->Core.cbUser >= sizeof(VBGLPHYSHEAPBLOCK) * 2 + VBGL_PH_MIN_SPLIT_FREE_BLOCK + cb) + { + pIter = (VBGLPHYSHEAPFREEBLOCK *)((uintptr_t)(&pBlock->Core + 1) + cb); + vbglPhysHeapInitFreeBlock(pIter, pBlock->Core.pChunk, pBlock->Core.cbUser - cb - sizeof(VBGLPHYSHEAPBLOCK)); + + pBlock->Core.cbUser = cb; + + /* Insert the new 'pIter' block after the 'pBlock' in the block list + and in the free list. */ + vbglPhysHeapInsertBlockAfter(&pIter->Core, &pBlock->Core); + vbglPhysHeapInsertFreeBlockAfter(pIter, pBlock); + } + + /* + * Unlink the block from the free list and mark it as allocated. + */ + vbglPhysHeapUnlinkFreeBlock(pBlock); + pBlock->Core.fAllocated = true; + + dumpheap("post alloc"); + + /* + * Return success. + */ + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + + VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns %p size %x\n", pBlock + 1, pBlock->Core.cbUser)); + return &pBlock->Core + 1; + } + + /* + * Return failure. + */ + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + AssertRC(rc); + + VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns NULL (requested %#x bytes)\n", cb)); + return NULL; +} + + +DECLR0VBGL(uint32_t) VbglR0PhysHeapGetPhysAddr(void *pv) +{ + /* + * Validate the incoming pointer. + */ + if (pv != NULL) + { + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)pv - 1; + if ( pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE + && pBlock->fAllocated) + { + /* + * Calculate and return its physical address. + */ + VBGLPHYSHEAPCHUNK *pChunk = pBlock->pChunk; + return pChunk->physAddr + (uint32_t)((uintptr_t)pv - (uintptr_t)pChunk); + } + + AssertMsgFailed(("Use after free or corrupt pointer variable: pv=%p pBlock=%p: u32Signature=%#x cb=%#x fAllocated=%d\n", + pv, pBlock, pBlock->u32Signature, pBlock->cbUser, pBlock->fAllocated)); + } + else + AssertMsgFailed(("Unexpected NULL pointer\n")); + return 0; +} + + +DECLR0VBGL(void) VbglR0PhysHeapFree(void *pv) +{ + if (pv != NULL) + { + VBGLPHYSHEAPFREEBLOCK *pBlock; + + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturnVoid(rc); + + dumpheap("pre free"); + + /* + * Validate the block header. + */ + pBlock = (VBGLPHYSHEAPFREEBLOCK *)((VBGLPHYSHEAPBLOCK *)pv - 1); + if ( pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE + && pBlock->Core.fAllocated + && pBlock->Core.cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE) + { + VBGLPHYSHEAPCHUNK *pChunk; + VBGLPHYSHEAPBLOCK *pNeighbour; + + /* + * Change the block status to freeed. + */ + VBGL_PH_DPRINTF(("VbglR0PhysHeapFree: %p size %#x\n", pv, pBlock->Core.cbUser)); + + pBlock->Core.fAllocated = false; + pBlock->pNextFree = pBlock->pPrevFree = NULL; + vbglPhysHeapInsertFreeBlock(pBlock); + + dumpheap("post insert"); + + /* + * Check if the block after this one is also free and we can merge it into this one. + */ + pChunk = pBlock->Core.pChunk; + + pNeighbour = pBlock->Core.pNext; + if ( pNeighbour + && !pNeighbour->fAllocated + && pNeighbour->pChunk == pChunk) + { + Assert((uintptr_t)pBlock + sizeof(pBlock->Core) + pBlock->Core.cbUser == (uintptr_t)pNeighbour); + + /* Adjust size of current memory block */ + pBlock->Core.cbUser += pNeighbour->cbUser + sizeof(VBGLPHYSHEAPBLOCK); + + /* Unlink the following node and invalid it. */ + vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pNeighbour); + vbglPhysHeapUnlinkBlock(pNeighbour); + + pNeighbour->u32Signature = ~VBGL_PH_BLOCKSIGNATURE; + pNeighbour->cbUser = UINT32_MAX / 4; + + dumpheap("post merge after"); + } + + /* + * Same check for the block before us. This invalidates pBlock. + */ + pNeighbour = pBlock->Core.pPrev; + if ( pNeighbour + && !pNeighbour->fAllocated + && pNeighbour->pChunk == pChunk) + { + Assert((uintptr_t)pNeighbour + sizeof(*pNeighbour) + pNeighbour->cbUser == (uintptr_t)pBlock); + + /* Adjust size of the block before us */ + pNeighbour->cbUser += pBlock->Core.cbUser + sizeof(VBGLPHYSHEAPBLOCK); + + /* Unlink this node and invalid it. */ + vbglPhysHeapUnlinkFreeBlock(pBlock); + vbglPhysHeapUnlinkBlock(&pBlock->Core); + + pBlock->Core.u32Signature = ~VBGL_PH_BLOCKSIGNATURE; + pBlock->Core.cbUser = UINT32_MAX / 8; + + pBlock = NULL; /* invalid */ + + dumpheap("post merge before"); + } + + /* + * If this chunk is now completely unused, delete it if there are + * more completely free ones. + */ + if ( pChunk->cFreeBlocks == pChunk->cBlocks + && (pChunk->pPrev || pChunk->pNext)) + { + VBGLPHYSHEAPCHUNK *pCurChunk; + uint32_t cUnusedChunks = 0; + for (pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext) + { + AssertBreak(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE); + if (pCurChunk->cFreeBlocks == pCurChunk->cBlocks) + { + cUnusedChunks++; + if (cUnusedChunks > 1) + { + /* Delete current chunk, it will also unlink all free blocks + * remaining in the chunk from the free list, so the pBlock + * will also be invalid after this. + */ + vbglPhysHeapChunkDelete(pChunk); + pBlock = NULL; /* invalid */ + pChunk = NULL; + pNeighbour = NULL; + break; + } + } + } + } + + dumpheap("post free"); + } + else + AssertMsgFailed(("pBlock: %p: u32Signature=%#x cb=%#x fAllocated=%d - double free?\n", + pBlock, pBlock->Core.u32Signature, pBlock->Core.cbUser, pBlock->Core.fAllocated)); + + rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + AssertRC(rc); + } +} + +#ifdef IN_TESTCASE /* For the testcase only */ + +/** + * Returns the sum of all free heap blocks. + * + * This is the amount of memory you can theoretically allocate if you do + * allocations exactly matching the free blocks. + * + * @returns The size of the free blocks. + * @returns 0 if heap was safely detected as being bad. + */ +DECLVBGL(size_t) VbglR0PhysHeapGetFreeSize(void) +{ + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, 0); + + size_t cbTotal = 0; + for (VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead; pCurBlock; pCurBlock = pCurBlock->pNextFree) + { + Assert(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE); + Assert(!pCurBlock->Core.fAllocated); + cbTotal += pCurBlock->Core.cbUser; + } + + RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + return cbTotal; +} + + +/** + * Checks the heap, caller responsible for locking. + * + * @returns VINF_SUCCESS if okay, error status if not. + * @param pErrInfo Where to return more error details, optional. + */ +static int vbglR0PhysHeapCheckLocked(PRTERRINFO pErrInfo) +{ + /* + * Scan the blocks in each chunk, walking the block list in parallel. + */ + const VBGLPHYSHEAPBLOCK *pPrevBlockListEntry = NULL; + const VBGLPHYSHEAPBLOCK *pCurBlockListEntry = g_vbgldata.pBlockHead; + unsigned acTotalBlocks[2] = { 0, 0 }; + for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead, *pPrevChunk = NULL; pCurChunk; pCurChunk = pCurChunk->pNext) + { + AssertReturn(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurChunk=%p: magic=%#x", pCurChunk, pCurChunk->u32Signature)); + AssertReturn(pCurChunk->pPrev == pPrevChunk, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurChunk=%p: pPrev=%p, expected %p", pCurChunk, pCurChunk->pPrev, pPrevChunk)); + + const VBGLPHYSHEAPBLOCK *pCurBlock = (const VBGLPHYSHEAPBLOCK *)(pCurChunk + 1); + uintptr_t const uEnd = (uintptr_t)pCurChunk + pCurChunk->cbChunk; + unsigned acBlocks[2] = { 0, 0 }; + while ((uintptr_t)pCurBlock < uEnd) + { + AssertReturn(pCurBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, + "pCurBlock=%p: magic=%#x", pCurBlock, pCurBlock->u32Signature)); + AssertReturn(pCurBlock->pChunk == pCurChunk, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurBlock=%p: pChunk=%p, expected %p", pCurBlock, pCurBlock->pChunk, pCurChunk)); + AssertReturn( pCurBlock->cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE + && pCurBlock->cbUser <= VBGL_PH_LARGEST_ALLOC_SIZE + && RT_ALIGN_32(pCurBlock->cbUser, VBGL_PH_ALLOC_ALIGN) == pCurBlock->cbUser, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, + "pCurBlock=%p: cbUser=%#x", pCurBlock, pCurBlock->cbUser)); + AssertReturn(pCurBlock == pCurBlockListEntry, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurChunk=%p: pCurBlock=%p, pCurBlockListEntry=%p\n", + pCurChunk, pCurBlock, pCurBlockListEntry)); + AssertReturn(pCurBlock->pPrev == pPrevBlockListEntry, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5, + "pCurChunk=%p: pCurBlock->pPrev=%p, pPrevBlockListEntry=%p\n", + pCurChunk, pCurBlock->pPrev, pPrevBlockListEntry)); + + acBlocks[pCurBlock->fAllocated] += 1; + + /* advance */ + pPrevBlockListEntry = pCurBlock; + pCurBlockListEntry = pCurBlock->pNext; + pCurBlock = (const VBGLPHYSHEAPBLOCK *)((uintptr_t)(pCurBlock + 1) + pCurBlock->cbUser); + } + AssertReturn((uintptr_t)pCurBlock == uEnd, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurBlock=%p uEnd=%p", pCurBlock, uEnd)); + + acTotalBlocks[1] += acBlocks[1]; + AssertReturn(acBlocks[0] + acBlocks[1] == (uint32_t)pCurChunk->cBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4, + "pCurChunk=%p: cBlocks=%u, expected %u", + pCurChunk, pCurChunk->cBlocks, acBlocks[0] + acBlocks[1])); + + acTotalBlocks[0] += acBlocks[0]; + AssertReturn(acBlocks[0] == (uint32_t)pCurChunk->cFreeBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5, + "pCurChunk=%p: cFreeBlocks=%u, expected %u", + pCurChunk, pCurChunk->cFreeBlocks, acBlocks[0])); + + pPrevChunk = pCurChunk; + } + + AssertReturn(acTotalBlocks[0] == (uint32_t)g_vbgldata.cFreeBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR, + "g_vbgldata.cFreeBlocks=%u, expected %u", g_vbgldata.cFreeBlocks, acTotalBlocks[0])); + AssertReturn(acTotalBlocks[0] + acTotalBlocks[1] == (uint32_t)g_vbgldata.cBlocks, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR, + "g_vbgldata.cBlocks=%u, expected %u", g_vbgldata.cBlocks, acTotalBlocks[0] + acTotalBlocks[1])); + + /* + * Check that the free list contains the same number of blocks as we + * encountered during the above scan. + */ + { + unsigned cFreeListBlocks = 0; + for (const VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead, *pPrevBlock = NULL; + pCurBlock; + pCurBlock = pCurBlock->pNextFree) + { + AssertReturn(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, + "pCurBlock=%p/free: magic=%#x", pCurBlock, pCurBlock->Core.u32Signature)); + AssertReturn(pCurBlock->pPrevFree == pPrevBlock, + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2, + "pCurBlock=%p/free: pPrev=%p, expected %p", pCurBlock, pCurBlock->pPrevFree, pPrevBlock)); + AssertReturn(pCurBlock->Core.pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, + RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurBlock=%p/free: chunk (%p) magic=%#x", + pCurBlock, pCurBlock->Core.pChunk, pCurBlock->Core.pChunk->u32Signature)); + cFreeListBlocks++; + pPrevBlock = pCurBlock; + } + + AssertReturn(cFreeListBlocks == acTotalBlocks[0], + RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3, + "Found %u in free list, expected %u", cFreeListBlocks, acTotalBlocks[0])); + } + return VINF_SUCCESS; +} + + +/** + * Performs a heap check. + * + * @returns Problem description on failure, NULL on success. + * @param pErrInfo Where to return more error details, optional. + */ +DECLVBGL(int) VbglR0PhysHeapCheck(PRTERRINFO pErrInfo) +{ + int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap); + AssertRCReturn(rc, 0); + + rc = vbglR0PhysHeapCheckLocked(pErrInfo); + + RTSemFastMutexRelease(g_vbgldata.hMtxHeap); + return rc; +} + +#endif /* IN_TESTCASE */ + +DECLR0VBGL(int) VbglR0PhysHeapInit(void) +{ + g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX; + + /* Allocate the first chunk of the heap. */ + VBGLPHYSHEAPFREEBLOCK *pBlock = vbglPhysHeapChunkAlloc(0); + if (pBlock) + return RTSemFastMutexCreate(&g_vbgldata.hMtxHeap); + return VERR_NO_CONT_MEMORY; +} + +DECLR0VBGL(void) VbglR0PhysHeapTerminate(void) +{ + while (g_vbgldata.pChunkHead) + vbglPhysHeapChunkDelete(g_vbgldata.pChunkHead); + + RTSemFastMutexDestroy(g_vbgldata.hMtxHeap); + g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c new file mode 100644 index 00000000..02a5c470 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c @@ -0,0 +1,716 @@ +/* $Id: VBoxGuestR0LibSharedFolders.c $ */ +/** @file + * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS +#include "VBoxGuestR0LibInternal.h" +#include <VBox/VBoxGuestLibSharedFolders.h> +#include <VBox/log.h> +#include <iprt/err.h> +#include <iprt/time.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> + +#ifdef VBGL_VBOXGUEST +# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code." +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBOX_INIT_CALL(a, b, c) \ + LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \ + VBGL_HGCM_HDR_INIT(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b); \ + (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */ + +#define VBOX_INIT_CALL_EX(a, b, c, a_cbReq) \ + LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \ + VBGL_HGCM_HDR_INIT_EX(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b, a_cbReq); \ + (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */ + + + +DECLVBGL(int) VbglR0SfInit(void) +{ + return VbglR0InitClient(); +} + +DECLVBGL(void) VbglR0SfTerm(void) +{ + VbglR0TerminateClient(); +} + +DECLVBGL(int) VbglR0SfConnect(PVBGLSFCLIENT pClient) +{ + int rc = VbglR0HGCMConnect(&pClient->handle, "VBoxSharedFolders", &pClient->idClient); + if (RT_SUCCESS(rc)) + LogFunc(("idClient=%d\n", pClient->idClient)); + else + LogFunc(("VbglR0HGCMConnect failed -> rc=%Rrc\n", rc)); + return rc; +} + +DECLVBGL(void) VbglR0SfDisconnect(PVBGLSFCLIENT pClient) +{ + int rc; + LogFunc(("u32ClientID=%d\n", pClient->idClient)); + if (pClient->handle == NULL) + return; /* not connected */ + + rc = VbglR0HGCMDisconnect(pClient->handle, pClient->idClient); + NOREF(rc); +/* Log(("VBOXSF: VbglR0SfDisconnect: VbglR0HGCMDisconnect -> %#x\n", rc)); */ + pClient->idClient = 0; + pClient->handle = NULL; + return; +} + +#if !defined(RT_OS_LINUX) + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfSetUtf8(PVBGLSFCLIENT pClient) +{ + int rc; + VBGLIOCHGCMCALL callInfo; + + VBOX_INIT_CALL(&callInfo, SET_UTF8, pClient); + rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo)); +/* Log(("VBOXSF: VbglR0SfSetUtf8: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +/** @name Deprecated VBGL shared folder helpers. + * + * @deprecated These are all use the slow VbglR0HGCMCall interface, that + * basically treat ring-0 and user land callers much the same. + * Since 6.0 there is VbglR0HGCMFastCall() that does not bother with + * repacking the request and locking/duplicating parameter buffers, + * but just passes it along to the host and handles the waiting. + * Also new in 6.0 is embedded buffers which saves a bit time on + * guest and host by embedding parameter buffers into the request. + * + * @{ + */ + +DECLVBGL(int) VbglR0SfQueryMappings(PVBGLSFCLIENT pClient, SHFLMAPPING paMappings[], uint32_t *pcMappings) +{ + int rc; + VBoxSFQueryMappings data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAPPINGS, pClient); + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = SHFL_MF_UCS2; + + data.numberOfMappings.type = VMMDevHGCMParmType_32bit; + data.numberOfMappings.u.value32 = *pcMappings; + + data.mappings.type = VMMDevHGCMParmType_LinAddr; + data.mappings.u.Pointer.size = sizeof(SHFLMAPPING) * *pcMappings; + data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&paMappings[0]; + +/* Log(("VBOXSF: in ifs difference %d\n", (char *)&data.flags.type - (char *)&data.callInfo.cParms)); */ + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfQueryMappings: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.result)); */ + if (RT_SUCCESS(rc)) + *pcMappings = data.numberOfMappings.u.value32; + + return rc; +} + +DECLVBGL(int) VbglR0SfQueryMapName(PVBGLSFCLIENT pClient, SHFLROOT root, SHFLSTRING *pString, uint32_t size) +{ + int rc; + VBoxSFQueryMapName data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAP_NAME, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.name.type = VMMDevHGCMParmType_LinAddr; + data.name.u.Pointer.size = size; + data.name.u.Pointer.u.linearAddr = (uintptr_t)pString; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfQueryMapName: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfMapFolder(PVBGLSFCLIENT pClient, PSHFLSTRING szFolderName, PVBGLSFMAP pMap) +{ + int rc; + VBoxSFMapFolder data; + + VBOX_INIT_CALL(&data.callInfo, MAP_FOLDER, pClient); + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(szFolderName); + data.path.u.Pointer.u.linearAddr = (uintptr_t)szFolderName; + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = 0; + + data.delimiter.type = VMMDevHGCMParmType_32bit; + data.delimiter.u.value32 = RTPATH_DELIMITER; + + data.fCaseSensitive.type = VMMDevHGCMParmType_32bit; +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + data.fCaseSensitive.u.value32 = 0; +#else + data.fCaseSensitive.u.value32 = 1; +#endif + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfMapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + pMap->root = data.root.u.value32; + rc = data.callInfo.Hdr.rc; + } + return rc; +} + +DECLVBGL(int) VbglR0SfUnmapFolder(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap) +{ + int rc; + VBoxSFUnmapFolder data; + + VBOX_INIT_CALL(&data.callInfo, UNMAP_FOLDER, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfUnmapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# if !defined(RT_OS_WINDOWS) + +DECLVBGL(int) VbglR0SfCreate(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, PSHFLCREATEPARMS pCreateParms) +{ + /** @todo copy buffers to physical or mapped memory. */ + int rc; + VBoxSFCreate data; + + VBOX_INIT_CALL(&data.callInfo, CREATE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.parms.type = VMMDevHGCMParmType_LinAddr; + data.parms.u.Pointer.size = sizeof(SHFLCREATEPARMS); + data.parms.u.Pointer.u.linearAddr = (uintptr_t)pCreateParms; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfCreate: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfClose(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE Handle) +{ + int rc; + VBoxSFClose data; + + VBOX_INIT_CALL(&data.callInfo, CLOSE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = Handle; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfClose: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + + +DECLVBGL(int) VbglR0SfRemove(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t flags) +{ + int rc = VINF_SUCCESS; + + VBoxSFRemove data; + + VBOX_INIT_CALL(&data.callInfo, REMOVE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRemove: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfRename(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, uint32_t flags) +{ + int rc; + VBoxSFRename data; + + VBOX_INIT_CALL(&data.callInfo, RENAME, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.src.type = VMMDevHGCMParmType_LinAddr_In; + data.src.u.Pointer.size = ShflStringSizeOfBuffer(pSrcPath); + data.src.u.Pointer.u.linearAddr = (uintptr_t)pSrcPath; + + data.dest.type = VMMDevHGCMParmType_LinAddr_In; + data.dest.u.Pointer.size = ShflStringSizeOfBuffer(pDestPath); + data.dest.u.Pointer.u.linearAddr = (uintptr_t)pDestPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRename: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfRead(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked) +{ + int rc; + VBoxSFRead data; + + VBOX_INIT_CALL(&data.callInfo, READ, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = (fLocked) ? VMMDevHGCMParmType_LinAddr_Locked_Out : VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfRead: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +DECLVBGL(int) VbglR0SfReadPageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToRead = *pcbBuffer; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFRead) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFRead *pData = (VBoxSFRead *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, READ, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToRead; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToRead; + pData->buffer.u.PageList.offset = sizeof(VBoxSFRead); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfReadPageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + +DECLVBGL(int) VbglR0SfWrite(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked) +{ + int rc; + VBoxSFWrite data; + + VBOX_INIT_CALL(&data.callInfo, WRITE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = fLocked ? VMMDevHGCMParmType_LinAddr_Locked_In : VMMDevHGCMParmType_LinAddr_In; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfWrite: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +DECLVBGL(int) VbglR0SfWritePhysCont(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, + uint32_t *pcbBuffer, RTCCPHYS PhysBuffer) +{ + uint32_t cbToWrite = *pcbBuffer; + uint32_t cPages = RT_ALIGN_32((PhysBuffer & PAGE_OFFSET_MASK) + cbToWrite, PAGE_SIZE) >> PAGE_SHIFT; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint32_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = (uint16_t)(PhysBuffer & PAGE_OFFSET_MASK); + pPgLst->cPages = cPages; + PhysBuffer &= ~(RTCCPHYS)PAGE_OFFSET_MASK; + for (iPage = 0; iPage < cPages; iPage++, PhysBuffer += PAGE_SIZE) + pPgLst->aPages[iPage] = PhysBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfWritePhysCont: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; + +} + +DECLVBGL(int) VbglR0SfWritePageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToWrite = *pcbBuffer; + uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages])); + VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData); +/* Log(("VBOXSF: VbglR0SfWritePageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = pData->callInfo.Hdr.rc; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +DECLVBGL(int) VbglR0SfFlush(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile) +{ + int rc; + VBoxSFFlush data; + + VBOX_INIT_CALL(&data.callInfo, FLUSH, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfFlush: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfDirInfo( + PVBGLSFCLIENT pClient, + PVBGLSFMAP pMap, + SHFLHANDLE hFile, + PSHFLSTRING ParsedPath, + uint32_t flags, + uint32_t index, + uint32_t *pcbBuffer, + PSHFLDIRINFO pBuffer, + uint32_t *pcFiles) +{ + int rc; + VBoxSFList data; + + VBOX_INIT_CALL(&data.callInfo, LIST, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ParsedPath ? ShflStringSizeOfBuffer(ParsedPath) : 0; + data.path.u.Pointer.u.linearAddr = (uintptr_t) ParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + data.resumePoint.type = VMMDevHGCMParmType_32bit; + data.resumePoint.u.value32 = index; + data.cFiles.type = VMMDevHGCMParmType_32bit; + data.cFiles.u.value32 = 0; /* out parameters only */ + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfDirInfo: rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + *pcbBuffer = data.cb.u.value32; + *pcFiles = data.cFiles.u.value32; + return rc; +} + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfFsInfo(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint32_t flags, uint32_t *pcbBuffer, PSHFLDIRINFO pBuffer) +{ + int rc; + VBoxSFInformation data; + + VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.info.type = VMMDevHGCMParmType_LinAddr; + data.info.u.Pointer.size = *pcbBuffer; + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfFsInfo: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + if (RT_SUCCESS(rc)) + { + rc = data.callInfo.Hdr.rc; + *pcbBuffer = data.cb.u.value32; + } + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +DECLVBGL(int) VbglR0SfLock(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint64_t cbSize, uint32_t fLock) +{ + int rc; + VBoxSFLock data; + + VBOX_INIT_CALL(&data.callInfo, LOCK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.length.type = VMMDevHGCMParmType_64bit; + data.length.u.value64 = cbSize; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = fLock; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfLock: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# ifndef RT_OS_WINDOWS + +DECLVBGL(int) VbglR0SfReadLink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t cbBuffer, uint8_t *pBuffer) +{ + int rc; + VBoxSFReadLink data; + + VBOX_INIT_CALL(&data.callInfo, READLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = cbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfReadLink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfSymlink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath, + PSHFLFSOBJINFO pBuffer) +{ + int rc; + VBoxSFSymlink data; + + VBOX_INIT_CALL(&data.callInfo, SYMLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = pMap->root; + + data.newPath.type = VMMDevHGCMParmType_LinAddr_In; + data.newPath.u.Pointer.size = ShflStringSizeOfBuffer (pNewPath); + data.newPath.u.Pointer.u.linearAddr = (uintptr_t)pNewPath; + + data.oldPath.type = VMMDevHGCMParmType_LinAddr_In; + data.oldPath.u.Pointer.size = ShflStringSizeOfBuffer (pOldPath); + data.oldPath.u.Pointer.u.linearAddr = (uintptr_t)pOldPath; + + data.info.type = VMMDevHGCMParmType_LinAddr_Out; + data.info.u.Pointer.size = sizeof(SHFLFSOBJINFO); + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data)); +/* Log(("VBOXSF: VbglR0SfSymlink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +DECLVBGL(int) VbglR0SfSetSymlinks(PVBGLSFCLIENT pClient) +{ + int rc; + VBGLIOCHGCMCALL callInfo; + + VBOX_INIT_CALL(&callInfo, SET_SYMLINKS, pClient); + rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo)); +/* Log(("VBOXSF: VbglR0SfSetSymlinks: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */ + return rc; +} + +# endif /* !RT_OS_WINDOWS */ + +#endif /* !RT_OS_LINUX */ + +/** @} */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp new file mode 100644 index 00000000..5909fd2c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp @@ -0,0 +1,51 @@ +/* $Id: VBoxGuestR0LibVMMDev.cpp $ */ +/** @file + * VBoxGuestLibR0 - VMMDev device related functions. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +DECLVBGL(int) VbglR0QueryVMMDevMemory(VMMDevMemory **ppVMMDevMemory) +{ + int rc = vbglR0Enter(); + if (RT_FAILURE(rc)) + return rc; + + /* If the memory was not found, return an error. */ + if (!g_vbgldata.pVMMDevMemory) + return VERR_NOT_SUPPORTED; + + *ppVMMDevMemory = g_vbgldata.pVMMDevMemory; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp new file mode 100644 index 00000000..4298b742 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp @@ -0,0 +1,485 @@ +/* $Id: VBoxGuestR3Lib.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(RT_OS_WINDOWS) +# include <iprt/nt/nt-and-windows.h> + +#elif defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> + +#elif defined(RT_OS_DARWIN) \ + || defined(RT_OS_FREEBSD) \ + || defined(RT_OS_HAIKU) \ + || defined(RT_OS_LINUX) \ + || defined(RT_OS_NETBSD) \ + || defined(RT_OS_SOLARIS) +# include <sys/types.h> +# include <sys/stat.h> +# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_NETBSD) + /** @todo check this on solaris+freebsd as well. */ +# include <sys/ioctl.h> +# endif +# if defined(RT_OS_DARWIN) +# include <mach/mach_port.h> +# include <IOKit/IOKitLib.h> +# endif +# include <errno.h> +# include <unistd.h> +#endif + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/time.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <VBox/log.h> +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +# define XF86_O_RDWR 0x0002 +typedef void *pointer; +extern "C" int xf86open(const char *, int, ...); +extern "C" int xf86close(int); +extern "C" int xf86ioctl(int, unsigned long, pointer); +# define VBOX_VBGLR3_XSERVER +#elif defined(VBOX_VBGLR3_XORG) +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +# include <sys/ioctl.h> +# define xf86open open +# define xf86close close +# define xf86ioctl ioctl +# define XF86_O_RDWR O_RDWR +# define VBOX_VBGLR3_XSERVER +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The VBoxGuest device handle. */ +#ifdef VBOX_VBGLR3_XSERVER +static int g_File = -1; +#elif defined(RT_OS_WINDOWS) +static HANDLE g_hFile = INVALID_HANDLE_VALUE; +#else +static RTFILE g_File = NIL_RTFILE; +#endif +/** User counter. + * A counter of the number of times the library has been initialised, for use with + * X.org drivers, where the library may be shared by multiple independent modules + * inside a single process space. + */ +static uint32_t volatile g_cInits = 0; +#ifdef RT_OS_DARWIN +/** I/O Kit connection handle. */ +static io_connect_t g_uConnection = 0; +#endif + + + +/** + * Implementation of VbglR3Init and VbglR3InitUser + */ +static int vbglR3Init(const char *pszDeviceName) +{ + int rc2; + uint32_t cInits = ASMAtomicIncU32(&g_cInits); + Assert(cInits > 0); + if (cInits > 1) + { + /* + * This will fail if two (or more) threads race each other calling VbglR3Init. + * However it will work fine for single threaded or otherwise serialized + * processed calling us more than once. + */ +#ifdef RT_OS_WINDOWS + if (g_hFile == INVALID_HANDLE_VALUE) +#elif !defined (VBOX_VBGLR3_XSERVER) + if (g_File == NIL_RTFILE) +#else + if (g_File == -1) +#endif + return VERR_INTERNAL_ERROR; + return VINF_SUCCESS; + } +#if defined(RT_OS_WINDOWS) + if (g_hFile != INVALID_HANDLE_VALUE) +#elif !defined(VBOX_VBGLR3_XSERVER) + if (g_File != NIL_RTFILE) +#else + if (g_File != -1) +#endif + return VERR_INTERNAL_ERROR; + +#if defined(RT_OS_WINDOWS) + /* + * Have to use CreateFile here as we want to specify FILE_FLAG_OVERLAPPED + * and possible some other bits not available thru iprt/file.h. + */ + HANDLE hFile = CreateFile(pszDeviceName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return VERR_OPEN_FAILED; + g_hFile = hFile; + +#elif defined(RT_OS_OS2) + /* + * We might wish to compile this with Watcom, so stick to + * the OS/2 APIs all the way. And in any case we have to use + * DosDevIOCtl for the requests, why not use Dos* for everything. + */ + HFILE hf = NULLHANDLE; + ULONG ulAction = 0; + APIRET rc = DosOpen((PCSZ)pszDeviceName, &hf, &ulAction, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, + NULL); + if (rc) + return RTErrConvertFromOS2(rc); + + if (hf < 16) + { + HFILE ahfs[16]; + unsigned i; + for (i = 0; i < RT_ELEMENTS(ahfs); i++) + { + ahfs[i] = 0xffffffff; + rc = DosDupHandle(hf, &ahfs[i]); + if (rc) + break; + } + + if (i-- > 1) + { + ULONG fulState = 0; + rc = DosQueryFHState(ahfs[i], &fulState); + if (!rc) + { + fulState |= OPEN_FLAGS_NOINHERIT; + fulState &= OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT; /* Turn off non-participating bits. */ + rc = DosSetFHState(ahfs[i], fulState); + } + if (!rc) + { + rc = DosClose(hf); + AssertMsg(!rc, ("%ld\n", rc)); + hf = ahfs[i]; + } + else + i++; + while (i-- > 0) + DosClose(ahfs[i]); + } + } + g_File = (RTFILE)hf; + +#elif defined(RT_OS_DARWIN) + /* + * Darwin is kind of special we need to engage the device via I/O first + * before we open it via the BSD device node. + */ + /* IOKit */ + mach_port_t MasterPort; + kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort); + if (kr != kIOReturnSuccess) + { + LogRel(("IOMasterPort -> %d\n", kr)); + return VERR_GENERAL_FAILURE; + } + + CFDictionaryRef ClassToMatch = IOServiceMatching("org_virtualbox_VBoxGuest"); + if (!ClassToMatch) + { + LogRel(("IOServiceMatching(\"org_virtualbox_VBoxGuest\") failed.\n")); + return VERR_GENERAL_FAILURE; + } + + io_service_t ServiceObject = IOServiceGetMatchingService(kIOMasterPortDefault, ClassToMatch); + if (!ServiceObject) + { + LogRel(("IOServiceGetMatchingService returned NULL\n")); + return VERR_NOT_FOUND; + } + + io_connect_t uConnection; + kr = IOServiceOpen(ServiceObject, mach_task_self(), VBOXGUEST_DARWIN_IOSERVICE_COOKIE, &uConnection); + IOObjectRelease(ServiceObject); + if (kr != kIOReturnSuccess) + { + LogRel(("IOServiceOpen returned %d. Driver open failed.\n", kr)); + return VERR_OPEN_FAILED; + } + + /* Regular unix FD. */ + RTFILE hFile; + int rc = RTFileOpen(&hFile, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + { + LogRel(("RTFileOpen(%s) returned %Rrc. Driver open failed.\n", pszDeviceName, rc)); + IOServiceClose(uConnection); + return rc; + } + g_File = hFile; + g_uConnection = uConnection; + +#elif defined(VBOX_VBGLR3_XSERVER) + int File = xf86open(pszDeviceName, XF86_O_RDWR); + if (File == -1) + return VERR_OPEN_FAILED; + g_File = File; + +#else + + /* The default implementation. (linux, solaris, freebsd, netbsd, haiku) */ + RTFILE File; + int rc = RTFileOpen(&File, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + return rc; + g_File = File; + +#endif + + /* + * Adjust the I/O control interface version. + */ + { + VBGLIOCDRIVERVERSIONINFO VerInfo; + VBGLREQHDR_INIT(&VerInfo.Hdr, DRIVER_VERSION_INFO); + VerInfo.u.In.uMinVersion = VBGL_IOC_VERSION & UINT32_C(0xffff0000); + VerInfo.u.In.uReqVersion = VBGL_IOC_VERSION; + VerInfo.u.In.uReserved1 = 0; + VerInfo.u.In.uReserved2 = 0; + rc2 = vbglR3DoIOCtl(VBGL_IOCTL_DRIVER_VERSION_INFO, &VerInfo.Hdr, sizeof(VerInfo)); +#ifndef VBOX_VBGLR3_XSERVER + AssertRC(rc2); /* otherwise ignored for now*/ +#endif + } + + +#ifndef VBOX_VBGLR3_XSERVER + /* + * Create release logger + */ + PRTLOGGER pReleaseLogger; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + rc2 = RTLogCreate(&pReleaseLogger, 0, "all", "VBOX_RELEASE_LOG", + RT_ELEMENTS(s_apszGroups), &s_apszGroups[0], RTLOGDEST_USER, NULL); + /* This may legitimately fail if we are using the mini-runtime. */ + if (RT_SUCCESS(rc2)) + RTLogRelSetDefaultInstance(pReleaseLogger); +#endif + + return VINF_SUCCESS; +} + + +/** + * Open the VBox R3 Guest Library. This should be called by system daemons + * and processes. + */ +VBGLR3DECL(int) VbglR3Init(void) +{ + return vbglR3Init(VBOXGUEST_DEVICE_NAME); +} + + +/** + * Open the VBox R3 Guest Library. Equivalent to VbglR3Init, but for user + * session processes. + */ +VBGLR3DECL(int) VbglR3InitUser(void) +{ + return vbglR3Init(VBOXGUEST_USER_DEVICE_NAME); +} + + +VBGLR3DECL(void) VbglR3Term(void) +{ + /* + * Decrement the reference count and see if we're the last one out. + */ + uint32_t cInits = ASMAtomicDecU32(&g_cInits); + if (cInits > 0) + return; +#if !defined(VBOX_VBGLR3_XSERVER) + AssertReturnVoid(!cInits); + +# if defined(RT_OS_WINDOWS) + HANDLE hFile = g_hFile; + g_hFile = INVALID_HANDLE_VALUE; + AssertReturnVoid(hFile != INVALID_HANDLE_VALUE); + BOOL fRc = CloseHandle(hFile); + Assert(fRc); NOREF(fRc); + +# elif defined(RT_OS_OS2) + RTFILE File = g_File; + g_File = NIL_RTFILE; + AssertReturnVoid(File != NIL_RTFILE); + APIRET rc = DosClose((uintptr_t)File); + AssertMsg(!rc, ("%ld\n", rc)); + +#elif defined(RT_OS_DARWIN) + io_connect_t uConnection = g_uConnection; + RTFILE hFile = g_File; + g_uConnection = 0; + g_File = NIL_RTFILE; + kern_return_t kr = IOServiceClose(uConnection); + AssertMsg(kr == kIOReturnSuccess, ("%#x (%d)\n", kr, kr)); NOREF(kr); + int rc = RTFileClose(hFile); + AssertRC(rc); + +# else /* The IPRT case. */ + RTFILE File = g_File; + g_File = NIL_RTFILE; + AssertReturnVoid(File != NIL_RTFILE); + int rc = RTFileClose(File); + AssertRC(rc); +# endif + +#else /* VBOX_VBGLR3_XSERVER */ + int File = g_File; + g_File = -1; + if (File == -1) + return; + xf86close(File); +#endif /* VBOX_VBGLR3_XSERVER */ +} + + +/** + * Internal wrapper around various OS specific ioctl implementations. + * + * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or + * an failure returned by the OS specific ioctl APIs. + * + * @param uFunction The requested function. + * @param pHdr The input and output request buffer. + * @param cbReq The size of the request buffer. + */ +int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq) +{ + Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut)); RT_NOREF1(cbReq); + Assert(pHdr->cbOut != 0); + +#if defined(RT_OS_WINDOWS) +# if 0 /*def USE_NT_DEVICE_IO_CONTROL_FILE*/ + IO_STATUS_BLOCK Ios; + Ios.Status = -1; + Ios.Information = 0; + NTSTATUS rcNt = NtDeviceIoControlFile(g_hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios, + (ULONG)uFunction, + pHdr /*pvInput */, pHdr->cbIn /* cbInput */, + pHdr /*pvOutput*/, pHdr->cbOut /* cbOutput */); + if (NT_SUCCESS(rcNt)) + { + if (NT_SUCCESS(Ios.Status)) + return VINF_SUCCESS; + rcNt = Ios.Status; + } + return RTErrConvertFromNtStatus(rcNt); + +# else + DWORD cbReturned = (ULONG)pHdr->cbOut; + if (DeviceIoControl(g_hFile, uFunction, pHdr, pHdr->cbIn, pHdr, cbReturned, &cbReturned, NULL)) + return 0; + return RTErrConvertFromWin32(GetLastError()); +# endif + +#elif defined(RT_OS_OS2) + ULONG cbOS2Parm = cbReq; + APIRET rc = DosDevIOCtl((uintptr_t)g_File, VBGL_IOCTL_CATEGORY, uFunction, pHdr, cbReq, &cbOS2Parm, NULL, 0, NULL); + if (RT_LIKELY(rc == NO_ERROR)) + return VINF_SUCCESS; + return RTErrConvertFromOS2(rc); + +#elif defined(VBOX_VBGLR3_XSERVER) + if (g_File != -1) + { + if (RT_LIKELY(xf86ioctl((int)g_File, uFunction, pHdr) >= 0)) + return VINF_SUCCESS; + return VERR_FILE_IO_ERROR; + } + return VERR_INVALID_HANDLE; + +#else + if (g_File != NIL_RTFILE) + { + if (RT_LIKELY(ioctl((int)(intptr_t)g_File, uFunction, pHdr) >= 0)) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); + } + return VERR_INVALID_HANDLE; +#endif +} + + +/** + * Internal wrapper around various OS specific ioctl implementations, that + * returns the status from the header. + * + * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or + * an failure returned by the OS specific ioctl APIs. + * + * @param uFunction The requested function. + * @param pHdr The input and output request buffer. + * @param cbReq The size of the request buffer. + */ +int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq) +{ + int rc = vbglR3DoIOCtlRaw(uFunction, pHdr, cbReq); + if (RT_SUCCESS(rc)) + rc = pHdr->rc; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp new file mode 100644 index 00000000..2ee88509 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp @@ -0,0 +1,363 @@ +/* $Id: VBoxGuestR3LibAdditions.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Additions Info. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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/err.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> +#ifdef RT_OS_WINDOWS +# include <iprt/utf16.h> +#endif +#include <VBox/log.h> +#include <VBox/version.h> +#include "VBoxGuestR3LibInternal.h" + + + +#ifdef RT_OS_WINDOWS + +/** + * Opens the "VirtualBox Guest Additions" registry key. + * + * @returns IPRT status code + * @param phKey Receives key handle on success. The returned handle must + * be closed by calling vbglR3WinCloseRegKey. + */ +static int vbglR3WinOpenAdditionRegisterKey(PHKEY phKey) +{ + /* + * Current vendor first. We keep the older ones just for the case that + * the caller isn't actually installed yet (no real use case AFAIK). + */ + static PCRTUTF16 s_apwszKeys[] = + { + L"SOFTWARE\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions", +#endif + L"SOFTWARE\\Sun\\VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\Sun\\VirtualBox Guest Additions", +#endif + L"SOFTWARE\\Sun\\xVM VirtualBox Guest Additions", +#ifdef RT_ARCH_AMD64 + L"SOFTWARE\\Wow6432Node\\Sun\\xVM VirtualBox Guest Additions", +#endif + }; + int rc = VERR_NOT_FOUND; + for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszKeys); i++) + { + LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_apwszKeys[i], 0 /* ulOptions*/, KEY_READ, phKey); + if (lrc == ERROR_SUCCESS) + return VINF_SUCCESS; + if (i == 0) + rc = RTErrConvertFromWin32(lrc); + } + return rc; +} + + +/** + * Closes the registry handle returned by vbglR3WinOpenAdditionRegisterKey(). + * + * @returns @a rc or IPRT failure status. + * @param hKey Handle to close. + * @param rc The current IPRT status of the operation. Error + * condition takes precedence over errors from this call. + */ +static int vbglR3WinCloseRegKey(HKEY hKey, int rc) +{ + LSTATUS lrc = RegCloseKey(hKey); + if ( lrc == ERROR_SUCCESS + || RT_FAILURE(rc)) + return rc; + return RTErrConvertFromWin32(lrc); +} + + +/** + * Queries a string value from a specified registry key. + * + * @return IPRT status code. + * @param hKey Handle of registry key to use. + * @param pwszValueName The name of the value to query. + * @param cbHint Size hint. + * @param ppszValue Where to return value string on success. Free + * with RTStrFree. + */ +static int vbglR3QueryRegistryString(HKEY hKey, PCRTUTF16 pwszValueName, uint32_t cbHint, char **ppszValue) +{ + AssertPtr(pwszValueName); + AssertPtrReturn(ppszValue, VERR_INVALID_POINTER); + + /* + * First try. + */ + int rc; + DWORD dwType; + DWORD cbTmp = cbHint; + PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16)); + if (pwszTmp) + { + LSTATUS lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp); + if (lrc == ERROR_MORE_DATA) + { + /* + * Allocate larger buffer and try again. + */ + RTMemTmpFree(pwszTmp); + cbTmp += 16; + pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16)); + if (!pwszTmp) + { + *ppszValue = NULL; + return VERR_NO_TMP_MEMORY; + } + lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp); + } + if (lrc == ERROR_SUCCESS) + { + /* + * Check the type and convert to UTF-8. + */ + if (dwType == REG_SZ) + rc = RTUtf16ToUtf8(pwszTmp, ppszValue); + else + rc = VERR_WRONG_TYPE; + } + else + rc = RTErrConvertFromWin32(lrc); + RTMemTmpFree(pwszTmp); + } + else + rc = VERR_NO_TMP_MEMORY; + if (RT_SUCCESS(rc)) + return rc; + *ppszValue = NULL; + return rc; +} + +#endif /* RT_OS_WINDOWS */ + + +/** + * Fallback for VbglR3GetAdditionsVersion. + * + * @copydoc VbglR3GetAdditionsVersion + */ +static int vbglR3GetAdditionsCompileTimeVersion(char **ppszVer, char **ppszVerExt, char **ppszRev) +{ + int rc = VINF_SUCCESS; + if (ppszVer) + rc = RTStrDupEx(ppszVer, VBOX_VERSION_STRING_RAW); + if (RT_SUCCESS(rc)) + { + if (ppszVerExt) + rc = RTStrDupEx(ppszVerExt, VBOX_VERSION_STRING); + if (RT_SUCCESS(rc)) + { + if (ppszRev) + rc = RTStrDupEx(ppszRev, RT_XSTR(VBOX_SVN_REV)); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + /* bail out: */ + } + if (ppszVerExt) + { + RTStrFree(*ppszVerExt); + *ppszVerExt = NULL; + } + } + if (ppszVer) + { + RTStrFree(*ppszVer); + *ppszVer = NULL; + } + return rc; +} + + +/** + * Retrieves the installed Guest Additions version and/or revision. + * + * @returns IPRT status code + * @param ppszVer Receives pointer of allocated raw version string + * (major.minor.build). NULL is accepted. The returned + * pointer must be freed using RTStrFree(). + * @param ppszVerExt Receives pointer of allocated full version string + * (raw version + vendor suffix(es)). NULL is + * accepted. The returned pointer must be freed using + * RTStrFree(). + * @param ppszRev Receives pointer of allocated revision string. NULL is + * accepted. The returned pointer must be freed using + * RTStrFree(). + */ +VBGLR3DECL(int) VbglR3GetAdditionsVersion(char **ppszVer, char **ppszVerExt, char **ppszRev) +{ + /* + * Zap the return value up front. + */ + if (ppszVer) + *ppszVer = NULL; + if (ppszVerExt) + *ppszVerExt = NULL; + if (ppszRev) + *ppszRev = NULL; + +#ifdef RT_OS_WINDOWS + HKEY hKey; + int rc = vbglR3WinOpenAdditionRegisterKey(&hKey); + if (RT_SUCCESS(rc)) + { + /* + * Version. + */ + if (ppszVer) + rc = vbglR3QueryRegistryString(hKey, L"Version", 64, ppszVer); + + if ( RT_SUCCESS(rc) + && ppszVerExt) + rc = vbglR3QueryRegistryString(hKey, L"VersionExt", 128, ppszVerExt); + + /* + * Revision. + */ + if ( RT_SUCCESS(rc) + && ppszRev) + rc = vbglR3QueryRegistryString(hKey, L"Revision", 64, ppszRev); + + rc = vbglR3WinCloseRegKey(hKey, rc); + + /* Clean up allocated strings on error. */ + if (RT_FAILURE(rc)) + { + if (ppszVer) + { + RTStrFree(*ppszVer); + *ppszVer = NULL; + } + if (ppszVerExt) + { + RTStrFree(*ppszVerExt); + *ppszVerExt = NULL; + } + if (ppszRev) + { + RTStrFree(*ppszRev); + *ppszRev = NULL; + } + } + } + /* + * No registry entries found, return the version string compiled into this binary. + */ + else + rc = vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev); + return rc; + +#else /* !RT_OS_WINDOWS */ + /* + * On non-Windows platforms just return the compile-time version string. + */ + return vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev); +#endif /* !RT_OS_WINDOWS */ +} + + +/** + * Retrieves the installation path of Guest Additions. + * + * @returns IPRT status code + * @param ppszPath Receives pointer of allocated installation path string. + * The returned pointer must be freed using + * RTStrFree(). + */ +VBGLR3DECL(int) VbglR3GetAdditionsInstallationPath(char **ppszPath) +{ + int rc; + +#ifdef RT_OS_WINDOWS + /* + * Get it from the registry. + */ + HKEY hKey; + rc = vbglR3WinOpenAdditionRegisterKey(&hKey); + if (RT_SUCCESS(rc)) + { + rc = vbglR3QueryRegistryString(hKey, L"InstallDir", MAX_PATH * sizeof(RTUTF16), ppszPath); + if (RT_SUCCESS(rc)) + RTPathChangeToUnixSlashes(*ppszPath, true /*fForce*/); + rc = vbglR3WinCloseRegKey(hKey, rc); + } +#else + /** @todo implement me */ + rc = VERR_NOT_IMPLEMENTED; + RT_NOREF1(ppszPath); +#endif + return rc; +} + + +/** + * Reports the Guest Additions status of a certain facility to the host. + * + * @returns IPRT status code + * @param enmFacility The facility to report the status on. + * @param enmStatus The new status of the facility. + * @param fReserved Flags reserved for future hacks. + */ +VBGLR3DECL(int) VbglR3ReportAdditionsStatus(VBoxGuestFacilityType enmFacility, VBoxGuestFacilityStatus enmStatus, + uint32_t fReserved) +{ + VMMDevReportGuestStatus Report; + RT_ZERO(Report); + int rc = vmmdevInitRequest(&Report.header, VMMDevReq_ReportGuestStatus); + if (RT_SUCCESS(rc)) + { + Report.guestStatus.facility = enmFacility; + Report.guestStatus.status = enmStatus; + Report.guestStatus.flags = fReserved; + + rc = vbglR3GRPerform(&Report.header); + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp new file mode 100644 index 00000000..680e6a5c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp @@ -0,0 +1,130 @@ +/* $Id: VBoxGuestR3LibAutoLogon.cpp $ */ +/** @file + * VBoxGuestR3LibAutoLogon - Ring-3 utility functions for auto-logon modules + * (VBoxGINA / VBoxCredProv / pam_vbox). + */ + +/* + * 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 * +*********************************************************************************************************************************/ +#ifdef RT_OS_WINDOWS +# include <iprt/win/windows.h> +#endif + +#include "VBoxGuestR3LibInternal.h" +#include <iprt/errcore.h> + + +/** + * Reports the current auto-logon status to the host. + * + * This makes sure that the Failed state is sticky. + * + * @return IPRT status code. + * @param enmStatus Status to report to the host. + */ +VBGLR3DECL(int) VbglR3AutoLogonReportStatus(VBoxGuestFacilityStatus enmStatus) +{ + /* + * VBoxGuestFacilityStatus_Failed is sticky. + */ + static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive; + if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed) + { + int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_AutoLogon, enmStatus, 0 /* Flags */); + if (rc == VERR_NOT_SUPPORTED) + { + /* + * To maintain backwards compatibility to older hosts which don't have + * VMMDevReportGuestStatus implemented we set the appropriate status via + * guest property to have at least something. + */ +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClient = 0; + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + const char *pszStatus; + switch (enmStatus) + { + case VBoxGuestFacilityStatus_Inactive: pszStatus = "Inactive"; break; + case VBoxGuestFacilityStatus_Paused: pszStatus = "Disabled"; break; + case VBoxGuestFacilityStatus_PreInit: pszStatus = "PreInit"; break; + case VBoxGuestFacilityStatus_Init: pszStatus = "Init"; break; + case VBoxGuestFacilityStatus_Active: pszStatus = "Active"; break; + case VBoxGuestFacilityStatus_Terminating: pszStatus = "Terminating"; break; + case VBoxGuestFacilityStatus_Terminated: pszStatus = "Terminated"; break; + case VBoxGuestFacilityStatus_Failed: pszStatus = "Failed"; break; + default: pszStatus = NULL; + } + if (pszStatus) + { + /* + * Use TRANSRESET when possible, fall back to TRANSIENT + * (generally sufficient unless the guest misbehaves). + */ + static const char s_szPath[] = "/VirtualBox/GuestInfo/OS/AutoLogonStatus"; + rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSRESET"); + if (rc == VERR_PARSE_ERROR) + rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSIENT"); + } + else + rc = VERR_INVALID_PARAMETER; + + VbglR3GuestPropDisconnect(idClient); + } +#endif + } + + s_enmLastStatus = enmStatus; + } + return VINF_SUCCESS; +} + + +/** + * Detects whether our process is running in a remote session or not. + * + * @return bool true if running in a remote session, false if not. + */ +VBGLR3DECL(bool) VbglR3AutoLogonIsRemoteSession(void) +{ +#ifdef RT_OS_WINDOWS + return GetSystemMetrics(SM_REMOTESESSION) != 0 ? true : false; +#else + return false; /* Not implemented. */ +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp new file mode 100644 index 00000000..b7b2a8f8 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp @@ -0,0 +1,83 @@ +/* $Id: VBoxGuestR3LibBalloon.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Ballooning. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" +#include <iprt/string.h> + + +/** + * Refresh the memory balloon after a change. + * + * @returns IPRT status code. + * @param pcChunks The size of the balloon in chunks of 1MB (out). + * @param pfHandleInR3 Allocating of memory in R3 required (out). + */ +VBGLR3DECL(int) VbglR3MemBalloonRefresh(uint32_t *pcChunks, bool *pfHandleInR3) +{ + VBGLIOCCHECKBALLOON Info; + VBGLREQHDR_INIT(&Info.Hdr, CHECK_BALLOON); + int rc = vbglR3DoIOCtl(VBGL_IOCTL_CHECK_BALLOON, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + { + *pcChunks = Info.u.Out.cBalloonChunks; + *pfHandleInR3 = Info.u.Out.fHandleInR3 != false; + } + return rc; +} + + +/** + * Change the memory by granting/reclaiming memory to/from R0. + * + * @returns IPRT status code. + * @param pv Memory chunk (1MB). + * @param fInflate true = inflate balloon (grant memory). + * false = deflate balloon (reclaim memory). + */ +VBGLR3DECL(int) VbglR3MemBalloonChange(void *pv, bool fInflate) +{ + VBGLIOCCHANGEBALLOON Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_BALLOON); + Info.u.In.pvChunk = pv; + Info.u.In.fInflate = fInflate; + RT_ZERO(Info.u.In.abPadding); + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_BALLOON, &Info.Hdr, sizeof(Info)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp new file mode 100644 index 00000000..f44b923a --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp @@ -0,0 +1,2609 @@ +/* $Id: VBoxGuestR3LibClipboard.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared Clipboard. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 <VBox/GuestHost/SharedClipboard.h> +#include <VBox/GuestHost/clipboard-helper.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <VBox/GuestHost/SharedClipboard-transfers.h> +#endif +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/err.h> +#include <iprt/assert.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <iprt/dir.h> +# include <iprt/file.h> +# include <iprt/path.h> +#endif +#include <iprt/string.h> +#include <iprt/cpp/ministring.h> + +#include "VBoxGuestR3LibInternal.h" + + +/* + * Function naming convention: + * + * FunctionNameRecv = Receives a host message (request). + * FunctionNameReply = Replies to a host message (request). + * FunctionNameSend = Sends a guest message to the host. + */ + + +/********************************************************************************************************************************* +* Prototypes * +*********************************************************************************************************************************/ + + +/** + * Connects to the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code + * @param pidClient Where to put the client id on success. The client id + * must be passed to all the other clipboard calls. + */ +VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient); + if (RT_FAILURE(rc)) + { + if (rc == VERR_HGCM_SERVICE_NOT_FOUND) + LogRel(("Shared Clipboard: Unabled to connect, as host service was not found, skipping\n")); + else + LogRel(("Shared Clipboard: Unabled to connect to host service, rc=%Rrc\n", rc)); + } + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Connects to the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Command context. This will be initialized by this + * call. + * @param fGuestFeatures The guest features supported by this client, + * VBOX_SHCL_GF_0_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardConnectEx(PVBGLR3SHCLCMDCTX pCtx, uint64_t fGuestFeatures) +{ + /* + * Intialize the context structure. + */ + pCtx->idClient = 0; + pCtx->fGuestFeatures = fGuestFeatures; + pCtx->fHostFeatures = 0; + pCtx->fUseLegacyProtocol = true; + pCtx->cParmsRecived = 0; + pCtx->idContext = 0; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + /* Init callback table. */ + RT_ZERO(pCtx->Transfers.Callbacks); + /* Indicate that this guest supports Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS; +# ifdef RT_OS_WINDOWS + /* Indicate that on Windows guest OSes we have our own IDataObject implementation which + * integrates nicely into the guest's Windows Explorer showing / handling the Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS_FRONTEND; +# endif + pCtx->Transfers.cbChunkSize = VBOX_SHCL_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */ + pCtx->Transfers.cbMaxChunkSize = VBOX_SHCL_MAX_CHUNK_SIZE; /** @todo Ditto. */ +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + + /* + * First step is connecting to the HGCM service. + */ + int rc = VbglR3ClipboardConnect(&pCtx->idClient); + if (RT_SUCCESS(rc)) + { + /* + * Next is reporting our features. If this fails, assume older host. + */ + rc = VbglR3ClipboardReportFeatures(pCtx->idClient, pCtx->fGuestFeatures, &pCtx->fHostFeatures); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Guest features: %#RX64 - Host features: %#RX64\n", + pCtx->fGuestFeatures, pCtx->fHostFeatures)); + + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_CONTEXT_ID) ) + { + pCtx->fUseLegacyProtocol = false; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_TRANSFERS) ) + { + VBoxShClParmNegotiateChunkSize MsgChunkSize; + do + { + VBGL_HGCM_HDR_INIT(&MsgChunkSize.hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE, + VBOX_SHCL_CPARMS_NEGOTIATE_CHUNK_SIZE); + MsgChunkSize.cb32MaxChunkSize.SetUInt32(pCtx->Transfers.cbMaxChunkSize); + MsgChunkSize.cb32ChunkSize.SetUInt32(0); /* If set to 0, let the host choose. */ + rc = VbglR3HGCMCall(&MsgChunkSize.hdr, sizeof(MsgChunkSize)); + } while (rc == VERR_INTERRUPTED); + if (RT_SUCCESS(rc)) + { + Assert(MsgChunkSize.cb32ChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbChunkSize = RT_MIN(MsgChunkSize.cb32ChunkSize.u.value32, pCtx->Transfers.cbChunkSize); + Assert(MsgChunkSize.cb32MaxChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbMaxChunkSize = RT_MIN(MsgChunkSize.cb32MaxChunkSize.u.value32, pCtx->Transfers.cbMaxChunkSize); + + LogRel2(("Shared Clipboard: Using chunk size %RU32 (maximum is %RU32)\n", + pCtx->Transfers.cbChunkSize, pCtx->Transfers.cbMaxChunkSize)); + } + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS)) + LogRel2(("Shared Clipboard: Host does not support transfers\n")); + } +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID)) + LogRel(("Shared Clipboard: Host does not support context IDs, using legacy protocol\n")); + + pCtx->fUseLegacyProtocol = true; + } + } + else + { + AssertLogRelMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, + ("Reporting features failed: %Rrc\n", rc)); + pCtx->fUseLegacyProtocol = true; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param fGuestFeatures Features to report, VBOX_SHCL_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_SHCL_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_SHCL_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Disconnects from the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Disconnects from the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnectEx(PVBGLR3SHCLCMDCTX pCtx) +{ + int rc = VbglR3ClipboardDisconnect(pCtx->idClient); + if (RT_SUCCESS(rc)) + { + pCtx->idClient = 0; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Receives reported formats from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the + * connection. + * @param pfFormats Where to store the received formats from the host. + */ +static int vbglR3ClipboardFormatsReportRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMATS pfFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormats, VERR_INVALID_POINTER); + + *pfFormats = 0; + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Formats; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt32(VBOX_SHCL_HOST_MSG_FORMATS_REPORT); + Msg.f32Formats.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Formats.GetUInt32(pfFormats); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA_CID message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadDataCid(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt64(VBOX_SHCL_HOST_MSG_READ_DATA_CID); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.id64Context.GetUInt64(&pCtx->idContext); + AssertRC(rc); + int rc2 = Msg.f32Format.GetUInt32(pfFormat); + AssertRCStmt(rc2, rc = rc2); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadData(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id32Msg; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id32Msg.SetUInt32(VBOX_SHCL_HOST_MSG_READ_DATA); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Format.GetUInt32(pfFormat); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Get a host message, legacy version (which does not have VBOX_SHCL_GUEST_FN_MSG_GET). Do not use anymore. + * + * Note: This is the old message which still is being used for the non-URI Shared Clipboard transfers, + * to not break compatibility with older additions / VBox versions. + * + * This will block until a message becomes available. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param pidMsg Where to store the message id. + * @param pfFormats Where to store the format(s) the message applies to. + */ +VBGLR3DECL(int) VbglR3ClipboardGetHostMsgOld(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats) +{ + VBoxShClGetHostMsgOld Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD); + VbglHGCMParmUInt32Set(&Msg.msg, 0); + VbglHGCMParmUInt32Set(&Msg.formats, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg); + if (RT_SUCCESS(rc)) + { + rc2 = VbglHGCMParmUInt32Get(&Msg.formats, pfFormats); + if (RT_SUCCESS(rc2)) + return rc; + } + rc = rc2; + } + *pidMsg = UINT32_MAX - 1; + *pfFormats = UINT32_MAX; + return rc; +} + + +/** + * Reads data from the host clipboard. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format we're requesting the data in. + * @param pvData Where to store the data. + * @param cbData The size of the buffer pointed to by \a pvData. + * @param pcbRead The actual size of the host clipboard data. May be larger than \a cbData. + */ +VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pvData, uint32_t cbData, + uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataRead Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_READ, VBOX_SHCL_CPARMS_DATA_READ); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet( &Msg.Parms.pData, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.Parms.cb32Needed, 0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbRead; + rc = VbglHGCMParmUInt32Get(&Msg.Parms.cb32Needed, &cbRead); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("cbRead=%RU32\n", cbRead)); + + if (cbRead > cbData) + rc = VINF_BUFFER_OVERFLOW; + + *pcbRead = cbRead; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reads clipboard data from the host clipboard. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param uFormat Clipboard format of clipboard data to be read. + * @param pvData Buffer where to store the read data. + * @param cbData Size (in bytes) of data buffer where to store the read data. + * @param pcbRead The actual size of the host clipboard data. + */ +VBGLR3DECL(int) VbglR3ClipboardReadDataEx(PVBGLR3SHCLCMDCTX pCtx, + SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + return VbglR3ClipboardReadData(pCtx->idClient, uFormat, pvData, cbData, pcbRead); +} + + +/** + * Query the host features. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param pfHostFeatures Where to store the host feature, VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_QUERY_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, 0); + VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & RT_BIT_64(63)) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + +/** + * Peeks at the next host message, waiting for one to turn up. + * + * This glosses over the difference between new (6.1) and old (1.3.2) host + * service versions, however it does so by abusing @a pcParameters, so don't use + * it directly when in legacy mode, always pass it on to + * VbglR3ClipboardEventGetNext() or VbglR3ClipboardEventGetNextEx(). + * + * @returns VBox status code. + * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so + * caller just have to repeat this call. + * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck). + * + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use + * for the VM restore check. Optional. + * + * @note Restore check is only performed optimally with a 6.0 host. + */ +VBGLR3DECL(int) VbglR3ClipboardMsgPeekWait(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pidMsg, + uint32_t *pcParameters, uint64_t *pidRestoreCheck) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */ + HGCMFunctionParameter cParameters; + } Msg; + int rc; + if (!pCtx->fUseLegacyProtocol) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT, 2); + VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0); + VbglHGCMParmUInt32Set(&Msg.cParameters, 0); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + LogFlowFunc(("VbglR3HGCMCall -> %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit + && Msg.cParameters.type == VMMDevHGCMParmType_32bit, + ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type), + VERR_INTERNAL_ERROR_3); + + *pidMsg = (uint32_t)Msg.idMsg.u.value64; + *pcParameters = Msg.cParameters.u.value32; + return rc; + } + + /* + * If restored, update pidRestoreCheck. + */ + if (rc == VERR_VM_RESTORED && pidRestoreCheck) + *pidRestoreCheck = Msg.idMsg.u.value64; + } + else + { + /* + * We do some crude stuff here by putting the 2nd parameter (foramts) in the parameter count, + * however it's supposed to be passed directly to VbglR3ClipboardEventGetNext or + * VbglR3ClipboardEventGetNextEx, so that's fine... + */ + rc = VbglR3ClipboardGetHostMsgOld(pCtx->idClient, pidMsg, pcParameters); + if (RT_SUCCESS(rc)) + return rc; + } + + /* + * If interrupted we must cancel the call so it doesn't prevent us from making another one. + */ + if (rc == VERR_INTERRUPTED) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_CANCEL, 0); + int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr)); + AssertRC(rc2); + } + + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; +} + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + +/** + * Reads a root list header from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Where to store the received root list header. + */ +static int vbglR3ClipboardRootListHdrRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(0); + + Msg.cRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(&pRootListHdr->fRoots); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.cRoots.GetUInt32(&pRootListHdr->cRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads a root list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to read. + * @param pRootListEntry Where to store the root list entry read from the host. + */ +static int vbglR3ClipboardRootListEntryRead(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pRootListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(pRootListEntry->fInfo); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pRootListEntry->pszName, pRootListEntry->cbName); + Msg.cbInfo.SetUInt32(pRootListEntry->cbInfo); + Msg.pvInfo.SetPtr(pRootListEntry->pvInfo, pRootListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(&pRootListEntry->fInfo); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + uint32_t cbInfo = 0; + rc = Msg.cbInfo.GetUInt32(&cbInfo); AssertRC(rc); + if (pRootListEntry->cbInfo != cbInfo) + rc = VERR_INVALID_PARAMETER; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads the root list from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param ppRootList Where to store the (allocated) root list. Must be free'd by the caller with + * SharedClipboardTransferRootListFree(). + */ +VBGLR3DECL(int) VbglR3ClipboardRootListRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppRootList, VERR_INVALID_POINTER); + + int rc; + + PSHCLROOTLIST pRootList = ShClTransferRootListAlloc(); + if (pRootList) + { + SHCLROOTLISTHDR srcRootListHdr; + rc = vbglR3ClipboardRootListHdrRead(pCtx, &srcRootListHdr); + if (RT_SUCCESS(rc)) + { + pRootList->Hdr.cRoots = srcRootListHdr.cRoots; + pRootList->Hdr.fRoots = 0; /** @todo Implement this. */ + + if (srcRootListHdr.cRoots) + { + pRootList->paEntries = + (PSHCLROOTLISTENTRY)RTMemAllocZ(srcRootListHdr.cRoots * sizeof(SHCLROOTLISTENTRY)); + if (pRootList->paEntries) + { + for (uint32_t i = 0; i < srcRootListHdr.cRoots; i++) + { + SHCLROOTLISTENTRY *pEntry = &pRootList->paEntries[i]; + AssertPtr(pEntry); + + rc = ShClTransferRootListEntryInit(pEntry); + if (RT_SUCCESS(rc)) + rc = vbglR3ClipboardRootListEntryRead(pCtx, i, pEntry); + + if (RT_FAILURE(rc)) + break; + } + } + else + rc = VERR_NO_MEMORY; + } + } + + if (RT_SUCCESS(rc)) + { + *ppRootList = pRootList; + } + else + ShClTransferRootListFree(pRootList); + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a transfer status from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pEnmDir Where to store the transfer direction for the reported transfer. + * @param pReport Where to store the transfer (status) report. + */ +VBGLR3DECL(int) VbglR3ClipboarTransferStatusRecv(PVBGLR3SHCLCMDCTX pCtx, + PSHCLTRANSFERDIR pEnmDir, PSHCLTRANSFERREPORT pReport) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pReport, VERR_INVALID_POINTER); + AssertPtrReturn(pEnmDir, VERR_INVALID_POINTER); + + VBoxShClTransferStatusMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_TRANSFER_STATUS); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS); + Msg.enmDir.SetUInt32(0); + Msg.enmStatus.SetUInt32(0); + Msg.rc.SetUInt32(0); + Msg.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.enmDir.GetUInt32((uint32_t *)pEnmDir); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.enmStatus.GetUInt32(&pReport->uStatus); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.rc.GetUInt32((uint32_t *)&pReport->rc); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.fFlags.GetUInt32(&pReport->fFlags); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a transfer report from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pTransfer Transfer of report to reply to. + * @param uStatus Tranfer status to reply. + * @param rcTransfer Result code (rc) to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardTransferStatusReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLTRANSFER pTransfer, + SHCLTRANSFERSTATUS uStatus, int rcTransfer) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransfer, VERR_INVALID_POINTER); + + RT_NOREF(pTransfer); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS); + Msg.rc.SetUInt32((uint32_t )rcTransfer); /* int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.TransferStatus.enmStatus.SetUInt32((uint32_t)uStatus); + + LogFlowFunc(("%s\n", ShClTransferStatusToStr(uStatus))); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list header from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfRoots Where to store the root list header flags to use, requested by the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pfRoots) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfRoots, VERR_INVALID_POINTER); + + VBoxShClRootListReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ); + Msg.ReqParms.fRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(pfRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list header request. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Root lsit header to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(pRootListHdr->fRoots); + + Msg.cRoots.SetUInt32(pRootListHdr->cRoots); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param puIndex Where to return the index of the root list entry the host wants to read. + * @param pfInfo Where to return the read flags the host wants to use. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *puIndex, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(puIndex, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClRootListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ); + + Msg.Parms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uIndex.GetUInt32(puIndex); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list entry read request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to reply. + * @param pEntry Actual root list entry to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReply(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pEntry->pszName, pEntry->cbName); + Msg.cbInfo.SetUInt32(pEntry->cbInfo); + Msg.pvInfo.SetPtr(pEntry->pvInfo, pEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to open a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms List open parameters to use for the open request. + * @param phList Where to return the list handle received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_OPEN, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.fList.SetUInt32(0); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms Where to store the open parameters the host wants to use for opening the list handle. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN); + Msg.fList.SetUInt32(0); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fList.GetUInt32(&pOpenParms->fList); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle of (guest) list to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to store the list handle to close, received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list handle close request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle the send the close reply for. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request for closing on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_CLOSE, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to read list header for. + * @param fFlags List header read flags to use. + * @param pListHdr Where to return the list header received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, uint32_t fFlags, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_READ, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(fFlags); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(0); + Msg.cTotalObjects.SetUInt64(0); + Msg.cbTotalSize.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.fFeatures.GetUInt32(&pListHdr->fFeatures); + if (RT_SUCCESS(rc)) + rc = Msg.cTotalObjects.GetUInt64(&pListHdr->cTotalObjects); + if (RT_SUCCESS(rc)) + rc = Msg.cbTotalSize.GetUInt64(&pListHdr->cbTotalSize); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list header on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read list header for. + * @param pfFlags Where to return the List header read flags to use. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClListHdrReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fFlags.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write list header for. + * @param pListHdr List header to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(0); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(pListHdr->fFeatures); + Msg.cTotalObjects.SetUInt64(pListHdr->cTotalObjects); + Msg.cbTotalSize.SetUInt64(pListHdr->cbTotalSize); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request to read a list entry for. + * @param pListEntry Where to return the list entry read from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(0); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.cbInfo.GetUInt32(&pListEntry->cbInfo); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read a list entry for. + * @param pfInfo Where to return the list read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_ENTRY_READ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fInfo.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list entry to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write a list etnry for. + * @param pListEntry List entry to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(pListEntry->fInfo); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Where to store the object open/create parameters received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr(pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fCreate.GetUInt32(&pCreateParms->fCreate); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies a host request to open an object. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle of opened object to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjOpen.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends an object open request to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Object open/create parameters to use for opening the object on the host. + * @param phObj Where to return the object handle from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, + PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_OPEN, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr((void *)pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(pCreateParms->fCreate); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to close from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to an object open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjClose.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle to close on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_CLOSE, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read from an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to read from. + * @param pcbToRead Where to return the amount (in bytes) to read. + * @param pfFlags Where to return the read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj, uint32_t *pcbToRead, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + AssertPtrReturn(pcbToRead, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClObjReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.cbToRead.SetUInt32(0); + Msg.ReqParms.fRead.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phObj); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.cbToRead.GetUInt32(pcbToRead); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fRead.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read from an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to read from. + * @param pvData Buffer where to store the read object data. + * @param cbData Size (in bytes) of buffer. + * @param pcbRead Where to store the amount (in bytes) read from the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + /* pcbRead is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_READ, VBOX_SHCL_CPARMS_OBJ_READ); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.cbData.SetUInt32(cbData); + Msg.pvData.SetPtr(pvData, cbData); + Msg.cbChecksum.SetUInt32(0); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbRead) + { + rc = Msg.cbData.GetUInt32(pcbRead); AssertRC(rc); + AssertReturn(cbData >= *pcbRead, VERR_TOO_MUCH_DATA); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to write to an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to write to. + * @param pvData Buffer of data to write to object. + * @param cbData Size (in bytes) of buffer. + * @param pcbWritten Where to store the amount (in bytes) written to the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjWriteSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + /* cbData can be 0. */ + /* pcbWritten is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_WRITE, VBOX_SHCL_CPARMS_OBJ_WRITE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.pvData.SetPtr(pvData, cbData); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbWritten) + *pcbWritten = cbData; /** @todo For now return all as being written. */ + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/********************************************************************************************************************************* +* Transfer interface implementations * +*********************************************************************************************************************************/ + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceGetRoots(PSHCLTXPROVIDERCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardRootListRead(pCmdCtx, ppRootList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListOpenSend(pCmdCtx, pOpenParms, phList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListCloseSend(pCmdCtx, hList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = ShClTransferListHdrInit(pListHdr); + if (RT_SUCCESS(rc)) + { + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrRead(pCmdCtx, hList, 0 /* fFlags */, pListHdr); + } + else + ShClTransferListHdrDestroy(pListHdr); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTENTRY pEntry) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListEntryRead(pCmdCtx, hList, pEntry); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, + PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjOpenSend(pCmdCtx, pCreateParms, phObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjCloseSend(pCmdCtx, hObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, + SHCLOBJHANDLE hObj, void *pvData, uint32_t cbData, + uint32_t fFlags, uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + RT_NOREF(fFlags); /* Not used yet. */ + + int rc = VbglR3ClipboardObjReadSend(pCmdCtx, hObj, pvData, cbData, pcbRead); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Starts a transfer on the guest side. + * + * @returns VBox status code. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to create transfer for. + * @param uTransferID ID to use for transfer to start. + * @param enmDir Direction of transfer to start. + * @param enmSource Source of transfer to start. + * @param ppTransfer Where to return the transfer object on success. Optional. + */ +static int vbglR3ClipboardTransferStart(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, + PSHCLTRANSFER *ppTransfer) +{ + PSHCLTRANSFER pTransfer; + int rc = ShClTransferCreate(&pTransfer); + if (RT_SUCCESS(rc)) + { + ShClTransferSetCallbacks(pTransfer, &pCmdCtx->Transfers.Callbacks); + + rc = ShClTransferInit(pTransfer, enmDir, enmSource); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferCtxTransferRegisterById(pTransferCtx, pTransfer, uTransferID); + if (RT_SUCCESS(rc)) + { + /* If this is a read transfer (reading data from host), set the interface to use + * our VbglR3 routines here. */ + if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + SHCLTXPROVIDERCREATIONCTX creationCtx; + RT_ZERO(creationCtx); + + creationCtx.Interface.pfnRootsGet = vbglR3ClipboardTransferIfaceGetRoots; + + creationCtx.Interface.pfnListOpen = vbglR3ClipboardTransferIfaceListOpen; + creationCtx.Interface.pfnListClose = vbglR3ClipboardTransferIfaceListClose; + creationCtx.Interface.pfnListHdrRead = vbglR3ClipboardTransferIfaceListHdrRead; + creationCtx.Interface.pfnListEntryRead = vbglR3ClipboardTransferIfaceListEntryRead; + + creationCtx.Interface.pfnObjOpen = vbglR3ClipboardTransferIfaceObjOpen; + creationCtx.Interface.pfnObjClose = vbglR3ClipboardTransferIfaceObjClose; + creationCtx.Interface.pfnObjRead = vbglR3ClipboardTransferIfaceObjRead; + + creationCtx.pvUser = pCmdCtx; + + rc = ShClTransferSetProviderIface(pTransfer, &creationCtx); + } + + if (RT_SUCCESS(rc)) + rc = ShClTransferStart(pTransfer); + } + + if (RT_FAILURE(rc)) + ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + } + } + + if (RT_SUCCESS(rc)) + { + if (ppTransfer) + *ppTransfer = pTransfer; + + LogRel2(("Shared Clipboard: Transfer ID=%RU32 (%s %s) successfully started\n", + uTransferID, + enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "reading from" : "writing to", + enmSource == SHCLSOURCE_LOCAL ? "local" : "remote")); + } + else + LogRel(("Shared Clipboard: Unable to start transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc); + if (RT_SUCCESS(rc)) + rc = rc2; + + if (RT_FAILURE(rc)) + { + ShClTransferDestroy(pTransfer); + pTransfer = NULL; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Stops a transfer on the guest side. + * + * @returns VBox status code, or VERR_NOT_FOUND if transfer has not been found. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to stop transfer for. + * @param uTransferID ID of transfer to stop. + */ +static int vbglR3ClipboardTransferStop(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID) +{ + int rc; + + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, uTransferID); + if (pTransfer) + { + rc = ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Transfer ID=%RU32 successfully stopped\n", uTransferID)); + } + else + LogRel(("Shared Clipboard: Unable to stop transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STOPPED : SHCLTRANSFERSTATUS_ERROR, rc); + AssertRC(rc2); + } + else + rc = VERR_NOT_FOUND; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets transfer callbacks of a Shared Clipboard command context. + * + * @param pCmdCtx Command context to set callbacks for. + * @param pCallbacks Pointer to callback table to set. + */ +VBGLR3DECL(void) VbglR3ClipboardTransferSetCallbacks(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCALLBACKTABLE pCallbacks) +{ + AssertPtrReturnVoid(pCmdCtx); + AssertPtrReturnVoid(pCallbacks); + + ShClTransferCopyCallbacks(&pCmdCtx->Transfers.Callbacks, pCallbacks); +} + +VBGLR3DECL(int) VbglR3ClipboardEventGetNextEx(uint32_t idMsg, uint32_t cParms, + PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + LogFunc(("Handling idMsg=%RU32 (%s), cParms=%RU32\n", idMsg, ShClHostMsgToStr(idMsg), cParms)); + + int rc; + if (!pCmdCtx->fUseLegacyProtocol) + { + bool fErrorSent = false; /* Whether an error has been reported back to the host already. */ + + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_TRANSFER_STATUS: + { + SHCLTRANSFERDIR enmDir; + SHCLTRANSFERREPORT transferReport; + rc = VbglR3ClipboarTransferStatusRecv(pCmdCtx, &enmDir, &transferReport); + if (RT_SUCCESS(rc)) + { + const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + LogFlowFunc(("[Transfer %RU32] enmDir=%RU32, status=%s\n", + uTransferID, enmDir, ShClTransferStatusToStr(transferReport.uStatus))); + + switch (transferReport.uStatus) + { + case SHCLTRANSFERSTATUS_INITIALIZED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_STARTED: + { + SHCLSOURCE enmSource = SHCLSOURCE_INVALID; + + /* The host announces the transfer direction from its point of view, so inverse the direction here. */ + if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) + { + enmDir = SHCLTRANSFERDIR_FROM_REMOTE; + enmSource = SHCLSOURCE_REMOTE; + } + else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + enmDir = SHCLTRANSFERDIR_TO_REMOTE; + enmSource = SHCLSOURCE_LOCAL; + } + else + AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER); + + rc = vbglR3ClipboardTransferStart(pCmdCtx, pTransferCtx, uTransferID, + enmDir, enmSource, NULL /* ppTransfer */); + break; + } + + case SHCLTRANSFERSTATUS_STOPPED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_CANCELED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_KILLED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_ERROR: + { + rc = vbglR3ClipboardTransferStop(pCmdCtx, pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + break; + } + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(rc)) + { + pEvent->u.TransferStatus.enmDir = enmDir; + pEvent->u.TransferStatus.Report = transferReport; + pEvent->u.TransferStatus.uID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS; + + LogRel2(("Shared Clipboard: Received status %s (rc=%Rrc) for transfer ID=%RU32\n", + ShClTransferStatusToStr(pEvent->u.TransferStatus.Report.uStatus), pEvent->u.TransferStatus.Report.rc, + pEvent->u.TransferStatus.uID)); + } + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ: + { + uint32_t fRoots; + rc = VbglR3ClipboardRootListHdrReadReq(pCmdCtx, &fRoots); + + /** @todo Validate / handle fRoots. */ + + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTHDR rootListHdr; + RT_ZERO(rootListHdr); + + rootListHdr.cRoots = ShClTransferRootsCount(pTransfer); + + LogFlowFunc(("cRoots=%RU32\n", rootListHdr.cRoots)); + + rc = VbglR3ClipboardRootListHdrReadReply(pCmdCtx, &rootListHdr); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ: + { + uint32_t uIndex; + uint32_t fInfo; + rc = VbglR3ClipboardRootListEntryReadReq(pCmdCtx, &uIndex, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTENTRY rootListEntry; + rc = ShClTransferRootsEntry(pTransfer, uIndex, &rootListEntry); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardRootListEntryReadReply(pCmdCtx, uIndex, &rootListEntry); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN: + { + SHCLLISTOPENPARMS openParmsList; + rc = ShClTransferListOpenParmsInit(&openParmsList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListOpenRecv(pCmdCtx, &openParmsList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + LogFlowFunc(("pszPath=%s\n", openParmsList.pszPath)); + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListOpenReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + ShClTransferListOpenParmsDestroy(&openParmsList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE: + { + SHCLLISTHANDLE hList; + rc = VbglR3ClipboardListCloseRecv(pCmdCtx, &hList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListClose(pTransfer, hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListCloseReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ: + { + /** @todo Handle filter + list features. */ + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + uint32_t fFlags = 0; + rc = VbglR3ClipboardListHdrReadRecvReq(pCmdCtx, &hList, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLLISTHDR hdrList; + rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrWrite(pCmdCtx, hList, &hdrList); + + ShClTransferListHdrDestroy(&hdrList); + } + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ: + { + LogFlowFunc(("VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ\n")); + + SHCLLISTENTRY entryList; + rc = ShClTransferListEntryInit(&entryList); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + uint32_t fInfo; + rc = VbglR3ClipboardListEntryReadRecvReq(pCmdCtx, &hList, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListRead(pTransfer, hList, &entryList); + if (RT_SUCCESS(rc)) + { + PSHCLFSOBJINFO pObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo; + Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO)); + + RT_NOREF(pObjInfo); + + LogFlowFunc(("\t%s (%RU64 bytes)\n", entryList.pszName, pObjInfo->cbObject)); + + rc = VbglR3ClipboardListEntryWrite(pCmdCtx, hList, &entryList); + } + } + + ShClTransferListEntryDestroy(&entryList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN: + { + SHCLOBJOPENCREATEPARMS openParms; + rc = ShClTransferObjOpenParmsInit(&openParms); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardObjOpenRecv(pCmdCtx, &openParms); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLOBJHANDLE hObj; + rc = ShClTransferObjOpen(pTransfer, &openParms, &hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjOpenReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + ShClTransferObjOpenParmsDestroy(&openParms); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE: + { + SHCLOBJHANDLE hObj; + rc = VbglR3ClipboardObjCloseRecv(pCmdCtx, &hObj); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferObjClose(pTransfer, hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjCloseReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ: + { + SHCLOBJHANDLE hObj; + uint32_t cbBuf; + uint32_t fFlags; + rc = VbglR3ClipboardObjReadRecv(pCmdCtx, &hObj, &cbBuf, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + AssertBreakStmt(pCmdCtx->Transfers.cbChunkSize, rc = VERR_INVALID_PARAMETER); + + const uint32_t cbToRead = RT_MIN(cbBuf, pCmdCtx->Transfers.cbChunkSize); + + LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, fFlags=0x%x -> cbChunkSize=%RU32, cbToRead=%RU32\n", + hObj, cbBuf, fFlags, pCmdCtx->Transfers.cbChunkSize, cbToRead)); + + void *pvBuf = RTMemAlloc(cbToRead); + if (pvBuf) + { + uint32_t cbRead; + rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, fFlags, &cbRead); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardObjWriteSend(pCmdCtx, hObj, pvBuf, cbRead, NULL /* pcbWritten */); + + RTMemFree(pvBuf); + } + else + rc = VERR_NO_MEMORY; + } + + break; + } + + default: + { + rc = VbglR3ClipboardEventGetNext(idMsg, cParms, pCmdCtx, pEvent); + if (RT_FAILURE(rc)) + fErrorSent = true; + break; + } + } + + if ( !fErrorSent + && RT_FAILURE(rc)) + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCmdCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + +VBGLR3DECL(int) VbglR3ClipboardEventGetNext(uint32_t idMsg, uint32_t cParms, PVBGLR3SHCLCMDCTX pCtx, PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + RT_NOREF(cParms); + + int rc; + if (!pCtx->fUseLegacyProtocol) + { + LogFunc(("Handling idMsg=%RU32 (%s)\n", idMsg, ShClHostMsgToStr(idMsg))); + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + { + rc = vbglR3ClipboardFormatsReportRecv(pCtx, &pEvent->u.fReportedFormats); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA_CID: + { + rc = vbglR3ClipboardFetchReadDataCid(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA: + { + rc = vbglR3ClipboardFetchReadData(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_QUIT: + { + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + rc = VINF_SUCCESS; + break; + } + + default: + { + /** @todo r=bird: BUGBUG - need a skip command here! */ + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if (RT_SUCCESS(rc)) + { + /* Copy over our command context to the event. */ + pEvent->cmdCtx = *pCtx; + } + else + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + pEvent->cmdCtx = *pCtx; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Frees (destroys) a formerly allocated Shared Clipboard event. + * + * @returns IPRT status code. + * @param pEvent Event to free (destroy). + */ +VBGLR3DECL(void) VbglR3ClipboardEventFree(PVBGLR3CLIPBOARDEVENT pEvent) +{ + if (!pEvent) + return; + + /* Some messages require additional cleanup. */ + switch (pEvent->enmType) + { + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +/** + * Reports (advertises) guest clipboard formats to the host. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormats The formats to report. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats) +{ + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmReportFormats Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FORMATS, VBOX_SHCL_CPARMS_REPORT_FORMATS); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Formats, fFormats); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * Legacy function kept for compatibility, do not use anymore. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format of the data. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWriteOld Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE_OLD); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet(&Msg.Parms.pData, pv, cb); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param fFormat Clipboard format to send. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteDataEx(PVBGLR3SHCLCMDCTX pCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData) +{ + LogFlowFunc(("ENTER: fFormat=%#x pvData=%p cbData=%#x\n", fFormat, pvData, cbData)); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + if (cbData > 0) + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + + int rc; + if (pCtx->fUseLegacyProtocol) + rc = VbglR3ClipboardWriteData(pCtx->idClient, fFormat, pvData, cbData); + else + { + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWrite Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE); + Msg.Parms.id64Context.SetUInt64(pCtx->idContext); + Msg.Parms.f32Format.SetUInt32(fFormat); + Msg.Parms.pData.SetPtr(pvData, cbData); + + LogFlowFunc(("CID=%RU32\n", pCtx->idContext)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Writes an error to the host. + * + * @returns IPRT status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteError(HGCMCLIENTID idClient, int rcErr) +{ + AssertReturn(idClient, VERR_INVALID_PARAMETER); + + VBoxShClWriteErrorMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_ERROR, VBOX_SHCL_CPARMS_ERROR); + + /** @todo Context ID not used yet. */ + Msg.uContext.SetUInt64(0); + Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + if (RT_FAILURE(rc)) + LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc)); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + + if (RT_FAILURE(rc)) + LogRel(("Shared Clipboard: Reporting error %Rrc to the host failed with %Rrc\n", rcErr, rc)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp new file mode 100644 index 00000000..35971006 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp @@ -0,0 +1,56 @@ +/* $Id: VBoxGuestR3LibCoreDump.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core Dumps. + */ + +/* + * 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 "VBoxGuestR3LibInternal.h" + + +/** + * Write guest core dump. + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3WriteCoreDump(void) +{ + VBGLIOCWRITECOREDUMP Req; + VBGLREQHDR_INIT(&Req.Hdr, WRITE_CORE_DUMP); + Req.u.In.fFlags = 0; + return vbglR3DoIOCtl(VBGL_IOCTL_WRITE_CORE_DUMP, &Req.Hdr, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp new file mode 100644 index 00000000..00aa0257 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp @@ -0,0 +1,133 @@ +/* $Id: VBoxGuestR3LibCpuHotPlug.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, CPU Hot Plugging. + */ + +/* + * 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 "VBoxGuestR3LibInternal.h" +#include <iprt/assert.h> +#include <iprt/errcore.h> + + +/** + * Initialize CPU hot plugging. + * + * This will enable the CPU hot plugging events. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugInit(void) +{ + int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_CPU_HOTPLUG, 0); + if (RT_FAILURE(rc)) + return rc; + + VMMDevCpuHotPlugStatusRequest Req; + vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus); + Req.enmStatusType = VMMDevCpuStatusType_Enable; + rc = vbglR3GRPerform(&Req.header); + if (RT_FAILURE(rc)) + VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG); + + return rc; +} + + +/** + * Terminate CPU hot plugging. + * + * This will disable the CPU hot plugging events. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugTerm(void) +{ + /* Clear the events. */ + VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG); + + VMMDevCpuHotPlugStatusRequest Req; + vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus); + Req.enmStatusType = VMMDevCpuStatusType_Disable; + return vbglR3GRPerform(&Req.header); +} + + +/** + * Waits for a CPU hot plugging event and retrieve the data associated with it. + * + * @returns VBox status code. + * @param penmEventType Where to store the event type on success. + * @param pidCpuCore Where to store the CPU core ID on success. + * @param pidCpuPackage Where to store the CPU package ID on success. + */ +VBGLR3DECL(int) VbglR3CpuHotPlugWaitForEvent(VMMDevCpuEventType *penmEventType, uint32_t *pidCpuCore, uint32_t *pidCpuPackage) +{ + AssertPtrReturn(penmEventType, VERR_INVALID_POINTER); + AssertPtrReturn(pidCpuCore, VERR_INVALID_POINTER); + AssertPtrReturn(pidCpuPackage, VERR_INVALID_POINTER); + + uint32_t fEvents = 0; + int rc = VbglR3WaitEvent(VMMDEV_EVENT_CPU_HOTPLUG, RT_INDEFINITE_WAIT, &fEvents); + if (RT_SUCCESS(rc)) + { + /* did we get the right event? */ + if (fEvents & VMMDEV_EVENT_CPU_HOTPLUG) + { + VMMDevGetCpuHotPlugRequest Req; + + /* get the CPU hot plugging request */ + vmmdevInitRequest(&Req.header, VMMDevReq_GetCpuHotPlugRequest); + Req.idCpuCore = UINT32_MAX; + Req.idCpuPackage = UINT32_MAX; + Req.enmEventType = VMMDevCpuEventType_None; + rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *penmEventType = Req.enmEventType; + *pidCpuCore = Req.idCpuCore; + *pidCpuPackage = Req.idCpuPackage; + return VINF_SUCCESS; + } + } + else + rc = VERR_TRY_AGAIN; + } + else if (rc == VERR_TIMEOUT) /* just in case */ + rc = VERR_TRY_AGAIN; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp new file mode 100644 index 00000000..b4d795f7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp @@ -0,0 +1,222 @@ +/* $Id: VBoxGuestR3LibCredentials.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, user credentials. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include <VBox/log.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Checks whether user credentials are available to the guest or not. + * + * @returns IPRT status value; VINF_SUCCESS if credentials are available, + * VERR_NOT_FOUND if not. Otherwise an error is occurred. + */ +VBGLR3DECL(int) VbglR3CredentialsQueryAvailability(void) +{ + VMMDevCredentials Req; + RT_ZERO(Req); + vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials); + Req.u32Flags |= VMMDEV_CREDENTIALS_QUERYPRESENCE; + + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + if ((Req.u32Flags & VMMDEV_CREDENTIALS_PRESENT) == 0) + rc = VERR_NOT_FOUND; + } + return rc; +} + + +/** + * Retrieves and clears the user credentials for logging into the guest OS. + * + * @returns IPRT status value + * @param ppszUser Receives pointer of allocated user name string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + * @param ppszPassword Receives pointer of allocated user password string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + * @param ppszDomain Receives pointer of allocated domain name string. + * The returned pointer must be freed using VbglR3CredentialsDestroy(). + */ +VBGLR3DECL(int) VbglR3CredentialsRetrieve(char **ppszUser, char **ppszPassword, char **ppszDomain) +{ + AssertPtrReturn(ppszUser, VERR_INVALID_POINTER); + AssertPtrReturn(ppszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(ppszDomain, VERR_INVALID_POINTER); + + VMMDevCredentials Req; + RT_ZERO(Req); + vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials); + Req.u32Flags |= VMMDEV_CREDENTIALS_READ | VMMDEV_CREDENTIALS_CLEAR; + + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszUser, Req.szUserName); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszPassword, Req.szPassword); + if (RT_SUCCESS(rc)) + { + rc = RTStrDupEx(ppszDomain, Req.szDomain); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTStrFree(*ppszPassword); + } + RTStrFree(*ppszUser); + } + } + return rc; +} + + +/** + * Retrieves and clears the user credentials for logging into the guest OS. + * UTF-16 version. + * + * @returns IPRT status value + * @param ppwszUser Receives pointer of allocated user name string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + * @param ppwszPassword Receives pointer of allocated user password string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + * @param ppwszDomain Receives pointer of allocated domain name string. + * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16(). + */ +VBGLR3DECL(int) VbglR3CredentialsRetrieveUtf16(PRTUTF16 *ppwszUser, PRTUTF16 *ppwszPassword, PRTUTF16 *ppwszDomain) +{ + AssertPtrReturn(ppwszUser, VERR_INVALID_POINTER); + AssertPtrReturn(ppwszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(ppwszDomain, VERR_INVALID_POINTER); + + char *pszUser, *pszPassword, *pszDomain; + int rc = VbglR3CredentialsRetrieve(&pszUser, &pszPassword, &pszDomain); + if (RT_SUCCESS(rc)) + { + PRTUTF16 pwszUser = NULL; + PRTUTF16 pwszPassword = NULL; + PRTUTF16 pwszDomain = NULL; + + rc = RTStrToUtf16(pszUser, &pwszUser); + if (RT_SUCCESS(rc)) + { + rc = RTStrToUtf16(pszPassword, &pwszPassword); + if (RT_SUCCESS(rc)) + rc = RTStrToUtf16(pszDomain, &pwszDomain); + } + + if (RT_SUCCESS(rc)) + { + *ppwszUser = pwszUser; + *ppwszPassword = pwszPassword; + *ppwszDomain = pwszDomain; + } + else + VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* Passes */); + VbglR3CredentialsDestroy(pszUser, pszPassword, pszDomain, 3 /* Passes */); + } + + return rc; +} + + +/** + * Clears and frees the three strings. + * + * @param pszUser Receives pointer of the user name string to destroy. + * Optional. + * @param pszPassword Receives pointer of the password string to destroy. + * Optional. + * @param pszDomain Receives pointer of allocated domain name string. + * Optional. + * @param cPasses Number of wipe passes. The more the better + slower. + */ +VBGLR3DECL(void) VbglR3CredentialsDestroy(char *pszUser, char *pszPassword, char *pszDomain, uint32_t cPasses) +{ + /* wipe first */ + if (pszUser) + RTMemWipeThoroughly(pszUser, strlen(pszUser) + 1, cPasses); + if (pszPassword) + RTMemWipeThoroughly(pszPassword, strlen(pszPassword) + 1, cPasses); + if (pszDomain) + RTMemWipeThoroughly(pszDomain, strlen(pszDomain) + 1, cPasses); + + /* then free. */ + RTStrFree(pszUser); + RTStrFree(pszPassword); + RTStrFree(pszDomain); +} + + +/** + * Clears and frees the three strings. UTF-16 version. + * + * @param pwszUser Receives pointer of the user name string to destroy. + * Optional. + * @param pwszPassword Receives pointer of the password string to destroy. + * Optional. + * @param pwszDomain Receives pointer of allocated domain name string. + * Optional. + * @param cPasses Number of wipe passes. The more the better + slower. + */ +VBGLR3DECL(void) VbglR3CredentialsDestroyUtf16(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, + uint32_t cPasses) +{ + /* wipe first */ + if (pwszUser) + RTMemWipeThoroughly(pwszUser, (RTUtf16Len(pwszUser) + 1) * sizeof(RTUTF16), cPasses); + if (pwszPassword) + RTMemWipeThoroughly(pwszPassword, (RTUtf16Len(pwszPassword) + 1) * sizeof(RTUTF16), cPasses); + if (pwszDomain) + RTMemWipeThoroughly(pwszDomain, (RTUtf16Len(pwszDomain) + 1) * sizeof(RTUTF16), cPasses); + + /* then free. */ + RTUtf16Free(pwszUser); + RTUtf16Free(pwszPassword); + RTUtf16Free(pwszDomain); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp new file mode 100644 index 00000000..2aad8c5c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp @@ -0,0 +1,326 @@ +/** $Id: VBoxGuestR3LibDaemonize.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(RT_OS_OS2) +# define INCL_BASE +# define INCL_ERRORS +# include <os2.h> + +# include <iprt/alloca.h> +# include <iprt/string.h> + +#elif defined(RT_OS_WINDOWS) +# error "PORTME" + +#else /* the unices */ +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/wait.h> +# include <stdio.h> +# include <fcntl.h> +# include <stdlib.h> +# include <unistd.h> +# include <signal.h> +# include <errno.h> +#endif + +#include <iprt/process.h> +#include <iprt/string.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Daemonize the process for running in the background. + * + * This is supposed to do the same job as the BSD daemon() call. + * + * @returns 0 on success + * + * @param fNoChDir Pass false to change working directory to root. + * @param fNoClose Pass false to redirect standard file streams to /dev/null. + * @param fRespawn Restart the daemonised process after five seconds if it + * terminates abnormally. + * @param pcRespawn Where to store a count of how often we have respawned, + * intended for avoiding error spamming. Optional. + * @param fReturnOnUpdate If True, this function will return control to caller when + * child process will terminate with exit code of VBGLR3EXITCODERELOAD, + * indicating that Guest Additions update has been started and this running + * process will be asked to be restarted by arrival of the next SIGUSR1 + * signal (caller should wait for SIGUSR1). If False, this functions will + * never return, but rather exit() when child process terminates with + * exit code 0. + * @param pfUpdateStarted A flag which passed to caller if fReturnOnUpdate is True (can be NULL). + * @param szPidfile Optional path to parent process' pidfile (can be NULL). + * @param phPidfile Optional path to parent process' pidfile handle (can not be NULL if + * szPidfile was specified). + * + * @todo Use RTProcDaemonize instead of this. + * @todo Implement fRespawn on OS/2. + * @todo Make the respawn interval configurable. But not until someone + * actually needs that. + */ +VBGLR3DECL(int) VbglR3DaemonizeEx(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn, + bool fReturnOnUpdate, bool *pfUpdateStarted, const char *szPidfile, + RTFILE *phPidfile) +{ +#if defined(RT_OS_OS2) + PPIB pPib; + PTIB pTib; + DosGetInfoBlocks(&pTib, &pPib); + + RT_NOREF(fReturnOnUpdate); + RT_NOREF(pfUpdateStarted); + RT_NOREF(szPidfile); + RT_NOREF(phPidfile); + + AssertRelease(!fRespawn); + /* Get the full path to the executable. */ + char szExe[CCHMAXPATH]; + APIRET rc = DosQueryModuleName(pPib->pib_hmte, sizeof(szExe), szExe); + if (rc) + return RTErrConvertFromOS2(rc); + + /* calc the length of the command line. */ + char *pch = pPib->pib_pchcmd; + size_t cch0 = strlen(pch); + pch += cch0 + 1; + size_t cch1 = strlen(pch); + pch += cch1 + 1; + char *pchArgs; + if (cch1 && *pch) + { + do pch = strchr(pch, '\0') + 1; + while (*pch); + + size_t cchTotal = pch - pPib->pib_pchcmd; + pchArgs = (char *)alloca(cchTotal + sizeof("--daemonized\0\0")); + memcpy(pchArgs, pPib->pib_pchcmd, cchTotal - 1); + memcpy(pchArgs + cchTotal - 1, "--daemonized\0\0", sizeof("--daemonized\0\0")); + } + else + { + size_t cchTotal = pch - pPib->pib_pchcmd + 1; + pchArgs = (char *)alloca(cchTotal + sizeof(" --daemonized ")); + memcpy(pchArgs, pPib->pib_pchcmd, cch0 + 1); + pch = pchArgs + cch0 + 1; + memcpy(pch, " --daemonized ", sizeof(" --daemonized ") - 1); + pch += sizeof(" --daemonized ") - 1; + if (cch1) + memcpy(pch, pPib->pib_pchcmd + cch0 + 1, cch1 + 2); + else + pch[0] = pch[1] = '\0'; + } + + /* spawn a detach process */ + char szObj[128]; + RESULTCODES ResCodes = { 0, 0 }; + szObj[0] = '\0'; + rc = DosExecPgm(szObj, sizeof(szObj), EXEC_BACKGROUND, (PCSZ)pchArgs, NULL, &ResCodes, (PCSZ)szExe); + if (rc) + { + /** @todo Change this to some standard log/print error?? */ + /* VBoxServiceError("DosExecPgm failed with rc=%d and szObj='%s'\n", rc, szObj); */ + return RTErrConvertFromOS2(rc); + } + DosExit(EXIT_PROCESS, 0); + return VERR_GENERAL_FAILURE; + +#elif defined(RT_OS_WINDOWS) +# error "PORTME" + +#else /* the unices */ + /* + * Fork the child process in a new session and quit the parent. + * + * - fork once and create a new session (setsid). This will detach us + * from the controlling tty meaning that we won't receive the SIGHUP + * (or any other signal) sent to that session. + * - The SIGHUP signal is ignored because the session/parent may throw + * us one before we get to the setsid. + * - When the parent exit(0) we will become an orphan and re-parented to + * the init process. + * - Because of the Linux / System V semantics of assigning the controlling + * tty automagically when a session leader first opens a tty, we will + * fork() once more on Linux to get rid of the session leadership role. + */ + + struct sigaction OldSigAct; + struct sigaction SigAct; + RT_ZERO(SigAct); + SigAct.sa_handler = SIG_IGN; + int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct); + + pid_t pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid != 0) + exit(0); + + /* + * The orphaned child becomes is reparented to the init process. + * We create a new session for it (setsid), point the standard + * file descriptors to /dev/null, and change to the root directory. + */ + pid_t newpgid = setsid(); + int SavedErrno = errno; + if (rcSigAct != -1) + sigaction(SIGHUP, &OldSigAct, NULL); + if (newpgid == -1) + return RTErrConvertFromErrno(SavedErrno); + + if (!fNoClose) + { + /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */ + int fd = open("/dev/null", O_RDWR); + if (fd == -1) /* paranoia */ + { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + fd = open("/dev/null", O_RDWR); + } + if (fd != -1) + { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > 2) + close(fd); + } + } + + if (!fNoChDir) + { + int rcShutUpGcc = chdir("/"); + RT_NOREF_PV(rcShutUpGcc); + } + + /* + * Change the umask - this is non-standard daemon() behavior. + */ + umask(027); + +# ifdef RT_OS_LINUX + /* + * And fork again to lose session leader status (non-standard daemon() + * behaviour). + */ + pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid != 0) + exit(0); +# endif /* RT_OS_LINUX */ + + /* Check if another instance is already running. */ + if (szPidfile != NULL) + { + if (phPidfile != NULL) + { + int rc = VbglR3PidfileWait(szPidfile, phPidfile, 5000); + + /* Another instance of process is already running. */ + if (rc == VERR_FILE_LOCK_VIOLATION) + { + LogRel(("cannot aquire pidfile %s, exitting\n", szPidfile)); + exit(1); + } + + /* Unable to lock on pidfile. */ + if (RT_FAILURE(rc)) + exit(1); + } + else + return VERR_INVALID_PARAMETER; + } + + if (fRespawn) + { + /* We implement re-spawning as a third fork(), with the parent process + * monitoring the child and re-starting it after a delay if it exits + * abnormally. */ + unsigned cRespawn = 0; + for (;;) + { + int iStatus, rcWait; + + if (pcRespawn != NULL) + *pcRespawn = cRespawn; + pid = fork(); + if (pid == -1) + return RTErrConvertFromErrno(errno); + if (pid == 0) + return VINF_SUCCESS; + do + rcWait = waitpid(pid, &iStatus, 0); + while (rcWait == -1 && errno == EINTR); + if (rcWait == -1) + exit(1); + if (WIFEXITED(iStatus)) + { + if (WEXITSTATUS(iStatus) == 0) + exit(0); + else if (fReturnOnUpdate && WEXITSTATUS(iStatus) == VBGLR3EXITCODERELOAD) + { + /* Tell caller that update has been started. */ + if (pfUpdateStarted != NULL) + *pfUpdateStarted = true; + + return VINF_SUCCESS; + } + } + sleep(5); + ++cRespawn; + } + } + return VINF_SUCCESS; +#endif +} + +/** + * A wrapper function for VbglR3DaemonizeEx. + */ +VBGLR3DECL(int) VbglR3Daemonize(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn) +{ + return VbglR3DaemonizeEx(fNoChDir, fNoClose, fRespawn, pcRespawn, false, NULL, NULL, NULL); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp new file mode 100644 index 00000000..68ba038b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp @@ -0,0 +1,1948 @@ +/* $Id: VBoxGuestR3LibDragAndDrop.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop. + */ + +/* + * 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/path.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/uri.h> +#include <iprt/thread.h> + +#include <iprt/cpp/list.h> +#include <iprt/cpp/ministring.h> + +#ifdef LOG_GROUP + #undef LOG_GROUP +#endif +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/log.h> + +#include <VBox/VBoxGuestLib.h> +#include <VBox/GuestHost/DragAndDrop.h> +#include <VBox/HostServices/DragAndDropSvc.h> + +using namespace DragAndDropSvc; + +#include "VBoxGuestR3LibInternal.h" + + +/********************************************************************************************************************************* +* Private internal functions * +*********************************************************************************************************************************/ + +/** + * Receives the next upcoming message for a given DnD context. + * + * @returns IPRT status code. + * Will return VERR_CANCELLED (implemented by the host service) if we need to bail out. + * @param pCtx DnD context to use. + * @param puMsg Where to store the message type. + * @param pcParms Where to store the number of parameters required for receiving the message. + * @param fWait Whether to wait (block) for a new message to arrive or not. + */ +static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(puMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParms, VERR_INVALID_POINTER); + + int rc; + + do + { + HGCMMsgGetNext Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GET_NEXT_HOST_MSG, 3); + Msg.uMsg.SetUInt32(0); + Msg.cParms.SetUInt32(0); + Msg.fBlock.SetUInt32(fWait ? 1 : 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc); + rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc); + } + + LogRel(("DnD: Received message %s (%#x) from host\n", DnDHostMsgToStr(*puMsg), *puMsg)); + + } while (rc == VERR_INTERRUPTED); + + return rc; +} + + +/** + * Sends a DnD error back to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3DnDSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgGHError Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_EVT_ERROR, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * Never return an error if the host did not accept the error at the current + * time. This can be due to the host not having any appropriate callbacks + * set which would handle that error. + * + * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it + * doesn't an appropriate callback. The code used to ignore ALL errors + * the host would return, also relevant ones. + */ + if (RT_FAILURE(rc)) + LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc)); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a so-called "action message" from the host. + * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages". + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param uMsg Which kind of message to receive. + * @param puScreenID Where to store the host screen ID the message is bound to. Optional. + * @param puX Where to store the absolute X coordinates. Optional. + * @param puY Where to store the absolute Y coordinates. Optional. + * @param puDefAction Where to store the default action to perform. Optional. + * @param puAllActions Where to store the available actions. Optional. + * @param ppszFormats Where to store List of formats. Optional. + * @param pcbFormats Size (in bytes) of where to store the list of formats. Optional. + * + * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1. + * This was part of the initial protocol and needs to go. + */ +static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx, + uint32_t uMsg, + uint32_t *puScreenID, + uint32_t *puX, + uint32_t *puY, + uint32_t *puDefAction, + uint32_t *puAllActions, + char **ppszFormats, + uint32_t *pcbFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* The rest is optional. */ + + const uint32_t cbFormatsTmp = pCtx->cbMaxChunkSize; + + char *pszFormatsTmp = static_cast<char *>(RTMemAlloc(cbFormatsTmp)); + if (!pszFormatsTmp) + return VERR_NO_MEMORY; + + HGCMMsgHGAction Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 8); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uScreenId.SetUInt32(0); + Msg.u.v3.uX.SetUInt32(0); + Msg.u.v3.uY.SetUInt32(0); + Msg.u.v3.uDefAction.SetUInt32(0); + Msg.u.v3.uAllActions.SetUInt32(0); + Msg.u.v3.pvFormats.SetPtr(pszFormatsTmp, cbFormatsTmp); + Msg.u.v3.cbFormats.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (RT_SUCCESS(rc) && puScreenID) + rc = Msg.u.v3.uScreenId.GetUInt32(puScreenID); + if (RT_SUCCESS(rc) && puX) + rc = Msg.u.v3.uX.GetUInt32(puX); + if (RT_SUCCESS(rc) && puY) + rc = Msg.u.v3.uY.GetUInt32(puY); + if (RT_SUCCESS(rc) && puDefAction) + rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction); + if (RT_SUCCESS(rc) && puAllActions) + rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); + if (RT_SUCCESS(rc) && pcbFormats) + rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormats); + + if (RT_SUCCESS(rc)) + { + if (ppszFormats) + { + *ppszFormats = RTStrDup(pszFormatsTmp); + if (!*ppszFormats) + rc = VERR_NO_MEMORY; + } + } + } + + RTStrFree(pszFormatsTmp); + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_EVT_LEAVE message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + */ +static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGLeave Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_EVT_LEAVE, 1); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_EVT_CANCEL message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + */ +static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGCancel Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_CANCEL, 1); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_SND_DIR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszDirname Where to store the directory name of the directory being created. + * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created. + * @param pcbDirnameRecv Size (in bytes) of the actual directory name received. + * @param pfMode Where to store the directory creation mode. + */ +static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx, + char *pszDirname, + uint32_t cbDirname, + uint32_t *pcbDirnameRecv, + uint32_t *pfMode) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszDirname, VERR_INVALID_POINTER); + AssertReturn(cbDirname, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + + HGCMMsgHGSendDir Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DIR, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname); + Msg.u.v3.cbName.SetUInt32(cbDirname); + Msg.u.v3.fMode.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc); + rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc); + + AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA); + } + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive a HOST_DND_FN_HG_SND_FILE_DATA message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Where to store the file data chunk. + * @param cbData Size (in bytes) of where to store the data chunk. + * @param pcbDataRecv Size (in bytes) of the actual data chunk size received. + */ +static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx, + void *pvData, + uint32_t cbData, + uint32_t *pcbDataRecv) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER); + + HGCMMsgHGSendFileData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvData.SetPtr(pvData, cbData); + Msg.u.v3.cbData.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc); + AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA); + /** @todo Add checksum support. */ + } + + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_FILE_HDR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszFilename Where to store the file name of the file being transferred. + * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred. + * @param puFlags File transfer flags. Currently not being used. + * @param pfMode Where to store the file creation mode. + * @param pcbTotal Where to store the file size (in bytes). + */ +static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx, + char *pszFilename, + uint32_t cbFilename, + uint32_t *puFlags, + uint32_t *pfMode, + uint64_t *pcbTotal) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(cbFilename, VERR_INVALID_PARAMETER); + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pfMode, VERR_INVALID_POINTER); + AssertReturn(pcbTotal, VERR_INVALID_POINTER); + + HGCMMsgHGSendFileHdr Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_HDR, 6); + Msg.uContext.SetUInt32(0); /** @todo Not used yet. */ + Msg.pvName.SetPtr(pszFilename, cbFilename); + Msg.cbName.SetUInt32(cbFilename); + Msg.uFlags.SetUInt32(0); + Msg.fMode.SetUInt32(0); + Msg.cbTotal.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Get context ID. */ + rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc); + rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc); + rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc); + } + + return rc; +} + +/** + * Host -> Guest + * Helper function for receiving URI data from the host. Do not call directly. + * This function also will take care of the file creation / locking on the guest. + * + * @returns IPRT status code. + * @retval VERR_CANCELLED if the transfer was cancelled by the host. + * @param pCtx DnD context to use. + * @param pDataHdr DnD data header to use. Needed for accounting. + * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking. + */ +static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES pDroppedFiles) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER); + + /* Only count the raw data minus the already received meta data. */ + Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta); + uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta; + uint64_t cToRecvObjs = pDataHdr->cObjects; + + LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n", + cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta)); + + /* Anything to do at all? */ + /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just + * a bunch of 0-byte files to be transferred. */ + if (!cToRecvObjs) + return VINF_SUCCESS; + + LogRel2(("DnD: Receiving URI data started\n")); + + /* + * Allocate temporary chunk buffer. + */ + uint32_t cbChunkMax = pCtx->cbMaxChunkSize; + void *pvChunk = RTMemAlloc(cbChunkMax); + if (!pvChunk) + return VERR_NO_MEMORY; + uint32_t cbChunkRead = 0; + + uint64_t cbFileSize = 0; /* Total file size (in bytes). */ + uint64_t cbFileWritten = 0; /* Written bytes. */ + + const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles); + AssertPtr(pszDropDir); + + int rc; + + /* + * Enter the main loop of retieving files + directories. + */ + DNDTRANSFEROBJECT objCur; + RT_ZERO(objCur); + + char szPathName[RTPATH_MAX] = { 0 }; + uint32_t cbPathName = 0; + uint32_t fFlags = 0; + uint32_t fMode = 0; + + do + { + LogFlowFunc(("Waiting for new message ...\n")); + + uint32_t uNextMsg; + uint32_t cNextParms; + rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, true /* fWait */); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms)); + + switch (uNextMsg) + { + case HOST_DND_FN_HG_SND_DIR: + { + rc = vbglR3DnDHGRecvDir(pCtx, + szPathName, + sizeof(szPathName), + &cbPathName, + &fMode); + LogFlowFunc(("HOST_DND_FN_HG_SND_DIR: " + "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n", + szPathName, cbPathName, fMode, rc)); + + char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName); + if (pszPathAbs) + { +#ifdef RT_OS_WINDOWS + uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL; +#else + uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU; +#endif + rc = RTDirCreate(pszPathAbs, fCreationMode, 0); + if (RT_SUCCESS(rc)) + rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs); + + if (RT_SUCCESS(rc)) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + break; + } + case HOST_DND_FN_HG_SND_FILE_HDR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_DATA: + { + if (uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR) + { + rc = vbglR3DnDHGRecvFileHdr(pCtx, + szPathName, + sizeof(szPathName), + &fFlags, + &fMode, + &cbFileSize); + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_HDR: " + "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n", + szPathName, fFlags, fMode, cbFileSize, rc)); + } + else + { + rc = vbglR3DnDHGRecvFileData(pCtx, + pvChunk, + cbChunkMax, + &cbChunkRead); + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: " + "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc)); + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR) + { + char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName); + if (pszPathAbs) + { + LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n", + szPathName, cbPathName, fMode, cbFileSize)); + + uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE + | RTFILE_O_CREATE_REPLACE; + + /* Is there already a file open, e.g. in transfer? */ + if (!DnDTransferObjectIsOpen(&objCur)) + { +#ifdef RT_OS_WINDOWS + uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL; +#else + uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR; +#endif + rc = DnDTransferObjectInitEx(&objCur, DNDTRANSFEROBJTYPE_FILE, + pszDropDir /* Source (base) path */, szPathName /* Destination path */); + if (RT_SUCCESS(rc)) + { + rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE); + if (RT_SUCCESS(rc)) + { + rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs); + if (RT_SUCCESS(rc)) + { + cbFileWritten = 0; + DnDTransferObjectSetSize(&objCur, cbFileSize); + } + } + } + } + else + { + AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur))); + rc = VERR_WRONG_ORDER; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_FN_HG_SND_FILE_DATA + && cbChunkRead) + { + uint32_t cbChunkWritten; + rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: " + "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n", + cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize)); + + cbFileWritten += cbChunkWritten; + + Assert(cbChunkRead <= cbToRecvBytes); + cbToRecvBytes -= cbChunkRead; + } + } + + /* Data transfer complete? Close the file. */ + bool fClose = DnDTransferObjectIsComplete(&objCur); + if (fClose) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + /* Only since protocol v2 we know the file size upfront. */ + Assert(cbFileWritten <= cbFileSize); + + if (fClose) + { + LogFlowFunc(("Closing file\n")); + DnDTransferObjectDestroy(&objCur); + } + + break; + } + case HOST_DND_FN_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + rc = VERR_CANCELLED; + break; + } + default: + { + LogRel(("DnD: Warning: Message %s (%#x) from host not supported or in wrong order\n", DnDHostMsgToStr(uNextMsg), uNextMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + } + + if (RT_FAILURE(rc)) + break; + + LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs)); + if ( !cbToRecvBytes + && !cToRecvObjs) + { + break; + } + + } while (RT_SUCCESS(rc)); + + LogFlowFunc(("Loop ended with %Rrc\n", rc)); + + /* All URI data processed? */ + if (rc == VERR_NO_DATA) + rc = VINF_SUCCESS; + + /* Delete temp buffer again. */ + if (pvChunk) + RTMemFree(pvChunk); + + /* Cleanup on failure or if the user has canceled the operation or + * something else went wrong. */ + if (RT_FAILURE(rc)) + { + if (rc == VERR_CANCELLED) + LogRel2(("DnD: Receiving URI data was cancelled by the host\n")); + else + LogRel(("DnD: Receiving URI data failed with %Rrc\n", rc)); + + DnDTransferObjectDestroy(&objCur); + DnDDroppedFilesRollback(pDroppedFiles); + } + else + { + LogRel2(("DnD: Receiving URI data finished\n")); + + /** @todo Compare the transfer list with the dirs/files we really transferred. */ + /** @todo Implement checksum verification, if any. */ + } + + /* + * Close the dropped files directory. + * Don't try to remove it here, however, as the files are being needed + * by the client's drag'n drop operation lateron. + */ + int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */); + if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */ + LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_DATA message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr DnD data header to use. Need for accounting and stuff. + * @param pvData Where to store the received data from the host. + * @param cbData Size (in bytes) of where to store the received data. + * @param pcbDataRecv Where to store the received amount of data (in bytes). + */ +static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, + void *pvData, uint32_t cbData, uint32_t *pcbDataRecv) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER); + + LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData)); + + HGCMMsgHGSendData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvData.SetPtr(pvData, cbData); + Msg.u.v3.cbData.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbDataRecv; + rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + /** @todo Use checksum for validating the received data. */ + if (pcbDataRecv) + *pcbDataRecv = cbDataRecv; + LogFlowFuncLeaveRC(rc); + return rc; + } + } + + /* failure */ + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Utility function to receive the HOST_DND_FN_HG_SND_DATA_HDR message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr Where to store the receivd DnD data header. + */ +static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + + Assert(pCtx->uProtocolDeprecated >= 3); /* Only for protocol v3 and up. */ + + HGCMMsgHGSendDataHdr Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA_HDR, 12); + Msg.uContext.SetUInt32(0); + Msg.uFlags.SetUInt32(0); + Msg.uScreenId.SetUInt32(0); + Msg.cbTotal.SetUInt64(0); + Msg.cbMeta.SetUInt32(0); + Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt); + Msg.cbMetaFmt.SetUInt32(0); + Msg.cObjects.SetUInt64(0); + Msg.enmCompression.SetUInt32(0); + Msg.enmChecksumType.SetUInt32(0); + Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum); + Msg.cbChecksum.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* Msg.uContext not needed here. */ + Msg.uFlags.GetUInt32(&pDataHdr->uFlags); + Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId); + Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal); + Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta); + Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt); + Msg.cObjects.GetUInt64(&pDataHdr->cObjects); + Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression); + Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType); + Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Helper function for receiving the actual DnD data from the host. Do not call directly. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pDataHdr Where to store the data header data. + * @param ppvData Returns the received meta data. Needs to be free'd by the caller. + * @param pcbData Where to store the size (in bytes) of the received meta data. + */ +static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, + void **ppvData, uint64_t *pcbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + AssertPtrReturn(ppvData, VERR_INVALID_POINTER); + AssertPtrReturn(pcbData, VERR_INVALID_POINTER); + + int rc; + uint32_t cbDataRecv; + + LogFlowFuncEnter(); + + rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr); + if (RT_FAILURE(rc)) + return rc; + + LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects)); + if (pDataHdr->cbMeta) + { + uint64_t cbDataTmp = 0; + void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta); + if (!pvDataTmp) + rc = VERR_NO_MEMORY; + + if (RT_SUCCESS(rc)) + { + uint8_t *pvDataOff = (uint8_t *)pvDataTmp; + while (cbDataTmp < pDataHdr->cbMeta) + { + rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr, + pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize), + &cbDataRecv); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp)); + Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta); + cbDataTmp += cbDataRecv; + pvDataOff += cbDataRecv; + } + else + break; + } + + if (RT_SUCCESS(rc)) + { + Assert(cbDataTmp == pDataHdr->cbMeta); + + LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp)); + + *ppvData = pvDataTmp; + *pcbData = cbDataTmp; + } + else + RTMemFree(pvDataTmp); + } + } + else + { + *ppvData = NULL; + *pcbData = 0; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Main function for receiving the actual DnD data from the host. + * + * @returns VBox status code. + * @retval VERR_CANCELLED if cancelled by the host. + * @param pCtx DnD context to use. + * @param pMeta Where to store the actual meta data received from the host. + */ +static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx, + PVBGLR3GUESTDNDMETADATA pMeta) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pMeta, VERR_INVALID_POINTER); + + AssertMsgReturn(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"), VERR_INVALID_PARAMETER); + + VBOXDNDDATAHDR dataHdr; + RT_ZERO(dataHdr); + dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize; + dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt); + if (!dataHdr.pvMetaFmt) + return VERR_NO_MEMORY; + + void *pvData = NULL; + uint64_t cbData = 0; + int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData); + if (RT_SUCCESS(rc)) + { + LogRel2(("DnD: Received %RU64 bytes meta data in format '%s'\n", cbData, (char *)dataHdr.pvMetaFmt)); + + /** + * Check if this is an URI event. If so, let VbglR3 do all the actual + * data transfer + file/directory creation internally without letting + * the caller know. + * + * This keeps the actual (guest OS-)dependent client (like VBoxClient / + * VBoxTray) small by not having too much redundant code. + */ + Assert(dataHdr.cbMetaFmt); + AssertPtr(dataHdr.pvMetaFmt); + if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */ + { + DNDDROPPEDFILES droppedFiles; + RT_ZERO(droppedFiles); + + rc = DnDDroppedFilesInit(&droppedFiles); + if (RT_SUCCESS(rc)) + rc = DnDDroppedFilesOpenTemp(&droppedFiles, DNDURIDROPPEDFILE_FLAGS_NONE); + + if (RT_FAILURE(rc)) + { + LogRel(("DnD: Initializing dropped files directory failed with %Rrc\n", rc)); + } + else + { + AssertPtr(pvData); + Assert(cbData); + + /* Use the dropped files directory as the root directory for the current transfer. */ + rc = DnDTransferListInitEx(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles), + DNDTRANSFERLISTFMT_NATIVE); + if (RT_SUCCESS(rc)) + { + rc = DnDTransferListAppendRootsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData, + DND_PATH_SEPARATOR_STR, 0 /* fFlags */); + if (RT_SUCCESS(rc)) + { + rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles); + if (RT_SUCCESS(rc)) + { + pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST; + } + } + } + } + } + else /* Raw data. */ + { + pMeta->u.Raw.cbMeta = cbData; + pMeta->u.Raw.pvMeta = pvData; + + pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW; + } + + if (pvData) + RTMemFree(pvData); + } + + if (dataHdr.pvMetaFmt) + RTMemFree(dataHdr.pvMetaFmt); + + if (RT_FAILURE(rc)) + { + if (rc != VERR_CANCELLED) + { + LogRel(("DnD: Receiving data failed with %Rrc\n", rc)); + + int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc); + if (RT_FAILURE(rc2)) + LogRel(("DnD: Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2)); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host + * Utility function to receive the HOST_DND_FN_GH_REQ_PENDING message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param puScreenID For which screen on the host the request is for. Optional. + */ +static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* pScreenID is optional. */ + + HGCMMsgGHReqPending Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_REQ_PENDING, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uScreenId.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (puScreenID) + rc = Msg.u.v3.uContext.GetUInt32(puScreenID); + } + + return rc; +} + +/** + * Guest -> Host + * Utility function to receive the HOST_DND_FN_GH_EVT_DROPPED message from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param ppszFormat Requested data format from the host. Optional. + * @param pcbFormat Size of requested data format (in bytes). Optional. + * @param puAction Requested action from the host. Optional. + */ +static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx, + char **ppszFormat, + uint32_t *pcbFormat, + uint32_t *puAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* The rest is optional. */ + + const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize; + + char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp)); + if (!pszFormatTmp) + return VERR_NO_MEMORY; + + HGCMMsgGHDropped Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_EVT_DROPPED, 4); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp); + Msg.u.v3.cbFormat.SetUInt32(0); + Msg.u.v3.uAction.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Context ID not used yet. */ + if (pcbFormat) + rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat); + if (RT_SUCCESS(rc) && puAction) + rc = Msg.u.v3.uAction.GetUInt32(puAction); + + if (RT_SUCCESS(rc)) + { + *ppszFormat = RTStrDup(pszFormatTmp); + if (!*ppszFormat) + rc = VERR_NO_MEMORY; + } + } + + RTMemFree(pszFormatTmp); + + return rc; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + + +/********************************************************************************************************************************* +* Public functions * +*********************************************************************************************************************************/ + +/** + * Connects a DnD context to the DnD host service. + * + * @returns IPRT status code. + * @param pCtx DnD context to connect. + */ +VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + /* Initialize header */ + int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID); + if (RT_FAILURE(rc)) + return rc; + Assert(pCtx->uClientID); + + /* Set the default protocol version we would like to use. + * Deprecated since VBox 6.1.x, but let this set to 3 to (hopefully) not break things. */ + pCtx->uProtocolDeprecated = 3; + + pCtx->fHostFeatures = VBOX_DND_HF_NONE; + pCtx->fGuestFeatures = VBOX_DND_GF_NONE; + + /* + * Get the VM's session ID. + * This is not fatal in case we're running with an ancient VBox version. + */ + pCtx->uSessionID = 0; + int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2); + LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2)); + + /* + * Try sending the connect message to tell the protocol version to use. + * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which + * does not implement this command. + */ + HGCMMsgConnect Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_CONNECT, 3); + Msg.u.v3.uContext.SetUInt32(0); /** @todo Context ID not used yet. */ + Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocolDeprecated); /* Deprecated since VBox 6.1.x. */ + Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */ + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* Set the protocol version we're going to use as told by the host. */ + rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocolDeprecated); AssertRC(rc); + + /* + * Next is reporting our features. If this fails, assume older host. + */ + rc2 = VbglR3DnDReportFeatures(pCtx->uClientID, pCtx->fGuestFeatures, &pCtx->fHostFeatures); + if (RT_SUCCESS(rc2)) + { + LogRel2(("DnD: Guest features: %#RX64 - Host features: %#RX64\n", + pCtx->fGuestFeatures, pCtx->fHostFeatures)); + } + else /* Failing here is not fatal; might be running with an older host. */ + { + AssertLogRelMsg(rc2 == VERR_NOT_SUPPORTED || rc2 == VERR_NOT_IMPLEMENTED, + ("Reporting features failed: %Rrc\n", rc2)); + } + + pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */ + } + else + pCtx->uProtocolDeprecated = 0; /* We're using protocol v0 (initial draft) as a fallback. */ + + LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocolDeprecated, rc)); + return rc; +} + +/** + * Disconnects a given DnD context from the DnD host service. + * + * @returns IPRT status code. + * @param pCtx DnD context to disconnect. + * The context is invalid afterwards on successful disconnection. + */ +VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + if (!pCtx->uClientID) /* Already disconnected? Bail out early. */ + return VINF_SUCCESS; + + int rc = VbglR3HGCMDisconnect(pCtx->uClientID); + if (RT_SUCCESS(rc)) + pCtx->uClientID = 0; + + return rc; +} + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3DnDConnect(). + * @param fGuestFeatures Features to report, VBOX_DND_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_DND_HF_XXX. + */ +VBGLR3DECL(int) VbglR3DnDReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_DND_FN_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_DND_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_DND_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + +/** + * Receives the next upcoming DnD event. + * + * This is the main function DnD clients call in order to implement any DnD functionality. + * The purpose of it is to abstract the actual DnD protocol handling as much as possible from + * the clients -- those only need to react to certain events, regardless of how the underlying + * protocol actually is working. + * + * @returns VBox status code. + * @param pCtx DnD context to work with. + * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling + * VbglR3DnDEventFree() when done. + */ +VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppEvent, VERR_INVALID_POINTER); + + PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT)); + if (!pEvent) + return VERR_NO_MEMORY; + + uint32_t uMsg = 0; + uint32_t cParms = 0; + int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */); + if (RT_SUCCESS(rc)) + { + /* Check for VM session change. */ + uint64_t uSessionID; + int rc2 = VbglR3GetSessionId(&uSessionID); + if ( RT_SUCCESS(rc2) + && (uSessionID != pCtx->uSessionID)) + { + LogRel2(("DnD: VM session ID changed to %RU64\n", uSessionID)); + rc = VbglR3DnDDisconnect(pCtx); + if (RT_SUCCESS(rc)) + rc = VbglR3DnDConnect(pCtx); + } + } + + if (rc == VERR_CANCELLED) /* Host service told us that we have to bail out. */ + { + LogRel2(("DnD: Host service requested termination\n")); + + pEvent->enmType = VBGLR3DNDEVENTTYPE_QUIT; + *ppEvent = pEvent; + + return VINF_SUCCESS; + } + + if (RT_SUCCESS(rc)) + { + LogFunc(("Handling uMsg=%RU32\n", uMsg)); + + switch(uMsg) + { + case HOST_DND_FN_HG_EVT_ENTER: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + &pEvent->u.HG_Enter.uScreenID, + NULL /* puXPos */, + NULL /* puYPos */, + NULL /* uDefAction */, + &pEvent->u.HG_Enter.dndLstActionsAllowed, + &pEvent->u.HG_Enter.pszFormats, + &pEvent->u.HG_Enter.cbFormats); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER; + break; + } + case HOST_DND_FN_HG_EVT_MOVE: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + NULL /* puScreenId */, + &pEvent->u.HG_Move.uXpos, + &pEvent->u.HG_Move.uYpos, + &pEvent->u.HG_Move.dndActionDefault, + NULL /* puAllActions */, + NULL /* pszFormats */, + NULL /* pcbFormats */); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE; + break; + } + case HOST_DND_FN_HG_EVT_DROPPED: + { + rc = vbglR3DnDHGRecvAction(pCtx, + uMsg, + NULL /* puScreenId */, + &pEvent->u.HG_Drop.uXpos, + &pEvent->u.HG_Drop.uYpos, + &pEvent->u.HG_Drop.dndActionDefault, + NULL /* puAllActions */, + NULL /* pszFormats */, + NULL /* pcbFormats */); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP; + break; + } + case HOST_DND_FN_HG_EVT_LEAVE: + { + rc = vbglR3DnDHGRecvLeave(pCtx); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE; + break; + } + case HOST_DND_FN_HG_SND_DATA_HDR: + { + rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE; + break; + } + case HOST_DND_FN_HG_SND_DIR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_HDR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_DATA: + { + /* + * All messages for this block are handled internally + * by vbglR3DnDHGRecvDataMain(), see above. + * + * So if we land here our code is buggy. + */ + rc = VERR_WRONG_ORDER; + break; + } + case HOST_DND_FN_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + rc = VERR_CANCELLED; /* Will emit a cancel event below. */ + break; + } +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case HOST_DND_FN_GH_REQ_PENDING: + { + rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING; + break; + } + case HOST_DND_FN_GH_EVT_DROPPED: + { + rc = vbglR3DnDGHRecvDropped(pCtx, + &pEvent->u.GH_Drop.pszFormat, + &pEvent->u.GH_Drop.cbFormat, + &pEvent->u.GH_Drop.dndActionRequested); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP; + break; + } +#endif + default: + { + rc = VERR_NOT_SUPPORTED; + break; + } + } + } + + if (RT_FAILURE(rc)) + { + /* Current operation cancelled? Set / overwrite event type and tell the caller. */ + if (rc == VERR_CANCELLED) + { + pEvent->enmType = VBGLR3DNDEVENTTYPE_CANCEL; + rc = VINF_SUCCESS; /* Deliver the event to the caller. */ + } + else + { + VbglR3DnDEventFree(pEvent); + LogRel(("DnD: Handling message %s (%#x) failed with %Rrc\n", DnDHostMsgToStr(uMsg), uMsg, rc)); + } + } + + if (RT_SUCCESS(rc)) + *ppEvent = pEvent; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Frees (destroys) a formerly allocated DnD event. + * + * @returns IPRT status code. + * @param pEvent Event to free (destroy). + */ +VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent) +{ + if (!pEvent) + return; + + /* Some messages require additional cleanup. */ + switch (pEvent->enmType) + { + case VBGLR3DNDEVENTTYPE_HG_ENTER: + { + if (pEvent->u.HG_Enter.pszFormats) + RTStrFree(pEvent->u.HG_Enter.pszFormats); + break; + } + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case VBGLR3DNDEVENTTYPE_GH_DROP: + { + if (pEvent->u.GH_Drop.pszFormat) + RTStrFree(pEvent->u.GH_Drop.pszFormat); + break; + } +#endif + case VBGLR3DNDEVENTTYPE_HG_RECEIVE: + { + PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta; + switch (pMeta->enmType) + { + case VBGLR3GUESTDNDMETADATATYPE_RAW: + { + if (pMeta->u.Raw.pvMeta) + { + Assert(pMeta->u.Raw.cbMeta); + RTMemFree(pMeta->u.Raw.pvMeta); + pMeta->u.Raw.cbMeta = 0; + } + break; + } + + case VBGLR3GUESTDNDMETADATATYPE_URI_LIST: + { + DnDTransferListDestroy(&pMeta->u.URI.Transfer); + break; + } + + default: + break; + } + break; + } + + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgHGAck Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_ACK_OP, 2); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uAction.SetUInt32(dndAction); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Requests the actual DnD data to be sent from the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pcszFormat Format to request the data from the host in. + */ +VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER); + if (!RTStrIsValidEncoding(pcszFormat)) + return VERR_INVALID_PARAMETER; + + const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */ + + HGCMMsgHGReqData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_REQ_DATA, 3); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat); + Msg.u.v3.cbFormat.SetUInt32(cbFormat); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Host -> Guest + * Reports back its progress back to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param uStatus DnD status to report. + * @param uPercent Overall progress (in percent) to report. + * @param rcErr Error code (IPRT-style) to report. + */ +VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER); + + HGCMMsgHGProgress Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_EVT_PROGRESS, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uStatus.SetUInt32(uStatus); + Msg.u.v3.uPercent.SetUInt32(uPercent); + Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host + * Acknowledges that there currently is a drag'n drop operation in progress on the guest, + * which eventually could be dragged over to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param dndActionDefault Default action for the operation to report. + * @param dndLstActionsAllowed All available actions for the operation to report. + * @param pcszFormats Available formats for the operation to report. + * @param cbFormats Size (in bytes) of formats to report. + */ +VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx, + VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed, + const char* pcszFormats, uint32_t cbFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER); + AssertReturn(cbFormats, VERR_INVALID_PARAMETER); + + if (!RTStrIsValidEncoding(pcszFormats)) + return VERR_INVALID_UTF8_ENCODING; + + HGCMMsgGHAckPending Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_ACK_PENDING, 5); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uDefAction.SetUInt32(dndActionDefault); + Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed); + Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats); + Msg.u.v3.cbFormats.SetUInt32(cbFormats); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Guest -> Host + * Utility function to send DnD data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Data block to send. + * @param cbData Size (in bytes) of data block to send. + * @param pDataHdr Data header to use -- needed for accounting. + */ +static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx, + void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER); + + HGCMMsgGHSendDataHdr MsgHdr; + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA_HDR, 12); + MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */ + MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal); + MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta); + MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt); + MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt); + MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects); + MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */ + MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */ + MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */ + MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */ + + int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr)); + + LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n", + pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc)); + + if (RT_SUCCESS(rc)) + { + HGCMMsgGHSendData MsgData; + VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA, 5); + MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */ + MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */ + MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */ + + uint32_t cbCurChunk; + const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize; + uint32_t cbSent = 0; + + while (cbSent < cbData) + { + cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk); + MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk); + MsgData.u.v3.cbData.SetUInt32(cbCurChunk); + + rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData)); + if (RT_FAILURE(rc)) + break; + + cbSent += cbCurChunk; + } + + LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n", + cbMaxChunk, cbData, cbSent, rc)); + + if (RT_SUCCESS(rc)) + Assert(cbSent == cbData); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host + * Utility function to send a guest directory to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj transfer object containing the directory to send. + */ +static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj) +{ + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER); + + const char *pcszPath = DnDTransferObjectGetDestPath(pObj); + const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */; + const RTFMODE fMode = DnDTransferObjectGetMode(pObj); + + LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode)); + + if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */ + return VERR_INVALID_PARAMETER; + + HGCMMsgGHSendDir Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DIR, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath); + Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath); + Msg.u.v3.fMode.SetUInt32(fMode); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Guest -> Host + * Utility function to send a file from the guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj Transfer object containing the file to send. + */ +static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE); + AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER); + + uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE; + + int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE); + if (RT_FAILURE(rc)) + return rc; + + uint32_t cbBuf = pCtx->cbMaxChunkSize; + AssertReturn(cbBuf, VERR_INVALID_PARAMETER); + void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */ + if (!pvBuf) + { + int rc2 = DnDTransferObjectClose(pObj); + AssertRC(rc2); + return VERR_NO_MEMORY; + } + + const char *pcszPath = DnDTransferObjectGetDestPath(pObj); + const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX); + const uint64_t cbSize = DnDTransferObjectGetSize(pObj); + const RTFMODE fMode = DnDTransferObjectGetMode(pObj); + + LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode)); + + HGCMMsgGHSendFileHdr MsgHdr; + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_HDR, 6); + MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */ + MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */ + MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */ + MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */ + MsgHdr.fMode.SetUInt32(fMode); /* File mode */ + MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */ + + rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr)); + + LogFlowFunc(("Sending file header resulted in %Rrc\n", rc)); + + if (RT_SUCCESS(rc)) + { + /* + * Send the actual file data, chunk by chunk. + */ + HGCMMsgGHSendFileData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_DATA, 5); + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvChecksum.SetPtr(NULL, 0); + Msg.u.v3.cbChecksum.SetUInt32(0); + + uint64_t cbToReadTotal = cbSize; + uint64_t cbWrittenTotal = 0; + while (cbToReadTotal) + { + uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf); + uint32_t cbRead = 0; + if (cbToRead) + rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead); + + LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n", + cbToReadTotal, cbToRead, cbRead, rc)); + + if ( RT_SUCCESS(rc) + && cbRead) + { + Msg.u.v3.pvData.SetPtr(pvBuf, cbRead); + Msg.u.v3.cbData.SetUInt32(cbRead); + /** @todo Calculate + set checksums. */ + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + + if (RT_FAILURE(rc)) + { + LogFlowFunc(("Reading failed with rc=%Rrc\n", rc)); + break; + } + + Assert(cbRead <= cbToReadTotal); + cbToReadTotal -= cbRead; + cbWrittenTotal += cbRead; + + LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize)); + }; + } + + RTMemFree(pvBuf); + int rc2 = DnDTransferObjectClose(pObj); + if (RT_SUCCESS(rc)) + rc = rc2; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host + * Utility function to send a transfer object from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj Transfer object to send from guest to the host. + */ +static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + + int rc; + + const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj); + + switch (enmType) + { + case DNDTRANSFEROBJTYPE_DIRECTORY: + rc = vbglR3DnDGHSendDir(pCtx, pObj); + break; + + case DNDTRANSFEROBJTYPE_FILE: + rc = vbglR3DnDGHSendFile(pCtx, pObj); + break; + + default: + AssertMsgFailed(("Object type %ld not implemented\n", enmType)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + return rc; +} + +/** + * Guest -> Host + * Utility function to send raw data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Block to raw data to send. + * @param cbData Size (in bytes) of raw data to send. + */ +static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + /* cbData can be 0. */ + + VBOXDNDDATAHDR dataHdr; + RT_ZERO(dataHdr); + + dataHdr.cbMeta = (uint32_t)cbData; + dataHdr.cbTotal = cbData; + + return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr); +} + +/** + * Guest -> Host + * Utility function to send transfer data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pTransferList Dnd transfer list to send. + */ +static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList) +{ + AssertPtrReturn(pCtx,VERR_INVALID_POINTER); + AssertPtrReturn(pTransferList, VERR_INVALID_POINTER); + + /* + * Send the (meta) data; in case of URIs it's the root entries of a + * transfer list the host needs to know upfront to set up the drag'n drop operation. + */ + char *pszList = NULL; + size_t cbList; + int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList); + if (RT_FAILURE(rc)) + return rc; + + void *pvURIList = (void *)pszList; + uint32_t cbURLIist = (uint32_t)cbList; + + /* The total size also contains the size of the meta data. */ + uint64_t cbTotal = cbURLIist; + cbTotal += DnDTransferListObjTotalBytes(pTransferList); + + /* We're going to send a transfer list in text format. */ + const char szMetaFmt[] = "text/uri-list"; + const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */ + + VBOXDNDDATAHDR dataHdr; + dataHdr.uFlags = 0; /* Flags not used yet. */ + dataHdr.cbTotal = cbTotal; + dataHdr.cbMeta = cbURLIist; + dataHdr.pvMetaFmt = (void *)szMetaFmt; + dataHdr.cbMetaFmt = cbMetaFmt; + dataHdr.cObjects = DnDTransferListObjCount(pTransferList); + + rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr); + + if (RT_SUCCESS(rc)) + { + while (DnDTransferListObjCount(pTransferList)) + { + PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList); + + rc = vbglR3DnDGHSendURIObject(pCtx, pObj); + if (RT_FAILURE(rc)) + break; + + DnDTransferListObjRemoveFirst(pTransferList); + } + + Assert(DnDTransferListObjCount(pTransferList) == 0); + } + + return rc; +} + +/** + * Guest -> Host + * Sends data, which either can be raw or URI data, from guest to the host. This function + * initiates the actual data transfer from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pszFormat In which format the data will be sent. + * @param pvData Data block to send. + * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR_STR. + * @param cbData Size (in bytes) of data block to send. + */ +VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData)); + + LogRel2(("DnD: Sending %RU32 bytes meta data in format '%s'\n", cbData, pszFormat)); + + int rc; + if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat))) + { + DNDTRANSFERLIST lstTransfer; + RT_ZERO(lstTransfer); + + rc = DnDTransferListInit(&lstTransfer); + if (RT_SUCCESS(rc)) + { + /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */ + /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */ + const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE; + + rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData, + DND_PATH_SEPARATOR_STR, fFlags); + if (RT_SUCCESS(rc)) + rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer); + DnDTransferListDestroy(&lstTransfer); + } + } + else + rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData); + + if (RT_FAILURE(rc)) + { + LogRel(("DnD: Sending data failed with rc=%Rrc\n", rc)); + + if (rc != VERR_CANCELLED) + { + int rc2 = VbglR3DnDSendError(pCtx, rc); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2)); + } + } + + return rc; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp new file mode 100644 index 00000000..66a8bca7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp @@ -0,0 +1,199 @@ +/* $Id: VBoxGuestR3LibDrmClient.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, DRM client handling. + */ + +/* + * Copyright (C) 2020-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" + +#include <iprt/env.h> +#include <iprt/path.h> +#include <iprt/process.h> + +#if defined(RT_OS_LINUX) +# include <VBox/HostServices/GuestPropertySvc.h> + +/** Defines the DRM client executable (image). */ +# define VBOX_DRMCLIENT_EXECUTABLE "/usr/bin/VBoxDRMClient" +# define VBOX_DRMCLIENT_LEGACY_EXECUTABLE "/usr/bin/VBoxClient" +/** Defines the guest property that defines if the DRM resizing client needs to be active or not. */ +# define VBOX_DRMCLIENT_GUEST_PROP_RESIZE "/VirtualBox/GuestAdd/DRMResize" + +/** + * Check if specified guest property exist. + * + * @returns \c true if the property exists and its flags do match, \c false otherwise. + * @param pszPropName Guest property name. + * @param fPropFlags Guest property flags mask to verify if property exist. + * If \p fPropFlags is 0, flags verification is omitted. + */ +static bool vbglR3DrmClientCheckProp(const char *pszPropName, uint32_t fPropFlags) +{ + bool fExist = false; +# if defined(VBOX_WITH_GUEST_PROPS) + uint32_t idClient; + + int rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + char *pcszFlags = NULL; + + rc = VbglR3GuestPropReadEx(idClient, pszPropName, NULL /* ppszValue */, &pcszFlags, NULL); + if (RT_SUCCESS(rc)) + { + /* Check property flags match. */ + if (fPropFlags) + { + uint32_t fFlags = 0; + + rc = GuestPropValidateFlags(pcszFlags, &fFlags); + fExist = RT_SUCCESS(rc) && (fFlags == fPropFlags); + } + else + fExist = true; + + RTStrFree(pcszFlags); + } + + VbglR3GuestPropDisconnect(idClient); + } +# endif /* VBOX_WITH_GUEST_PROPS */ + return fExist; +} +#endif /* RT_OS_LINUX */ + +/** + * Returns true if the DRM resizing client is needed. + * This is achieved by querying existence of a guest property. + * + * @returns \c true if the DRM resizing client is needed, \c false if not. + */ +VBGLR3DECL(bool) VbglR3DrmClientIsNeeded(void) +{ +#if defined(RT_OS_LINUX) + return vbglR3DrmClientCheckProp(VBOX_DRMCLIENT_GUEST_PROP_RESIZE, 0); +#else + return false; +#endif +} + +/** + * Returns true if the DRM IPC server socket access should be restricted. + * + * Restricted access means that only users from a certain group should + * be granted with read and write access permission to IPC socket. Check + * is done by examining \c VBGLR3DRMIPCPROPRESTRICT guest property. Property + * is only considered valid if is read-only for guest. I.e., the following + * property should be set on the host side: + * + * VBoxManage guestproperty set <VM> /VirtualBox/GuestAdd/DRMIpcRestricted 1 --flags RDONLYGUEST + * + * @returns \c true if restricted socket access is required, \c false otherwise. + */ +VBGLR3DECL(bool) VbglR3DrmRestrictedIpcAccessIsNeeded(void) +{ +#if defined(RT_OS_LINUX) + return vbglR3DrmClientCheckProp(VBGLR3DRMIPCPROPRESTRICT, GUEST_PROP_F_RDONLYGUEST); +#else + return false; +#endif +} + +/** + * Returns true if the DRM resizing client already is running. + * This is achieved by querying existence of a guest property. + * + * @returns \c true if the DRM resizing client is running, \c false if not. + */ +VBGLR3DECL(bool) VbglR3DrmClientIsRunning(void) +{ + return VbglR3DrmClientIsNeeded(); +} + +#if defined(RT_OS_LINUX) +static int VbglR3DrmStart(const char *pszCmd, const char **apszArgs) +{ + return RTProcCreate(pszCmd, apszArgs, RTENV_DEFAULT, + RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL); +} +#endif + +/** + * Starts (executes) the DRM resizing client process ("VBoxDRMClient"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmClientStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[2] = { VBOX_DRMCLIENT_EXECUTABLE, NULL }; /** @todo r=andy Pass path + process name as argv0? */ + return VbglR3DrmStart(VBOX_DRMCLIENT_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + +/** + * Starts (executes) the legacy DRM resizing client process ("VBoxClient --vmsvga"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmLegacyClientStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--vmsvga", NULL }; + return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + +/** + * Starts (executes) the legacy X11 resizing agent process ("VBoxClient --display"). + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3DrmLegacyX11AgentStart(void) +{ +#if defined(RT_OS_LINUX) + const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--display", NULL }; + return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs); +#else + return VERR_NOT_SUPPORTED; +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp new file mode 100644 index 00000000..f459f607 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp @@ -0,0 +1,103 @@ +/* $Id: VBoxGuestR3LibEvent.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Events. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 <VBox/log.h> +#include <iprt/assert.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Wait for the host to signal one or more events and return which. + * + * The events will only be delivered by the host if they have been enabled + * previously using @a VbglR3CtlFilterMask. If one or several of the events + * have already been signalled but not yet waited for, this function will return + * immediately and return those events. + * + * @returns IPRT status code. + * + * @param fMask The events we want to wait for, or-ed together. + * @param cMillies How long to wait before giving up and returning + * (VERR_TIMEOUT). Use RT_INDEFINITE_WAIT to wait until we + * are interrupted or one of the events is signalled. + * @param pfEvents Where to store the events signalled. Optional. + */ +VBGLR3DECL(int) VbglR3WaitEvent(uint32_t fMask, uint32_t cMillies, uint32_t *pfEvents) +{ + LogFlow(("VbglR3WaitEvent: fMask=%#x, cMillies=%u, pfEvents=%p\n", fMask, cMillies, pfEvents)); + AssertReturn((fMask & ~VMMDEV_EVENT_VALID_EVENT_MASK) == 0, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfEvents, VERR_INVALID_POINTER); + + VBGLIOCWAITFOREVENTS WaitEvents; + VBGLREQHDR_INIT(&WaitEvents.Hdr, WAIT_FOR_EVENTS); + WaitEvents.u.In.fEvents = fMask; + WaitEvents.u.In.cMsTimeOut = cMillies; + int rc = vbglR3DoIOCtl(VBGL_IOCTL_WAIT_FOR_EVENTS, &WaitEvents.Hdr, sizeof(WaitEvents)); + if (pfEvents) + { + if (RT_SUCCESS(rc)) + *pfEvents = WaitEvents.u.Out.fEvents; + else + *pfEvents = 0; + } + + LogFlow(("VbglR3WaitEvent: rc=%Rrc fEvents=%#x\n", rc, WaitEvents.u.Out.fEvents)); + return rc; +} + + +/** + * Causes any pending VbglR3WaitEvent calls (VBGL_IOCTL_WAIT_FOR_EVENTS) to + * return with a VERR_INTERRUPTED status. + * + * Can be used in combination with a termination flag variable for interrupting + * event loops. After calling this, VBGL_IOCTL_WAIT_FOR_EVENTS should no longer + * be called in the same session. At the time of writing this is not enforced; + * at the time of reading it may be. + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3InterruptEventWaits(void) +{ + VBGLREQHDR Req; + VBGLREQHDR_INIT(&Req, INTERRUPT_ALL_WAIT_FOR_EVENTS); + return vbglR3DoIOCtl(VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS, &Req, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp new file mode 100644 index 00000000..e80f1f55 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp @@ -0,0 +1,90 @@ +/* $Id: VBoxGuestR3LibGR.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, GR. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/errcore.h> +#include "VBoxGuestR3LibInternal.h" + + +int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType) +{ + VMMDevRequestHeader *pReq; + + AssertPtrReturn(ppReq, VERR_INVALID_PARAMETER); + AssertMsgReturn(cb >= sizeof(VMMDevRequestHeader) && cb < _1G, ("%#zx vs %#zx\n", cb, sizeof(VMMDevRequestHeader)), + VERR_INVALID_PARAMETER); + + pReq = (VMMDevRequestHeader *)RTMemTmpAlloc(cb); + if (RT_LIKELY(pReq)) + { + pReq->size = (uint32_t)cb; + pReq->version = VMMDEV_REQUEST_HEADER_VERSION; + pReq->requestType = enmReqType; + pReq->rc = VERR_GENERAL_FAILURE; + pReq->reserved1 = 0; + pReq->fRequestor = 0; + + *ppReq = pReq; + + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +int vbglR3GRPerform(VMMDevRequestHeader *pReq) +{ + PVBGLREQHDR pReqHdr = (PVBGLREQHDR)pReq; + uint32_t const cbReq = pReqHdr->cbIn; + Assert(pReqHdr->cbOut == 0 || pReqHdr->cbOut == cbReq); + pReqHdr->cbOut = cbReq; + if (pReq->size < _1K) + return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST(cbReq), pReqHdr, cbReq); + return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST_BIG, pReqHdr, cbReq); +} + + +void vbglR3GRFree(VMMDevRequestHeader *pReq) +{ + RTMemTmpFree(pReq); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp new file mode 100644 index 00000000..fc29936c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp @@ -0,0 +1,2214 @@ +/* $Id: VBoxGuestR3LibGuestCtrl.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest control. + */ + +/* + * 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/string.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/cpp/autores.h> +#include <iprt/stdarg.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/GuestHost/GuestControl.h> +#include <VBox/HostServices/GuestControlSvc.h> + +#ifndef RT_OS_WINDOWS +# include <signal.h> +# ifdef RT_OS_DARWIN +# include <pthread.h> +# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */ +# endif +#endif + +#include "VBoxGuestR3LibInternal.h" + +using namespace guestControl; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Set if GUEST_MSG_PEEK_WAIT and friends are supported. */ +static int g_fVbglR3GuestCtrlHavePeekGetCancel = -1; + + +/** + * Connects to the guest control service. + * + * @returns VBox status code + * @param pidClient Where to put The client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pidClient) +{ + return VbglR3HGCMConnect("VBoxGuestControlSvc", pidClient); +} + + +/** + * Disconnect from the guest control service. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Waits until a new host message arrives. + * This will block until a message becomes available. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + */ +static int vbglR3GuestCtrlMsgWaitFor(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + HGCMMsgWaitFor Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, + GUEST_MSG_WAIT, /* Tell the host we want our next message. */ + 2); /* Just peek for the next message! */ + VbglHGCMParmUInt32Set(&Msg.msg, 0); + VbglHGCMParmUInt32Set(&Msg.num_parms, 0); + + /* + * We should always get a VERR_TOO_MUCH_DATA response here, see + * guestControl::HostMessage::Peek() and its caller ClientState::SendReply(). + * We accept success too here, in case someone decide to make the protocol + * slightly more sane. + * + * Note! A really sane protocol design would have a separate call for getting + * info about a pending message (returning VINF_SUCCESS), and a separate + * one for retriving the actual message parameters. Not this weird + * stuff, to put it rather bluntly. + * + * Note! As a result of this weird design, we are not able to correctly + * retrieve message if we're interrupted by a signal, like SIGCHLD. + * Because IPRT wants to use waitpid(), we're forced to have a handler + * installed for SIGCHLD, so when working with child processes there + * will be signals in the air and we will get VERR_INTERRUPTED returns. + * The way HGCM handles interrupted calls is to silently (?) drop them + * as they complete (see VMMDev), so the server knows little about it + * and just goes on to the next message inline. + * + * So, as a "temporary" mesasure, we block SIGCHLD here while waiting, + * because it will otherwise be impossible do simple stuff like 'mkdir' + * on a mac os x guest, and probably most other unix guests. + */ +#ifdef RT_OS_WINDOWS + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +#else + sigset_t SigSet; + sigemptyset(&SigSet); + sigaddset(&SigSet, SIGCHLD); + sigprocmask(SIG_BLOCK, &SigSet, NULL); + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + sigprocmask(SIG_UNBLOCK, &SigSet, NULL); +#endif + if ( rc == VERR_TOO_MUCH_DATA + || RT_SUCCESS(rc)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg); + if (RT_SUCCESS(rc2)) + { + rc2 = VbglHGCMParmUInt32Get(&Msg.num_parms, pcParameters); + if (RT_SUCCESS(rc2)) + { + /* Ok, so now we know what message type and how much parameters there are. */ + return rc; + } + } + rc = rc2; + } + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; +} + + +/** + * Determins the value of g_fVbglR3GuestCtrlHavePeekGetCancel. + * + * @returns true if supported, false if not. + * @param idClient The client ID to use for the testing. + */ +DECL_NO_INLINE(static, bool) vbglR3GuestCtrlDetectPeekGetCancelSupport(uint32_t idClient) +{ + /* + * Seems we get VINF_SUCCESS back from the host if we try unsupported + * guest control messages, so we need to supply some random message + * parameters and check that they change. + */ + uint32_t const idDummyMsg = UINT32_C(0x8350bdca); + uint32_t const cDummyParmeters = UINT32_C(0x7439604f); + uint32_t const cbDummyMask = UINT32_C(0xc0ffe000); + Assert(cDummyParmeters > VMMDEV_MAX_HGCM_PARMS); + + int rc; + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; + HGCMFunctionParameter cParams; + HGCMFunctionParameter acbParams[14]; + } PeekCall; + Assert(RT_ELEMENTS(PeekCall.acbParams) + 2 < VMMDEV_MAX_HGCM_PARMS); + + do + { + memset(&PeekCall, 0xf6, sizeof(PeekCall)); + VBGL_HGCM_HDR_INIT(&PeekCall.Hdr, idClient, GUEST_MSG_PEEK_NOWAIT, 16); + VbglHGCMParmUInt32Set(&PeekCall.idMsg, idDummyMsg); + VbglHGCMParmUInt32Set(&PeekCall.cParams, cDummyParmeters); + for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++) + VbglHGCMParmUInt32Set(&PeekCall.acbParams[i], i | cbDummyMask); + + rc = VbglR3HGCMCall(&PeekCall.Hdr, sizeof(PeekCall)); + } while (rc == VERR_INTERRUPTED); + + LogRel2(("vbglR3GuestCtrlDetectPeekGetCancelSupport: rc=%Rrc %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x\n", + rc, PeekCall.idMsg.u.value32, PeekCall.cParams.u.value32, + PeekCall.acbParams[ 0].u.value32, PeekCall.acbParams[ 1].u.value32, + PeekCall.acbParams[ 2].u.value32, PeekCall.acbParams[ 3].u.value32, + PeekCall.acbParams[ 4].u.value32, PeekCall.acbParams[ 5].u.value32, + PeekCall.acbParams[ 6].u.value32, PeekCall.acbParams[ 7].u.value32, + PeekCall.acbParams[ 8].u.value32, PeekCall.acbParams[ 9].u.value32, + PeekCall.acbParams[10].u.value32, PeekCall.acbParams[11].u.value32, + PeekCall.acbParams[12].u.value32, PeekCall.acbParams[13].u.value32)); + + /* + * VERR_TRY_AGAIN is likely and easy. + */ + if ( rc == VERR_TRY_AGAIN + && PeekCall.idMsg.u.value32 == 0 + && PeekCall.cParams.u.value32 == 0 + && PeekCall.acbParams[0].u.value32 == 0 + && PeekCall.acbParams[1].u.value32 == 0 + && PeekCall.acbParams[2].u.value32 == 0 + && PeekCall.acbParams[3].u.value32 == 0) + { + g_fVbglR3GuestCtrlHavePeekGetCancel = 1; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#1)\n")); + return true; + } + + /* + * VINF_SUCCESS is annoying but with 16 parameters we've got plenty to check. + */ + if ( rc == VINF_SUCCESS + && PeekCall.idMsg.u.value32 != idDummyMsg + && PeekCall.idMsg.u.value32 != 0 + && PeekCall.cParams.u.value32 <= VMMDEV_MAX_HGCM_PARMS) + { + for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++) + if (PeekCall.acbParams[i].u.value32 != (i | cbDummyMask)) + { + g_fVbglR3GuestCtrlHavePeekGetCancel = 0; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#1)\n")); + return false; + } + g_fVbglR3GuestCtrlHavePeekGetCancel = 1; + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#2)\n")); + return true; + } + + /* + * Okay, pretty sure it's not supported then. + */ + LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#3)\n")); + g_fVbglR3GuestCtrlHavePeekGetCancel = 0; + return false; +} + + +/** + * Reads g_fVbglR3GuestCtrlHavePeekGetCancel and resolved -1. + * + * @returns true if supported, false if not. + * @param idClient The client ID to use for the testing. + */ +DECLINLINE(bool) vbglR3GuestCtrlSupportsPeekGetCancel(uint32_t idClient) +{ + int fState = g_fVbglR3GuestCtrlHavePeekGetCancel; + if (RT_LIKELY(fState != -1)) + return fState != 0; + return vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient); +} + + +/** + * Figures which getter function to use to retrieve the message. + */ +DECLINLINE(uint32_t) vbglR3GuestCtrlGetMsgFunctionNo(uint32_t idClient) +{ + return vbglR3GuestCtrlSupportsPeekGetCancel(idClient) ? GUEST_MSG_GET : GUEST_MSG_WAIT; +} + + +/** + * Checks if the host supports the optimizes message and session functions. + * + * @returns true / false. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * We may need to use this for checking. + * @since 6.0 + */ +VBGLR3DECL(bool) VbglR3GuestCtrlSupportsOptimizations(uint32_t idClient) +{ + return vbglR3GuestCtrlSupportsPeekGetCancel(idClient); +} + + +/** + * Make us the guest control master client. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMakeMeMaster(uint32_t idClient) +{ + int rc; + do + { + VBGLIOCHGCMCALL Hdr; + VBGL_HGCM_HDR_INIT(&Hdr, idClient, GUEST_MSG_MAKE_ME_MASTER, 0); + rc = VbglR3HGCMCall(&Hdr, sizeof(Hdr)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param fGuestFeatures Features to report, VBOX_GUESTCTRL_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_GUESTCTRL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3GuestCtrlReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_GUESTCTRL_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_GUESTCTRL_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Query the host features. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pfHostFeatures Where to store the host feature, VBOX_GUESTCTRL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3GuestCtrlQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_QUERY_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, 0); + VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & RT_BIT_64(63)) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Peeks at the next host message, waiting for one to turn up. + * + * @returns VBox status code. + * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so + * caller just have to repeat this call. + * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck). + * + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use + * for the VM restore check. Optional. + * + * @note Restore check is only performed optimally with a 6.0 host. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgPeekWait(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters, uint64_t *pidRestoreCheck) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + int rc; + if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient)) + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */ + HGCMFunctionParameter cParameters; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_PEEK_WAIT, 2); + VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0); + VbglHGCMParmUInt32Set(&Msg.cParameters, 0); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + LogRel2(("VbglR3GuestCtrlMsgPeekWait -> %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit + && Msg.cParameters.type == VMMDevHGCMParmType_32bit, + ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type), + VERR_INTERNAL_ERROR_3); + + *pidMsg = (uint32_t)Msg.idMsg.u.value64; + *pcParameters = Msg.cParameters.u.value32; + return rc; + } + + /* + * If interrupted we must cancel the call so it doesn't prevent us from making another one. + */ + if (rc == VERR_INTERRUPTED) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_CANCEL, 0); + int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr)); + AssertRC(rc2); + } + + /* + * If restored, update pidRestoreCheck. + */ + if (rc == VERR_VM_RESTORED && pidRestoreCheck) + *pidRestoreCheck = Msg.idMsg.u.value64; + + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; + } + + /* + * Fallback if host < v6.0. + * + * Note! The restore check isn't perfect. Would require checking afterwards + * and stash the result if we were restored during the call. Too much + * hazzle for a downgrade scenario. + */ + if (pidRestoreCheck) + { + uint64_t idRestoreCur = *pidRestoreCheck; + rc = VbglR3GetSessionId(&idRestoreCur); + if (RT_SUCCESS(rc) && idRestoreCur != *pidRestoreCheck) + { + *pidRestoreCheck = idRestoreCur; + return VERR_VM_RESTORED; + } + } + + rc = vbglR3GuestCtrlMsgWaitFor(idClient, pidMsg, pcParameters); + if (rc == VERR_TOO_MUCH_DATA) + rc = VINF_SUCCESS; + return rc; +} + + +/** + * Asks the host guest control service to set a message filter to this + * client so that it only will receive certain messages in the future. + * The filter(s) are a bitmask for the context IDs, served from the host. + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param uValue The value to filter messages for. + * @param uMaskAdd Filter mask to add. + * @param uMaskRemove Filter mask to remove. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgFilterSet(uint32_t idClient, uint32_t uValue, uint32_t uMaskAdd, uint32_t uMaskRemove) +{ + HGCMMsgFilterSet Msg; + + /* Tell the host we want to set a filter. */ + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_FILTER_SET, 4); + VbglHGCMParmUInt32Set(&Msg.value, uValue); + VbglHGCMParmUInt32Set(&Msg.mask_add, uMaskAdd); + VbglHGCMParmUInt32Set(&Msg.mask_remove, uMaskRemove); + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Replies to a message from the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param rc Guest rc to reply. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc) +{ + return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */, + NULL /* pvPayload */, 0 /* cbPayload */); +} + + +/** + * Replies to a message from the host, extended version. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param rc Guest rc to reply. + * @param uType Reply type; not used yet and must be 0. + * @param pvPayload Pointer to data payload to reply. Optional. + * @param cbPayload Size of data payload (in bytes) to reply. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgReplyEx(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc, uint32_t uType, + void *pvPayload, uint32_t cbPayload) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* Everything else is optional. */ + + HGCMMsgReply Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_REPLY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmUInt32Set(&Msg.rc, (uint32_t)rc); /* int vs. uint32_t */ + VbglHGCMParmPtrSet(&Msg.payload, pvPayload, cbPayload); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Tell the host to skip the current message replying VERR_NOT_SUPPORTED + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + * @param rcSkip The status code to pass back to Main when skipping. + * @param idMsg The message ID to skip, pass UINT32_MAX to pass any. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t idClient, int rcSkip, uint32_t idMsg) +{ + if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient)) + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter rcSkip; + HGCMFunctionParameter idMsg; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SKIP, 2); + VbglHGCMParmUInt32Set(&Msg.rcSkip, (uint32_t)rcSkip); + VbglHGCMParmUInt32Set(&Msg.idMsg, idMsg); + return VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } + + /* This is generally better than nothing... */ + return VbglR3GuestCtrlMsgSkipOld(idClient); +} + + +/** + * Tells the host service to skip the current message returned by + * VbglR3GuestCtrlMsgWaitFor(). + * + * @return IPRT status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgSkipOld(uint32_t idClient) +{ + HGCMMsgSkip Msg; + + /* Tell the host we want to skip the current assigned message. */ + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_SKIP_OLD, 1); + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Asks the host to cancel (release) all pending waits which were deferred. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t idClient) +{ + HGCMMsgCancelPendingWaits Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_CANCEL, 0); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Prepares a session. + * @since 6.0 + * @sa GUEST_SESSION_PREPARE + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionPrepare(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + HGCMFunctionParameter pKey; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_PREPARE, 2); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Accepts a session. + * @since 6.0 + * @sa GUEST_SESSION_ACCEPT + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionAccept(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + HGCMFunctionParameter pKey; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_ACCEPT, 2); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Cancels a prepared session. + * @since 6.0 + * @sa GUEST_SESSION_CANCEL_PREPARED + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionCancelPrepared(uint32_t idClient, uint32_t idSession) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idSession; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_CANCEL_PREPARED, 1); + VbglHGCMParmUInt32Set(&Msg.idSession, idSession); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } while (rc == VERR_INTERRUPTED); + return rc; +} + + +/** + * Invalidates the internal state because the (VM) session has been changed (i.e. restored). + * + * @returns VBox status code. + * @param idClient Client ID to use for invalidating state. + * @param idNewControlSession New control session ID. Currently unused. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionHasChanged(uint32_t idClient, uint64_t idNewControlSession) +{ + RT_NOREF(idNewControlSession); + + vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient); + + return VINF_SUCCESS; +} + + +/** + * Asks a specific guest session to close. + * + * @return IPRT status code. + * @param pCtx Guest control command context to use. + * @param fFlags Some kind of flag. Figure it out yourself. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t fFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + HGCMMsgSessionClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_CLOSE, pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Notifies a guest session. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uType Notification type of type GUEST_SESSION_NOTIFYTYPE_XXX. + * @param iResult Result code (rc) to notify. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionNotify(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uType, int32_t iResult) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgSessionNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmUInt32Set(&Msg.result, (uint32_t)iResult); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + +/** + * Initializes a session startup info, extended version. + * + * @returns VBox status code. + * @param pStartupInfo Session startup info to initializes. + * @param cbUser Size (in bytes) to use for the user name buffer. + * @param cbPassword Size (in bytes) to use for the password buffer. + * @param cbDomain Size (in bytes) to use for the domain name buffer. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInitEx(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo, + size_t cbUser, size_t cbPassword, size_t cbDomain) +{ + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + +#define ALLOC_STR(a_Str, a_cb) \ + if ((a_cb) > 0) \ + { \ + pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \ + AssertPtrBreak(pStartupInfo->psz##a_Str); \ + pStartupInfo->cb##a_Str = (uint32_t)a_cb; \ + } + + do + { + ALLOC_STR(User, cbUser); + ALLOC_STR(Password, cbPassword); + ALLOC_STR(Domain, cbDomain); + + return VINF_SUCCESS; + + } while (0); + +#undef ALLOC_STR + + VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo); + return VERR_NO_MEMORY; +} + +/** + * Initializes a session startup info. + * + * @returns VBox status code. + * @param pStartupInfo Session startup info to initializes. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInit(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + return VbglR3GuestCtrlSessionStartupInfoInitEx(pStartupInfo, + GUEST_PROC_DEF_USER_LEN, GUEST_PROC_DEF_PASSWORD_LEN, + GUEST_PROC_DEF_DOMAIN_LEN); +} + +/** + * Destroys a session startup info. + * + * @param pStartupInfo Session startup info to destroy. + */ +VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoDestroy(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + RTStrFree(pStartupInfo->pszUser); + RTStrFree(pStartupInfo->pszPassword); + RTStrFree(pStartupInfo->pszDomain); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); +} + +/** + * Free's a session startup info. + * + * @param pStartupInfo Session startup info to free. + * The pointer will not be valid anymore after return. + */ +VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoFree(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo); + + RTMemFree(pStartupInfo); + pStartupInfo = NULL; +} + +/** + * Duplicates a session startup info. + * + * @returns Duplicated session startup info on success, or NULL on error. + * @param pStartupInfo Session startup info to duplicate. + */ +VBGLR3DECL(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO) VbglR3GuestCtrlSessionStartupInfoDup(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo) +{ + AssertPtrReturn(pStartupInfo, NULL); + + PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO) + RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + if (pStartupInfoDup) + { + do + { + pStartupInfoDup->pszUser = NULL; + pStartupInfoDup->pszPassword = NULL; + pStartupInfoDup->pszDomain = NULL; + +#define DUP_STR(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \ + } + DUP_STR(User); + DUP_STR(Password); + DUP_STR(Domain); + +#undef DUP_STR + + return pStartupInfoDup; + + } while (0); /* To use break macros above. */ + + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfoDup); + } + + return NULL; +} + +/** + * Retrieves a HOST_SESSION_CREATE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param ppStartupInfo Where to store the allocated session startup info. + * Needs to be free'd by VbglR3GuestCtrlSessionStartupInfoFree(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLSESSIONSTARTUPINFO *ppStartupInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER); + AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER); + + PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo + = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO)); + if (!pStartupInfo) + return VERR_NO_MEMORY; + + int rc = VbglR3GuestCtrlSessionStartupInfoInit(pStartupInfo); + if (RT_FAILURE(rc)) + { + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo); + return rc; + } + + do + { + HGCMMsgSessionOpen Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CREATE); + VbglHGCMParmUInt32Set(&Msg.protocol, 0); + VbglHGCMParmPtrSet(&Msg.username, pStartupInfo->pszUser, pStartupInfo->cbUser); + VbglHGCMParmPtrSet(&Msg.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword); + VbglHGCMParmPtrSet(&Msg.domain, pStartupInfo->pszDomain, pStartupInfo->cbDomain); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.protocol.GetUInt32(&pStartupInfo->uProtocol); + Msg.flags.GetUInt32(&pStartupInfo->fFlags); + + pStartupInfo->uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if (RT_SUCCESS(rc)) + { + *ppStartupInfo = pStartupInfo; + } + else + VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo); + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Retrieves a HOST_SESSION_CLOSE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfFlags, uint32_t *pidSession) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgSessionClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CLOSE); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + + if (pidSession) + *pidSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_RENAME message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetRename(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszSource, uint32_t cbSource, + char *pszDest, uint32_t cbDest, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszSource, VERR_INVALID_POINTER); + AssertReturn(cbSource, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDest, VERR_INVALID_POINTER); + AssertReturn(cbDest, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgPathRename Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_RENAME); + VbglHGCMParmPtrSet(&Msg.source, pszSource, cbSource); + VbglHGCMParmPtrSet(&Msg.dest, pszDest, cbDest); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + } + + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_USER_DOCUMENTS message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserDocuments(PVBGLR3GUESTCTRLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER); + + int rc; + do + { + HGCMMsgPathUserDocuments Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_DOCUMENTS); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + Msg.context.GetUInt32(&pCtx->uContextID); + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_PATH_USER_HOME message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserHome(PVBGLR3GUESTCTRLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER); + + int rc; + do + { + HGCMMsgPathUserHome Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_HOME); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + Msg.context.GetUInt32(&pCtx->uContextID); + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + +/** + * Retrieves a HOST_MSG_SHUTDOWN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param pfAction Where to store the action flags on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlGetShutdown(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfAction, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgShutdown Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SHUTDOWN); + VbglHGCMParmUInt32Set(&Msg.action, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.action.GetUInt32(pfAction); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + +/** + * Initializes a process startup info, extended version. + * + * @returns VBox status code. + * @param pStartupInfo Process startup info to initializes. + * @param cbCmd Size (in bytes) to use for the command buffer. + * @param cbUser Size (in bytes) to use for the user name buffer. + * @param cbPassword Size (in bytes) to use for the password buffer. + * @param cbDomain Size (in bytes) to use for the domain buffer. + * @param cbArgs Size (in bytes) to use for the arguments buffer. + * @param cbEnv Size (in bytes) to use for the environment buffer. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInitEx(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo, + size_t cbCmd, + size_t cbUser, size_t cbPassword, size_t cbDomain, + size_t cbArgs, size_t cbEnv) +{ + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + AssertReturn(cbCmd, VERR_INVALID_PARAMETER); + AssertReturn(cbUser, VERR_INVALID_PARAMETER); + AssertReturn(cbPassword, VERR_INVALID_PARAMETER); + AssertReturn(cbDomain, VERR_INVALID_PARAMETER); + AssertReturn(cbArgs, VERR_INVALID_PARAMETER); + AssertReturn(cbEnv, VERR_INVALID_PARAMETER); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + +#define ALLOC_STR(a_Str, a_cb) \ + if ((a_cb) > 0) \ + { \ + pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \ + AssertPtrBreak(pStartupInfo->psz##a_Str); \ + pStartupInfo->cb##a_Str = (uint32_t)a_cb; \ + } + + do + { + ALLOC_STR(Cmd, cbCmd); + ALLOC_STR(Args, cbArgs); + ALLOC_STR(Env, cbEnv); + ALLOC_STR(User, cbUser); + ALLOC_STR(Password, cbPassword); + ALLOC_STR(Domain, cbDomain); + + return VINF_SUCCESS; + + } while (0); + +#undef ALLOC_STR + + VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo); + return VERR_NO_MEMORY; +} + +/** + * Initializes a process startup info with default values. + * + * @param pStartupInfo Process startup info to initializes. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInit(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + return VbglR3GuestCtrlProcStartupInfoInitEx(pStartupInfo, + GUEST_PROC_DEF_CMD_LEN, + GUEST_PROC_DEF_USER_LEN /* Deprecated, now handled via session creation. */, + GUEST_PROC_DEF_PASSWORD_LEN /* Ditto. */, + GUEST_PROC_DEF_DOMAIN_LEN /* Ditto. */, + GUEST_PROC_DEF_ARGS_LEN, GUEST_PROC_DEF_ENV_LEN); +} + +/** + * Destroys a process startup info. + * + * @param pStartupInfo Process startup info to destroy. + */ +VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoDestroy(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + RTStrFree(pStartupInfo->pszCmd); + RTStrFree(pStartupInfo->pszArgs); + RTStrFree(pStartupInfo->pszEnv); + RTStrFree(pStartupInfo->pszUser); + RTStrFree(pStartupInfo->pszPassword); + RTStrFree(pStartupInfo->pszDomain); + + RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); +} + +/** + * Free's a process startup info. + * + * @param pStartupInfo Process startup info to free. + * The pointer will not be valid anymore after return. + */ +VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoFree(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + if (!pStartupInfo) + return; + + VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo); + + RTMemFree(pStartupInfo); + pStartupInfo = NULL; +} + +/** + * Duplicates a process startup info. + * + * @returns Duplicated process startup info on success, or NULL on error. + * @param pStartupInfo Process startup info to duplicate. + */ +VBGLR3DECL(PVBGLR3GUESTCTRLPROCSTARTUPINFO) VbglR3GuestCtrlProcStartupInfoDup(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo) +{ + AssertPtrReturn(pStartupInfo, NULL); + + PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLPROCSTARTUPINFO) + RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + if (pStartupInfoDup) + { + do + { + pStartupInfoDup->pszCmd = NULL; + pStartupInfoDup->pszArgs = NULL; + pStartupInfoDup->pszEnv = NULL; + pStartupInfoDup->pszUser = NULL; + pStartupInfoDup->pszPassword = NULL; + pStartupInfoDup->pszDomain = NULL; + +#define DUP_STR(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \ + } + +#define DUP_MEM(a_Str) \ + if (pStartupInfo->cb##a_Str) \ + { \ + pStartupInfoDup->psz##a_Str = (char *)RTMemDup(pStartupInfo->psz##a_Str, pStartupInfo->cb##a_Str); \ + AssertPtrBreak(pStartupInfoDup->psz##a_Str); \ + pStartupInfoDup->cb##a_Str = (uint32_t)pStartupInfo->cb##a_Str; \ + } + + DUP_STR(Cmd); + DUP_MEM(Args); + DUP_MEM(Env); + DUP_STR(User); + DUP_STR(Password); + DUP_STR(Domain); + +#undef DUP_STR +#undef DUP_MEM + + return pStartupInfoDup; + + } while (0); /* To use break macros above. */ + + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfoDup); + } + + return NULL; +} + +/** + * Retrieves a HOST_EXEC_CMD message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param ppStartupInfo Where to store the allocated session startup info. + * Needs to be free'd by VbglR3GuestCtrlProcStartupInfoFree(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLPROCSTARTUPINFO *ppStartupInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER); + + PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo + = (PVBGLR3GUESTCTRLPROCSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO)); + if (!pStartupInfo) + return VERR_NO_MEMORY; + + int rc = VbglR3GuestCtrlProcStartupInfoInit(pStartupInfo); + if (RT_FAILURE(rc)) + { + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo); + return rc; + } + + unsigned cRetries = 0; + const unsigned cMaxRetries = 32; /* Should be enough for now. */ + const unsigned cGrowthFactor = 2; /* By how much the buffers will grow if they're too small yet. */ + + do + { + LogRel(("VbglR3GuestCtrlProcGetStart: Retrieving\n")); + + HGCMMsgProcExec Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_CMD); + VbglHGCMParmPtrSet(&Msg.cmd, pStartupInfo->pszCmd, pStartupInfo->cbCmd); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.num_args, 0); + VbglHGCMParmPtrSet(&Msg.args, pStartupInfo->pszArgs, pStartupInfo->cbArgs); + VbglHGCMParmUInt32Set(&Msg.num_env, 0); + VbglHGCMParmUInt32Set(&Msg.cb_env, 0); + VbglHGCMParmPtrSet(&Msg.env, pStartupInfo->pszEnv, pStartupInfo->cbEnv); + if (pCtx->uProtocol < 2) + { + VbglHGCMParmPtrSet(&Msg.u.v1.username, pStartupInfo->pszUser, pStartupInfo->cbUser); + VbglHGCMParmPtrSet(&Msg.u.v1.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword); + VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0); + } + else + { + VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0); + VbglHGCMParmPtrSet(&Msg.u.v2.affinity, pStartupInfo->uAffinity, sizeof(pStartupInfo->uAffinity)); + } + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_FAILURE(rc)) + { + LogRel(("VbglR3GuestCtrlProcGetStart: 1 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + + if ( rc == VERR_BUFFER_OVERFLOW + && cRetries++ < cMaxRetries) + { +#define GROW_STR(a_Str, a_cbMax) \ + pStartupInfo->psz##a_Str = (char *)RTMemRealloc(pStartupInfo->psz##a_Str, \ + RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax)); \ + AssertPtrBreakStmt(pStartupInfo->psz##a_Str, VERR_NO_MEMORY); \ + pStartupInfo->cb##a_Str = RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax); + + /* We can't tell which parameter doesn't fit, so we have to resize all. */ + GROW_STR(Cmd , GUEST_PROC_MAX_CMD_LEN); + GROW_STR(Args, GUEST_PROC_MAX_ARGS_LEN); + GROW_STR(Env, GUEST_PROC_MAX_ENV_LEN); + +#undef GROW_STR + LogRel(("VbglR3GuestCtrlProcGetStart: 2 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + LogRel(("g_fVbglR3GuestCtrlHavePeekGetCancel=%RTbool\n", RT_BOOL(g_fVbglR3GuestCtrlHavePeekGetCancel))); + } + else + break; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(&pStartupInfo->fFlags); + Msg.num_args.GetUInt32(&pStartupInfo->cArgs); + Msg.num_env.GetUInt32(&pStartupInfo->cEnvVars); + Msg.cb_env.GetUInt32(&pStartupInfo->cbEnv); + if (pCtx->uProtocol < 2) + Msg.u.v1.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS); + else + { + Msg.u.v2.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS); + Msg.u.v2.priority.GetUInt32(&pStartupInfo->uPriority); + Msg.u.v2.num_affinity.GetUInt32(&pStartupInfo->cAffinity); + } + } + } while (( rc == VERR_INTERRUPTED + || rc == VERR_BUFFER_OVERFLOW) && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if (RT_SUCCESS(rc)) + { + *ppStartupInfo = pStartupInfo; + } + else + VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo); + + LogRel(("VbglR3GuestCtrlProcGetStart: Returning %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n", + rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Allocates and gets host data, based on the message ID. + * + * This will block until data becomes available. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param puPID Where to return the guest PID to retrieve output from on success. + * @param puHandle Where to return the guest process handle to retrieve output from on success. + * @param pfFlags Where to return the output flags on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puHandle, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcOutput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_GET_OUTPUT); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMMsgProcOutput, data)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.handle.GetUInt32(puHandle); + Msg.flags.GetUInt32(pfFlags); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves the input data from host which then gets sent to the started + * process (HOST_EXEC_SET_INPUT). + * + * This will block until data becomes available. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *pfFlags, + void *pvData, uint32_t cbData, + uint32_t *pcbSize) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcInput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_SET_INPUT); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.flags.GetUInt32(pfFlags); + Msg.size.GetUInt32(pcbSize); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_DIR_REMOVE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszPath, uint32_t cbPath, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgDirRemove Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_REMOVE); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_OPEN message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszFileName, uint32_t cbFileName, + char *pszAccess, uint32_t cbAccess, + char *pszDisposition, uint32_t cbDisposition, + char *pszSharing, uint32_t cbSharing, + uint32_t *puCreationMode, + uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 7, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertReturn(cbFileName, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszAccess, VERR_INVALID_POINTER); + AssertReturn(cbAccess, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDisposition, VERR_INVALID_POINTER); + AssertReturn(cbDisposition, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszSharing, VERR_INVALID_POINTER); + AssertReturn(cbSharing, VERR_INVALID_PARAMETER); + AssertPtrReturn(puCreationMode, VERR_INVALID_POINTER); + AssertPtrReturn(poffAt, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileOpen Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_OPEN); + VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName); + VbglHGCMParmPtrSet(&Msg.openmode, pszAccess, cbAccess); + VbglHGCMParmPtrSet(&Msg.disposition, pszDisposition, cbDisposition); + VbglHGCMParmPtrSet(&Msg.sharing, pszSharing, cbSharing); + VbglHGCMParmUInt32Set(&Msg.creationmode, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.creationmode.GetUInt32(puCreationMode); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_CLOSE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileClose Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_READ message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *puToRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileRead Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(puToRead); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_READ_AT message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetReadAt(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puToRead, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileReadAt Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ_AT); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.offset.GetUInt64(poffAt); + Msg.size.GetUInt32(puToRead); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_WRITE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileWrite Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(pcbSize); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_FILE_WRITE_AT message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWriteAt(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileWriteAt Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE_AT); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.size, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(pcbSize); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + + if ( rc != VERR_TOO_MUCH_DATA + || g_fVbglR3GuestCtrlHavePeekGetCancel) + return rc; + return VERR_BUFFER_OVERFLOW; +} + + +/** + * Retrieves a HOST_FILE_SEEK message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puSeekMethod, uint64_t *poffAt) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puSeekMethod, VERR_INVALID_POINTER); + AssertPtrReturn(poffAt, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileSeek Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_SEEK); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.method, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.method.GetUInt32(puSeekMethod); + Msg.offset.GetUInt64(poffAt); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_TELL message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileTell Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_TELL); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_FILE_SET_SIZE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileGetSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint64_t *pcbNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(pcbNew, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgFileSetSize Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.id32Context, HOST_MSG_FILE_SET_SIZE); + VbglHGCMParmUInt32Set(&Msg.id32Handle, 0); + VbglHGCMParmUInt64Set(&Msg.cb64NewSize, 0); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.id32Context.GetUInt32(&pCtx->uContextID); + Msg.id32Handle.GetUInt32(puHandle); + Msg.cb64NewSize.GetUInt64(pcbNew); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_EXEC_TERMINATE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetTerminate(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puPID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcTerminate Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_TERMINATE); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Retrieves a HOST_EXEC_WAIT_FOR message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puWaitFlags, uint32_t *puTimeoutMS) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + int rc; + do + { + HGCMMsgProcWaitFor Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms); + VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_WAIT_FOR); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.timeout, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.flags.GetUInt32(puWaitFlags); + Msg.timeout.GetUInt32(puTimeoutMS); + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Replies to a HOST_MSG_FILE_OPEN message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param uFileHandle File handle of opened file on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint32_t uFileHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_OPEN); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.open.handle, uFileHandle); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.open)); +} + + +/** + * Replies to a HOST_MSG_FILE_CLOSE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u)); +} + + +/** + * Sends an unexpected file handling error to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbError(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_ERROR); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u)); +} + + +/** + * Replies to a HOST_MSG_FILE_READ message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pvData Pointer to read file data from guest on success. + * @param cbData Size (in bytes) of read file data from guest on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmPtrSet(&Msg.u.read.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.read)); +} + + +/** + * Replies to a HOST_MSG_FILE_READ_AT message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param pvData Pointer to read file data from guest on success. + * @param cbData Size (in bytes) of read file data from guest on success. + * @param offNew New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbReadOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, + void *pvData, uint32_t cbData, int64_t offNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ_OFFSET); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmPtrSet(&Msg.u.ReadOffset.pvData, pvData, cbData); + VbglHGCMParmUInt64Set(&Msg.u.ReadOffset.off64New, (uint64_t)offNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.ReadOffset)); +} + + +/** + * Replies to a HOST_MSG_FILE_WRITE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.write.written, cbWritten); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.write)); +} + + +/** + * Replies to a HOST_MSG_FILE_WRITE_AT message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial. + * @param offNew New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWriteOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten, int64_t offNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt32Set(&Msg.u.WriteOffset.cb32Written, cbWritten); + VbglHGCMParmUInt64Set(&Msg.u.WriteOffset.off64New, (uint64_t)offNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.WriteOffset)); +} + + +/** + * Replies to a HOST_MSG_FILE_SEEK message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param offCurrent New offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SEEK); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.seek.offset, offCurrent); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.seek)); +} + + +/** + * Replies to a HOST_MSG_FILE_TELL message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param offCurrent Current offset (in bytes) the guest file pointer points at on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_TELL); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.tell.offset, offCurrent); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.tell)); +} + + +/** + * Replies to a HOST_MSG_FILE_SET_SIZE message. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uRc Guest rc of operation (note: IPRT-style signed int). + * @param cbNew New file size (in bytes) of the guest file on success. + */ +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t cbNew) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SET_SIZE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + VbglHGCMParmUInt64Set(&Msg.u.SetSize.cb64Size, cbNew); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.SetSize)); +} + + +/** + * Callback for reporting a guest process status (along with some other stuff) to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uStatus Status to report. Of type PROC_STS_XXX. + * @param fFlags Additional status flags, depending on the reported status. See RTPROCSTATUS. + * @param pvData Pointer to additional status data. Optional. + * @param cbData Size (in bytes) of additional status data. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, uint32_t fFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatus Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_STATUS, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Sends output (from stdout/stderr) from a running process. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uHandle Guest process handle the output belong to. + * @param fFlags Additional output flags. + * @param pvData Pointer to actual output data. + * @param cbData Size (in bytes) of output data. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID,uint32_t uHandle, uint32_t fFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcOutput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_OUTPUT, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.handle, uHandle); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Callback for reporting back the input status of a guest process to the host. + * + * @returns VBox status code. + * @param pCtx Guest control command context to use. + * @param uPID Guest process PID to report status for. + * @param uStatus Status to report. Of type INPUT_STS_XXX. + * @param fFlags Additional input flags. + * @param cbWritten Size (in bytes) of input data handled. + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, + uint32_t fFlags, uint32_t cbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatusInput Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_INPUT_STATUS, 5); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, fFlags); + VbglHGCMParmUInt32Set(&Msg.written, cbWritten); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp new file mode 100644 index 00000000..106be26e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp @@ -0,0 +1,1032 @@ +/* $Id: VBoxGuestR3LibGuestProp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#if defined(VBOX_VBGLR3_XFREE86) || defined(VBOX_VBGLR3_XORG) +# define VBOX_VBGLR3_XSERVER +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#ifndef VBOX_VBGLR3_XSERVER +# include <iprt/mem.h> +#endif +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/stdarg.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/HostServices/GuestPropertySvc.h> + +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" char* xf86strcpy(char*,const char*); +# undef strcpy +# define strcpy xf86strcpy +extern "C" void* xf86memchr(const void*,int,xf86size_t); +# undef memchr +# define memchr xf86memchr +extern "C" void* xf86memset(const void*,int,xf86size_t); +# undef memset +# define memset xf86memset + +#endif /* VBOX_VBGLR3_XFREE86 */ + +#ifdef VBOX_VBGLR3_XSERVER + +# undef RTStrEnd +# define RTStrEnd xf86RTStrEnd + +DECLINLINE(char const *) RTStrEnd(char const *pszString, size_t cchMax) +{ + /* Avoid potential issues with memchr seen in glibc. + * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */ + while (cchMax > RTSTR_MEMCHR_MAX) + { + char const *pszRet = (char const *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX); + if (RT_LIKELY(pszRet)) + return pszRet; + pszString += RTSTR_MEMCHR_MAX; + cchMax -= RTSTR_MEMCHR_MAX; + } + return (char const *)memchr(pszString, '\0', cchMax); +} + +DECLINLINE(char *) RTStrEnd(char *pszString, size_t cchMax) +{ + /* Avoid potential issues with memchr seen in glibc. + * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */ + while (cchMax > RTSTR_MEMCHR_MAX) + { + char *pszRet = (char *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX); + if (RT_LIKELY(pszRet)) + return pszRet; + pszString += RTSTR_MEMCHR_MAX; + cchMax -= RTSTR_MEMCHR_MAX; + } + return (char *)memchr(pszString, '\0', cchMax); +} + +#endif /* VBOX_VBGLR3_XSERVER */ + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Structure containing information needed to enumerate through guest + * properties. + * + * @remarks typedef in VBoxGuestLib.h. + */ +struct VBGLR3GUESTPROPENUM +{ + /** @todo add a magic and validate the handle. */ + /** The buffer containing the raw enumeration data */ + char *pchBuf; + /** The end of the buffer */ + char *pchBufEnd; + /** Pointer to the next entry to enumerate inside the buffer */ + char *pchNext; +}; + + + +/** + * Connects to the guest property service. + * + * @returns VBox status code + * @returns VERR_NOT_SUPPORTED if guest properties are not available on the host. + * @param pidClient Where to put the client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3GuestPropConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxGuestPropSvc", pidClient); + if (rc == VERR_NOT_IMPLEMENTED || rc == VERR_HGCM_SERVICE_NOT_FOUND) + rc = VERR_NOT_SUPPORTED; + return rc; +} + + +/** + * Disconnect from the guest property service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + */ +VBGLR3DECL(int) VbglR3GuestPropDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Checks if @a pszPropName exists. + * + * @returns \c true if the guest property exists, \c false if not. + * @param idClient The HGCM client ID for the guest property session. + * @param pszPropName The property name. + */ +VBGLR3DECL(bool) VbglR3GuestPropExist(uint32_t idClient, const char *pszPropName) +{ + return RT_SUCCESS(VbglR3GuestPropReadEx(idClient, pszPropName, NULL /*ppszValue*/, NULL /* ppszFlags */, NULL /* puTimestamp */)); +} + + +/** + * Write a property value. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Utf8 + * @param pszValue The value to store. Utf8. If this is NULL then + * the property will be removed. + * @param pszFlags The flags for the property + */ +VBGLR3DECL(int) VbglR3GuestPropWrite(HGCMCLIENTID idClient, const char *pszName, const char *pszValue, const char *pszFlags) +{ + int rc; + + if (pszValue != NULL) + { + GuestPropMsgSetProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 3); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSetString(&Msg.value, pszValue); + VbglHGCMParmPtrSetString(&Msg.flags, pszFlags); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + else + { + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + return rc; +} + + +/** + * Write a property value. + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValue The value to store. Must be valid UTF-8. + * If this is NULL then the property will be removed. + * + * @note if the property already exists and pszValue is not NULL then the + * property's flags field will be left unchanged + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValue(HGCMCLIENTID idClient, const char *pszName, const char *pszValue) +{ + int rc; + + if (pszValue != NULL) + { + GuestPropMsgSetPropertyValue Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 2); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSetString(&Msg.value, pszValue); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + else + { + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + } + return rc; +} + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Write a property value where the value is formatted in RTStrPrintfV fashion. + * + * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY. + * + * @param idClient The client ID returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted. + * @param va The format arguments. + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValueV(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, va_list va) +{ + /* + * Format the value and pass it on to the setter. + */ + int rc = VERR_NO_STR_MEMORY; + char *pszValue; + if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0) + { + rc = VbglR3GuestPropWriteValue(idClient, pszName, pszValue); + RTStrFree(pszValue); + } + return rc; +} + + +/** + * Write a property value where the value is formatted in RTStrPrintf fashion. + * + * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY. + * + * @param idClient The client ID returned by VbglR3InvsSvcConnect(). + * @param pszName The property to save to. Must be valid UTF-8. + * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted. + * @param ... The format arguments. + */ +VBGLR3DECL(int) VbglR3GuestPropWriteValueF(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, ...) +{ + va_list va; + va_start(va, pszValueFormat); + int rc = VbglR3GuestPropWriteValueV(idClient, pszName, pszValueFormat, va); + va_end(va); + return rc; +} +#endif /* VBOX_VBGLR3_XSERVER */ + +/** + * Retrieve a property. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, pszValue, pu64Timestamp and pszFlags + * containing valid data. + * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pcBuf is not large + * enough. In this case the size needed will be placed in + * @a pcbBufActual if it is not NULL. + * @retval VERR_NOT_FOUND if the key wasn't found. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Utf8 + * @param pvBuf A scratch buffer to store the data retrieved into. + * The returned data is only valid for it's lifetime. + * @a ppszValue will point to the start of this buffer. + * @param cbBuf The size of @a pcBuf + * @param ppszValue Where to store the pointer to the value retrieved. + * Optional. + * @param pu64Timestamp Where to store the timestamp. Optional. + * @param ppszFlags Where to store the pointer to the flags. Optional. + * @param pcbBufActual If @a pcBuf is not large enough, the size needed. + * Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropRead(HGCMCLIENTID idClient, const char *pszName, + void *pvBuf, uint32_t cbBuf, + char **ppszValue, uint64_t *pu64Timestamp, + char **ppszFlags, + uint32_t *pcbBufActual) +{ + /* + * Create the GET_PROP message and call the host. + */ + GuestPropMsgGetProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_GET_PROP, 4); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf); + VbglHGCMParmUInt64Set(&Msg.timestamp, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * The cbBufActual parameter is also returned on overflow so the call can + * adjust his/her buffer. + */ + if ( rc == VERR_BUFFER_OVERFLOW + || pcbBufActual != NULL) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual); + AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2); + } + if (RT_FAILURE(rc)) + return rc; + + /* + * Buffer layout: Value\0Flags\0. + * + * If the caller cares about any of these strings, make sure things are + * properly terminated (paranoia). + */ + if ( RT_SUCCESS(rc) + && (ppszValue != NULL || ppszFlags != NULL)) + { + /* Validate / skip 'Name'. */ + char *pszFlags = RTStrEnd((char *)pvBuf, cbBuf) + 1; + AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA); + if (ppszValue) + *ppszValue = (char *)pvBuf; + + if (ppszFlags) + { + /* Validate 'Flags'. */ + char *pszEos = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf)); + AssertPtrReturn(pszEos, VERR_TOO_MUCH_DATA); + *ppszFlags = pszFlags; + } + } + + /* And the timestamp, if requested. */ + if (pu64Timestamp != NULL) + { + rc = VbglHGCMParmUInt64Get(&Msg.timestamp, pu64Timestamp); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} + +/** + * Reads a guest property by returning allocated values. + * + * @returns VBox status code, fully bitched. + * + * @param idClient The HGCM client ID for the guest property session. + * @param pszPropName The property name. + * @param ppszValue Where to return the value. This is always set + * to NULL. Needs to be free'd using RTStrFree(). Optional. + * @param ppszFlags Where to return the value flags. + * Needs to be free'd using RTStrFree(). Optional. + * @param puTimestamp Where to return the timestamp. This is only set + * on success. Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropReadEx(uint32_t idClient, + const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) +{ + AssertPtrReturn(pszPropName, VERR_INVALID_POINTER); + + uint32_t cbBuf = _1K; + void *pvBuf = NULL; + int rc = VINF_SUCCESS; /* MSC can't figure out the loop */ + + if (ppszValue) + *ppszValue = NULL; + + for (unsigned cTries = 0; cTries < 10; cTries++) + { + /* + * (Re-)Allocate the buffer and try read the property. + */ + RTMemFree(pvBuf); + pvBuf = RTMemAlloc(cbBuf); + if (!pvBuf) + { + rc = VERR_NO_MEMORY; + break; + } + char *pszValue; + char *pszFlags; + uint64_t uTimestamp; + rc = VbglR3GuestPropRead(idClient, pszPropName, pvBuf, cbBuf, &pszValue, &uTimestamp, &pszFlags, NULL); + if (RT_FAILURE(rc)) + { + if (rc == VERR_BUFFER_OVERFLOW) + { + /* try again with a bigger buffer. */ + cbBuf *= 2; + continue; + } + break; + } + + if (ppszValue) + { + *ppszValue = RTStrDup(pszValue); + if (!*ppszValue) + { + rc = VERR_NO_MEMORY; + break; + } + } + + if (puTimestamp) + *puTimestamp = uTimestamp; + if (ppszFlags) + *ppszFlags = RTStrDup(pszFlags); + break; /* done */ + } + + if (pvBuf) + RTMemFree(pvBuf); + return rc; +} + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Retrieve a property value, allocating space for it. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, *ppszValue containing valid data. + * @retval VERR_NOT_FOUND if the key wasn't found. + * @retval VERR_TOO_MUCH_DATA if we were unable to determine the right size + * to allocate for the buffer. This can happen as the result of a + * race between our allocating space and the host changing the + * property value. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Must be valid UTF-8. + * @param ppszValue Where to store the pointer to the value returned. + * This is always set to NULL or to the result, even + * on failure. + */ +VBGLR3DECL(int) VbglR3GuestPropReadValueAlloc(HGCMCLIENTID idClient, const char *pszName, char **ppszValue) +{ + /* + * Quick input validation. + */ + AssertPtr(ppszValue); + *ppszValue = NULL; + AssertPtrReturn(pszName, VERR_INVALID_PARAMETER); + + /* + * There is a race here between our reading the property size and the + * host changing the value before we read it. Try up to ten times and + * report the problem if that fails. + */ + char *pszValue = NULL; + void *pvBuf = NULL; + uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN; + int rc = VERR_BUFFER_OVERFLOW; + for (unsigned i = 0; i < 10 && rc == VERR_BUFFER_OVERFLOW; ++i) + { + /* We leave a bit of space here in case the maximum value is raised. */ + cbBuf += 1024; + void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf); + if (pvTmpBuf) + { + pvBuf = pvTmpBuf; + rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cbBuf, &pszValue, NULL, NULL, &cbBuf); + } + else + rc = VERR_NO_MEMORY; + } + if (RT_SUCCESS(rc)) + { + Assert(pszValue == (char *)pvBuf); + *ppszValue = pszValue; + } + else + { + RTMemFree(pvBuf); + if (rc == VERR_BUFFER_OVERFLOW) + /* VERR_BUFFER_OVERFLOW has a different meaning here as a + * return code, but we need to report the race. */ + rc = VERR_TOO_MUCH_DATA; + } + + return rc; +} + + +/** + * Free the memory used by VbglR3GuestPropReadValueAlloc for returning a + * value. + * + * @param pszValue the memory to be freed. NULL pointers will be ignored. + */ +VBGLR3DECL(void) VbglR3GuestPropReadValueFree(char *pszValue) +{ + RTMemFree(pszValue); +} +#endif /* VBOX_VBGLR3_XSERVER */ + +/** + * Retrieve a property value, using a user-provided buffer to store it. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, pszValue containing valid data. + * @retval VERR_BUFFER_OVERFLOW and the size needed in pcchValueActual if the + * buffer provided was too small + * @retval VERR_NOT_FOUND if the key wasn't found. + * + * @note There is a race here between obtaining the size of the buffer + * needed to hold the value and the value being updated. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszName The value to read. Utf8 + * @param pszValue Where to store the value retrieved. + * @param cchValue The size of the buffer pointed to by @a pszValue + * @param pcchValueActual Where to store the size of the buffer needed if + * the buffer supplied is too small. Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropReadValue(HGCMCLIENTID idClient, const char *pszName, + char *pszValue, uint32_t cchValue, + uint32_t *pcchValueActual) +{ + void *pvBuf = pszValue; + uint32_t cchValueActual; + int rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cchValue, &pszValue, NULL, NULL, &cchValueActual); + if (pcchValueActual != NULL) + *pcchValueActual = cchValueActual; + return rc; +} + + +#ifndef VBOX_VBGLR3_XSERVER +/** + * Raw API for enumerating guest properties which match a given pattern. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success and pcBuf points to a packed array + * of the form \<name\>, \<value\>, \<timestamp string\>, \<flags\>, + * terminated by four empty strings. pcbBufActual will contain the + * total size of the array. + * @retval VERR_BUFFER_OVERFLOW if the buffer provided was too small. In + * this case pcbBufActual will contain the size of the buffer needed. + * @returns IPRT error code in other cases, and pchBufActual is undefined. + * + * @param idClient The client ID returned by VbglR3GuestPropConnect + * @param pszzPatterns A packed array of zero terminated strings, terminated + * by an empty string. + * @param pcBuf The buffer to store the results to. + * @param cbBuf The size of the buffer + * @param pcbBufActual Where to store the size of the returned data on + * success or the buffer size needed if @a pcBuf is too + * small. + */ +VBGLR3DECL(int) VbglR3GuestPropEnumRaw(HGCMCLIENTID idClient, + const char *pszzPatterns, + char *pcBuf, + uint32_t cbBuf, + uint32_t *pcbBufActual) +{ + GuestPropMsgEnumProperties Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3); + + /* Get the length of the patterns array... */ + size_t cchPatterns = 0; + for (size_t cchCurrent = strlen(pszzPatterns); cchCurrent != 0; + cchCurrent = strlen(pszzPatterns + cchPatterns)) + cchPatterns += cchCurrent + 1; + /* ...including the terminator. */ + ++cchPatterns; + VbglHGCMParmPtrSet(&Msg.patterns, (char *)pszzPatterns, (uint32_t)cchPatterns); + VbglHGCMParmPtrSet(&Msg.strings, pcBuf, cbBuf); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if ( pcbBufActual + && ( RT_SUCCESS(rc) + || rc == VERR_BUFFER_OVERFLOW)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual); + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Start enumerating guest properties which match a given pattern. + * + * This function creates a handle which can be used to continue enumerating. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, *ppHandle points to a handle for continuing + * the enumeration and *ppszName, *ppszValue, *pu64Timestamp and + * *ppszFlags are set. + * @retval VERR_TOO_MUCH_DATA if it was not possible to determine the amount + * of local space needed to store all the enumeration data. This is + * due to a race between allocating space and the host adding new + * data, so retrying may help here. Other parameters are left + * uninitialised + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param papszPatterns The patterns against which the properties are + * matched. Pass NULL if everything should be matched. + * @param cPatterns The number of patterns in @a papszPatterns. 0 means + * match everything. + * @param ppHandle where the handle for continued enumeration is stored + * on success. This must be freed with + * VbglR3GuestPropEnumFree when it is no longer needed. + * @param ppszName Where to store the next property name. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param ppszValue Where to store the next property value. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param pu64Timestamp Where to store the next property timestamp. This + * will be set to zero if there are no more properties + * to enumerate. Optional. + * @param ppszFlags Where to store the next property flags. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * + * @remarks While all output parameters are optional, you need at least one to + * figure out when to stop. + */ +VBGLR3DECL(int) VbglR3GuestPropEnum(HGCMCLIENTID idClient, + char const * const *papszPatterns, + uint32_t cPatterns, + PVBGLR3GUESTPROPENUM *ppHandle, + char const **ppszName, + char const **ppszValue, + uint64_t *pu64Timestamp, + char const **ppszFlags) +{ + /* Create the handle. */ + PVBGLR3GUESTPROPENUM pHandle = (PVBGLR3GUESTPROPENUM)RTMemAllocZ(sizeof(VBGLR3GUESTPROPENUM)); + if (RT_LIKELY(pHandle)) + {/* likely */} + else + return VERR_NO_MEMORY; + + /* Get the length of the pattern string, including the final terminator. */ + size_t cbPatterns = 1; + for (uint32_t i = 0; i < cPatterns; ++i) + cbPatterns += strlen(papszPatterns[i]) + 1; + + /* Pack the pattern array. */ + char *pszzPatterns = (char *)RTMemAlloc(cbPatterns); + size_t off = 0; + for (uint32_t i = 0; i < cPatterns; ++i) + { + size_t cb = strlen(papszPatterns[i]) + 1; + memcpy(&pszzPatterns[off], papszPatterns[i], cb); + off += cb; + } + pszzPatterns[off] = '\0'; + + /* In reading the guest property data we are racing against the host + * adding more of it, so loop a few times and retry on overflow. */ + uint32_t cbBuf = 4096; /* picked out of thin air */ + char *pchBuf = NULL; + int rc = VINF_SUCCESS; + for (int i = 0; i < 10; ++i) + { + void *pvNew = RTMemRealloc(pchBuf, cbBuf); + if (pvNew) + pchBuf = (char *)pvNew; + else + { + rc = VERR_NO_MEMORY; + break; + } + rc = VbglR3GuestPropEnumRaw(idClient, pszzPatterns, pchBuf, cbBuf, &cbBuf); + if (rc != VERR_BUFFER_OVERFLOW) + break; + cbBuf += 4096; /* Just to increase our chances */ + } + RTMemFree(pszzPatterns); + if (RT_SUCCESS(rc)) + { + /* + * Complete the handle and call VbglR3GuestPropEnumNext to retrieve the first entry. + */ + pHandle->pchNext = pchBuf; + pHandle->pchBuf = pchBuf; + pHandle->pchBufEnd = pchBuf + cbBuf; + + const char *pszNameTmp; + if (!ppszName) + ppszName = &pszNameTmp; + rc = VbglR3GuestPropEnumNext(pHandle, ppszName, ppszValue, pu64Timestamp, ppszFlags); + if (RT_SUCCESS(rc)) + { + *ppHandle = pHandle; + return rc; + } + } + else if (rc == VERR_BUFFER_OVERFLOW) + rc = VERR_TOO_MUCH_DATA; + RTMemFree(pchBuf); + RTMemFree(pHandle); + return rc; +} + + +/** + * Get the next guest property. + * + * See @a VbglR3GuestPropEnum. + * + * @returns VBox status code. + * + * @param pHandle Handle obtained from @a VbglR3GuestPropEnum. + * @param ppszName Where to store the next property name. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param ppszValue Where to store the next property value. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * @param pu64Timestamp Where to store the next property timestamp. This + * will be set to zero if there are no more properties + * to enumerate. Optional. + * @param ppszFlags Where to store the next property flags. This will be + * set to NULL if there are no more properties to + * enumerate. This pointer should not be freed. Optional. + * + * @remarks While all output parameters are optional, you need at least one to + * figure out when to stop. + */ +VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle, + char const **ppszName, + char const **ppszValue, + uint64_t *pu64Timestamp, + char const **ppszFlags) +{ + /* + * The VBGLR3GUESTPROPENUM structure contains a buffer containing the raw + * properties data and a pointer into the buffer which tracks how far we + * have parsed so far. The buffer contains packed strings in groups of + * four - name, value, timestamp (as a decimal string) and flags. It is + * terminated by four empty strings. We can rely on this layout unless + * the caller has been poking about in the structure internals, in which + * case they must take responsibility for the results. + * + * Layout: + * Name\0Value\0Timestamp\0Flags\0 + */ + char *pchNext = pHandle->pchNext; /* The cursor. */ + char *pchEnd = pHandle->pchBufEnd; /* End of buffer, for size calculations. */ + + char *pszName = pchNext; + char *pszValue = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); /* 0x1 is also an invalid pointer :) */ + + char *pszTimestamp = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + char *pszFlags = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + /* + * Don't move the index pointer if we found the terminating "\0\0\0\0" entry. + * Don't try convert the timestamp either. + */ + uint64_t u64Timestamp; + if (*pszName != '\0') + { + pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1; + AssertPtrReturn(pchNext, VERR_PARSE_ERROR); + + /* Convert the timestamp string into a number. */ + int rc = RTStrToUInt64Full(pszTimestamp, 0, &u64Timestamp); + AssertRCSuccessReturn(rc, VERR_PARSE_ERROR); + + pHandle->pchNext = pchNext; + AssertPtr(pchNext); + } + else + { + u64Timestamp = 0; + AssertMsgReturn(!*pszValue && !*pszTimestamp && !*pszFlags, + ("'%s' '%s' '%s'\n", pszValue, pszTimestamp, pszFlags), + VERR_PARSE_ERROR); + } + + /* + * Everything is fine, set the return values. + */ + if (ppszName) + *ppszName = *pszName != '\0' ? pszName : NULL; + if (ppszValue) + *ppszValue = *pszValue != '\0' ? pszValue : NULL; + if (pu64Timestamp) + *pu64Timestamp = u64Timestamp; + if (ppszFlags) + *ppszFlags = *pszFlags != '\0' ? pszFlags : NULL; + return VINF_SUCCESS; +} + + +/** + * Free an enumeration handle returned by @a VbglR3GuestPropEnum. + * @param pHandle the handle to free + */ +VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle) +{ + if (!pHandle) + return; + RTMemFree(pHandle->pchBuf); + RTMemFree(pHandle); +} + + +/** + * Deletes a guest property. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to delete. Utf8 + */ +VBGLR3DECL(int) VbglR3GuestPropDelete(HGCMCLIENTID idClient, const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + + GuestPropMsgDelProperty Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1); + VbglHGCMParmPtrSetString(&Msg.name, pszName); + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Deletes a set of keys. + * + * The set is specified in the same way as for VbglR3GuestPropEnum. + * + * @returns VBox status code. Stops on first failure. + * See also VbglR3GuestPropEnum. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param papszPatterns The patterns against which the properties are + * matched. Pass NULL if everything should be matched. + * @param cPatterns The number of patterns in @a papszPatterns. 0 means + * match everything. + */ +VBGLR3DECL(int) VbglR3GuestPropDelSet(HGCMCLIENTID idClient, + const char * const *papszPatterns, + uint32_t cPatterns) +{ + PVBGLR3GUESTPROPENUM pHandle; + char const *pszName, *pszValue, *pszFlags; + uint64_t pu64Timestamp; + int rc = VbglR3GuestPropEnum(idClient, + (char **)papszPatterns, /** @todo fix this cast. */ + cPatterns, + &pHandle, + &pszName, + &pszValue, + &pu64Timestamp, + &pszFlags); + + while (RT_SUCCESS(rc) && pszName) + { + rc = VbglR3GuestPropWriteValue(idClient, pszName, NULL); + if (RT_FAILURE(rc)) + break; + + rc = VbglR3GuestPropEnumNext(pHandle, + &pszName, + &pszValue, + &pu64Timestamp, + &pszFlags); + } + + VbglR3GuestPropEnumFree(pHandle); + return rc; +} + + +/** + * Wait for notification of changes to a guest property. If this is called in + * a loop, the timestamp of the last notification seen can be passed as a + * parameter to be sure that no notifications are missed. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success, @a ppszName, @a ppszValue, + * @a pu64Timestamp and @a ppszFlags containing valid data. + * @retval VINF_NOT_FOUND if no previous notification could be found with the + * timestamp supplied. This will normally mean that a large number + * of notifications occurred in between. + * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pvBuf is not large + * enough. In this case the size needed will be placed in + * @a pcbBufActual if it is not NULL. + * @retval VERR_TIMEOUT if a timeout occurred before a notification was seen. + * + * @param idClient The client id returned by VbglR3GuestPropConnect(). + * @param pszPatterns The patterns that the property names must matchfor + * the change to be reported. + * @param pvBuf A scratch buffer to store the data retrieved into. + * The returned data is only valid for it's lifetime. + * @a ppszValue will point to the start of this buffer. + * @param cbBuf The size of @a pvBuf + * @param u64Timestamp The timestamp of the last event seen. Pass zero + * to wait for the next event. + * @param cMillies Timeout in milliseconds. Use RT_INDEFINITE_WAIT + * to wait indefinitely. + * @param ppszName Where to store the pointer to the name retrieved. + * Optional. + * @param ppszValue Where to store the pointer to the value retrieved. + * Optional. + * @param pu64Timestamp Where to store the timestamp. Optional. + * @param ppszFlags Where to store the pointer to the flags. Optional. + * @param pcbBufActual If @a pcBuf is not large enough, the size needed. + * Optional. + * @param pfWasDeleted A flag which indicates that property was deleted. + * Optional. + */ +VBGLR3DECL(int) VbglR3GuestPropWait(HGCMCLIENTID idClient, + const char *pszPatterns, + void *pvBuf, uint32_t cbBuf, + uint64_t u64Timestamp, uint32_t cMillies, + char ** ppszName, char **ppszValue, + uint64_t *pu64Timestamp, char **ppszFlags, + uint32_t *pcbBufActual, bool *pfWasDeleted) +{ + /* + * Create the GET_NOTIFICATION message and call the host. + */ + GuestPropMsgGetNotification Msg; + VBGL_HGCM_HDR_INIT_TIMED(&Msg.hdr, idClient, GUEST_PROP_FN_GET_NOTIFICATION, 4, cMillies); + + VbglHGCMParmPtrSetString(&Msg.patterns, pszPatterns); + RT_BZERO(pvBuf, cbBuf); + VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf); + VbglHGCMParmUInt64Set(&Msg.timestamp, u64Timestamp); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + /* + * The cbBufActual parameter is also returned on overflow so the caller can + * adjust their buffer. + */ + if ( rc == VERR_BUFFER_OVERFLOW + && pcbBufActual != NULL) + { + int rc2 = Msg.size.GetUInt32(pcbBufActual); + AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2); + } + if (RT_FAILURE(rc)) + return rc; + + /* + * Buffer layout: Name\0Value\0Flags\0fWasDeleted\0. + * + * If the caller cares about any of these strings, make sure things are + * properly terminated (paranoia). + */ + if ( RT_SUCCESS(rc) + && (ppszName != NULL || ppszValue != NULL || ppszFlags != NULL || pfWasDeleted != NULL)) + { + /* Validate / skip 'Name'. */ + char *pszValue = RTStrEnd((char *)pvBuf, cbBuf) + 1; + AssertPtrReturn(pszValue, VERR_TOO_MUCH_DATA); + if (ppszName) + *ppszName = (char *)pvBuf; + + /* Validate / skip 'Value'. */ + char *pszFlags = RTStrEnd(pszValue, cbBuf - (pszValue - (char *)pvBuf)) + 1; + AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA); + if (ppszValue) + *ppszValue = pszValue; + + if (ppszFlags) + *ppszFlags = pszFlags; + + /* Skip 'Flags' and deal with 'fWasDeleted' if it's present. */ + char *pszWasDeleted = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf)) + 1; + AssertPtrReturn(pszWasDeleted, VERR_TOO_MUCH_DATA); + char chWasDeleted = 0; + if ( (size_t)pszWasDeleted - (size_t)pvBuf < cbBuf + && (chWasDeleted = *pszWasDeleted) != '\0') + AssertMsgReturn((chWasDeleted == '0' || chWasDeleted == '1') && pszWasDeleted[1] == '\0', + ("'%s'\n", pszWasDeleted), VERR_PARSE_ERROR); + if (pfWasDeleted) + *pfWasDeleted = chWasDeleted == '1'; + } + + /* And the timestamp, if requested. */ + if (pu64Timestamp != NULL) + { + rc = Msg.timestamp.GetUInt64(pu64Timestamp); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} +#endif /* VBOX_VBGLR3_XSERVER */ diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp new file mode 100644 index 00000000..afa280fa --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp @@ -0,0 +1,119 @@ +/* $Id: VBoxGuestR3LibGuestUser.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * guest user reporting / utility functions. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <VBox/log.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Reports a state change of a specific guest user. + * + * @returns IPRT status value + * @param pszUser Guest user name to report state for. + * @param pszDomain Domain the guest user's account is bound to. + * @param enmState Guest user state to report. + * @param puDetails Pointer to state details. Optional. + * @param cbDetails Size (in bytes) of state details. Pass 0 + * if puDetails is NULL. + */ +VBGLR3DECL(int) VbglR3GuestUserReportState(const char *pszUser, const char *pszDomain, VBoxGuestUserState enmState, + uint8_t *puDetails, uint32_t cbDetails) +{ + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + /* pszDomain is optional. */ + /* puDetails is optional. */ + AssertReturn(cbDetails == 0 || puDetails != NULL, VERR_INVALID_PARAMETER); + AssertReturn(cbDetails < 16U*_1M, VERR_OUT_OF_RANGE); + + uint32_t cbBase = sizeof(VMMDevReportGuestUserState); + uint32_t cbUser = (uint32_t)strlen(pszUser) + 1; /* Include terminating zero */ + uint32_t cbDomain = pszDomain ? (uint32_t)strlen(pszDomain) + 1 /* Ditto */ : 0; + + /* Allocate enough space for all fields. */ + uint32_t cbSize = cbBase + + cbUser + + cbDomain + + cbDetails; + VMMDevReportGuestUserState *pReport = (VMMDevReportGuestUserState *)RTMemAllocZ(cbSize); + if (!pReport) + return VERR_NO_MEMORY; + + int rc = vmmdevInitRequest(&pReport->header, VMMDevReq_ReportGuestUserState); + if (RT_SUCCESS(rc)) + { + pReport->header.size = cbSize; + + pReport->status.state = enmState; + pReport->status.cbUser = cbUser; + pReport->status.cbDomain = cbDomain; + pReport->status.cbDetails = cbDetails; + + /* + * Note: cbOffDynamic contains the first dynamic array entry within + * VBoxGuestUserStatus. + * Therefore it's vital to *not* change the order of the struct members + * without altering this code. Don't try this at home. + */ + uint32_t cbOffDynamic = RT_UOFFSETOF(VBoxGuestUserStatus, szUser); + + /* pDynamic marks the beginning for the dynamically allocated areas. */ + uint8_t *pDynamic = (uint8_t *)&pReport->status; + pDynamic += cbOffDynamic; + AssertPtr(pDynamic); + + memcpy(pDynamic, pszUser, cbUser); + if (cbDomain) + memcpy(pDynamic + cbUser, pszDomain, cbDomain); + if (cbDetails) + memcpy(pDynamic + cbUser + cbDomain, puDetails, cbDetails); + + rc = vbglR3GRPerform(&pReport->header); + } + + RTMemFree(pReport); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp new file mode 100644 index 00000000..8053f0be --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp @@ -0,0 +1,108 @@ +/* $Id: VBoxGuestR3LibHGCM.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * generic HGCM. + */ + +/* + * Copyright (C) 2015-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR3LibInternal.h" +#include <VBox/VBoxGuestLib.h> +#include <iprt/string.h> + + +/** + * Connects to an HGCM service. + * + * @returns VBox status code + * @param pszServiceName Name of the host service. + * @param pidClient Where to put the client ID on success. The client ID + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient) +{ + AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER); + AssertPtrReturn(pidClient, VERR_INVALID_POINTER); + + VBGLIOCHGCMCONNECT Info; + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT); + Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + int rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName); + if (RT_FAILURE(rc)) + return rc; + rc = vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info)); + if (RT_SUCCESS(rc)) + *pidClient = Info.u.Out.idClient; + return rc; +} + + +/** + * Disconnect from an HGCM service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3HGCMConnect(). + */ +VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient) +{ + VBGLIOCHGCMDISCONNECT Info; + VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT); + Info.u.In.idClient = idClient; + + return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info)); +} + + +/** + * Makes a fully prepared HGCM call. + * + * @returns VBox status code. + * @param pInfo Fully prepared HGCM call info. + * @param cbInfo Size of the info. This may sometimes be larger than + * what the parameter count indicates because of + * parameter changes between versions and such. + */ +VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo) +{ + /* Expect caller to have filled in pInfo. */ + AssertMsg(pInfo->Hdr.cbIn == cbInfo, ("cbIn=%#x cbInfo=%#zx\n", pInfo->Hdr.cbIn, cbInfo)); + AssertMsg(pInfo->Hdr.cbOut == cbInfo, ("cbOut=%#x cbInfo=%#zx\n", pInfo->Hdr.cbOut, cbInfo)); + Assert(sizeof(*pInfo) + pInfo->cParms * sizeof(HGCMFunctionParameter) <= cbInfo); + Assert(pInfo->u32ClientID != 0); + + return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CALL(cbInfo), &pInfo->Hdr, cbInfo); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp new file mode 100644 index 00000000..9c9e377e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp @@ -0,0 +1,235 @@ +/* $Id: VBoxGuestR3LibHostChannel.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Host Channel. + */ + +/* + * 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 + */ + + +#include <iprt/mem.h> + +#include <VBox/HostServices/VBoxHostChannel.h> + +#include "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3HostChannelInit(uint32_t *pidClient) +{ + return VbglR3HGCMConnect("VBoxHostChannel", pidClient); +} + +VBGLR3DECL(void) VbglR3HostChannelTerm(uint32_t idClient) +{ + VbglR3HGCMDisconnect(idClient); +} + +VBGLR3DECL(int) VbglR3HostChannelAttach(uint32_t *pu32ChannelHandle, + uint32_t u32HGCMClientId, + const char *pszName, + uint32_t u32Flags) +{ + /* Make a heap copy of the name, because HGCM can not use some of other memory types. */ + size_t cbName = strlen(pszName) + 1; + char *pszCopy = (char *)RTMemAlloc(cbName); + if (pszCopy == NULL) + { + return VERR_NO_MEMORY; + } + + memcpy(pszCopy, pszName, cbName); + + VBoxHostChannelAttach parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_ATTACH, 3); + VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName); + VbglHGCMParmUInt32Set(&parms.flags, u32Flags); + VbglHGCMParmUInt32Set(&parms.handle, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + *pu32ChannelHandle = parms.handle.u.value32; + + RTMemFree(pszCopy); + + return rc; +} + +VBGLR3DECL(void) VbglR3HostChannelDetach(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId) +{ + VBoxHostChannelDetach parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_DETACH, 1); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + + VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelSend(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + void *pvData, + uint32_t cbData) +{ + VBoxHostChannelSend parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_SEND, 2); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + + return VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelRecv(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeReceived, + uint32_t *pu32SizeRemaining) +{ + VBoxHostChannelRecv parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_RECV, 4); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeReceived, 0); + VbglHGCMParmUInt32Set(&parms.sizeRemaining, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeReceived = parms.sizeReceived.u.value32; + *pu32SizeRemaining = parms.sizeRemaining.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelControl(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + VBoxHostChannelControl parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_CONTROL, 5); + VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle); + VbglHGCMParmUInt32Set(&parms.code, u32Code); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeDataReturned = parms.sizeDataReturned.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelEventWait(uint32_t *pu32ChannelHandle, + uint32_t u32HGCMClientId, + uint32_t *pu32EventId, + void *pvParm, + uint32_t cbParm, + uint32_t *pu32SizeReturned) +{ + VBoxHostChannelEventWait parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_WAIT, 4); + VbglHGCMParmUInt32Set(&parms.handle, 0); + VbglHGCMParmUInt32Set(&parms.id, 0); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmUInt32Set(&parms.sizeReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32ChannelHandle = parms.handle.u.value32; + *pu32EventId = parms.id.u.value32; + *pu32SizeReturned = parms.sizeReturned.u.value32; + } + + return rc; +} + +VBGLR3DECL(int) VbglR3HostChannelEventCancel(uint32_t u32ChannelHandle, + uint32_t u32HGCMClientId) +{ + RT_NOREF1(u32ChannelHandle); + + VBoxHostChannelEventCancel parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_CANCEL, 0); + + return VbglR3HGCMCall(&parms.hdr, sizeof(parms)); +} + +VBGLR3DECL(int) VbglR3HostChannelQuery(const char *pszName, + uint32_t u32HGCMClientId, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + /* Make a heap copy of the name, because HGCM can not use some of other memory types. */ + size_t cbName = strlen(pszName) + 1; + char *pszCopy = (char *)RTMemAlloc(cbName); + if (pszCopy == NULL) + { + return VERR_NO_MEMORY; + } + + memcpy(pszCopy, pszName, cbName); + + VBoxHostChannelQuery parms; + VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_QUERY, 5); + VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName); + VbglHGCMParmUInt32Set(&parms.code, u32Code); + VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm); + VbglHGCMParmPtrSet(&parms.data, pvData, cbData); + VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0); + + int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms)); + + if (RT_SUCCESS(rc)) + { + *pu32SizeDataReturned = parms.sizeDataReturned.u.value32; + } + + RTMemFree(pszCopy); + + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp new file mode 100644 index 00000000..f3d7740f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp @@ -0,0 +1,226 @@ +/* $Id: */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, host version check. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/string.h> +#include <VBox/log.h> + +#ifdef RT_OS_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <iprt/win/windows.h> +#endif + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Checks for a Guest Additions update by comparing the installed version on the + * guest and the reported host version. + * + * @returns VBox status code + * + * @param idClient The client id returned by + * VbglR3InfoSvcConnect(). + * @param pfUpdate Receives pointer to boolean flag indicating + * whether an update was found or not. + * @param ppszHostVersion Receives pointer of allocated version string. + * The returned pointer must be freed using + * VbglR3GuestPropReadValueFree(). Always set to + * NULL. + * @param ppszGuestVersion Receives pointer of allocated revision string. + * The returned pointer must be freed using + * VbglR3GuestPropReadValueFree(). Always set to + * NULL. + */ +VBGLR3DECL(int) VbglR3HostVersionCheckForUpdate(HGCMCLIENTID idClient, bool *pfUpdate, char **ppszHostVersion, char **ppszGuestVersion) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(pfUpdate); + AssertPtr(ppszHostVersion); + AssertPtr(ppszGuestVersion); + + *ppszHostVersion = NULL; + *ppszGuestVersion = NULL; + + /* We assume we have an update initially. + Every block down below is allowed to veto */ + *pfUpdate = true; + + /* Do we need to do all this stuff? */ + char *pszCheckHostVersion; + int rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/CheckHostVersion", &pszCheckHostVersion); + if (RT_FAILURE(rc)) + { + if (rc == VERR_NOT_FOUND) + rc = VINF_SUCCESS; /* If we don't find the value above we do the check by default */ + else + LogFlow(("Could not read check host version flag! rc = %Rrc\n", rc)); + } + else + { + /* Only don't do the check if we have a valid "0" in it */ + if (!strcmp(pszCheckHostVersion, "0")) + { + LogRel(("No host version update check performed (disabled).\n")); + *pfUpdate = false; + } + VbglR3GuestPropReadValueFree(pszCheckHostVersion); + } + + /* Collect all needed information */ + /* Make sure we only notify the user once by comparing the host version with + * the last checked host version (if any) */ + if (RT_SUCCESS(rc) && *pfUpdate) + { + /* Look up host version */ + rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/HostInfo/VBoxVer", ppszHostVersion); + if (RT_FAILURE(rc)) + { + LogFlow(("Could not read VBox host version! rc = %Rrc\n", rc)); + } + else + { + LogFlow(("Host version: %s\n", *ppszHostVersion)); + + /* Get last checked host version */ + char *pszLastCheckedHostVersion; + rc = VbglR3HostVersionLastCheckedLoad(idClient, &pszLastCheckedHostVersion); + if (RT_SUCCESS(rc)) + { + LogFlow(("Last checked host version: %s\n", pszLastCheckedHostVersion)); + if (strcmp(*ppszHostVersion, pszLastCheckedHostVersion) == 0) + *pfUpdate = false; /* We already notified this version, skip */ + VbglR3GuestPropReadValueFree(pszLastCheckedHostVersion); + } + else if (rc == VERR_NOT_FOUND) /* Never wrote a last checked host version before */ + { + LogFlow(("Never checked a host version before.\n")); + rc = VINF_SUCCESS; + } + } + + /* Look up guest version */ + if (RT_SUCCESS(rc)) + { + rc = VbglR3GetAdditionsVersion(ppszGuestVersion, NULL /* Extended version not needed here */, + NULL /* Revision not needed here */); + if (RT_FAILURE(rc)) + LogFlow(("Could not read VBox guest version! rc = %Rrc\n", rc)); + } + } + + /* Do the actual version comparison (if needed, see block(s) above) */ + if (RT_SUCCESS(rc) && *pfUpdate) + { + if (RTStrVersionCompare(*ppszHostVersion, *ppszGuestVersion) > 0) /* Is host version greater than guest add version? */ + { + /* Yay, we have an update! */ + LogRel(("Guest Additions update found! Please upgrade this machine to the latest Guest Additions.\n")); + } + else + { + /* How sad ... */ + *pfUpdate = false; + } + } + + /* Cleanup on failure */ + if (RT_FAILURE(rc)) + { + if (*ppszHostVersion) + { + VbglR3GuestPropReadValueFree(*ppszHostVersion); + *ppszHostVersion = NULL; + } + if (*ppszGuestVersion) + { + VbglR3GuestPropReadValueFree(*ppszGuestVersion); + *ppszGuestVersion = NULL; + } + } + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, pfUpdate, ppszHostVersion, ppszGuestVersion); + return VERR_NOT_SUPPORTED; +#endif +} + + +/** Retrieves the last checked host version. + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param ppszVer Receives pointer of allocated version string. + * The returned pointer must be freed using RTStrFree() on VINF_SUCCESS. + */ +VBGLR3DECL(int) VbglR3HostVersionLastCheckedLoad(HGCMCLIENTID idClient, char **ppszVer) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(ppszVer); + return VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", ppszVer); +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, ppszVer); + return VERR_NOT_SUPPORTED; +#endif +} + + +/** Stores the last checked host version for later lookup. + * Requires strings in form of "majorVer.minorVer.build". + * + * @returns VBox status code. + * + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param pszVer Pointer to version string to store. + */ +VBGLR3DECL(int) VbglR3HostVersionLastCheckedStore(HGCMCLIENTID idClient, const char *pszVer) +{ +#ifdef VBOX_WITH_GUEST_PROPS + Assert(idClient > 0); + AssertPtr(pszVer); + return VbglR3GuestPropWriteValue(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", pszVer); +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idClient, pszVer); + return VERR_NOT_SUPPORTED; +#endif +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h new file mode 100644 index 00000000..fb97ace3 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h @@ -0,0 +1,131 @@ +/* $Id: VBoxGuestR3LibInternal.h $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 support library for the guest additions, Internal header. + */ + +/* + * 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 + */ + +#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h +#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/VMMDev.h> +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +typedef unsigned long xf86size_t; +extern "C" xf86size_t xf86strlen(const char*); +# undef strlen +# define strlen xf86strlen +#endif /* VBOX_VBGLR3_XFREE86 */ + +RT_C_DECLS_BEGIN + +int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq); +int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq); +int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType); +int vbglR3GRPerform(VMMDevRequestHeader *pReq); +void vbglR3GRFree(VMMDevRequestHeader *pReq); + + + +DECLINLINE(void) VbglHGCMParmUInt32Set(HGCMFunctionParameter *pParm, uint32_t u32) +{ + pParm->type = VMMDevHGCMParmType_32bit; + pParm->u.value64 = 0; /* init unused bits to 0 */ + pParm->u.value32 = u32; +} + + +DECLINLINE(int) VbglHGCMParmUInt32Get(HGCMFunctionParameter *pParm, uint32_t *pu32) +{ + if (pParm->type == VMMDevHGCMParmType_32bit) + { + *pu32 = pParm->u.value32; + return VINF_SUCCESS; + } + *pu32 = UINT32_MAX; /* shut up gcc */ + return VERR_WRONG_PARAMETER_TYPE; +} + + +DECLINLINE(void) VbglHGCMParmUInt64Set(HGCMFunctionParameter *pParm, uint64_t u64) +{ + pParm->type = VMMDevHGCMParmType_64bit; + pParm->u.value64 = u64; +} + + +DECLINLINE(int) VbglHGCMParmUInt64Get(HGCMFunctionParameter *pParm, uint64_t *pu64) +{ + if (pParm->type == VMMDevHGCMParmType_64bit) + { + *pu64 = pParm->u.value64; + return VINF_SUCCESS; + } + *pu64 = UINT64_MAX; /* shut up gcc */ + return VERR_WRONG_PARAMETER_TYPE; +} + + +DECLINLINE(void) VbglHGCMParmPtrSet(HGCMFunctionParameter *pParm, void *pv, uint32_t cb) +{ + pParm->type = VMMDevHGCMParmType_LinAddr; + pParm->u.Pointer.size = cb; + pParm->u.Pointer.u.linearAddr = (uintptr_t)pv; +} + + +#ifdef IPRT_INCLUDED_string_h + +DECLINLINE(void) VbglHGCMParmPtrSetString(HGCMFunctionParameter *pParm, const char *psz) +{ + pParm->type = VMMDevHGCMParmType_LinAddr_In; + pParm->u.Pointer.size = (uint32_t)strlen(psz) + 1; + pParm->u.Pointer.u.linearAddr = (uintptr_t)psz; +} + +#endif /* IPRT_INCLUDED_string_h */ + +#ifdef VBOX_VBGLR3_XFREE86 +# undef strlen +#endif /* VBOX_VBGLR3_XFREE86 */ + +RT_C_DECLS_END + +#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp new file mode 100644 index 00000000..04a167e0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp @@ -0,0 +1,94 @@ +/* $Id: VBoxGuestR3LibLog.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Logging. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Write to the backdoor logger from ring 3 guest code. + * + * @returns IPRT status code. + * + * @param pch The string to log. Does not need to be terminated. + * @param cch The number of chars (bytes) to log. + * + * @remarks This currently does not accept more than 255 bytes of data at + * one time. It should probably be rewritten to use pass a pointer + * in the IOCtl. + */ +VBGLR3DECL(int) VbglR3WriteLog(const char *pch, size_t cch) +{ + /* + * Quietly skip empty strings. + * (Happens in the RTLogBackdoorPrintf case.) + */ + int rc; + if (cch > 0) + { + if (RT_VALID_PTR(pch)) + { + /* + * We need to repackage the string for ring-0. + */ + size_t cbMsg = VBGL_IOCTL_LOG_SIZE(cch); + PVBGLIOCLOG pMsg = (PVBGLIOCLOG)RTMemTmpAlloc(cbMsg); + if (pMsg) + { + VBGLREQHDR_INIT_EX(&pMsg->Hdr, VBGL_IOCTL_LOG_SIZE_IN(cch), VBGL_IOCTL_LOG_SIZE_OUT); + memcpy(pMsg->u.In.szMsg, pch, cch); + pMsg->u.In.szMsg[cch] = '\0'; + rc = vbglR3DoIOCtl(VBGL_IOCTL_LOG(cch), &pMsg->Hdr, cbMsg); + + RTMemTmpFree(pMsg); + } + else + rc = VERR_NO_TMP_MEMORY; + } + else + rc = VERR_INVALID_POINTER; + } + else + rc = VINF_SUCCESS; + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp new file mode 100644 index 00000000..81e7b272 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp @@ -0,0 +1,135 @@ +/* $Id: VBoxGuestR3LibMisc.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Misc. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 <VBox/log.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Change the IRQ filter mask. + * + * @returns IPRT status code. + * @param fOr The OR mask. + * @param fNot The NOT mask. + */ +VBGLR3DECL(int) VbglR3CtlFilterMask(uint32_t fOr, uint32_t fNot) +{ + VBGLIOCCHANGEFILTERMASK Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_FILTER_MASK); + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_FILTER_MASK, &Info.Hdr, sizeof(Info)); +} + + +/** + * Report a change in the capabilities that we support to the host. + * + * @returns IPRT status code. + * @param fOr Capabilities which have been added. + * @param fNot Capabilities which have been removed. + * + * @todo Move to a different file. + */ +VBGLR3DECL(int) VbglR3SetGuestCaps(uint32_t fOr, uint32_t fNot) +{ + VBGLIOCSETGUESTCAPS Info; + VBGLREQHDR_INIT(&Info.Hdr, CHANGE_GUEST_CAPABILITIES); + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info)); +} + + +/** + * Acquire capabilities to report to the host. + * + * The capabilities which can be acquired are the same as those reported by + * VbglR3SetGuestCaps, and once a capability has been acquired once is is + * switched to "acquire mode" and can no longer be set using VbglR3SetGuestCaps. + * Capabilities can also be switched to acquire mode without actually being + * acquired. A client can not acquire a capability which has been acquired and + * not released by another client. Capabilities acquired are automatically + * released on session termination. + * + * @returns IPRT status code + * @returns VERR_RESOURCE_BUSY and acquires nothing if another client has + * acquired and not released at least one of the @a fOr capabilities + * @param fOr Capabilities to acquire or to switch to acquire mode + * @param fNot Capabilities to release + * @param fConfig if set, capabilities in @a fOr are switched to acquire mode + * but not acquired, and @a fNot is ignored. See + * VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE for details. + */ +VBGLR3DECL(int) VbglR3AcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fConfig) +{ + VBGLIOCACQUIREGUESTCAPS Info; + VBGLREQHDR_INIT(&Info.Hdr, ACQUIRE_GUEST_CAPABILITIES); + Info.u.In.fFlags = fConfig ? VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE : VBGL_IOC_AGC_FLAGS_DEFAULT; + Info.u.In.fOrMask = fOr; + Info.u.In.fNotMask = fNot; + return vbglR3DoIOCtl(VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info)); +} + + +/** + * Query the session ID of this VM. + * + * The session id is an unique identifier that gets changed for each VM start, + * reset or restore. Useful for detection a VM restore. + * + * @returns IPRT status code. + * @param pu64IdSession Session id (out). This is NOT changed on + * failure, so the caller can depend on this to + * deal with backward compatibility (see + * VBoxServiceVMInfoWorker() for an example.) + */ +VBGLR3DECL(int) VbglR3GetSessionId(uint64_t *pu64IdSession) +{ + VMMDevReqSessionId Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetSessionId); + Req.idSession = 0; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + *pu64IdSession = Req.idSession; + + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp new file mode 100644 index 00000000..371b42d7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp @@ -0,0 +1,180 @@ +/* $Id: VBoxGuestR3LibModule.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared modules. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" +#include <iprt/mem.h> +#include <iprt/string.h> + +/** + * Registers a new shared module for the VM + * + * @returns IPRT status code. + * @param pszModuleName Module name + * @param pszVersion Module version + * @param GCBaseAddr Module base address + * @param cbModule Module size + * @param cRegions Number of shared region descriptors + * @param pRegions Shared region(s) + */ +VBGLR3DECL(int) VbglR3RegisterSharedModule(char *pszModuleName, char *pszVersion, + RTGCPTR64 GCBaseAddr, uint32_t cbModule, + unsigned cRegions, VMMDEVSHAREDREGIONDESC *pRegions) +{ + VMMDevSharedModuleRegistrationRequest *pReq; + int rc; + + /* Sanity check. */ + AssertReturn(cRegions < VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER); + + pReq = (VMMDevSharedModuleRegistrationRequest *)RTMemAllocZ(RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest, + aRegions[cRegions])); + AssertReturn(pReq, VERR_NO_MEMORY); + + vmmdevInitRequest(&pReq->header, VMMDevReq_RegisterSharedModule); + pReq->header.size = RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest, aRegions[cRegions]); + pReq->GCBaseAddr = GCBaseAddr; + pReq->cbModule = cbModule; + pReq->cRegions = cRegions; +#ifdef RT_OS_WINDOWS +# if ARCH_BITS == 32 + pReq->enmGuestOS = VBOXOSFAMILY_Windows32; +# else + pReq->enmGuestOS = VBOXOSFAMILY_Windows64; +# endif +#else + /** @todo */ + pReq->enmGuestOS = VBOXOSFAMILY_Unknown; +#endif + for (unsigned i = 0; i < cRegions; i++) + pReq->aRegions[i] = pRegions[i]; + + if ( RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName) != VINF_SUCCESS + || RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion) != VINF_SUCCESS) + { + rc = VERR_BUFFER_OVERFLOW; + goto end; + } + + rc = vbglR3GRPerform(&pReq->header); + +end: + RTMemFree(pReq); + return rc; + +} + +/** + * Unregisters a shared module for the VM + * + * @returns IPRT status code. + * @param pszModuleName Module name + * @param pszVersion Module version + * @param GCBaseAddr Module base address + * @param cbModule Module size + */ +VBGLR3DECL(int) VbglR3UnregisterSharedModule(char *pszModuleName, char *pszVersion, RTGCPTR64 GCBaseAddr, uint32_t cbModule) +{ + VMMDevSharedModuleUnregistrationRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_UnregisterSharedModule); + Req.GCBaseAddr = GCBaseAddr; + Req.cbModule = cbModule; + + if ( RTStrCopy(Req.szName, sizeof(Req.szName), pszModuleName) != VINF_SUCCESS + || RTStrCopy(Req.szVersion, sizeof(Req.szVersion), pszVersion) != VINF_SUCCESS) + { + return VERR_BUFFER_OVERFLOW; + } + return vbglR3GRPerform(&Req.header); +} + +/** + * Checks registered modules for shared pages + * + * @returns IPRT status code. + */ +VBGLR3DECL(int) VbglR3CheckSharedModules() +{ + VMMDevSharedModuleCheckRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_CheckSharedModules); + return vbglR3GRPerform(&Req.header); +} + +/** + * Checks if page sharing is enabled. + * + * @returns true/false enabled/disabled + */ +VBGLR3DECL(bool) VbglR3PageSharingIsEnabled() +{ + VMMDevPageSharingStatusRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetPageSharingStatus); + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + return Req.fEnabled; + return false; +} + +/** + * Checks if page sharing is enabled. + * + * @returns true/false enabled/disabled + */ +VBGLR3DECL(int) VbglR3PageIsShared(RTGCPTR pPage, bool *pfShared, uint64_t *puPageFlags) +{ +#ifdef DEBUG + VMMDevPageIsSharedRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_DebugIsPageShared); + Req.GCPtrPage = pPage; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pfShared = Req.fShared; + *puPageFlags = Req.uPageFlags; + } + return rc; +#else + RT_NOREF3(pPage, pfShared, puPageFlags); + return VERR_NOT_IMPLEMENTED; +#endif +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp new file mode 100644 index 00000000..b93872d4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp @@ -0,0 +1,90 @@ +/* $Id: VBoxGuestR3LibMouse.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Mouse. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" + + +/** + * Retrieve mouse coordinates and features from the host. + * + * @returns VBox status code. + * + * @param pfFeatures Where to store the mouse features. + * @param px Where to store the X co-ordinate. + * @param py Where to store the Y co-ordinate. + */ +VBGLR3DECL(int) VbglR3GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py) +{ + VMMDevReqMouseStatus Req; + vmmdevInitRequest(&Req.header, VMMDevReq_GetMouseStatus); + Req.mouseFeatures = 0; + Req.pointerXPos = 0; + Req.pointerYPos = 0; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + if (pfFeatures) + *pfFeatures = Req.mouseFeatures; + if (px) + *px = Req.pointerXPos; + if (py) + *py = Req.pointerYPos; + } + return rc; +} + + +/** + * Send mouse features to the host. + * + * @returns VBox status code. + * + * @param fFeatures Supported mouse pointer features. The main guest driver + * will mediate different callers and show the host any + * feature enabled by any guest caller. + */ +VBGLR3DECL(int) VbglR3SetMouseStatus(uint32_t fFeatures) +{ + VBGLIOCSETMOUSESTATUS Req; + VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS); + Req.u.In.fStatus = fFeatures; + return vbglR3DoIOCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req)); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp new file mode 100644 index 00000000..ff7c312d --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp @@ -0,0 +1,151 @@ +/** $Id: VBoxGuestR3LibPidFile.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * Create a PID file. + */ + +/* + * Copyright (C) 2015-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/file.h> +#include <iprt/string.h> +#include <iprt/process.h> +#include <iprt/thread.h> +#include <iprt/err.h> +#include "VBoxGuestR3LibInternal.h" + +/* A time to wait before starting the next attempt to check a pidfile. */ +#define VBGL_PIDFILE_WAIT_RELAX_TIME_MS (250) + +/** + * Creates a PID File and returns the open file descriptor. + * + * On DOS based system, file sharing (deny write) is used for locking the PID + * file. + * + * On Unix-y systems, an exclusive advisory lock is used for locking the PID + * file since the file sharing support is usually missing there. + * + * This API will overwrite any existing PID Files without a lock on them, on the + * assumption that they are stale files which an old process did not properly + * clean up. + * + * @returns IPRT status code. + * @param pszPath The path and filename to create the PID File under + * @param phFile Where to store the file descriptor of the open (and locked + * on Unix-y systems) PID File. On failure, or if another + * process owns the PID File, this will be set to NIL_RTFILE. + */ +VBGLR3DECL(int) VbglR3PidFile(const char *pszPath, PRTFILE phFile) +{ + AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(phFile, VERR_INVALID_PARAMETER); + *phFile = NIL_RTFILE; + + RTFILE hPidFile; + int rc = RTFileOpen(&hPidFile, pszPath, + RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE + | (0644 << RTFILE_O_CREATE_MODE_SHIFT)); + if (RT_SUCCESS(rc)) + { +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + /** @todo using size 0 for locking means lock all on Posix. + * We should adopt this as our convention too, or something + * similar. */ + rc = RTFileLock(hPidFile, RTFILE_LOCK_WRITE, 0, 0); + if (RT_FAILURE(rc)) + RTFileClose(hPidFile); + else +#endif + { + char szBuf[256]; + size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", + RTProcSelf()); + RTFileWrite(hPidFile, szBuf, cbPid, NULL); + *phFile = hPidFile; + } + } + return rc; +} + + +/** + * Close and remove an open PID File. + * + * @param pszPath The path to the PID File, + * @param hFile The handle for the file. NIL_RTFILE is ignored as usual. + */ +VBGLR3DECL(void) VbglR3ClosePidFile(const char *pszPath, RTFILE hFile) +{ + AssertPtrReturnVoid(pszPath); + if (hFile != NIL_RTFILE) + { +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + RTFileWriteAt(hFile, 0, "-1", 2, NULL); +#else + RTFileDelete(pszPath); +#endif + RTFileClose(hFile); + } +} + + +/** + * Wait for other process to release pidfile. + * + * This function is a wrapper to VbglR3PidFile(). + * + * @returns IPRT status code. + * @param szPidfile Path to pidfile. + * @param phPidfile Handle to pidfile. + * @param u64TimeoutMs A timeout value in milliseconds to wait for + * other process to release pidfile. + */ +VBGLR3DECL(int) VbglR3PidfileWait(const char *szPidfile, RTFILE *phPidfile, uint64_t u64TimeoutMs) +{ + int rc = VERR_FILE_LOCK_VIOLATION; + uint64_t u64Start = RTTimeSystemMilliTS(); + + AssertPtrReturn(szPidfile, VERR_INVALID_PARAMETER); + AssertPtrReturn(phPidfile, VERR_INVALID_PARAMETER); + + while ( !RT_SUCCESS((rc = VbglR3PidFile(szPidfile, phPidfile))) + && (RTTimeSystemMilliTS() - u64Start < u64TimeoutMs)) + { + RTThreadSleep(VBGL_PIDFILE_WAIT_RELAX_TIME_MS); + } + + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp new file mode 100644 index 00000000..8a9d15c0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp @@ -0,0 +1,108 @@ +/* $Id: VBoxGuestR3LibRuntimeXF86.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * implements the minimum of runtime functions needed for + * XFree86 driver code. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#if defined(VBOX_VBGLR3_XFREE86) +extern "C" { +# define XFree86LOADER +# include <xf86_ansic.h> +# undef size_t +} +#else +# include <stdarg.h> +# include <stdlib.h> +# define xalloc malloc +# define xfree free +extern "C" void ErrorF(const char *f, ...); +#endif + +RTDECL(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + ErrorF("Assertion failed! Expression: %s at %s in\n", pszExpr, + pszFunction); + ErrorF("%s:%u\n", pszFile, uLine); +} + +RTDECL(void) RTAssertMsg2Weak(const char *pszFormat, ...) +{ + NOREF(pszFormat); +} + +RTDECL(bool) RTAssertShouldPanic(void) +{ + return false; +} + +RTDECL(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup) +{ + NOREF(fFlagsAndGroup); + return NULL; +} + +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstance(void) +{ + return NULL; +} + +RTDECL(PRTLOGGER) RTLogRelGetDefaultInstanceEx(uint32_t fFlagsAndGroup) +{ + NOREF(fFlagsAndGroup); + return NULL; +} + +RTDECL(void) RTLogLoggerEx(PRTLOGGER, unsigned, unsigned, const char *pszFormat, ...) +{ + NOREF(pszFormat); +} + +RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) +{ + NOREF(pszTag); + return xalloc(cb); +} + +RTDECL(void) RTMemTmpFree(void *pv) +{ + xfree(pv); +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp new file mode 100644 index 00000000..a13a77b9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp @@ -0,0 +1,209 @@ +/* $Id: VBoxGuestR3LibSeamless.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Seamless mode. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/string.h> + +#include <VBox/log.h> + +#include "VBoxGuestR3LibInternal.h" + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" void* xf86memcpy(void*,const void*,xf86size_t); +# undef memcpy +# define memcpy xf86memcpy +#endif /* VBOX_VBGLR3_XFREE86 */ + +/** + * Tell the host that we support (or no longer support) seamless mode. + * + * @returns IPRT status value + * @param fState whether or not we support seamless mode + */ +VBGLR3DECL(int) VbglR3SeamlessSetCap(bool fState) +{ + if (fState) + return VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS, 0); + return VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS); +} + +/** + * Wait for a seamless mode change event. + * + * @returns IPRT status value. + * @param[out] pMode On success, the seamless mode to switch into (i.e. + * disabled, visible region or host window). + */ +VBGLR3DECL(int) VbglR3SeamlessWaitEvent(VMMDevSeamlessMode *pMode) +{ + AssertPtrReturn(pMode, VERR_INVALID_POINTER); + + /** @todo r=andy The (similar / duplicate) Windows code does similar waiting. Merge / fix this. */ + uint32_t fEvent = 0; + int rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 1000 /* ms */, &fEvent); + if (RT_SUCCESS(rc)) + { + /* did we get the right event? */ + if (fEvent & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST) + { + VMMDevSeamlessChangeRequest seamlessChangeRequest; + + /* get the seamless change request */ + vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest); + seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1; + seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + rc = vbglR3GRPerform(&seamlessChangeRequest.header); + if (RT_SUCCESS(rc)) + { + *pMode = seamlessChangeRequest.mode; + return VINF_SUCCESS; + } + } + else + rc = VERR_TRY_AGAIN; + } + else if ( rc == VERR_INTERRUPTED + || rc == VERR_TIMEOUT /* just in case */) + rc = VERR_TRY_AGAIN; + return rc; +} + +/** + * Request the last seamless mode switch from the host again. + * + * @returns IPRT status value. + * @param[out] pMode On success, the seamless mode that was switched + * into (i.e. disabled, visible region or host window). + */ +VBGLR3DECL(int) VbglR3SeamlessGetLastEvent(VMMDevSeamlessMode *pMode) +{ + VMMDevSeamlessChangeRequest seamlessChangeRequest; + int rc; + + AssertPtrReturn(pMode, VERR_INVALID_PARAMETER); + + /* get the seamless change request */ + vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest); + seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1; + seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + rc = vbglR3GRPerform(&seamlessChangeRequest.header); + if (RT_SUCCESS(rc)) + { + *pMode = seamlessChangeRequest.mode; + return VINF_SUCCESS; + } + return rc; +} + +/** + * Inform the host about the visible region + * + * @returns IPRT status code + * @param cRects number of rectangles in the list of visible rectangles + * @param pRects list of visible rectangles on the guest display + * + * @todo A scatter-gather version of vbglR3GRPerform would be nice, so that we don't have + * to copy our rectangle and header data into a single structure and perform an + * additional allocation. + * @todo Would that really gain us much, given that the rectangles may not + * be grouped at all, or in the format we need? Keeping the memory + * for our "single structure" around (re-alloc-ing it if necessary) + * sounds like a simpler optimisation if we need it. + */ +VBGLR3DECL(int) VbglR3SeamlessSendRects(uint32_t cRects, PRTRECT pRects) +{ + VMMDevVideoSetVisibleRegion *pReq; + int rc; + + AssertReturn(pRects || cRects == 0, VERR_INVALID_PARAMETER); + AssertMsgReturn(cRects <= _1M, ("%u\n", cRects), VERR_OUT_OF_RANGE); + + rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, + sizeof(VMMDevVideoSetVisibleRegion) + + cRects * sizeof(RTRECT) + - sizeof(RTRECT), + VMMDevReq_VideoSetVisibleRegion); + if (RT_SUCCESS(rc)) + { + pReq->cRect = cRects; + if (cRects) + memcpy(&pReq->Rect, pRects, cRects * sizeof(RTRECT)); + /* This will fail harmlessly for cRect == 0 and older host code */ + rc = vbglR3GRPerform(&pReq->header); + LogFunc(("Visible region request returned %Rrc, internal %Rrc.\n", + rc, pReq->header.rc)); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + LogFunc(("Sending %u rectangles to the host: %Rrc\n", cRects, rc)); + return rc; +} + +VBGLR3DECL(int) VbglR3SeamlessSendMonitorPositions(uint32_t cPositions, PRTPOINT pPositions) +{ + if (!pPositions || cPositions <= 0) + return VERR_INVALID_PARAMETER; + + VMMDevVideoUpdateMonitorPositions *pReq; + int rc; + + rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, + sizeof(VMMDevVideoUpdateMonitorPositions) + + (cPositions - 1) * sizeof(RTPOINT), + VMMDevReq_VideoUpdateMonitorPositions); + if (RT_SUCCESS(rc)) + { + pReq->cPositions = cPositions; + if (cPositions) + memcpy(&pReq->aPositions, pPositions, cPositions * sizeof(RTPOINT)); + rc = vbglR3GRPerform(&pReq->header); + LogFunc(("Monitor position update request returned %Rrc, internal %Rrc.\n", + rc, pReq->header.rc)); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + LogFunc(("Sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc)); + return rc; +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp new file mode 100644 index 00000000..47137fd2 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp @@ -0,0 +1,432 @@ +/* $Id: VBoxGuestR3LibSharedFolders.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, shared folders. + */ + +/* + * 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/string.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/cpp/autores.h> +#include <iprt/stdarg.h> +#include <VBox/log.h> +#include <VBox/shflsvc.h> /** @todo File should be moved to VBox/HostServices/SharedFolderSvc.h */ + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Connects to the shared folder service. + * + * @returns VBox status code + * @param pidClient Where to put the client id on success. The client id + * must be passed to all the other calls to the service. + */ +VBGLR3DECL(int) VbglR3SharedFolderConnect(HGCMCLIENTID *pidClient) +{ + return VbglR3HGCMConnect("VBoxSharedFolders", pidClient); +} + + +/** + * Disconnect from the shared folder service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + */ +VBGLR3DECL(int) VbglR3SharedFolderDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Checks whether a shared folder share exists or not. + * + * @returns True if shared folder exists, false if not. + * @param idClient The client id returned by VbglR3InfoSvcConnect(). + * @param pszShareName Shared folder name to check. + */ +VBGLR3DECL(bool) VbglR3SharedFolderExists(HGCMCLIENTID idClient, const char *pszShareName) +{ + AssertPtr(pszShareName); + + uint32_t cMappings; + VBGLR3SHAREDFOLDERMAPPING *paMappings; + + /** @todo Use some caching here? */ + bool fFound = false; + int rc = VbglR3SharedFolderGetMappings(idClient, true /* Only process auto-mounted folders */, &paMappings, &cMappings); + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < cMappings && !fFound; i++) + { + char *pszName = NULL; + rc = VbglR3SharedFolderGetName(idClient, paMappings[i].u32Root, &pszName); + if ( RT_SUCCESS(rc) + && *pszName) + { + if (RTStrICmp(pszName, pszShareName) == 0) + fFound = true; + RTStrFree(pszName); + } + } + VbglR3SharedFolderFreeMappings(paMappings); + } + return fFound; +} + + +/** + * Get the list of available shared folders. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3SharedFolderConnect(). + * @param fAutoMountOnly Flag whether only auto-mounted shared folders + * should be reported. + * @param ppaMappings Allocated array which will retrieve the mapping info. Needs + * to be freed with VbglR3SharedFolderFreeMappings() later. + * @param pcMappings The number of mappings returned in @a ppaMappings. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMappings(HGCMCLIENTID idClient, bool fAutoMountOnly, + PVBGLR3SHAREDFOLDERMAPPING *ppaMappings, uint32_t *pcMappings) +{ + AssertPtrReturn(pcMappings, VERR_INVALID_PARAMETER); + AssertPtrReturn(ppaMappings, VERR_INVALID_PARAMETER); + + *pcMappings = 0; + *ppaMappings = NULL; + + VBoxSFQueryMappings Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAPPINGS, 3); + + /* Set the mapping flags. */ + uint32_t u32Flags = 0; /** @todo SHFL_MF_UTF8 is not implemented yet. */ + if (fAutoMountOnly) /* We only want the mappings which get auto-mounted. */ + u32Flags |= SHFL_MF_AUTOMOUNT; + VbglHGCMParmUInt32Set(&Msg.flags, u32Flags); + + /* + * Prepare and get the actual mappings from the host service. + */ + int rc = VINF_SUCCESS; + uint32_t cMappings = 8; /* Should be a good default value. */ + uint32_t cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING); + VBGLR3SHAREDFOLDERMAPPING *ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)RTMemAllocZ(cbSize); + if (!ppaMappingsTemp) + return VERR_NO_MEMORY; + + do + { + VbglHGCMParmUInt32Set(&Msg.numberOfMappings, cMappings); + VbglHGCMParmPtrSet(&Msg.mappings, ppaMappingsTemp, cbSize); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + VbglHGCMParmUInt32Get(&Msg.numberOfMappings, pcMappings); + + /* Do we have more mappings than we have allocated space for? */ + if (rc == VINF_BUFFER_OVERFLOW) + { + cMappings = *pcMappings; + cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING); + void *pvNew = RTMemRealloc(ppaMappingsTemp, cbSize); + AssertPtrBreakStmt(pvNew, rc = VERR_NO_MEMORY); + ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)pvNew; + } + } + } while (rc == VINF_BUFFER_OVERFLOW); /** @todo r=bird: This won't happen because the weird host code never returns it. */ + + if ( RT_FAILURE(rc) + || !*pcMappings) + { + RTMemFree(ppaMappingsTemp); + ppaMappingsTemp = NULL; + } + + /* In this case, just return success with 0 mappings */ + if ( rc == VERR_INVALID_PARAMETER + && fAutoMountOnly) + rc = VINF_SUCCESS; + + *ppaMappings = ppaMappingsTemp; + + return rc; +} + + +/** + * Frees the shared folder mappings allocated by + * VbglR3SharedFolderGetMappings() before. + * + * @param paMappings What + */ +VBGLR3DECL(void) VbglR3SharedFolderFreeMappings(PVBGLR3SHAREDFOLDERMAPPING paMappings) +{ + if (paMappings) + RTMemFree(paMappings); +} + + +/** + * Get the real name of a shared folder. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3InvsSvcConnect(). + * @param u32Root Root ID of shared folder to get the name for. + * @param ppszName Where to return the name string. This shall be + * freed by calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetName(HGCMCLIENTID idClient, uint32_t u32Root, char **ppszName) +{ + AssertPtr(ppszName); + + VBoxSFQueryMapName Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_NAME, 2); + + int rc; + uint32_t cbString = SHFLSTRING_HEADER_SIZE + SHFL_MAX_LEN * sizeof(RTUTF16); + PSHFLSTRING pString = (PSHFLSTRING)RTMemAlloc(cbString); + if (pString) + { + if (!ShflStringInitBuffer(pString, cbString)) + { + RTMemFree(pString); + return VERR_INVALID_PARAMETER; + } + + VbglHGCMParmUInt32Set(&Msg.root, u32Root); + VbglHGCMParmPtrSet(&Msg.name, pString, cbString); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + *ppszName = NULL; + rc = RTUtf16ToUtf8(&pString->String.ucs2[0], ppszName); + } + RTMemFree(pString); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Queries information about a shared folder. + * + * @returns VBox status code. + * + * @param idClient The client ID. + * @param idRoot The root ID of the folder to query information for. + * @param fQueryFlags SHFL_MIQF_XXX. + * @param ppszName Where to return the pointer to the name. + * Free using RTStrFree. Optional. + * @param ppszMountPoint Where to return the pointer to the auto mount point. + * Free using RTStrFree. Optional. + * @param pfFlags Where to return the flags (SHFL_MIF_XXX). Optional. + * @param puRootIdVersion where to return the root ID version. Optional. + * This helps detecting root-id reuse. + * + * @remarks ASSUMES UTF-16 connection to host. + */ +VBGLR3DECL(int) VbglR3SharedFolderQueryFolderInfo(HGCMCLIENTID idClient, uint32_t idRoot, uint64_t fQueryFlags, + char **ppszName, char **ppszMountPoint, + uint64_t *pfFlags, uint32_t *puRootIdVersion) +{ + AssertReturn(!(fQueryFlags & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH)), VERR_INVALID_FLAGS); + + /* + * Allocate string buffers first. + */ + int rc; + PSHFLSTRING pNameBuf = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16)); + PSHFLSTRING pMountPoint = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16)); + if (pNameBuf && pMountPoint) + { + ShflStringInitBuffer(pNameBuf, SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16)); + ShflStringInitBuffer(pMountPoint, SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16)); + + /* + * Make the call. + */ + VBoxSFQueryMapInfo Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_INFO, 5); + VbglHGCMParmUInt32Set(&Msg.root, idRoot); + VbglHGCMParmPtrSet(&Msg.name, pNameBuf, SHFLSTRING_HEADER_SIZE + pNameBuf->u16Size); + VbglHGCMParmPtrSet(&Msg.mountPoint, pMountPoint, SHFLSTRING_HEADER_SIZE + pMountPoint->u16Size); + VbglHGCMParmUInt64Set(&Msg.flags, fQueryFlags); + VbglHGCMParmUInt32Set(&Msg.rootIdVersion, 0); + + rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /* + * Copy out the results. + */ + if (puRootIdVersion) + *puRootIdVersion = Msg.rootIdVersion.u.value64; + + if (pfFlags) + *pfFlags = Msg.flags.u.value64; + + if (ppszName) + { + *ppszName = NULL; + rc = RTUtf16ToUtf8Ex(pNameBuf->String.utf16, pNameBuf->u16Length / sizeof(RTUTF16), ppszName, 0, NULL); + } + + if (ppszMountPoint && RT_SUCCESS(rc)) + { + *ppszMountPoint = NULL; + rc = RTUtf16ToUtf8Ex(pMountPoint->String.utf16, pMountPoint->u16Length / sizeof(RTUTF16), ppszMountPoint, 0, NULL); + if (RT_FAILURE(rc) && ppszName) + { + RTStrFree(*ppszName); + *ppszName = NULL; + } + } + } + } + else + rc = VERR_NO_MEMORY; + RTMemFree(pMountPoint); + RTMemFree(pNameBuf); + return rc; +} + + +/** + * Waits for changes to the mappings (add, remove, restore). + * + * @returns VBox status code. + * @retval VINF_SUCCESS on change + * @retval VINF_TRY_AGAIN on restore. + * @retval VERR_OUT_OF_RESOURCES if there are too many guys waiting. + * + * @param idClient The client ID. + * @param uPrevVersion The mappings config version number returned the last + * time around. Use UINT32_MAX for the first call. + * @param puCurVersion Where to return the current mappings config version. + */ +VBGLR3DECL(int) VbglR3SharedFolderWaitForMappingsChanges(HGCMCLIENTID idClient, uint32_t uPrevVersion, uint32_t *puCurVersion) +{ + VBoxSFWaitForMappingsChanges Msg; + VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES, 1); + VbglHGCMParmUInt32Set(&Msg.version, uPrevVersion); + + int rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg)); + + *puCurVersion = Msg.version.u.value32; + return rc; +} + + +/** + * Cancels all threads currently waiting for changes for this client. + * + * @returns VBox status code. + * @param idClient The client ID. + */ +VBGLR3DECL(int) VbglR3SharedFolderCancelMappingsChangesWaits(HGCMCLIENTID idClient) +{ + VBGLIOCHGCMCALL CallInfo; + VBGL_HGCM_HDR_INIT(&CallInfo, idClient, SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS, 0); + + return VbglR3HGCMCall(&CallInfo, sizeof(CallInfo)); +} + + +/** + * Retrieves the prefix for a shared folder mount point. If no prefix + * is set in the guest properties "sf_" is returned. + * + * @returns VBox status code. + * @param ppszPrefix Where to return the prefix string. This shall be + * freed by calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMountPrefix(char **ppszPrefix) +{ + AssertPtrReturn(ppszPrefix, VERR_INVALID_POINTER); + int rc; +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClientGuestProp; + rc = VbglR3GuestPropConnect(&idClientGuestProp); + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountPrefix", ppszPrefix); + if (rc == VERR_NOT_FOUND) /* No prefix set? Then set the default. */ + { +#endif +/** @todo r=bird: Inconsistent! VbglR3SharedFolderGetMountDir does not return a default. */ + rc = RTStrDupEx(ppszPrefix, "sf_"); +#ifdef VBOX_WITH_GUEST_PROPS + } + VbglR3GuestPropDisconnect(idClientGuestProp); + } +#endif + return rc; +} + + +/** + * Retrieves the mount root directory for auto-mounted shared + * folders. mount point. If no string is set (VERR_NOT_FOUND) + * it's up on the caller (guest) to decide where to mount. + * + * @returns VBox status code. + * @param ppszDir Where to return the directory + * string. This shall be freed by + * calling RTStrFree. + */ +VBGLR3DECL(int) VbglR3SharedFolderGetMountDir(char **ppszDir) +{ + AssertPtrReturn(ppszDir, VERR_INVALID_POINTER); + int rc = VERR_NOT_FOUND; +#ifdef VBOX_WITH_GUEST_PROPS + HGCMCLIENTID idClientGuestProp; + rc = VbglR3GuestPropConnect(&idClientGuestProp); + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountDir", ppszDir); + VbglR3GuestPropDisconnect(idClientGuestProp); + } +#endif + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp new file mode 100644 index 00000000..30bc4631 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp @@ -0,0 +1,79 @@ +/* $Id: VBoxGuestR3LibStat.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Statistics. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" + + +/** + * Query the current statistics update interval. + * + * @returns IPRT status code. + * @param pcMsInterval Update interval in ms (out). + */ +VBGLR3DECL(int) VbglR3StatQueryInterval(PRTMSINTERVAL pcMsInterval) +{ + VMMDevGetStatisticsChangeRequest Req; + + vmmdevInitRequest(&Req.header, VMMDevReq_GetStatisticsChangeRequest); + Req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST; + Req.u32StatInterval = 1; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pcMsInterval = Req.u32StatInterval * 1000; + if (*pcMsInterval / 1000 != Req.u32StatInterval) + *pcMsInterval = ~(RTMSINTERVAL)0; + } + return rc; +} + + +/** + * Report guest statistics. + * + * @returns IPRT status code. + * @param pReq Request packet with statistics. + */ +VBGLR3DECL(int) VbglR3StatReport(VMMDevReportGuestStats *pReq) +{ + vmmdevInitRequest(&pReq->header, VMMDevReq_ReportGuestStats); + return vbglR3GRPerform(&pReq->header); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp new file mode 100644 index 00000000..603e83f7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp @@ -0,0 +1,55 @@ +/* $Id: VBoxGuestR3LibTime.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Time. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3GetHostTime(PRTTIMESPEC pTime) +{ + VMMDevReqHostTime Req; + vmmdevInitRequest(&Req.header, VMMDevReq_GetHostTime); + Req.time = UINT64_MAX; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + RTTimeSpecSetMilli(pTime, (int64_t)Req.time); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp new file mode 100644 index 00000000..38a79c58 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp @@ -0,0 +1,618 @@ +/* $Id: VBoxGuestR3LibVideo.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Video. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 "VBoxGuestR3LibInternal.h" + +#include <VBox/log.h> +#include <VBox/HostServices/GuestPropertySvc.h> /* For Save and RetrieveVideoMode */ +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#ifdef VBOX_VBGLR3_XFREE86 +/* Rather than try to resolve all the header file conflicts, I will just + prototype what we need here. */ +extern "C" void* xf86memcpy(void*,const void*,xf86size_t); +# undef memcpy +# define memcpy xf86memcpy +extern "C" void* xf86memset(const void*,int,xf86size_t); +# undef memset +# define memset xf86memset +#endif /* VBOX_VBGLR3_XFREE86 */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VIDEO_PROP_PREFIX "/VirtualBox/GuestAdd/Vbgl/Video/" + + +/** + * Enable or disable video acceleration. + * + * @returns VBox status code. + * + * @param fEnable Pass zero to disable, any other value to enable. + */ +VBGLR3DECL(int) VbglR3VideoAccelEnable(bool fEnable) +{ + VMMDevVideoAccelEnable Req; + vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelEnable); + Req.u32Enable = fEnable; + Req.cbRingBuffer = VMMDEV_VBVA_RING_BUFFER_SIZE; + Req.fu32Status = 0; + return vbglR3GRPerform(&Req.header); +} + + +/** + * Flush the video buffer. + * + * @returns VBox status code. + */ +VBGLR3DECL(int) VbglR3VideoAccelFlush(void) +{ + VMMDevVideoAccelFlush Req; + vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelFlush); + return vbglR3GRPerform(&Req.header); +} + + +/** + * Send mouse pointer shape information to the host. + * + * @returns VBox status code. + * + * @param fFlags Mouse pointer flags. + * @param xHot X coordinate of hot spot. + * @param yHot Y coordinate of hot spot. + * @param cx Pointer width. + * @param cy Pointer height. + * @param pvImg Pointer to the image data (can be NULL). + * @param cbImg Size of the image data pointed to by pvImg. + */ +VBGLR3DECL(int) VbglR3SetPointerShape(uint32_t fFlags, uint32_t xHot, uint32_t yHot, uint32_t cx, uint32_t cy, + const void *pvImg, size_t cbImg) +{ + VMMDevReqMousePointer *pReq; + size_t cbReq = vmmdevGetMousePointerReqSize(cx, cy); + AssertReturn( !pvImg + || cbReq == RT_UOFFSETOF(VMMDevReqMousePointer, pointerData) + cbImg, + VERR_INVALID_PARAMETER); + int rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, cbReq, VMMDevReq_SetPointerShape); + if (RT_SUCCESS(rc)) + { + pReq->fFlags = fFlags; + pReq->xHot = xHot; + pReq->yHot = yHot; + pReq->width = cx; + pReq->height = cy; + if (pvImg) + memcpy(pReq->pointerData, pvImg, cbImg); + + rc = vbglR3GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + vbglR3GRFree(&pReq->header); + } + return rc; +} + + +/** + * Send mouse pointer shape information to the host. + * This version of the function accepts a request for clients that + * already allocate and manipulate the request structure directly. + * + * @returns VBox status code. + * + * @param pReq Pointer to the VMMDevReqMousePointer structure. + */ +VBGLR3DECL(int) VbglR3SetPointerShapeReq(VMMDevReqMousePointer *pReq) +{ + int rc = vbglR3GRPerform(&pReq->header); + if (RT_SUCCESS(rc)) + rc = pReq->header.rc; + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param pcx Where to store the horizontal pixel resolution + * @param pcy Where to store the vertical pixel resolution + * requested (a value of zero means do not change). + * @param pcBits Where to store the bits per pixel requested (a value + * of zero means do not change). + * @param piDisplay Where to store the display number the request was for + * - 0 for the primary display, 1 for the first + * secondary display, etc. + * @param fAck whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + * + */ +static int getDisplayChangeRequest2(uint32_t *pcx, uint32_t *pcy, + uint32_t *pcBits, uint32_t *piDisplay, + bool fAck) +{ + VMMDevDisplayChangeRequest2 Req; + + AssertPtrReturn(pcx, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcy, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER); + AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER); + RT_ZERO(Req); + vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequest2); + if (fAck) + Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + rc = Req.header.rc; + if (RT_SUCCESS(rc)) + { + *pcx = Req.xres; + *pcy = Req.yres; + *pcBits = Req.bpp; + *piDisplay = Req.display; + } + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param pcx Where to store the horizontal pixel resolution + * requested (a value of zero means do not change). + * @param pcy Where to store the vertical pixel resolution + * requested (a value of zero means do not change). + * @param pcBits Where to store the bits per pixel requested (a value + * of zero means do not change). + * @param piDisplay Where to store the display number the request was for + * - 0 for the primary display, 1 for the first + * secondary display, etc. + * @param fAck whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + * + * @param pdx New horizontal position of the secondary monitor. + * Optional. + * @param pdy New vertical position of the secondary monitor. + * Optional. + * @param pfEnabled Secondary monitor is enabled or not. Optional. + * @param pfChangeOrigin Whether the mode hint retrieved included + * information about origin/display offset inside the + * frame-buffer. Optional. + * + */ +VBGLR3DECL(int) VbglR3GetDisplayChangeRequest(uint32_t *pcx, uint32_t *pcy, + uint32_t *pcBits, + uint32_t *piDisplay, + uint32_t *pdx, uint32_t *pdy, + bool *pfEnabled, + bool *pfChangeOrigin, + bool fAck) +{ + VMMDevDisplayChangeRequestEx Req; + int rc = VINF_SUCCESS; + + AssertPtrReturn(pcx, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcy, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pdx, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pdy, VERR_INVALID_PARAMETER); + AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfEnabled, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pfChangeOrigin, VERR_INVALID_PARAMETER); + + RT_ZERO(Req); + rc = vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequestEx); + AssertRCReturn(rc, rc); + if (fAck) + Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + rc = Req.header.rc; + if (RT_SUCCESS(rc)) + { + *pcx = Req.xres; + *pcy = Req.yres; + *pcBits = Req.bpp; + *piDisplay = Req.display; + if (pdx) + *pdx = Req.cxOrigin; + if (pdy) + *pdy = Req.cyOrigin; + if (pfEnabled) + *pfEnabled = Req.fEnabled; + if (pfChangeOrigin) + *pfChangeOrigin = Req.fChangeOrigin; + return VINF_SUCCESS; + } + + /* NEEDS TESTING: test below with current Additions on VBox 4.1 or older. */ + /** @todo Can we find some standard grep-able string for "NEEDS TESTING"? */ + if (rc == VERR_NOT_IMPLEMENTED) /* Fall back to the old API. */ + { + if (pfEnabled) + *pfEnabled = true; + if (pfChangeOrigin) + *pfChangeOrigin = false; + return getDisplayChangeRequest2(pcx, pcy, pcBits, piDisplay, fAck); + } + return rc; +} + + +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param cDisplaysIn How many elements in the paDisplays array. + * @param pcDisplaysOut How many elements were returned. + * @param paDisplays Display information. + * @param fAck Whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + */ +VBGLR3DECL(int) VbglR3GetDisplayChangeRequestMulti(uint32_t cDisplaysIn, + uint32_t *pcDisplaysOut, + VMMDevDisplayDef *paDisplays, + bool fAck) +{ + VMMDevDisplayChangeRequestMulti *pReq; + size_t cbDisplays; + size_t cbAlloc; + int rc = VINF_SUCCESS; + + AssertReturn(cDisplaysIn > 0 && cDisplaysIn <= 64 /* VBOX_VIDEO_MAX_SCREENS */, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcDisplaysOut, VERR_INVALID_PARAMETER); + AssertPtrReturn(paDisplays, VERR_INVALID_PARAMETER); + + cbDisplays = cDisplaysIn * sizeof(VMMDevDisplayDef); + cbAlloc = RT_UOFFSETOF(VMMDevDisplayChangeRequestMulti, aDisplays) + cbDisplays; + pReq = (VMMDevDisplayChangeRequestMulti *)RTMemTmpAlloc(cbAlloc); + AssertPtrReturn(pReq, VERR_NO_MEMORY); + + memset(pReq, 0, cbAlloc); + rc = vmmdevInitRequest(&pReq->header, VMMDevReq_GetDisplayChangeRequestMulti); + AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc); + + pReq->header.size += (uint32_t)cbDisplays; + pReq->cDisplays = cDisplaysIn; + if (fAck) + pReq->eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + + rc = vbglR3GRPerform(&pReq->header); + AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc); + + rc = pReq->header.rc; + if (RT_SUCCESS(rc)) + { + memcpy(paDisplays, pReq->aDisplays, pReq->cDisplays * sizeof(VMMDevDisplayDef)); + *pcDisplaysOut = pReq->cDisplays; + } + + RTMemTmpFree(pReq); + return rc; +} + + +/** + * Query the host as to whether it likes a specific video mode. + * + * @returns the result of the query + * @param cx the width of the mode being queried + * @param cy the height of the mode being queried + * @param cBits the bpp of the mode being queried + */ +VBGLR3DECL(bool) VbglR3HostLikesVideoMode(uint32_t cx, uint32_t cy, uint32_t cBits) +{ + bool fRc = true; /* If for some reason we can't contact the host then + * we like everything. */ + int rc; + VMMDevVideoModeSupportedRequest req; + + vmmdevInitRequest(&req.header, VMMDevReq_VideoModeSupported); + req.width = cx; + req.height = cy; + req.bpp = cBits; + req.fSupported = true; + rc = vbglR3GRPerform(&req.header); + if (RT_SUCCESS(rc) && RT_SUCCESS(req.header.rc)) + fRc = req.fSupported; + return fRc; +} + +/** + * Get the highest screen number for which there is a saved video mode or "0" + * if there are no saved modes. + * + * @returns iprt status value + * @returns VERR_NOT_SUPPORTED if the guest property service is not available. + * @param pcScreen where to store the virtual screen number + */ +VBGLR3DECL(int) VbglR3VideoModeGetHighestSavedScreen(unsigned *pcScreen) +{ +#if defined(VBOX_WITH_GUEST_PROPS) + int rc; + HGCMCLIENTID idClient = 0; + PVBGLR3GUESTPROPENUM pHandle = NULL; + const char *pszName = NULL; + unsigned cHighestScreen = 0; + + /* Validate input. */ + AssertPtrReturn(pcScreen, VERR_INVALID_POINTER); + + /* Query the data. */ + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + const char *pszPattern = VIDEO_PROP_PREFIX"*"; + rc = VbglR3GuestPropEnum(idClient, &pszPattern, 1, &pHandle, &pszName, NULL, NULL, NULL); + int rc2 = VbglR3GuestPropDisconnect(idClient); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc = rc2; + } + + /* Process the data. */ + while (RT_SUCCESS(rc) && pszName != NULL) + { + uint32_t cScreen; + + rc = RTStrToUInt32Full(pszName + sizeof(VIDEO_PROP_PREFIX) - 1, 10, &cScreen); + if (RT_SUCCESS(rc)) /* There may be similar properties with text. */ + cHighestScreen = RT_MAX(cHighestScreen, cScreen); + rc = VbglR3GuestPropEnumNext(pHandle, &pszName, NULL, NULL, NULL); + } + + VbglR3GuestPropEnumFree(pHandle); + + /* Return result. */ + if (RT_SUCCESS(rc)) + *pcScreen = cHighestScreen; + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(pcScreen); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} + +/** + * Save video mode parameters to the guest property store. + * + * @returns iprt status value + * @param idScreen The virtual screen number. + * @param cx mode width + * @param cy mode height + * @param cBits bits per pixel for the mode + * @param x virtual screen X offset + * @param y virtual screen Y offset + * @param fEnabled is this virtual screen enabled? + */ +VBGLR3DECL(int) VbglR3SaveVideoMode(unsigned idScreen, unsigned cx, unsigned cy, unsigned cBits, + unsigned x, unsigned y, bool fEnabled) +{ +#ifdef VBOX_WITH_GUEST_PROPS + unsigned cHighestScreen = 0; + int rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen); + if (RT_SUCCESS(rc)) + { + HGCMCLIENTID idClient = 0; + rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + int rc2; + char szModeName[GUEST_PROP_MAX_NAME_LEN]; + char szModeParms[GUEST_PROP_MAX_VALUE_LEN]; + RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen); + RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u,%ux%u,%u", cx, cy, cBits, x, y, (unsigned) fEnabled); + + rc = VbglR3GuestPropWriteValue(idClient, szModeName, szModeParms); + /* Write out the mode using the legacy name too, in case the user + * re-installs older Additions. */ + if (idScreen == 0) + { + RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u", cx, cy, cBits); + VbglR3GuestPropWriteValue(idClient, VIDEO_PROP_PREFIX "SavedMode", szModeParms); + } + + rc2 = VbglR3GuestPropDisconnect(idClient); + if (rc != VINF_PERMISSION_DENIED) + { + if (RT_SUCCESS(rc)) + rc = rc2; + if (RT_SUCCESS(rc)) + { + /* Sanity check 1. We do not try to make allowance for someone else + * changing saved settings at the same time as us. */ + bool fEnabled2 = false; + unsigned cx2 = 0; + unsigned cy2 = 0; + unsigned cBits2 = 0; + unsigned x2 = 0; + unsigned y2 = 0; + rc = VbglR3RetrieveVideoMode(idScreen, &cx2, &cy2, &cBits2, &x2, &y2, &fEnabled2); + if ( RT_SUCCESS(rc) + && (cx != cx2 || cy != cy2 || cBits != cBits2 || x != x2 || y != y2 || fEnabled != fEnabled2)) + rc = VERR_WRITE_ERROR; + /* Sanity check 2. Same comment. */ + else if (RT_SUCCESS(rc)) + { + unsigned cHighestScreen2 = 0; + rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen2); + if (RT_SUCCESS(rc)) + if (cHighestScreen2 != RT_MAX(cHighestScreen, idScreen)) + rc = VERR_INTERNAL_ERROR; + } + } + } + } + } + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idScreen, cx, cy, cBits, x, y, fEnabled); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} + + +/** + * Retrieve video mode parameters from the guest property store. + * + * @returns iprt status value + * @param idScreen The virtual screen number. + * @param pcx where to store the mode width + * @param pcy where to store the mode height + * @param pcBits where to store the bits per pixel for the mode + * @param px where to store the virtual screen X offset + * @param py where to store the virtual screen Y offset + * @param pfEnabled where to store whether this virtual screen is enabled + */ +VBGLR3DECL(int) VbglR3RetrieveVideoMode(unsigned idScreen, + unsigned *pcx, unsigned *pcy, + unsigned *pcBits, + unsigned *px, unsigned *py, + bool *pfEnabled) +{ +#ifdef VBOX_WITH_GUEST_PROPS + /* + * First we retrieve the video mode which is saved as a string in the + * guest property store. + */ + HGCMCLIENTID idClient = 0; + int rc = VbglR3GuestPropConnect(&idClient); + if (RT_SUCCESS(rc)) + { + int rc2; + /* The buffer for VbglR3GuestPropReadValue. If this is too small then + * something is wrong with the data stored in the property. */ + char szModeParms[1024]; + char szModeName[GUEST_PROP_MAX_NAME_LEN]; /** @todo add a VbglR3GuestPropReadValueF/FV that does the RTStrPrintf for you. */ + RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen); + rc = VbglR3GuestPropReadValue(idClient, szModeName, szModeParms, sizeof(szModeParms), NULL); + /* Try legacy single screen name. */ + if (rc == VERR_NOT_FOUND && idScreen == 0) + rc = VbglR3GuestPropReadValue(idClient, + VIDEO_PROP_PREFIX"SavedMode", + szModeParms, sizeof(szModeParms), + NULL); + rc2 = VbglR3GuestPropDisconnect(idClient); + if (RT_SUCCESS(rc)) + rc = rc2; + + /* + * Now we convert the string returned to numeric values. + */ + if (RT_SUCCESS(rc)) + { + /* Mandatory chunk: 640x480x32 */ + char *pszNext; + uint32_t cx = 0; + rc = VERR_PARSE_ERROR; + rc2 = RTStrToUInt32Ex(szModeParms, &pszNext, 10, &cx); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + uint32_t cy = 0; + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &cy); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + uint8_t cBits = 0; + rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &cBits); + if (rc2 == VINF_SUCCESS || rc2 == VWRN_TRAILING_CHARS) + { + /* Optional chunk: ,32x64,1 (we fail if this is partially there) */ + uint32_t x = 0; + uint32_t y = 0; + uint8_t fEnabled = 1; + if (rc2 == VINF_SUCCESS) + rc = VINF_SUCCESS; + else if (*pszNext == ',') + { + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &x); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x') + { + rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &y); + if (rc2 == VWRN_TRAILING_CHARS && *pszNext == ',') + { + rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &fEnabled); + if (rc2 == VINF_SUCCESS) + rc = VINF_SUCCESS; + } + } + } + + /* + * Set result if successful. + */ + if (rc == VINF_SUCCESS) + { + if (pcx) + *pcx = cx; + if (pcy) + *pcy = cy; + if (pcBits) + *pcBits = cBits; + if (px) + *px = x; + if (py) + *py = y; + if (pfEnabled) + *pfEnabled = RT_BOOL(fEnabled); + } + } + } + } + } + } + + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + RT_NOREF(idScreen, pcx, pcy, pcBits, px, py, pfEnabled); + return VERR_NOT_SUPPORTED; +#endif /* !VBOX_WITH_GUEST_PROPS */ +} diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp new file mode 100644 index 00000000..89a19d09 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp @@ -0,0 +1,64 @@ +/* $Id: VBoxGuestR3LibVrdp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, VRDP. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +VBGLR3DECL(int) VbglR3VrdpGetChangeRequest(bool *pfActive, uint32_t *puExperienceLevel) +{ + VMMDevVRDPChangeRequest Req; + RT_ZERO(Req); /* implicit padding */ + vmmdevInitRequest(&Req.header, VMMDevReq_GetVRDPChangeRequest); //VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetVRDPChangeRequest); + int rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + { + *pfActive = Req.u8VRDPActive != 0; + *puExperienceLevel = Req.u32VRDPExperienceLevel; + } + else + { + *pfActive = false; + *puExperienceLevel = 0; + } + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp new file mode 100644 index 00000000..38646784 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp @@ -0,0 +1,52 @@ +/* $Id: VbglR0CanUsePhysPageList.cpp $ */ +/** @file + * VBoxGuestLibR0 - Physical memory heap. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxGuestR0LibInternal.h" + + +/** + * Checks whether the host supports physical page lists or not. + * + * @returns true if it does, false if it doesn't. + */ +DECLR0VBGL(bool) VbglR0CanUsePhysPageList(void) +{ + /* a_fLocked is false, because the actual capability of the host is requested. + * See VBGLR0_CAN_USE_PHYS_PAGE_LIST definition. + */ + int rc = vbglR0Enter(); + return RT_SUCCESS(rc) + && VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk new file mode 100644 index 00000000..6d99a40f --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk @@ -0,0 +1,52 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the common guest addition code library testcases. +# + +# +# 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 +# + +SUB_DEPTH = ../../../../../../.. +include $(KBUILD_PATH)/subheader.kmk +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD) + + # + # Testcase for the physical heap. + # + PROGRAMS += tstVbglR0PhysHeap-1 + tstVbglR0PhysHeap-1_TEMPLATE = VBoxR3TstExe + tstVbglR0PhysHeap-1_SOURCES = \ + tstVbglR0PhysHeap-1.cpp + + +endif # defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD) +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp new file mode 100644 index 00000000..3646fd95 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp @@ -0,0 +1,415 @@ +/* $Id: tstVbglR0PhysHeap-1.cpp $ */ +/** @file + * IPRT Testcase - Offset Based Heap. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/initterm.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/rand.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/param.h> +#include <iprt/test.h> +#include <iprt/time.h> + +#define IN_TESTCASE +#define IN_RING0 /* pretend we're in ring-0 so we get access to the functions */ +#include "../VBoxGuestR0LibInternal.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct +{ + uint32_t cb; + void *pv; +} TSTHISTORYENTRY; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +VBGLDATA g_vbgldata; + +int g_cChunks = 0; +size_t g_cbChunks = 0; + +/** Drop-in replacement for RTMemContAlloc */ +static void *tstMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + RTTESTI_CHECK(cb > 0); + +#define TST_MAX_CHUNKS 24 + if (g_cChunks < TST_MAX_CHUNKS) + { + void *pvRet = RTMemAlloc(cb); + if (pvRet) + { + g_cChunks++; + g_cbChunks += cb; + *pPhys = (uint32_t)(uintptr_t)pvRet ^ (UINT32_C(0xf0f0f0f0) & ~(uint32_t)PAGE_OFFSET_MASK); + + /* Avoid problematic values that won't happen in real life: */ + if (!*pPhys) + *pPhys = 4U << PAGE_SHIFT; + if (UINT32_MAX - *pPhys < cb) + *pPhys -= RT_ALIGN_32(cb, PAGE_SIZE); + + return pvRet; + } + } + + *pPhys = NIL_RTCCPHYS; + return NULL; +} + + +/** Drop-in replacement for RTMemContFree */ +static void tstMemContFree(void *pv, size_t cb) +{ + RTTESTI_CHECK(RT_VALID_PTR(pv)); + RTTESTI_CHECK(cb > 0); + RTTESTI_CHECK(g_cChunks > 0); + RTMemFree(pv); + g_cChunks--; + g_cbChunks -= cb; +} + + +#define RTMemContAlloc tstMemContAlloc +#define RTMemContFree tstMemContFree +#include "../VBoxGuestR0LibPhysHeap.cpp" + + +static void PrintStats(TSTHISTORYENTRY const *paHistory, size_t cHistory, const char *pszDesc) +{ + size_t cbAllocated = 0; + unsigned cLargeBlocks = 0; + unsigned cAllocated = 0; + for (size_t i = 0; i < cHistory; i++) + if (paHistory[i].pv) + { + cAllocated += 1; + cbAllocated += paHistory[i].cb; + cLargeBlocks += paHistory[i].cb > _1K; + } + + size_t const cbOverhead = g_cChunks * sizeof(VBGLPHYSHEAPCHUNK) + cAllocated * sizeof(VBGLPHYSHEAPBLOCK); + size_t const cbFragmentation = g_cbChunks - cbOverhead - cbAllocated; + RTTestIPrintf(RTTESTLVL_ALWAYS, + "%s: %'9zu bytes in %2d chunks; %'9zu bytes in %4u blocks (%2u large)\n" + " => int-frag %'9zu (%2zu.%1zu%%) overhead %'9zu (%1zu.%02zu%%)\n", + pszDesc, + g_cbChunks, g_cChunks, + cbAllocated, cAllocated, cLargeBlocks, + cbFragmentation, cbFragmentation * 100 / g_cbChunks, (cbFragmentation * 1000 / g_cbChunks) % 10, + cbOverhead, cbOverhead * 100 / g_cbChunks, (cbOverhead * 10000 / g_cbChunks) % 100); +} + + +int main(int argc, char **argv) +{ + RT_NOREF_PV(argc); RT_NOREF_PV(argv); + + /* + * Init runtime. + */ + RTTEST hTest; + int rc = RTTestInitAndCreate("tstVbglR0PhysHeap-1", &hTest); + if (rc) + return rc; + RTTestBanner(hTest); + + /* + * Arguments are taken to be random seeding. + */ + uint64_t uRandSeed = RTTimeNanoTS(); + for (int i = 1; i < argc; i++) + { + rc = RTStrToUInt64Full(argv[i], 0, &uRandSeed); + if (rc != VINF_SUCCESS) + { + RTTestIFailed("Invalid parameter: %Rrc: %s\n", rc, argv[i]); + return RTTestSummaryAndDestroy(hTest); + } + } + + /* + * Create a heap. + */ + RTTestSub(hTest, "Basics"); + RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + +#define CHECK_PHYS_ADDR(a_pv) do { \ + uint32_t const uPhys = VbglR0PhysHeapGetPhysAddr(a_pv); \ + if (uPhys == 0 || uPhys == UINT32_MAX || (uPhys & PAGE_OFFSET_MASK) != ((uintptr_t)(a_pv) & PAGE_OFFSET_MASK)) \ + RTTestIFailed("line %u: %s=%p: uPhys=%#x\n", __LINE__, #a_pv, (a_pv), uPhys); \ + } while (0) + + /* + * Try allocate. + */ + static struct TstPhysHeapOps + { + uint32_t cb; + unsigned iFreeOrder; + void *pvAlloc; + } s_aOps[] = + { + { 16, 0, NULL }, // 0 + { 16, 1, NULL }, + { 16, 2, NULL }, + { 16, 5, NULL }, + { 16, 4, NULL }, + { 32, 3, NULL }, // 5 + { 31, 6, NULL }, + { 1024, 8, NULL }, + { 1024, 10, NULL }, + { 1024, 12, NULL }, + { PAGE_SIZE, 13, NULL }, // 10 + { 1024, 9, NULL }, + { PAGE_SIZE, 11, NULL }, + { PAGE_SIZE, 14, NULL }, + { 16, 15, NULL }, + { 9, 7, NULL }, // 15 + { 16, 7, NULL }, + { 36, 7, NULL }, + { 16, 7, NULL }, + { 12344, 7, NULL }, + { 50, 7, NULL }, // 20 + { 16, 7, NULL }, + }; + uint32_t i; + //RTHeapOffsetDump(Heap, (PFNRTHEAPOFFSETPRINTF)(uintptr_t)RTPrintf); /** @todo Add some detail info output with a signature identical to RTPrintf. */ + //size_t cbBefore = VbglR0PhysHeapGetFreeSize(); + static char const s_szFill[] = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* allocate */ + for (i = 0; i < RT_ELEMENTS(s_aOps); i++) + { + s_aOps[i].pvAlloc = VbglR0PhysHeapAlloc(s_aOps[i].cb); + RTTESTI_CHECK_MSG(s_aOps[i].pvAlloc, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i)); + if (!s_aOps[i].pvAlloc) + return RTTestSummaryAndDestroy(hTest); + + memset(s_aOps[i].pvAlloc, s_szFill[i], s_aOps[i].cb); + RTTESTI_CHECK_MSG(RT_ALIGN_P(s_aOps[i].pvAlloc, sizeof(void *)) == s_aOps[i].pvAlloc, + ("VbglR0PhysHeapAlloc(%#x) -> %p\n", s_aOps[i].cb, i)); + + CHECK_PHYS_ADDR(s_aOps[i].pvAlloc); + + /* Check heap integrity: */ + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + } + + /* free and allocate the same node again. */ + for (i = 0; i < RT_ELEMENTS(s_aOps); i++) + { + if (!s_aOps[i].pvAlloc) + continue; + //RTPrintf("debug: i=%d pv=%#x cb=%#zx align=%#zx cbReal=%#zx\n", i, s_aOps[i].pvAlloc, + // s_aOps[i].cb, s_aOps[i].uAlignment, RTHeapOffsetSize(Heap, s_aOps[i].pvAlloc)); + size_t cbBeforeSub = VbglR0PhysHeapGetFreeSize(); + VbglR0PhysHeapFree(s_aOps[i].pvAlloc); + size_t cbAfterSubFree = VbglR0PhysHeapGetFreeSize(); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + + void *pv; + pv = VbglR0PhysHeapAlloc(s_aOps[i].cb); + RTTESTI_CHECK_MSG(pv, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i)); + if (!pv) + return RTTestSummaryAndDestroy(hTest); + CHECK_PHYS_ADDR(pv); + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + + //RTPrintf("debug: i=%d pv=%p cbReal=%#zx cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx \n", i, pv, RTHeapOffsetSize(Heap, pv), + // cbBeforeSub, cbAfterSubFree, VbglR0PhysHeapGetFreeSize()); + + if (pv != s_aOps[i].pvAlloc) + RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: Free+Alloc returned different address. new=%p old=%p i=%d\n", pv, s_aOps[i].pvAlloc, i); + s_aOps[i].pvAlloc = pv; + size_t cbAfterSubAlloc = VbglR0PhysHeapGetFreeSize(); + if (cbBeforeSub != cbAfterSubAlloc) + { + RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx. i=%d\n", + cbBeforeSub, cbAfterSubFree, cbAfterSubAlloc, i); + //return 1; - won't work correctly until we start creating free block instead of donating memory on alignment. + } + } + + VbglR0PhysHeapTerminate(); + RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks)); + + + /* + * Use random allocation pattern + */ + RTTestSub(hTest, "Random Test"); + RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + + RTRAND hRand; + RTTESTI_CHECK_RC(rc = RTRandAdvCreateParkMiller(&hRand), VINF_SUCCESS); + if (RT_FAILURE(rc)) + return RTTestSummaryAndDestroy(hTest); + RTRandAdvSeed(hRand, uRandSeed); + RTTestValue(hTest, "RandSeed", uRandSeed, RTTESTUNIT_NONE); + + static TSTHISTORYENTRY s_aHistory[3072]; + RT_ZERO(s_aHistory); + + for (unsigned iTest = 0; iTest < 131072; iTest++) + { + i = RTRandAdvU32Ex(hRand, 0, RT_ELEMENTS(s_aHistory) - 1); + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 8, 1024); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = 9; + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + } + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0xbb, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + } + else + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + +#if 1 + /* Check heap integrity: */ + RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL)); + int cChunks = 0; + for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext) + cChunks++; + RTTESTI_CHECK_MSG(cChunks == g_cChunks, ("g_cChunks=%u, but only %u chunks in the list!\n", g_cChunks, cChunks)); +#endif + + if ((iTest % 7777) == 7776) + { + /* exhaust the heap */ + PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-pre "); + + for (i = 0; i < RT_ELEMENTS(s_aHistory) && (VbglR0PhysHeapGetFreeSize() >= 256 || g_cChunks < TST_MAX_CHUNKS); i++) + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, VBGL_PH_CHUNKSIZE / 8, VBGL_PH_CHUNKSIZE / 2 + VBGL_PH_CHUNKSIZE / 4); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + } + + size_t cbFree = VbglR0PhysHeapGetFreeSize(); + if (cbFree) + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + if (!s_aHistory[i].pv) + { + s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 1, (uint32_t)cbFree); + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + while (s_aHistory[i].pv == NULL && s_aHistory[i].cb > 2) + { + s_aHistory[i].cb >>= 1; + s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb); + } + if (s_aHistory[i].pv) + { + memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb); + CHECK_PHYS_ADDR(s_aHistory[i].pv); + } + + cbFree = VbglR0PhysHeapGetFreeSize(); + if (!cbFree) + break; + } + + RTTESTI_CHECK_MSG(VbglR0PhysHeapGetFreeSize() == 0, ("%zu\n", VbglR0PhysHeapGetFreeSize())); + PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-post"); + } + else if ((iTest % 7777) == 1111) + { + /* free all */ + RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-pre: cFreeBlocks=%u cAllocedBlocks=%u in %u chunk(s)\n", + g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks - g_vbgldata.cFreeBlocks, g_cChunks); + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-post: cFreeBlocks=%u in %u chunk(s)\n", g_vbgldata.cFreeBlocks, g_cChunks); + RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks)); + RTTESTI_CHECK_MSG(g_vbgldata.cFreeBlocks == g_vbgldata.cBlocks, + ("g_vbgldata.cFreeBlocks=%d cBlocks=%d\n", g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks)); + + //size_t cbAfterRand = VbglR0PhysHeapGetFreeSize(); + //RTTESTI_CHECK_MSG(cbAfterRand == cbAfter, ("cbAfterRand=%zu cbAfter=%zu\n", cbAfterRand, cbAfter)); + } + } + + /* free the rest. */ + for (i = 0; i < RT_ELEMENTS(s_aHistory); i++) + { + VbglR0PhysHeapFree(s_aHistory[i].pv); + s_aHistory[i].pv = NULL; + } + + RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks)); + + VbglR0PhysHeapTerminate(); + RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks)); + + RTTESTI_CHECK_RC(rc = RTRandAdvDestroy(hRand), VINF_SUCCESS); + return RTTestSummaryAndDestroy(hTest); +} + |