diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Additions/common/VBoxGuest/lib | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest/lib')
48 files changed, 13843 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..1aa4102c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk @@ -0,0 +1,237 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the common guest addition code library. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# + +SUB_DEPTH = ../../../../../.. +include $(KBUILD_PATH)/subheader.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 = VBOXGUESTR0LIB +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 = VBOXGUESTR0LIB +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 +# +if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Lib everywhere +VBoxGuestR3Lib_TEMPLATE = VBoxGuestR3Lib +else +VBoxGuestR3Lib_TEMPLATE = VBOXGUESTR3LIB +endif +VBoxGuestR3Lib_DEFS = \ + VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \ + $(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 \ + VBoxGuestR3LibClipboard.cpp \ + VBoxGuestR3LibCoreDump.cpp \ + VBoxGuestR3LibCpuHotPlug.cpp \ + VBoxGuestR3LibCredentials.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_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. +# +if "$(KBUILD_TARGET)" == "win" || defined(VBOX_WITH_MASOCHISTIC_WARNINGS) ## @todo use VBoxGuestR3Lib everywhere +VBoxGuestR3LibShared_TEMPLATE = VBoxGuestR3Dll +else +VBoxGuestR3LibShared_TEMPLATE = VBOXGUESTR3DLL +endif +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 = VBOXGUESTR3XF86LIB +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,) +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,) +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..688a7004 --- /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-2019 Oracle Corporation + * + * 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..1e559e8b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp @@ -0,0 +1,185 @@ +/* $Id: VBoxGuestR0LibGenericRequest.cpp $ */ +/** @file + * VBoxGuestLibR0 - Generic VMMDev request management. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * 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_HGCMCall32 + || pReq->requestType == VMMDevReq_HGCMCall64 +#else + || pReq->requestType == VMMDevReq_HGCMCall +#endif + || pReq->requestType == VMMDevReq_RegisterSharedModule + || pReq->requestType == VMMDevReq_ReportGuestUserState + || pReq->requestType == VMMDevReq_LogString + || pReq->requestType == VMMDevReq_SetPointerShape + || pReq->requestType == VMMDevReq_VideoSetVisibleRegion) + { + 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..78a554e8 --- /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-2019 Oracle Corporation + * + * 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..4eb46c2a --- /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-2019 Oracle Corporation + * + * 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(); + 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..7ee61d93 --- /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-2019 Oracle Corporation + * + * 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..27d04dca --- /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-2019 Oracle Corporation + * + * 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..40bd4ab7 --- /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-2019 Oracle Corporation + * + * 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..c2c38952 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp @@ -0,0 +1,192 @@ +/* $Id: VBoxGuestR0LibIdc-win.cpp $ */ +/** @file + * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Windows specific. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * 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. + */ + 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 + pIrp->Flags |= IRP_SYNCHRONOUS_API; + 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..a6cfa673 --- /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-2019 Oracle Corporation + * + * 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..3a758d70 --- /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-2019 Oracle Corporation + * + * 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..34f14ac4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h @@ -0,0 +1,202 @@ +/* $Id: VBoxGuestR0LibInternal.h $ */ +/** @file + * VBoxGuestLibR0 - Internal header. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * 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 _VBGLPHYSHEAPCHUNK; +typedef struct _VBGLPHYSHEAPCHUNK VBGLPHYSHEAPCHUNK; + +enum VbglLibStatus +{ + VbglStatusNotInitialized = 0, + VbglStatusInitializing, + VbglStatusReady +}; + +/** + * Global VBGL ring-0 data. + * Lives in VbglR0Init.cpp. + */ +typedef struct VBGLDATA +{ + enum VbglLibStatus status; + + RTIOPORT portVMMDev; + + VMMDevMemory *pVMMDevMemory; + + /** + * Physical memory heap data. + * @{ + */ + + VBGLPHYSHEAPBLOCK *pFreeBlocksHead; + VBGLPHYSHEAPBLOCK *pAllocBlocksHead; + VBGLPHYSHEAPCHUNK *pChunkHead; + + RTSEMFASTMUTEX mutexHeap; + /** @} */ + + /** + * 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..b41c31bb --- /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-2019 Oracle Corporation + * + * 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..c409530e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp @@ -0,0 +1,644 @@ +/* $Id: VBoxGuestR0LibPhysHeap.cpp $ */ +/** @file + * VBoxGuestLibR0 - Physical memory heap. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * 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/alloc.h> + +/* Physical memory heap consists of double linked list + * of chunks. Memory blocks are allocated inside these chunks + * and are members of Allocated and Free double linked lists. + * + * When allocating a block, we search in Free linked + * list for a suitable free block. If there is no such block, + * a new chunk is allocated and the new block is taken from + * the new chunk as the only chunk-sized free block. + * Allocated block is excluded from the Free list and goes to + * Alloc list. + * + * When freeing block, we check the pointer and then + * exclude block from Alloc list and move it to free list. + * + * For each chunk we maintain the allocated blocks counter. + * if 2 (or more) entire chunks are free they are immediately + * deallocated, so we always have at most 1 free chunk. + * + * When freeing blocks, two subsequent free blocks are always + * merged together. Current implementation merges blocks only + * when there is a block after the just freed one. + * + */ + +#define VBGL_PH_ASSERT Assert +#define VBGL_PH_ASSERTMsg AssertMsg + +// #define DUMPHEAP + +#ifdef DUMPHEAP +# define VBGL_PH_dprintf(a) RTAssertMsg2Weak a +#else +# define VBGL_PH_dprintf(a) +#endif + +/* Heap block signature */ +#define VBGL_PH_BLOCKSIGNATURE (0xADDBBBBB) + + +/* Heap chunk signature */ +#define VBGL_PH_CHUNKSIGNATURE (0xADDCCCCC) +/* Heap chunk allocation unit */ +#define VBGL_PH_CHUNKSIZE (0x10000) + +/* Heap block bit flags */ +#define VBGL_PH_BF_ALLOCATED (0x1) + +struct _VBGLPHYSHEAPBLOCK +{ + uint32_t u32Signature; + + /* Size of user data in the block. Does not include the block header. */ + uint32_t cbDataSize; + + uint32_t fu32Flags; + + struct _VBGLPHYSHEAPBLOCK *pNext; + struct _VBGLPHYSHEAPBLOCK *pPrev; + + struct _VBGLPHYSHEAPCHUNK *pChunk; +}; + +struct _VBGLPHYSHEAPCHUNK +{ + uint32_t u32Signature; + + /* Size of the chunk. Includes the chunk header. */ + uint32_t cbSize; + + /* Physical address of the chunk */ + uint32_t physAddr; + + /* Number of allocated blocks in the chunk */ + int32_t cAllocatedBlocks; + + struct _VBGLPHYSHEAPCHUNK *pNext; + struct _VBGLPHYSHEAPCHUNK *pPrev; +}; + + +#ifndef DUMPHEAP +#define dumpheap(a) +#else +void dumpheap (char *point) +{ + VBGL_PH_dprintf(("VBGL_PH dump at '%s'\n", point)); + + VBGL_PH_dprintf(("Chunks:\n")); + + VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead; + + while (pChunk) + { + VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, allocated = %8d, phys = %08X\n", + pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbSize, pChunk->cAllocatedBlocks, pChunk->physAddr)); + + pChunk = pChunk->pNext; + } + + VBGL_PH_dprintf(("Allocated blocks:\n")); + + VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pAllocBlocksHead; + + while (pBlock) + { + VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n", + pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk)); + + pBlock = pBlock->pNext; + } + + VBGL_PH_dprintf(("Free blocks:\n")); + + pBlock = g_vbgldata.pFreeBlocksHead; + + while (pBlock) + { + VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n", + pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk)); + + pBlock = pBlock->pNext; + } + + VBGL_PH_dprintf(("VBGL_PH dump at '%s' done\n", point)); +} +#endif + + +DECLINLINE(void *) vbglPhysHeapBlock2Data (VBGLPHYSHEAPBLOCK *pBlock) +{ + return (void *)(pBlock? (char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK): NULL); +} + +DECLINLINE(VBGLPHYSHEAPBLOCK *) vbglPhysHeapData2Block (void *p) +{ + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)(p? (char *)p - sizeof (VBGLPHYSHEAPBLOCK): NULL); + + VBGL_PH_ASSERTMsg(pBlock == NULL || pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE, + ("pBlock->u32Signature = %08X\n", pBlock->u32Signature)); + + return pBlock; +} + +DECLINLINE(int) vbglPhysHeapEnter (void) +{ + int rc = RTSemFastMutexRequest(g_vbgldata.mutexHeap); + + VBGL_PH_ASSERTMsg(RT_SUCCESS(rc), + ("Failed to request heap mutex, rc = %Rrc\n", rc)); + + return rc; +} + +DECLINLINE(void) vbglPhysHeapLeave (void) +{ + RTSemFastMutexRelease(g_vbgldata.mutexHeap); +} + + +static void vbglPhysHeapInitBlock (VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbDataSize) +{ + VBGL_PH_ASSERT(pBlock != NULL); + VBGL_PH_ASSERT(pChunk != NULL); + + pBlock->u32Signature = VBGL_PH_BLOCKSIGNATURE; + pBlock->cbDataSize = cbDataSize; + pBlock->fu32Flags = 0; + pBlock->pNext = NULL; + pBlock->pPrev = NULL; + pBlock->pChunk = pChunk; +} + + +static void vbglPhysHeapInsertBlock (VBGLPHYSHEAPBLOCK *pInsertAfter, VBGLPHYSHEAPBLOCK *pBlock) +{ + VBGL_PH_ASSERTMsg(pBlock->pNext == NULL, + ("pBlock->pNext = %p\n", pBlock->pNext)); + VBGL_PH_ASSERTMsg(pBlock->pPrev == NULL, + ("pBlock->pPrev = %p\n", pBlock->pPrev)); + + if (pInsertAfter) + { + pBlock->pNext = pInsertAfter->pNext; + pBlock->pPrev = pInsertAfter; + + if (pInsertAfter->pNext) + { + pInsertAfter->pNext->pPrev = pBlock; + } + + pInsertAfter->pNext = pBlock; + } + else + { + /* inserting to head of list */ + pBlock->pPrev = NULL; + + if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) + { + pBlock->pNext = g_vbgldata.pAllocBlocksHead; + + if (g_vbgldata.pAllocBlocksHead) + { + g_vbgldata.pAllocBlocksHead->pPrev = pBlock; + } + + g_vbgldata.pAllocBlocksHead = pBlock; + } + else + { + pBlock->pNext = g_vbgldata.pFreeBlocksHead; + + if (g_vbgldata.pFreeBlocksHead) + { + g_vbgldata.pFreeBlocksHead->pPrev = pBlock; + } + + g_vbgldata.pFreeBlocksHead = pBlock; + } + } +} + +static void vbglPhysHeapExcludeBlock (VBGLPHYSHEAPBLOCK *pBlock) +{ + if (pBlock->pNext) + { + pBlock->pNext->pPrev = pBlock->pPrev; + } + else + { + /* this is tail of list but we do not maintain tails of block lists. + * so do nothing. + */ + ; + } + + if (pBlock->pPrev) + { + pBlock->pPrev->pNext = pBlock->pNext; + } + else + { + /* this is head of list but we do not maintain tails of block lists. */ + if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) + { + g_vbgldata.pAllocBlocksHead = pBlock->pNext; + } + else + { + g_vbgldata.pFreeBlocksHead = pBlock->pNext; + } + } + + pBlock->pNext = NULL; + pBlock->pPrev = NULL; +} + +static VBGLPHYSHEAPBLOCK *vbglPhysHeapChunkAlloc (uint32_t cbSize) +{ + RTCCPHYS physAddr; + VBGLPHYSHEAPCHUNK *pChunk; + VBGLPHYSHEAPBLOCK *pBlock; + VBGL_PH_dprintf(("Allocating new chunk of size %d\n", cbSize)); + + /* Compute chunk size to allocate */ + if (cbSize < VBGL_PH_CHUNKSIZE) + { + /* Includes case of block size 0 during initialization */ + cbSize = VBGL_PH_CHUNKSIZE; + } + else + { + /* Round up to next chunk size, which must be power of 2 */ + cbSize = (cbSize + (VBGL_PH_CHUNKSIZE - 1)) & ~(VBGL_PH_CHUNKSIZE - 1); + } + + physAddr = 0; + /* This function allocates physical contiguous memory (below 4GB) according to the IPRT docs. + * Address < 4G is required for the port IO. + */ + pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize); + + if (!pChunk) + { + LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u contiguous bytes.\n", cbSize)); + return NULL; + } + + AssertRelease(physAddr < _4G && physAddr + cbSize <= _4G); + + pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE; + pChunk->cbSize = cbSize; + pChunk->physAddr = (uint32_t)physAddr; + pChunk->cAllocatedBlocks = 0; + pChunk->pNext = g_vbgldata.pChunkHead; + pChunk->pPrev = NULL; + + /* Initialize the free block, which now occupies entire chunk. */ + pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK)); + + vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK)); + + vbglPhysHeapInsertBlock (NULL, pBlock); + + g_vbgldata.pChunkHead = pChunk; + + VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize)); + + return pBlock; +} + + +void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk) +{ + char *p; + VBGL_PH_ASSERT(pChunk != NULL); + VBGL_PH_ASSERTMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, + ("pChunk->u32Signature = %08X\n", pChunk->u32Signature)); + + VBGL_PH_dprintf(("Deleting chunk %p size %x\n", pChunk, pChunk->cbSize)); + + /* first scan the chunk and exclude all blocks from lists */ + + p = (char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK); + + while (p < (char *)pChunk + pChunk->cbSize) + { + VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)p; + + p += pBlock->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK); + + vbglPhysHeapExcludeBlock (pBlock); + } + + VBGL_PH_ASSERTMsg(p == (char *)pChunk + pChunk->cbSize, + ("p = %p, (char *)pChunk + pChunk->cbSize = %p, pChunk->cbSize = %08X\n", + p, (char *)pChunk + pChunk->cbSize, pChunk->cbSize)); + + /* Exclude chunk from the chunk list */ + if (pChunk->pNext) + { + pChunk->pNext->pPrev = pChunk->pPrev; + } + else + { + /* we do not maintain tail */ + ; + } + + if (pChunk->pPrev) + { + pChunk->pPrev->pNext = pChunk->pNext; + } + else + { + /* the chunk was head */ + g_vbgldata.pChunkHead = pChunk->pNext; + } + + RTMemContFree (pChunk, pChunk->cbSize); +} + + +DECLR0VBGL(void *) VbglR0PhysHeapAlloc (uint32_t cbSize) +{ + VBGLPHYSHEAPBLOCK *pBlock, *iter; + int rc = vbglPhysHeapEnter (); + + if (RT_FAILURE(rc)) + return NULL; + + dumpheap ("pre alloc"); + + pBlock = NULL; + + /* If there are free blocks in the heap, look at them. */ + iter = g_vbgldata.pFreeBlocksHead; + + /* There will be not many blocks in the heap, so + * linear search would be fast enough. + */ + + while (iter) + { + if (iter->cbDataSize == cbSize) + { + /* exact match */ + pBlock = iter; + break; + } + + /* Looking for a free block with nearest size */ + if (iter->cbDataSize > cbSize) + { + if (pBlock) + { + if (iter->cbDataSize < pBlock->cbDataSize) + { + pBlock = iter; + } + } + else + { + pBlock = iter; + } + } + + iter = iter->pNext; + } + + if (!pBlock) + { + /* No free blocks, allocate a new chunk, + * the only free block of the chunk will + * be returned. + */ + pBlock = vbglPhysHeapChunkAlloc (cbSize); + } + + if (pBlock) + { + VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE, + ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature)); + VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0, + ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags)); + + /* We have a free block, either found or allocated. */ + + if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK))) + { + /* Data will occupy less than a half of the block, + * the block should be split. + */ + iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize); + + /* Init the new 'iter' block, initialized blocks are always marked as free. */ + vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK)); + + pBlock->cbDataSize = cbSize; + + /* Insert the new 'iter' block after the 'pBlock' in the free list */ + vbglPhysHeapInsertBlock (pBlock, iter); + } + + /* Exclude pBlock from free list */ + vbglPhysHeapExcludeBlock (pBlock); + + /* Mark as allocated */ + pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED; + + /* Insert to allocated list */ + vbglPhysHeapInsertBlock (NULL, pBlock); + + /* Adjust the chunk allocated blocks counter */ + pBlock->pChunk->cAllocatedBlocks++; + } + + dumpheap ("post alloc"); + + vbglPhysHeapLeave (); + VBGL_PH_dprintf(("VbglR0PhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize)); + + return vbglPhysHeapBlock2Data (pBlock); +} + +DECLR0VBGL(uint32_t) VbglR0PhysHeapGetPhysAddr (void *p) +{ + uint32_t physAddr = 0; + VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p); + + if (pBlock) + { + VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0, + ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags)); + + if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) + physAddr = pBlock->pChunk->physAddr + (uint32_t)((uintptr_t)p - (uintptr_t)pBlock->pChunk); + } + + return physAddr; +} + +DECLR0VBGL(void) VbglR0PhysHeapFree(void *p) +{ + VBGLPHYSHEAPBLOCK *pBlock; + VBGLPHYSHEAPBLOCK *pNeighbour; + + int rc = vbglPhysHeapEnter (); + if (RT_FAILURE(rc)) + return; + + dumpheap ("pre free"); + + pBlock = vbglPhysHeapData2Block (p); + + if (!pBlock) + { + vbglPhysHeapLeave (); + return; + } + + VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0, + ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags)); + + /* Exclude from allocated list */ + vbglPhysHeapExcludeBlock (pBlock); + + dumpheap ("post exclude"); + + VBGL_PH_dprintf(("VbglR0PhysHeapFree %x size %x\n", p, pBlock->cbDataSize)); + + /* Mark as free */ + pBlock->fu32Flags &= ~VBGL_PH_BF_ALLOCATED; + + /* Insert to free list */ + vbglPhysHeapInsertBlock (NULL, pBlock); + + dumpheap ("post insert"); + + /* Adjust the chunk allocated blocks counter */ + pBlock->pChunk->cAllocatedBlocks--; + + VBGL_PH_ASSERT(pBlock->pChunk->cAllocatedBlocks >= 0); + + /* Check if we can merge 2 free blocks. To simplify heap maintenance, + * we will look at block after the just freed one. + * This will not prevent us from detecting free memory chunks. + * Also in most cases blocks are deallocated in reverse allocation order + * and in that case the merging will work. + */ + + pNeighbour = (VBGLPHYSHEAPBLOCK *)((char *)p + pBlock->cbDataSize); + + if ((char *)pNeighbour < (char *)pBlock->pChunk + pBlock->pChunk->cbSize + && (pNeighbour->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0) + { + /* The next block is free as well. */ + + /* Adjust size of current memory block */ + pBlock->cbDataSize += pNeighbour->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK); + + /* Exclude the next neighbour */ + vbglPhysHeapExcludeBlock (pNeighbour); + } + + dumpheap ("post merge"); + + /* now check if there are 2 or more free chunks */ + if (pBlock->pChunk->cAllocatedBlocks == 0) + { + VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead; + + uint32_t u32FreeChunks = 0; + + while (pChunk) + { + if (pChunk->cAllocatedBlocks == 0) + { + u32FreeChunks++; + } + + pChunk = pChunk->pNext; + } + + if (u32FreeChunks > 1) + { + /* Delete current chunk, it will also exclude all free blocks + * remaining in the chunk from the free list, so the pBlock + * will also be invalid after this. + */ + vbglPhysHeapChunkDelete (pBlock->pChunk); + } + } + + dumpheap ("post free"); + + vbglPhysHeapLeave (); +} + +DECLR0VBGL(int) VbglR0PhysHeapInit (void) +{ + int rc = VINF_SUCCESS; + + /* Allocate the first chunk of the heap. */ + VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapChunkAlloc (0); + + if (!pBlock) + rc = VERR_NO_MEMORY; + + RTSemFastMutexCreate(&g_vbgldata.mutexHeap); + + return rc; +} + +DECLR0VBGL(void) VbglR0PhysHeapTerminate (void) +{ + while (g_vbgldata.pChunkHead) + { + vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead); + } + + RTSemFastMutexDestroy(g_vbgldata.mutexHeap); +} + 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..13db0c1e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c @@ -0,0 +1,704 @@ +/* $Id: VBoxGuestR0LibSharedFolders.c $ */ +/** @file + * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * 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 SHFL_CPARMS_SET_UTF8 0 +#define SHFL_CPARMS_SET_SYMLINKS 0 + +#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). */ + + + +/** @todo We only need HGCM, not physical memory, so other guests should also + * switch to calling vbglR0HGCMInit() and vbglR0HGCMTerminate() instead + * of VbglR0SfInit() and VbglR0SfTerm(). */ +#ifndef RT_OS_LINUX +DECLVBGL(int) VbglR0SfInit(void) +{ + return VbglR0InitClient(); +} + +DECLVBGL(void) VbglR0SfTerm(void) +{ + VbglR0TerminateClient(); +} +#endif + +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; +} + +/** @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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + + +/** @} */ + 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..850ee33a --- /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-2019 Oracle Corporation + * + * 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..eab9c3e9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp @@ -0,0 +1,475 @@ +/* $Id: VBoxGuestR3Lib.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..f3bb9a57 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp @@ -0,0 +1,353 @@ +/* $Id: VBoxGuestR3LibAdditions.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Additions Info. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..3374326c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp @@ -0,0 +1,120 @@ +/* $Id: VBoxGuestR3LibAutoLogon.cpp $ */ +/** @file + * VBoxGuestR3LibAutoLogon - Ring-3 utility functions for auto-logon modules + * (VBoxGINA / VBoxCredProv / pam_vbox). + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..2c192f0d --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp @@ -0,0 +1,73 @@ +/* $Id: VBoxGuestR3LibBalloon.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Ballooning. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..f7653bcf --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp @@ -0,0 +1,180 @@ +/* $Id: VBoxGuestR3LibClipboard.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Clipboard. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/err.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include "VBoxGuestR3LibInternal.h" + + +/** + * Connects to the clipboard 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 clipboard calls. + */ +VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient); + if (rc == VERR_HGCM_SERVICE_NOT_FOUND) + rc = VINF_PERMISSION_DENIED; + return rc; +} + + +/** + * Disconnect from the clipboard service. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Get a host message. + * + * 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) VbglR3ClipboardGetHostMsg(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats) +{ + VBoxClipboardGetHostMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, 2); + 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. + * + * @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 pv Where to store the data. + * @param cb The size of the buffer pointed to by pv. + * @param pcb The actual size of the host clipboard data. May be larger than cb. + */ +VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcb) +{ + VBoxClipboardReadData Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_READ_DATA, 3); + VbglHGCMParmUInt32Set(&Msg.format, fFormat); + VbglHGCMParmPtrSet(&Msg.ptr, pv, cb); + VbglHGCMParmUInt32Set(&Msg.size, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbActual; + int rc2 = VbglHGCMParmUInt32Get(&Msg.size, &cbActual); + if (RT_SUCCESS(rc2)) + { + *pcb = cbActual; + if (cbActual > cb) + return VINF_BUFFER_OVERFLOW; + return rc; + } + rc = rc2; + } + return rc; +} + + +/** + * Advertises guest clipboard formats to the host. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormats The formats to advertise. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats) +{ + VBoxClipboardFormats Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_FORMATS, 1); + VbglHGCMParmUInt32Set(&Msg.formats, fFormats); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + + +/** + * Send guest clipboard data to the host. + * + * This is usually called in reply to a VBOX_SHARED_CLIPBOARD_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 pv The data. + * @param cb The size of the data. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb) +{ + VBoxClipboardWriteData Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, 2); + VbglHGCMParmUInt32Set(&Msg.format, fFormat); + VbglHGCMParmPtrSet(&Msg.ptr, pv, cb); + + return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); +} + 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..3792db30 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp @@ -0,0 +1,46 @@ +/* $Id: VBoxGuestR3LibCoreDump.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core Dumps. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..6f21aae0 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp @@ -0,0 +1,123 @@ +/* $Id: VBoxGuestR3LibCpuHotPlug.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, CPU Hot Plugging. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..63771dc7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp @@ -0,0 +1,212 @@ +/* $Id: VBoxGuestR3LibCredentials.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, user credentials. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..f31ebc15 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp @@ -0,0 +1,254 @@ +/** $Id: VBoxGuestR3LibDaemonize.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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 "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. + * + * @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) VbglR3Daemonize(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn) +{ +#if defined(RT_OS_OS2) + PPIB pPib; + PTIB pTib; + DosGetInfoBlocks(&pTib, &pPib); + + 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 */ + + 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) && WEXITSTATUS(iStatus) == 0) + exit(0); + sleep(5); + ++cRespawn; + } + } + return VINF_SUCCESS; +#endif +} 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..1385fa52 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp @@ -0,0 +1,1898 @@ +/* $Id: VBoxGuestR3LibDragAndDrop.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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. + * @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); + + VBOXDNDNEXTMSGMSG Msg; + RT_ZERO(Msg); + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GET_NEXT_HOST_MSG, 3); + Msg.uMsg.SetUInt32(0); + Msg.cParms.SetUInt32(0); + Msg.fBlock.SetUInt32(fWait ? 1 : 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc); + rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc); + } + + 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; + + VBOXDNDHGACTIONMSG Msg; + RT_ZERO(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_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); + + VBOXDNDHGLEAVEMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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); + + VBOXDNDHGCANCELMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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); + + VBOXDNDHGSENDDIRMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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); + + VBOXDNDHGSENDFILEDATAMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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); + + VBOXDNDHGSENDFILEHDRMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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. + * @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, DnDDroppedFiles *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; + + /* + * 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. */ + + /* + * Create and query the (unique) drop target directory in the user's temporary directory. + */ + int rc = pDroppedFiles->OpenTemp(0 /* fFlags */); + if (RT_FAILURE(rc)) + { + RTMemFree(pvChunk); + return VERR_NO_MEMORY; + } + + const char *pszDropDir = pDroppedFiles->GetDirAbs(); + AssertPtr(pszDropDir); + + /* + * Enter the main loop of retieving files + directories. + */ + DnDURIObject objFile(DnDURIObject::Type_File); + + char szPathName[RTPATH_MAX] = { 0 }; + uint32_t cbPathName = 0; + uint32_t fFlags = 0; + uint32_t fMode = 0; + + do + { + LogFlowFunc(("Wating 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_HG_SND_DIR: + { + rc = vbglR3DnDHGRecvDir(pCtx, + szPathName, + sizeof(szPathName), + &cbPathName, + &fMode); + LogFlowFunc(("HOST_DND_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 = pDroppedFiles->AddDir(pszPathAbs); + + if (RT_SUCCESS(rc)) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + break; + } + case HOST_DND_HG_SND_FILE_HDR: + case HOST_DND_HG_SND_FILE_DATA: + { + if (uNextMsg == HOST_DND_HG_SND_FILE_HDR) + { + rc = vbglR3DnDHGRecvFileHdr(pCtx, + szPathName, + sizeof(szPathName), + &fFlags, + &fMode, + &cbFileSize); + LogFlowFunc(("HOST_DND_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_HG_SND_FILE_DATA: " + "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc)); + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_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 (!objFile.IsOpen()) + { + RTCString strPathAbs(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_IRUSR | RTFS_UNIX_IWUSR; +#endif + rc = objFile.OpenEx(strPathAbs, DnDURIObject::View_Target, fOpen, fCreationMode); + if (RT_SUCCESS(rc)) + { + rc = pDroppedFiles->AddFile(strPathAbs.c_str()); + if (RT_SUCCESS(rc)) + { + cbFileWritten = 0; + objFile.SetSize(cbFileSize); + } + } + } + else + { + AssertMsgFailed(("ObjType=%RU32\n", objFile.GetType())); + rc = VERR_WRONG_ORDER; + } + + RTStrFree(pszPathAbs); + } + else + rc = VERR_NO_MEMORY; + } + + if ( RT_SUCCESS(rc) + && uNextMsg == HOST_DND_HG_SND_FILE_DATA + && cbChunkRead) + { + uint32_t cbChunkWritten; + rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("HOST_DND_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 = objFile.IsComplete(); + if (fClose) + { + Assert(cToRecvObjs); + cToRecvObjs--; + } + + /* Only since protocol v2 we know the file size upfront. */ + Assert(cbFileWritten <= cbFileSize); + + if (fClose) + { + LogFlowFunc(("Closing file\n")); + objFile.Close(); + } + + break; + } + case HOST_DND_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + rc = VERR_CANCELLED; + break; + } + default: + { + LogFlowFunc(("Message %RU32 not supported\n", 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)) + { + objFile.Close(); + pDroppedFiles->Rollback(); + } + else + { + /** @todo Compare the URI 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 = pDroppedFiles->Reset(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_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)); + + VBOXDNDHGSENDDATAMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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->uProtocol >= 3); /* Only for protocol v3 and up. */ + + VBOXDNDHGSENDDATAHDRMSG Msg; + RT_ZERO(Msg); + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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, extended version. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pEnmType Where to store the meta data type. Optional. + * @param ppvData Returns the received meta data. Needs to be free'd by the caller. Optional. + * @param pcbData Where to store the size (in bytes) of the received meta data. Optional. + */ +static int vbglR3DnDHGRecvDataMainEx(PVBGLR3GUESTDNDCMDCTX pCtx, + VBGLR3GUESTDNDMETADATATYPE *pEnmType, + void **ppvData, + uint32_t *pcbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* The rest is optional. */ + + VBOXDNDDATAHDR dataHdr; + RT_ZERO(dataHdr); + + AssertMsg(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n")); + + dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize; + dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt); + if (!dataHdr.pvMetaFmt) + return VERR_NO_MEMORY; + + DnDURIList lstURI; + DnDDroppedFiles droppedFiles; + + void *pvData = NULL; + uint64_t cbData = 0; + + int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData); + if (RT_SUCCESS(rc)) + { + /** + * 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. */ + { + AssertPtr(pvData); + Assert(cbData); + + rc = lstURI.SetFromURIData(pvData, cbData, 0 /* fFlags */); + if (RT_SUCCESS(rc)) + rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles); + + if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */ + { + if (pvData) + { + /* Reuse data buffer to fill in the transformed URI file list. */ + RTMemFree(pvData); + pvData = NULL; + } + + RTCString strData = lstURI.GetRootEntries(droppedFiles.GetDirAbs()); + Assert(!strData.isEmpty()); + + cbData = strData.length() + 1; + LogFlowFunc(("URI list has %zu bytes\n", cbData)); + + pvData = RTMemAlloc(cbData); + if (pvData) + { + memcpy(pvData, strData.c_str(), cbData); + + if (pEnmType) + *pEnmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST; + } + else + rc = VERR_NO_MEMORY; + } + } + else /* Raw data. */ + { + if (pEnmType) + *pEnmType = VBGLR3GUESTDNDMETADATATYPE_RAW; + } + } + + if (dataHdr.pvMetaFmt) + RTMemFree(dataHdr.pvMetaFmt); + + if (RT_SUCCESS(rc)) + { + if ( pvData + && cbData) + { + if (pcbData) + *pcbData = cbData; + if (ppvData) + *ppvData = pvData; + else + RTMemFree(pvData); + } + } + else if ( RT_FAILURE(rc) + && rc != VERR_CANCELLED) + { + if (pvData) + RTMemFree(pvData); + + int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2)); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest + * Main function for receiving the actual DnD data from the host. + * + * @returns IPRT status code. + * @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(pMeta, VERR_INVALID_POINTER); + + int rc = vbglR3DnDHGRecvDataMainEx(pCtx, + &pMeta->enmType, + &pMeta->pvMeta, + &pMeta->cbMeta); + return rc; +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host + * Utility function to receive the HOST_DND_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. */ + + VBOXDNDGHREQPENDINGMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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_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; + + VBOXDNDGHDROPPEDMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_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; + + /* Set the default protocol version to use. */ + pCtx->uProtocol = 3; + Assert(pCtx->uClientID); + + /* + * 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); + LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2)); + + /* + * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT. + */ + bool fSupportsConnectReq = false; + if (RT_SUCCESS(rc)) + { + /* The guest property service might not be available. Not fatal. */ + uint32_t uGuestPropSvcClientID; + rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID); + if (RT_SUCCESS(rc2)) + { + char *pszHostVersion; + rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion); + if (RT_SUCCESS(rc2)) + { + fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0; + LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq)); + VbglR3GuestPropReadValueFree(pszHostVersion); + } + + VbglR3GuestPropDisconnect(uGuestPropSvcClientID); + } + + if (RT_FAILURE(rc2)) + LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2)); + } + + if (fSupportsConnectReq) + { + /* + * 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. + */ + VBOXDNDCONNECTMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 3); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol); + Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */ + + rc2 = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_FAILURE(rc2)) + fSupportsConnectReq = false; + + LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2)); + } + + if (fSupportsConnectReq) + { + pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */ + } + else /* GUEST_DND_CONNECT not supported; the user needs to upgrade the host. */ + rc = VERR_NOT_SUPPORTED; + + LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, 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); + int rc = VbglR3HGCMDisconnect(pCtx->uClientID); + if (RT_SUCCESS(rc)) + pCtx->uClientID = 0; + 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 IPRT 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)) + { + LogFlowFunc(("VM session ID changed to %RU64\n", uSessionID)); + } + } + + if (RT_SUCCESS(rc)) + { + LogFunc(("Handling uMsg=%RU32\n", uMsg)); + + switch(uMsg) + { + case HOST_DND_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_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_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_HG_EVT_LEAVE: + { + rc = vbglR3DnDHGRecvLeave(pCtx); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE; + break; + } + case HOST_DND_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_HG_SND_DIR: + RT_FALL_THROUGH(); + case HOST_DND_HG_SND_FILE_DATA: + { + /* + * All messages in this case are handled internally + * by vbglR3DnDHGRecvDataMain() and must be specified + * by preceeding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR calls. + */ + rc = VERR_WRONG_ORDER; + break; + } + case HOST_DND_CANCEL: + { + rc = vbglR3DnDHGRecvCancel(pCtx); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL; + break; + } +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case HOST_DND_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_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)) + { + VbglR3DnDEventFree(pEvent); + LogFlowFunc(("Failed with %Rrc\n", rc)); + } + else + *ppEvent = pEvent; + + 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; + if (pMeta->pvMeta) + { + Assert(pMeta->cbMeta); + RTMemFree(pMeta->pvMeta); + pMeta->cbMeta = 0; + } + break; + } + + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBOXDNDHGACKOPMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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 */ + + VBOXDNDHGREQDATAMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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); + + VBOXDNDHGEVTPROGRESSMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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; + + VBOXDNDGHACKPENDINGMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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); + + VBOXDNDGHSENDDATAHDRMSG MsgHdr; + RT_ZERO(MsgHdr); + + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_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)) + { + VBOXDNDGHSENDDATAMSG MsgData; + RT_ZERO(MsgData); + + VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_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; + + HGCMFunctionParameter *pParm = &MsgData.u.v3.pvData; + + while (cbSent < cbData) + { + cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk); + pParm->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 URI object containing the directory to send. + */ +static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj) +{ + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pObj->GetType() == DnDURIObject::Type_Directory, VERR_INVALID_PARAMETER); + + RTCString strPath = pObj->GetDestPathAbs(); + LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n", + strPath.c_str(), strPath.length(), pObj->GetMode())); + + if (strPath.length() > RTPATH_MAX) + return VERR_INVALID_PARAMETER; + + const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */ + + VBOXDNDGHSENDDIRMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4); + /** @todo Context ID not used yet. */ + Msg.u.v3.uContext.SetUInt32(0); + Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath); + Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath); + Msg.u.v3.fMode.SetUInt32(pObj->GetMode()); + + 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 URI object containing the file to send. + */ +static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + AssertReturn(pObj->GetType() == DnDURIObject::Type_File, VERR_INVALID_PARAMETER); + AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE); + + uint32_t cbBuf = _64K; /** @todo Make this configurable? */ + void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */ + if (!pvBuf) + return VERR_NO_MEMORY; + + RTCString strPath = pObj->GetDestPathAbs(); + + LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(), + pObj->GetSize(), pObj->GetMode())); + + VBOXDNDGHSENDFILEHDRMSG MsgHdr; + RT_ZERO(MsgHdr); + + VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6); + MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */ + MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1)); + MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1)); + MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */ + MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */ + MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */ + + int 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. + */ + VBOXDNDGHSENDFILEDATAMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_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 = pObj->GetSize(); + uint64_t cbWrittenTotal = 0; + while (cbToReadTotal) + { + uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf); + uint32_t cbRead = 0; + if (cbToRead) + rc = pObj->Read(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, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize())); + }; + } + + RTMemFree(pvBuf); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host + * Utility function to send an URI object from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pObj URI object to send from guest to the host. + */ +static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pObj, VERR_INVALID_POINTER); + + int rc; + + switch (pObj->GetType()) + { + case DnDURIObject::Type_Directory: + rc = vbglR3DnDGHSendDir(pCtx, pObj); + break; + + case DnDURIObject::Type_File: + rc = vbglR3DnDGHSendFile(pCtx, pObj); + break; + + default: + AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType())); + 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); + + /* For raw data only the total size is required to be specified. */ + dataHdr.cbTotal = cbData; + + return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr); +} + +/** + * Guest -> Host + * Utility function to send URI data from guest to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param pvData Block to URI data to send. + * @param cbData Size (in bytes) of URI data to send. + */ +static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + RTCList<RTCString> lstPaths = + RTCString((const char *)pvData, cbData).split("\r\n"); + + /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */ + /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */ + uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN; + + DnDURIList lstURI; + int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags); + if (RT_SUCCESS(rc)) + { + /* + * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory + * URI list the host needs to know upfront to set up the drag'n drop operation. + */ + RTCString strRootDest = lstURI.GetRootEntries(); + if (strRootDest.isNotEmpty()) + { + void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */ + uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */ + + /* The total size also contains the size of the meta data. */ + uint64_t cbTotal = cbURLIist; + cbTotal += lstURI.GetTotalBytes(); + + /* We're going to send an URI 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 = lstURI.GetTotalCount(); + + rc = vbglR3DnDGHSendDataInternal(pCtx, + pvURIList, cbURLIist, &dataHdr); + } + else + rc = VERR_INVALID_PARAMETER; + } + + if (RT_SUCCESS(rc)) + { + while (!lstURI.IsEmpty()) + { + DnDURIObject *pNextObj = lstURI.First(); + + rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj); + if (RT_FAILURE(rc)) + break; + + lstURI.RemoveFirst(); + } + } + + 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. + * @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)); + + int rc; + if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat))) + { + /* Send file data. */ + rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData); + } + else + rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData); + + if (RT_FAILURE(rc)) + { + int rc2 = VbglR3DnDGHSendError(pCtx, rc); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2)); + } + + return rc; +} + +/** + * Guest -> Host + * Send an error back to the host. + * + * @returns IPRT status code. + * @param pCtx DnD context to use. + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBOXDNDGHEVTERRORMSG Msg; + RT_ZERO(Msg); + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_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; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + 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..b96f18af --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp @@ -0,0 +1,93 @@ +/* $Id: VBoxGuestR3LibEvent.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Events. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..583a2ea9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp @@ -0,0 +1,80 @@ +/* $Id: VBoxGuestR3LibGR.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, GR. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..d3db9cdf --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp @@ -0,0 +1,1531 @@ +/* $Id: VBoxGuestR3LibGuestCtrl.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest control. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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/HostServices/GuestControlSvc.h> + +#ifndef RT_OS_WINDOWS +# include <signal.h> +#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; +} + + +/** + * 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)); +} + + +VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc) +{ + return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */, + NULL /* pvPayload */, 0 /* cbPayload */); +} + + +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; +} + + +/** + * Asks a specific guest session to close. + * + * @return IPRT status code. + * @param pCtx Host context. + * @param fFlags Some kind of flag. Figure it out yourself. + ** @todo Docs! + */ +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)); +} + + +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)); +} + + +/** + * Retrieves a HOST_SESSION_CREATE message. + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puProtocol, + char *pszUser, uint32_t cbUser, + char *pszPassword, uint32_t cbPassword, + char *pszDomain, uint32_t cbDomain, + uint32_t *pfFlags, uint32_t *pidSession) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puProtocol, VERR_INVALID_POINTER); + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + AssertPtrReturn(pszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(pszDomain, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + int 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, pszUser, cbUser); + VbglHGCMParmPtrSet(&Msg.password, pszPassword, cbPassword); + VbglHGCMParmPtrSet(&Msg.domain, pszDomain, cbDomain); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.protocol.GetUInt32(puProtocol); + 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_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_EXEC_CMD message. + * + * @todo Move the parameters in an own struct! + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszCmd, uint32_t cbCmd, + uint32_t *pfFlags, + char *pszArgs, uint32_t cbArgs, uint32_t *pcArgs, + char *pszEnv, uint32_t *pcbEnv, uint32_t *pcEnvVars, + char *pszUser, uint32_t cbUser, + char *pszPassword, uint32_t cbPassword, + uint32_t *puTimeoutMS, + uint32_t *puPriority, + uint64_t *puAffinity, uint32_t cbAffinity, uint32_t *pcAffinity) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertPtrReturn(pszCmd, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + AssertPtrReturn(pszArgs, VERR_INVALID_POINTER); + AssertPtrReturn(pcArgs, VERR_INVALID_POINTER); + AssertPtrReturn(pszEnv, VERR_INVALID_POINTER); + AssertPtrReturn(pcbEnv, VERR_INVALID_POINTER); + AssertPtrReturn(pcEnvVars, VERR_INVALID_POINTER); + AssertPtrReturn(puTimeoutMS, VERR_INVALID_POINTER); + + int rc; + do + { + 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, pszCmd, cbCmd); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.num_args, 0); + VbglHGCMParmPtrSet(&Msg.args, pszArgs, cbArgs); + VbglHGCMParmUInt32Set(&Msg.num_env, 0); + VbglHGCMParmUInt32Set(&Msg.cb_env, 0); + VbglHGCMParmPtrSet(&Msg.env, pszEnv, *pcbEnv); + if (pCtx->uProtocol < 2) + { + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + AssertReturn(cbUser, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszPassword, VERR_INVALID_POINTER); + AssertReturn(pszPassword, VERR_INVALID_PARAMETER); + + VbglHGCMParmPtrSet(&Msg.u.v1.username, pszUser, cbUser); + VbglHGCMParmPtrSet(&Msg.u.v1.password, pszPassword, cbPassword); + VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0); + } + else + { + AssertPtrReturn(puAffinity, VERR_INVALID_POINTER); + AssertReturn(cbAffinity, VERR_INVALID_PARAMETER); + + VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0); + VbglHGCMParmPtrSet(&Msg.u.v2.affinity, puAffinity, cbAffinity); + } + + rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(pfFlags); + Msg.num_args.GetUInt32(pcArgs); + Msg.num_env.GetUInt32(pcEnvVars); + Msg.cb_env.GetUInt32(pcbEnv); + if (pCtx->uProtocol < 2) + Msg.u.v1.timeout.GetUInt32(puTimeoutMS); + else + { + Msg.u.v2.timeout.GetUInt32(puTimeoutMS); + Msg.u.v2.priority.GetUInt32(puPriority); + Msg.u.v2.num_affinity.GetUInt32(pcAffinity); + } + } + } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel); + return rc; +} + + +/** + * Allocates and gets host data, based on the message id. + * + * This will block until data becomes available. + * + * @returns VBox status code. + ** @todo Docs! + */ +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); + VbglHGCMParmUInt32Set(&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); + VbglHGCMParmUInt32Set(&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_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; +} + + +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)); +} + + +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)); +} + + +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)); +} + + +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)); +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint32_t uWritten) +{ + 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, uWritten); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.write)); +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint64_t uOffActual) +{ + 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, uOffActual); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.seek)); +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint64_t uOffActual) +{ + 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, uOffActual); + + return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.tell)); +} + + +/** + * Callback for reporting a guest process status (along with some other stuff) to the host. + * + * @returns VBox status code. + ** @todo Docs! + */ +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. + ** @todo Docs! + */ +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. + ** @todo Docs! + */ +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..be8018e9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp @@ -0,0 +1,924 @@ +/* $Id: VBoxGuestR3LibGuestProp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#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/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); +} + + +/** + * 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; +} + +#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. + */ +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) +{ + /* + * 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); + 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\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)) + { + /* 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) + { + /* 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 = 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..c8fb7450 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp @@ -0,0 +1,109 @@ +/* $Id: VBoxGuestR3LibGuestUser.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * guest user reporting / utility functions. + */ + +/* + * Copyright (C) 2013-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..c2f907a7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp @@ -0,0 +1,95 @@ +/* $Id: VBoxGuestR3LibHGCM.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * generic HGCM. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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) +{ + VBGLIOCHGCMCONNECT Info; + RT_ZERO(Info); + VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT); + Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; + strcpy(Info.u.In.Loc.u.host.achName, pszServiceName); + + int 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 VbglR3InfoSvcConnect(). + */ +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..3e6ad14b --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp @@ -0,0 +1,225 @@ +/* $Id: VBoxGuestR3LibHostChannel.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Host Channel. + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +#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..278b3076 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp @@ -0,0 +1,201 @@ +/* $Id: */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, host version check. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <stdio.h> /* Required for sscanf */ +#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) +{ + 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; +} + + +/** 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) +{ + Assert(idClient > 0); + AssertPtr(ppszVer); + return VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", ppszVer); +} + + +/** 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) +{ + Assert(idClient > 0); + AssertPtr(pszVer); + return VbglR3GuestPropWriteValue(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", pszVer); +} + 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..2444b7d6 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h @@ -0,0 +1,119 @@ +/* $Id: VBoxGuestR3LibInternal.h $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 support library for the guest additions, Internal header. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#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; + } + return VERR_INVALID_PARAMETER; +} + + +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; + } + return VERR_INVALID_PARAMETER; +} + + +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..1663f2fe --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp @@ -0,0 +1,84 @@ +/* $Id: VBoxGuestR3LibLog.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Logging. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..67def73a --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp @@ -0,0 +1,125 @@ +/* $Id: VBoxGuestR3LibMisc.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Misc. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..a43bbf7c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp @@ -0,0 +1,170 @@ +/* $Id: VBoxGuestR3LibModule.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared modules. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..8cfd8100 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp @@ -0,0 +1,80 @@ +/* $Id: VBoxGuestR3LibMouse.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Mouse. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..c0396cbb --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp @@ -0,0 +1,108 @@ +/** $Id: VBoxGuestR3LibPidFile.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * Create a PID file. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/file.h> +#include <iprt/string.h> +#include <iprt/process.h> +#include "VBoxGuestR3LibInternal.h" + +/** + * 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); + } +} + 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..e9707800 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp @@ -0,0 +1,98 @@ +/* $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-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..78c19eb7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp @@ -0,0 +1,172 @@ +/* $Id: VBoxGuestR3LibSeamless.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Seamless mode. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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) +{ + uint32_t fEvent = 0; + int rc; + + AssertPtrReturn(pMode, VERR_INVALID_PARAMETER); + rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &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; +} + 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..0fb9a6a9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp @@ -0,0 +1,422 @@ +/* $Id: VBoxGuestR3LibSharedFolders.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, shared folders. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..22e5caee --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp @@ -0,0 +1,69 @@ +/* $Id: VBoxGuestR3LibStat.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Statistics. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..ecabde0e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp @@ -0,0 +1,45 @@ +/* $Id: VBoxGuestR3LibTime.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Time. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..9757d2dc --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp @@ -0,0 +1,575 @@ +/* $Id: VBoxGuestR3LibVideo.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Video. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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> + +#include <stdio.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 */ + +#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 */ + 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 */ + 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)) + { + unsigned cx = 0; + unsigned cy = 0; + unsigned cBits = 0; + unsigned x = 0; + unsigned y = 0; + unsigned fEnabled = 1; + char ch1 = 0; + char ch2 = 0; + int cMatches = sscanf(szModeParms, "%5ux%5ux%2u%c%5ux%5u,%1u%c", &cx, &cy, &cBits, &ch1, &x, &y, &fEnabled, &ch2); + if ( (cMatches == 7 && ch1 == ',') + || cMatches == 3) + { + 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); + rc = VINF_SUCCESS; + } + else if (cMatches < 0) + rc = VERR_READ_ERROR; + else + rc = VERR_PARSE_ERROR; + } + } + + return rc; +#else /* !VBOX_WITH_GUEST_PROPS */ + 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..5e7099ca --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp @@ -0,0 +1,54 @@ +/* $Id: VBoxGuestR3LibVrdp.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, VRDP. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* 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..a9c93970 --- /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-2019 Oracle Corporation + * + * 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); +} + |