summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common/VBoxGuest/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Additions/common/VBoxGuest/lib
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest/lib')
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk254
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp134
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp183
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp240
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp1175
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp85
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp97
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp64
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp208
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp205
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp333
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h212
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp130
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp1197
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c716
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp51
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp485
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp363
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp130
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp83
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp2609
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp56
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp133
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp222
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp326
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp1948
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp199
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp103
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp90
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp2214
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp1032
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp119
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp235
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp226
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h131
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp94
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp135
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp180
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp90
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp151
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp108
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp209
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp432
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp79
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp55
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp618
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp64
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp52
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk52
-rw-r--r--src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp415
51 files changed, 18830 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk
new file mode 100644
index 00000000..eead95e5
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/Makefile.kmk
@@ -0,0 +1,254 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the common guest addition code library.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# Target config.
+#
+if defined(VBOX_WITH_ADDITION_DRIVERS) && !defined(VBOX_ONLY_VALIDATIONKIT)
+ LIBRARIES += \
+ VBoxGuestR0Lib \
+ VBoxGuestR0LibBase
+endif
+LIBRARIES += \
+ VBoxGuestR3Lib \
+ VBoxGuestR3LibShared
+ifndef VBOX_ONLY_VALIDATIONKIT
+ if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd)
+ ifndef VBOX_USE_SYSTEM_XORG_HEADERS
+ LIBRARIES += \
+ VBoxGuestR3LibXFree86
+ endif
+ endif
+ if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris)
+ LIBRARIES += \
+ VBoxGuestR3LibXOrg
+ endif
+endif
+LIBRARIES.win.amd64 += VBoxGuestR3Lib-x86 VBoxGuestR3LibShared-x86
+
+
+#
+# VBoxGuestR0Lib
+#
+VBoxGuestR0Lib_TEMPLATE = VBoxGuestR0DrvLib
+VBoxGuestR0Lib_DEFS = VBOX_WITH_HGCM \
+ $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
+VBoxGuestR0Lib_INCS = \
+ $(VBoxGuestR0Lib_0_OUTDIR)
+VBoxGuestR0Lib_SOURCES = \
+ VBoxGuestR0LibInit.cpp \
+ VBoxGuestR0LibPhysHeap.cpp \
+ VBoxGuestR0LibGenericRequest.cpp \
+ VBoxGuestR0LibVMMDev.cpp \
+ VBoxGuestR0LibHGCM.cpp \
+ VbglR0CanUsePhysPageList.cpp \
+ \
+ VBoxGuestR0LibIdc.cpp \
+ VBoxGuestR0LibSharedFolders.c \
+ VBoxGuestR0LibCrOgl.cpp \
+ VBoxGuestR0LibMouse.cpp
+VBoxGuestR0Lib_SOURCES.os2 = VBoxGuestR0LibIdc-os2.cpp
+VBoxGuestR0Lib_SOURCES.solaris = VBoxGuestR0LibIdc-solaris.cpp
+VBoxGuestR0Lib_SOURCES.win = VBoxGuestR0LibIdc-win.cpp
+ifn1of ($(KBUILD_TARGET), os2 solaris win)
+ VBoxGuestR0Lib_SOURCES += VBoxGuestR0LibIdc-unix.cpp
+endif
+
+#
+# VBoxGuestR0LibBase
+#
+VBoxGuestR0LibBase_TEMPLATE = VBoxGuestR0DrvLib
+VBoxGuestR0LibBase_DEFS = VBOX_WITH_HGCM VBGL_VBOXGUEST \
+ $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
+VBoxGuestR0LibBase_INCS = $(VBoxGuestR0Lib_INCS)
+VBoxGuestR0LibBase_INCS.win = $(VBoxGuestR0Lib_INCS.win)
+VBoxGuestR0LibBase_SOURCES = \
+ VBoxGuestR0LibInit.cpp \
+ VBoxGuestR0LibPhysHeap.cpp \
+ VBoxGuestR0LibGenericRequest.cpp \
+ VBoxGuestR0LibVMMDev.cpp \
+ VBoxGuestR0LibHGCMInternal.cpp \
+ VbglR0CanUsePhysPageList.cpp
+
+#
+# VBoxGuestR3Lib
+#
+VBoxGuestR3Lib_TEMPLATE := VBoxGuestR3Lib
+VBoxGuestR3Lib_DEFS = \
+ VBOX_WITH_HGCM \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_WITH_SHARED_CLIPBOARD),VBOX_WITH_SHARED_CLIPBOARD,) \
+ $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,) \
+ $(if $(VBOX_WITH_SHARED_FOLDERS),VBOX_WITH_SHARED_FOLDERS,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL,)
+VBoxGuestR3Lib_SOURCES = \
+ VBoxGuestR3Lib.cpp \
+ VBoxGuestR3LibAdditions.cpp \
+ VBoxGuestR3LibAutoLogon.cpp \
+ VBoxGuestR3LibBalloon.cpp \
+ VBoxGuestR3LibCoreDump.cpp \
+ VBoxGuestR3LibCpuHotPlug.cpp \
+ VBoxGuestR3LibCredentials.cpp \
+ VBoxGuestR3LibDrmClient.cpp \
+ VBoxGuestR3LibEvent.cpp \
+ VBoxGuestR3LibGuestUser.cpp \
+ VBoxGuestR3LibGR.cpp \
+ VBoxGuestR3LibHGCM.cpp \
+ VBoxGuestR3LibHostChannel.cpp \
+ VBoxGuestR3LibLog.cpp \
+ VBoxGuestR3LibMisc.cpp \
+ VBoxGuestR3LibStat.cpp \
+ VBoxGuestR3LibTime.cpp \
+ VBoxGuestR3LibModule.cpp \
+ VBoxGuestR3LibPidFile.cpp \
+ VBoxGuestR3LibVrdp.cpp \
+ VBoxGuestR3LibMouse.cpp \
+ VBoxGuestR3LibSeamless.cpp \
+ VBoxGuestR3LibVideo.cpp
+ifneq ($(KBUILD_TARGET),win)
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibDaemonize.cpp
+endif
+ifdef VBOX_WITH_GUEST_PROPS
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibGuestProp.cpp \
+ VBoxGuestR3LibHostVersion.cpp
+endif
+ifdef VBOX_WITH_SHARED_CLIPBOARD
+ VBoxGuestR3Lib_DEFS += VBOX_WITH_SHARED_CLIPBOARD_GUEST
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibClipboard.cpp
+ ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ VBoxGuestR3Lib_SOURCES += \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardMIME.cpp
+ endif
+endif
+ifdef VBOX_WITH_SHARED_FOLDERS
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibSharedFolders.cpp
+endif
+ifdef VBOX_WITH_GUEST_CONTROL
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibGuestCtrl.cpp
+endif
+ifdef VBOX_WITH_DRAG_AND_DROP
+ VBoxGuestR3Lib_DEFS += \
+ VBOX_WITH_DRAG_AND_DROP \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
+ VBoxGuestR3Lib_SOURCES += \
+ VBoxGuestR3LibDragAndDrop.cpp
+endif
+
+VBoxGuestR3LibAdditions.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+#
+# VBoxGuestR3LibShared - a PIC variant of VBoxGuestR3Lib for linking into .so/.dll/.dylib.
+#
+VBoxGuestR3LibShared_TEMPLATE := VBoxGuestR3Dll
+VBoxGuestR3LibShared_DEFS := $(VBoxGuestR3Lib_DEFS)
+VBoxGuestR3LibShared_SOURCES := $(VBoxGuestR3Lib_SOURCES)
+VBoxGuestR3LibShared_INST := $(INST_ADDITIONS_LIB)
+
+
+#
+# VBoxGuestR3Lib-x86 - an x86 (32-bit) variant of VBoxGuestR3Lib for 64-bit Windows.
+#
+VBoxGuestR3Lib-x86_EXTENDS := VBoxGuestR3Lib
+VBoxGuestR3Lib-x86_BLD_TRG_ARCH := x86
+
+
+#
+# VBoxGuestR3LibShared-x86 - an x86 (32-bit) variant of VBoxGuestR3LibShared for 64-bit Windows.
+#
+VBoxGuestR3LibShared-x86_EXTENDS := VBoxGuestR3LibShared
+VBoxGuestR3LibShared-x86_BLD_TRG_ARCH := x86
+
+
+#
+# VBoxGuestR3LibXFree86 - a reduced version of the guest library which uses
+# the X server runtime instead of IPRT, for use with old servers where the
+# C library is not available.
+#
+VBoxGuestR3LibXFree86_TEMPLATE = VBoxGuestR3XFree86Lib
+VBoxGuestR3LibXFree86_DEFS = \
+ VBOX_WITH_HGCM \
+ VBOX_VBGLR3_XFREE86 \
+ RTMEM_NO_WRAP_TO_EF_APIS \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \
+ $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,)
+VBoxGuestR3LibXFree86_SOURCES = \
+ VBoxGuestR3Lib.cpp \
+ VBoxGuestR3LibGR.cpp \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBoxGuestR3LibGuestProp.cpp,) \
+ VBoxGuestR3LibMouse.cpp \
+ VBoxGuestR3LibMisc.cpp \
+ VBoxGuestR3LibSeamless.cpp \
+ VBoxGuestR3LibVideo.cpp \
+ VBoxGuestR3LibRuntimeXF86.cpp
+VBoxGuestR3LibXFree86_INCS = \
+ $(VBOX_PATH_X11_ROOT)/XFree86-4.3/Xserver \
+ $(VBOX_PATH_X11_ROOT)/XFree86-4.3 \
+ $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11
+
+#
+# VBoxGuestR3LibXOrg - a reduced version of the guest library which uses
+# the C server runtime instead of IPRT.
+#
+VBoxGuestR3LibXOrg_TEMPLATE = VBoxGuestR3XOrgLib
+VBoxGuestR3LibXOrg_DEFS = \
+ VBOX_WITH_HGCM \
+ VBOX_VBGLR3_XORG \
+ RTMEM_NO_WRAP_TO_EF_APIS \
+ IN_RT_STATIC \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP),VBOX_WITH_DRAG_AND_DROP,) \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) \
+ $(if $(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS),VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS,)
+VBoxGuestR3LibXOrg_SOURCES = $(VBoxGuestR3LibXFree86_SOURCES)
+
+VBoxGuestR3LibRuntimeXF86.cpp_CXXFLAGS = -Wno-shadow
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp
new file mode 100644
index 00000000..a6638dc0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibCrOgl.cpp
@@ -0,0 +1,134 @@
+/* $Id: VBoxGuestR0LibCrOgl.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-3 Support Library for VirtualBox guest additions, Chromium OpenGL Service.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include "VBoxGuestR0LibInternal.h"
+
+#ifdef VBGL_VBOXGUEST
+# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code."
+#endif
+
+
+DECLR0VBGL(int) VbglR0CrCtlCreate(VBGLCRCTLHANDLE *phCtl)
+{
+ int rc;
+
+ if (phCtl)
+ {
+ struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc();
+ if (pHandleData)
+ {
+ rc = VbglR0IdcOpen(&pHandleData->IdcHandle,
+ VBGL_IOC_VERSION /*uReqVersion*/,
+ VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/,
+ NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/);
+ if (RT_SUCCESS(rc))
+ {
+ *phCtl = pHandleData;
+ return VINF_SUCCESS;
+ }
+
+ vbglR0HGCMHandleFree(pHandleData);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ *phCtl = NULL;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0CrCtlDestroy(VBGLCRCTLHANDLE hCtl)
+{
+ VbglR0IdcClose(&hCtl->IdcHandle);
+
+ vbglR0HGCMHandleFree(hCtl);
+
+ return VINF_SUCCESS;
+}
+
+DECLR0VBGL(int) VbglR0CrCtlConConnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID *pidClient)
+{
+ VBGLIOCHGCMCONNECT info;
+ int rc;
+
+ if (!hCtl || !pidClient)
+ return VERR_INVALID_PARAMETER;
+
+ RT_ZERO(info);
+ VBGLREQHDR_INIT(&info.Hdr, HGCM_CONNECT);
+ info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
+ RTStrCopy(info.u.In.Loc.u.host.achName, sizeof(info.u.In.Loc.u.host.achName), "VBoxSharedCrOpenGL");
+ rc = VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &info.Hdr, sizeof(info));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(info.u.Out.idClient);
+ *pidClient = info.u.Out.idClient;
+ return rc;
+ }
+
+ AssertRC(rc);
+ *pidClient = 0;
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0CrCtlConDisconnect(VBGLCRCTLHANDLE hCtl, HGCMCLIENTID idClient)
+{
+ VBGLIOCHGCMDISCONNECT info;
+ VBGLREQHDR_INIT(&info.Hdr, HGCM_DISCONNECT);
+ info.u.In.idClient = idClient;
+ return VbglR0IdcCall(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &info.Hdr, sizeof(info));
+}
+
+DECLR0VBGL(int) VbglR0CrCtlConCallRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo)
+{
+ return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo);
+}
+
+DECLR0VBGL(int) VbglR0CrCtlConCall(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo)
+{
+ int rc = VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbCallInfo), &pCallInfo->Hdr, cbCallInfo);
+ if (RT_SUCCESS(rc))
+ rc = pCallInfo->Hdr.rc;
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0CrCtlConCallUserDataRaw(VBGLCRCTLHANDLE hCtl, PVBGLIOCHGCMCALL pCallInfo, int cbCallInfo)
+{
+ return VbglR0IdcCallRaw(&hCtl->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbCallInfo), &pCallInfo->Hdr, cbCallInfo);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp
new file mode 100644
index 00000000..72518a3e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp
@@ -0,0 +1,183 @@
+/* $Id: VBoxGuestR0LibGenericRequest.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Generic VMMDev request management.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <VBox/err.h>
+
+
+DECLR0VBGL(int) VbglGR0Verify(const VMMDevRequestHeader *pReq, size_t cbReq)
+{
+ size_t cbReqExpected;
+
+ if (RT_UNLIKELY(!pReq || cbReq < sizeof(VMMDevRequestHeader)))
+ {
+ dprintf(("VbglGR0Verify: Invalid parameter: pReq = %p, cbReq = %zu\n", pReq, cbReq));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_UNLIKELY(pReq->size > cbReq))
+ {
+ dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* The request size must correspond to the request type. */
+ cbReqExpected = vmmdevGetRequestSize(pReq->requestType);
+ if (RT_UNLIKELY(cbReq < cbReqExpected))
+ {
+ dprintf(("VbglGR0Verify: buffer size %zu < expected size %zu\n", cbReq, cbReqExpected));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (cbReqExpected == cbReq)
+ {
+ /*
+ * This is most likely a fixed size request, and in this case the
+ * request size must be also equal to the expected size.
+ */
+ if (RT_UNLIKELY(pReq->size != cbReqExpected))
+ {
+ dprintf(("VbglGR0Verify: request size %u != expected size %zu\n", pReq->size, cbReqExpected));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * This can be a variable size request. Check the request type and limit the size
+ * to VMMDEV_MAX_VMMDEVREQ_SIZE, which is max size supported by the host.
+ *
+ * Note: Keep this list sorted for easier human lookup!
+ */
+ if ( pReq->requestType == VMMDevReq_ChangeMemBalloon
+ || pReq->requestType == VMMDevReq_GetDisplayChangeRequestMulti
+#ifdef VBOX_WITH_64_BITS_GUESTS
+ || pReq->requestType == VMMDevReq_HGCMCall64
+#endif
+ || pReq->requestType == VMMDevReq_HGCMCall32
+ || pReq->requestType == VMMDevReq_RegisterSharedModule
+ || pReq->requestType == VMMDevReq_ReportGuestUserState
+ || pReq->requestType == VMMDevReq_LogString
+ || pReq->requestType == VMMDevReq_SetPointerShape
+ || pReq->requestType == VMMDevReq_VideoSetVisibleRegion
+ || pReq->requestType == VMMDevReq_VideoUpdateMonitorPositions)
+ {
+ if (RT_UNLIKELY(cbReq > VMMDEV_MAX_VMMDEVREQ_SIZE))
+ {
+ dprintf(("VbglGR0Verify: VMMDevReq_LogString: buffer size %zu too big\n", cbReq));
+ return VERR_BUFFER_OVERFLOW; /** @todo is this error code ok? */
+ }
+ }
+ else
+ {
+ dprintf(("VbglGR0Verify: request size %u > buffer size %zu\n", pReq->size, cbReq));
+ return VERR_IO_BAD_LENGTH; /** @todo is this error code ok? */
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLR0VBGL(int) VbglR0GRAlloc(VMMDevRequestHeader **ppReq, size_t cbReq, VMMDevRequestType enmReqType)
+{
+ int rc = vbglR0Enter();
+ if (RT_SUCCESS(rc))
+ {
+ if ( ppReq
+ && cbReq >= sizeof(VMMDevRequestHeader)
+ && cbReq == (uint32_t)cbReq)
+ {
+ VMMDevRequestHeader *pReq = (VMMDevRequestHeader *)VbglR0PhysHeapAlloc((uint32_t)cbReq);
+ AssertMsgReturn(pReq, ("VbglR0GRAlloc: no memory (cbReq=%u)\n", cbReq), VERR_NO_MEMORY);
+ memset(pReq, 0xAA, cbReq);
+
+ pReq->size = (uint32_t)cbReq;
+ pReq->version = VMMDEV_REQUEST_HEADER_VERSION;
+ pReq->requestType = enmReqType;
+ pReq->rc = VERR_GENERAL_FAILURE;
+ pReq->reserved1 = 0;
+#ifdef VBGL_VBOXGUEST
+ pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV
+#else
+ pReq->fRequestor = VMMDEV_REQUESTOR_KERNEL | VMMDEV_REQUESTOR_USR_DRV_OTHER
+#endif
+
+ | VMMDEV_REQUESTOR_CON_DONT_KNOW | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
+ *ppReq = pReq;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ dprintf(("VbglR0GRAlloc: Invalid parameter: ppReq=%p cbReq=%u\n", ppReq, cbReq));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0GRPerform(VMMDevRequestHeader *pReq)
+{
+ int rc = vbglR0Enter();
+ if (RT_SUCCESS(rc))
+ {
+ if (pReq)
+ {
+ RTCCPHYS PhysAddr = VbglR0PhysHeapGetPhysAddr(pReq);
+ if ( PhysAddr != 0
+ && PhysAddr < _4G) /* Port IO is 32 bit. */
+ {
+ ASMOutU32(g_vbgldata.portVMMDev + VMMDEV_PORT_OFF_REQUEST, (uint32_t)PhysAddr);
+ /* Make the compiler aware that the host has changed memory. */
+ ASMCompilerBarrier();
+ rc = pReq->rc;
+ }
+ else
+ rc = VERR_VBGL_INVALID_ADDR;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+ return rc;
+}
+
+DECLR0VBGL(void) VbglR0GRFree(VMMDevRequestHeader *pReq)
+{
+ int rc = vbglR0Enter();
+ if (RT_SUCCESS(rc))
+ VbglR0PhysHeapFree(pReq);
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp
new file mode 100644
index 00000000..e2e28ebb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp
@@ -0,0 +1,240 @@
+/* $Id: VBoxGuestR0LibHGCM.cpp $ */
+/** @file
+ * VBoxGuestLib - Host-Guest Communication Manager, ring-0 client drivers.
+ *
+ * These public functions can be only used by other drivers. They all
+ * do an IOCTL to VBoxGuest via IDC.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <VBox/err.h>
+
+#ifdef VBGL_VBOXGUEST
+# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code."
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBGL_HGCM_ASSERT_MSG AssertReleaseMsg
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Fast heap for HGCM handles data.
+ * @{
+ */
+static RTSEMFASTMUTEX g_hMtxHGCMHandleData;
+static struct VBGLHGCMHANDLEDATA g_aHGCMHandleData[64];
+/** @} */
+
+
+/**
+ * Initializes the HGCM VBGL bits.
+ *
+ * @return VBox status code.
+ */
+DECLR0VBGL(int) VbglR0HGCMInit(void)
+{
+ AssertReturn(g_hMtxHGCMHandleData == NIL_RTSEMFASTMUTEX, VINF_ALREADY_INITIALIZED);
+ return RTSemFastMutexCreate(&g_hMtxHGCMHandleData);
+}
+
+/**
+ * Initializes the HGCM VBGL bits.
+ *
+ * @return VBox status code.
+ */
+DECLR0VBGL(int) VbglR0HGCMTerminate(void)
+{
+ RTSemFastMutexDestroy(g_hMtxHGCMHandleData);
+ g_hMtxHGCMHandleData = NIL_RTSEMFASTMUTEX;
+
+ return VINF_SUCCESS;
+}
+
+DECLINLINE(int) vbglR0HandleHeapEnter(void)
+{
+ int rc = RTSemFastMutexRequest(g_hMtxHGCMHandleData);
+
+ VBGL_HGCM_ASSERT_MSG(RT_SUCCESS(rc), ("Failed to request handle heap mutex, rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+DECLINLINE(void) vbglR0HandleHeapLeave(void)
+{
+ RTSemFastMutexRelease(g_hMtxHGCMHandleData);
+}
+
+struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void)
+{
+ struct VBGLHGCMHANDLEDATA *p = NULL;
+ int rc = vbglR0HandleHeapEnter();
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t i;
+
+ /* Simple linear search in array. This will be called not so often, only connect/disconnect. */
+ /** @todo bitmap for faster search and other obvious optimizations. */
+ for (i = 0; i < RT_ELEMENTS(g_aHGCMHandleData); i++)
+ {
+ if (!g_aHGCMHandleData[i].fAllocated)
+ {
+ p = &g_aHGCMHandleData[i];
+ p->fAllocated = 1;
+ break;
+ }
+ }
+
+ vbglR0HandleHeapLeave();
+
+ VBGL_HGCM_ASSERT_MSG(p != NULL, ("Not enough HGCM handles.\n"));
+ }
+ return p;
+}
+
+void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle)
+{
+ if (pHandle)
+ {
+ int rc = vbglR0HandleHeapEnter();
+ if (RT_SUCCESS(rc))
+ {
+ VBGL_HGCM_ASSERT_MSG(pHandle->fAllocated, ("Freeing not allocated handle.\n"));
+
+ RT_ZERO(*pHandle);
+ vbglR0HandleHeapLeave();
+ }
+ }
+}
+
+DECLR0VBGL(int) VbglR0HGCMConnect(VBGLHGCMHANDLE *pHandle, const char *pszServiceName, HGCMCLIENTID *pidClient)
+{
+ int rc;
+ if (pHandle && pszServiceName && pidClient)
+ {
+ struct VBGLHGCMHANDLEDATA *pHandleData = vbglR0HGCMHandleAlloc();
+ if (pHandleData)
+ {
+ rc = VbglR0IdcOpen(&pHandleData->IdcHandle,
+ VBGL_IOC_VERSION /*uReqVersion*/,
+ VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/,
+ NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*uDriverRevision*/);
+ if (RT_SUCCESS(rc))
+ {
+ VBGLIOCHGCMCONNECT Info;
+ RT_ZERO(Info);
+ VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT);
+ Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
+ rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0IdcCall(&pHandleData->IdcHandle, VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info));
+ if (RT_SUCCESS(rc))
+ {
+ *pidClient = Info.u.Out.idClient;
+ *pHandle = pHandleData;
+ return rc;
+ }
+ }
+
+ VbglR0IdcClose(&pHandleData->IdcHandle);
+ }
+
+ vbglR0HGCMHandleFree(pHandleData);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0HGCMDisconnect(VBGLHGCMHANDLE handle, HGCMCLIENTID idClient)
+{
+ int rc;
+ VBGLIOCHGCMDISCONNECT Info;
+
+ RT_ZERO(Info);
+ VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT);
+ Info.u.In.idClient = idClient;
+ rc = VbglR0IdcCall(&handle->IdcHandle, VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info));
+
+ VbglR0IdcClose(&handle->IdcHandle);
+
+ vbglR0HGCMHandleFree(handle);
+
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0HGCMCallRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData)
+{
+ VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter),
+ ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms,
+ sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL)));
+
+ return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbData), &pData->Hdr, cbData);
+}
+
+DECLR0VBGL(int) VbglR0HGCMCall(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData)
+{
+ int rc = VbglR0HGCMCallRaw(handle, pData, cbData);
+ if (RT_SUCCESS(rc))
+ rc = pData->Hdr.rc;
+ return rc;
+}
+
+DECLR0VBGL(int) VbglR0HGCMCallUserDataRaw(VBGLHGCMHANDLE handle, PVBGLIOCHGCMCALL pData, uint32_t cbData)
+{
+ VBGL_HGCM_ASSERT_MSG(cbData >= sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(HGCMFunctionParameter),
+ ("cbData = %d, cParms = %d (calculated size %d)\n", cbData, pData->cParms,
+ sizeof(VBGLIOCHGCMCALL) + pData->cParms * sizeof(VBGLIOCHGCMCALL)));
+
+ return VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL_WITH_USER_DATA(cbData), &pData->Hdr, cbData);
+}
+
+
+DECLR0VBGL(int) VbglR0HGCMFastCall(VBGLHGCMHANDLE hHandle, PVBGLIOCIDCHGCMFASTCALL pCallReq, uint32_t cbCallReq)
+{
+ /* pCallReq->Hdr.rc and pCallReq->HgcmCallReq.header.header.rc; are not used by this IDC. */
+ return VbglR0IdcCallRaw(&hHandle->IdcHandle, VBGL_IOCTL_IDC_HGCM_FAST_CALL, &pCallReq->Hdr, cbCallReq);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp
new file mode 100644
index 00000000..2a237db1
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp
@@ -0,0 +1,1175 @@
+/* $Id: VBoxGuestR0LibHGCMInternal.cpp $ */
+/** @file
+ * VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_HGCM
+
+#include "VBoxGuestR0LibInternal.h"
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/memobj.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <VBox/err.h>
+
+#ifndef VBGL_VBOXGUEST
+# error "This file should only be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest."
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max parameter buffer size for a user request. */
+#define VBGLR0_MAX_HGCM_USER_PARM (24*_1M)
+/** The max parameter buffer size for a kernel request. */
+#define VBGLR0_MAX_HGCM_KERNEL_PARM (16*_1M)
+/** The max embedded buffer size. */
+#define VBGLR0_MAX_HGCM_EMBEDDED_BUFFER _64K
+
+#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
+/** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted
+ * side effects.
+ * Darwin 32bit & 64bit also needs this because of 4GB/4GB user/kernel space. */
+# define USE_BOUNCE_BUFFERS
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Lock info structure used by VbglR0HGCMInternalCall and its helpers.
+ */
+struct VbglR0ParmInfo
+{
+ uint32_t cLockBufs;
+ struct
+ {
+ uint32_t iParm;
+ RTR0MEMOBJ hObj;
+#ifdef USE_BOUNCE_BUFFERS
+ void *pvSmallBuf;
+#endif
+ } aLockBufs[10];
+};
+
+
+
+/* These functions can be only used by VBoxGuest. */
+
+DECLR0VBGL(int) VbglR0HGCMInternalConnect(HGCMServiceLocation const *pLoc, uint32_t fRequestor, HGCMCLIENTID *pidClient,
+ PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
+{
+ int rc;
+ if ( RT_VALID_PTR(pLoc)
+ && RT_VALID_PTR(pidClient)
+ && RT_VALID_PTR(pfnAsyncCallback))
+ {
+ /* Allocate request */
+ VMMDevHGCMConnect *pHGCMConnect = NULL;
+ rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMConnect, sizeof(VMMDevHGCMConnect), VMMDevReq_HGCMConnect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize request memory */
+ pHGCMConnect->header.header.fRequestor = fRequestor;
+
+ pHGCMConnect->header.fu32Flags = 0;
+
+ memcpy(&pHGCMConnect->loc, pLoc, sizeof(pHGCMConnect->loc));
+ pHGCMConnect->u32ClientID = 0;
+
+ /* Issue request */
+ rc = VbglR0GRPerform (&pHGCMConnect->header.header);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check if host decides to process the request asynchronously. */
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ /* Wait for request completion interrupt notification from host */
+ pfnAsyncCallback(&pHGCMConnect->header, pvAsyncData, u32AsyncData);
+ }
+
+ rc = pHGCMConnect->header.result;
+ if (RT_SUCCESS(rc))
+ *pidClient = pHGCMConnect->u32ClientID;
+ }
+ VbglR0GRFree(&pHGCMConnect->header.header);
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ return rc;
+}
+
+
+DECLR0VBGL(int) VbglR0HGCMInternalDisconnect(HGCMCLIENTID idClient, uint32_t fRequestor,
+ PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
+{
+ int rc;
+ if ( idClient != 0
+ && pfnAsyncCallback)
+ {
+ /* Allocate request */
+ VMMDevHGCMDisconnect *pHGCMDisconnect = NULL;
+ rc = VbglR0GRAlloc ((VMMDevRequestHeader **)&pHGCMDisconnect, sizeof (VMMDevHGCMDisconnect), VMMDevReq_HGCMDisconnect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize request memory */
+ pHGCMDisconnect->header.header.fRequestor = fRequestor;
+
+ pHGCMDisconnect->header.fu32Flags = 0;
+
+ pHGCMDisconnect->u32ClientID = idClient;
+
+ /* Issue request */
+ rc = VbglR0GRPerform(&pHGCMDisconnect->header.header);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check if host decides to process the request asynchronously. */
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ /* Wait for request completion interrupt notification from host */
+ pfnAsyncCallback(&pHGCMDisconnect->header, pvAsyncData, u32AsyncData);
+ }
+
+ rc = pHGCMDisconnect->header.result;
+ }
+
+ VbglR0GRFree(&pHGCMDisconnect->header.header);
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ return rc;
+}
+
+
+/**
+ * Preprocesses the HGCM call, validating and locking/buffering parameters.
+ *
+ * @returns VBox status code.
+ *
+ * @param pCallInfo The call info.
+ * @param cbCallInfo The size of the call info structure.
+ * @param fIsUser Is it a user request or kernel request.
+ * @param pcbExtra Where to return the extra request space needed for
+ * physical page lists.
+ */
+static int vbglR0HGCMInternalPreprocessCall(PCVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo,
+ bool fIsUser, struct VbglR0ParmInfo *pParmInfo, size_t *pcbExtra)
+{
+ HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo);
+ uint32_t const cParms = pCallInfo->cParms;
+ uint32_t iParm;
+ uint32_t cb;
+
+ /*
+ * Lock down the any linear buffers so we can get their addresses
+ * and figure out how much extra storage we need for page lists.
+ *
+ * Note! With kernel mode users we can be assertive. For user mode users
+ * we should just (debug) log it and fail without any fanfare.
+ */
+ *pcbExtra = 0;
+ pParmInfo->cLockBufs = 0;
+ for (iParm = 0; iParm < cParms; iParm++, pSrcParm++)
+ {
+ switch (pSrcParm->type)
+ {
+ case VMMDevHGCMParmType_32bit:
+ Log4(("GstHGCMCall: parm=%u type=32bit: %#010x\n", iParm, pSrcParm->u.value32));
+ break;
+
+ case VMMDevHGCMParmType_64bit:
+ Log4(("GstHGCMCall: parm=%u type=64bit: %#018RX64\n", iParm, pSrcParm->u.value64));
+ break;
+
+ case VMMDevHGCMParmType_PageList:
+ case VMMDevHGCMParmType_ContiguousPageList:
+ if (fIsUser)
+ return VERR_INVALID_PARAMETER;
+ cb = pSrcParm->u.PageList.size;
+ if (cb)
+ {
+ uint32_t off = pSrcParm->u.PageList.offset;
+ HGCMPageListInfo *pPgLst;
+ uint32_t cPages;
+ uint32_t u32;
+
+ AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
+ VERR_OUT_OF_RANGE);
+ AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter)
+ && off <= cbCallInfo - sizeof(HGCMPageListInfo),
+ ("offset=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo),
+ VERR_INVALID_PARAMETER);
+
+ pPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + off);
+ cPages = pPgLst->cPages;
+ u32 = RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]) + off;
+ AssertMsgReturn(u32 <= cbCallInfo,
+ ("u32=%#x (cPages=%#x offset=%#x) cbCallInfo=%#x\n", u32, cPages, off, cbCallInfo),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pPgLst->offFirstPage < PAGE_SIZE, ("#x\n", pPgLst->offFirstPage), VERR_INVALID_PARAMETER);
+ u32 = RT_ALIGN_32(pPgLst->offFirstPage + cb, PAGE_SIZE) >> PAGE_SHIFT;
+ AssertMsgReturn(cPages == u32, ("cPages=%#x u32=%#x\n", cPages, u32), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pPgLst->flags), ("%#x\n", pPgLst->flags), VERR_INVALID_PARAMETER);
+ Log4(("GstHGCMCall: parm=%u type=pglst: cb=%#010x cPgs=%u offPg0=%#x flags=%#x\n",
+ iParm, cb, cPages, pPgLst->offFirstPage, pPgLst->flags));
+ u32 = cPages;
+ while (u32-- > 0)
+ {
+ Log4(("GstHGCMCall: pg#%u=%RHp\n", u32, pPgLst->aPages[u32]));
+ AssertMsgReturn(!(pPgLst->aPages[u32] & (PAGE_OFFSET_MASK | UINT64_C(0xfff0000000000000))),
+ ("pg#%u=%RHp\n", u32, pPgLst->aPages[u32]),
+ VERR_INVALID_PARAMETER);
+ }
+
+ *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[pPgLst->cPages]);
+ }
+ else
+ Log4(("GstHGCMCall: parm=%u type=pglst: cb=0\n", iParm));
+ break;
+
+ case VMMDevHGCMParmType_Embedded:
+ if (fIsUser) /// @todo relax this.
+ return VERR_INVALID_PARAMETER;
+ cb = pSrcParm->u.Embedded.cbData;
+ if (cb)
+ {
+ uint32_t off = pSrcParm->u.Embedded.offData;
+ AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_EMBEDDED_BUFFER, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_EMBEDDED_BUFFER),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(cb <= cbCallInfo - cParms * sizeof(HGCMFunctionParameter),
+ ("cb=%#x cParms=%#x cbCallInfo=%3x\n", cb, cParms, cbCallInfo),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn( off >= cParms * sizeof(HGCMFunctionParameter)
+ && off <= cbCallInfo - cb,
+ ("offData=%#x cParms=%#x cbCallInfo=%#x\n", off, cParms, cbCallInfo),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(VBOX_HGCM_F_PARM_ARE_VALID(pSrcParm->u.Embedded.fFlags),
+ ("%#x\n", pSrcParm->u.Embedded.fFlags), VERR_INVALID_PARAMETER);
+
+ *pcbExtra += RT_ALIGN_32(cb, 8);
+ }
+ else
+ Log4(("GstHGCMCall: parm=%u type=embed: cb=0\n", iParm));
+ break;
+
+
+ case VMMDevHGCMParmType_LinAddr_Locked_In:
+ case VMMDevHGCMParmType_LinAddr_Locked_Out:
+ case VMMDevHGCMParmType_LinAddr_Locked:
+ if (fIsUser)
+ return VERR_INVALID_PARAMETER;
+ if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true))
+ {
+ cb = pSrcParm->u.Pointer.size;
+ AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
+ VERR_OUT_OF_RANGE);
+ if (cb != 0)
+ Log4(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p\n",
+ iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr));
+ else
+ Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type));
+ break;
+ }
+ RT_FALL_THRU();
+
+ case VMMDevHGCMParmType_LinAddr_In:
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr:
+ cb = pSrcParm->u.Pointer.size;
+ if (cb != 0)
+ {
+#ifdef USE_BOUNCE_BUFFERS
+ void *pvSmallBuf = NULL;
+#endif
+ uint32_t iLockBuf = pParmInfo->cLockBufs;
+ RTR0MEMOBJ hObj;
+ int rc;
+ uint32_t fAccess = pSrcParm->type == VMMDevHGCMParmType_LinAddr_In
+ || pSrcParm->type == VMMDevHGCMParmType_LinAddr_Locked_In
+ ? RTMEM_PROT_READ
+ : RTMEM_PROT_READ | RTMEM_PROT_WRITE;
+
+ AssertReturn(iLockBuf < RT_ELEMENTS(pParmInfo->aLockBufs), VERR_INVALID_PARAMETER);
+ if (!fIsUser)
+ {
+ AssertMsgReturn(cb <= VBGLR0_MAX_HGCM_KERNEL_PARM, ("%#x > %#x\n", cb, VBGLR0_MAX_HGCM_KERNEL_PARM),
+ VERR_OUT_OF_RANGE);
+ rc = RTR0MemObjLockKernel(&hObj, (void *)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess);
+ if (RT_FAILURE(rc))
+ {
+ Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockKernel(,%p,%#x) -> %Rrc\n",
+ pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc));
+ return rc;
+ }
+ Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked kernel -> %p\n",
+ iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj));
+ }
+ else if (cb > VBGLR0_MAX_HGCM_USER_PARM)
+ {
+ Log(("GstHGCMCall: id=%#x fn=%u parm=%u pv=%p cb=%#x > %#x -> out of range\n",
+ pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr,
+ cb, VBGLR0_MAX_HGCM_USER_PARM));
+ return VERR_OUT_OF_RANGE;
+ }
+ else
+ {
+#ifndef USE_BOUNCE_BUFFERS
+ rc = RTR0MemObjLockUser(&hObj, (RTR3PTR)pSrcParm->u.Pointer.u.linearAddr, cb, fAccess, NIL_RTR0PROCESS);
+ if (RT_FAILURE(rc))
+ {
+ Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemObjLockUser(,%p,%#x,nil) -> %Rrc\n",
+ pCallInfo->u32ClientID, pCallInfo->u32Function, iParm, pSrcParm->u.Pointer.u.linearAddr, cb, rc));
+ return rc;
+ }
+ Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p locked user -> %p\n",
+ iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj));
+
+#else /* USE_BOUNCE_BUFFERS */
+ /*
+ * This is a bit massive, but we don't want to waste a
+ * whole page for a 3 byte string buffer (guest props).
+ *
+ * The threshold is ASSUMING sizeof(RTMEMHDR) == 16 and
+ * the system is using some power of two allocator.
+ */
+ /** @todo A more efficient strategy would be to combine buffers. However it
+ * is probably going to be more massive than the current code, so
+ * it can wait till later. */
+ bool fCopyIn = pSrcParm->type != VMMDevHGCMParmType_LinAddr_Out
+ && pSrcParm->type != VMMDevHGCMParmType_LinAddr_Locked_Out;
+ if (cb <= PAGE_SIZE / 2 - 16)
+ {
+ pvSmallBuf = fCopyIn ? RTMemTmpAlloc(cb) : RTMemTmpAllocZ(cb);
+ if (RT_UNLIKELY(!pvSmallBuf))
+ return VERR_NO_MEMORY;
+ if (fCopyIn)
+ {
+ rc = RTR0MemUserCopyFrom(pvSmallBuf, pSrcParm->u.Pointer.u.linearAddr, cb);
+ if (RT_FAILURE(rc))
+ {
+ RTMemTmpFree(pvSmallBuf);
+ Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n",
+ pCallInfo->u32ClientID, pCallInfo->u32Function, iParm,
+ pSrcParm->u.Pointer.u.linearAddr, cb, rc));
+ return rc;
+ }
+ }
+ rc = RTR0MemObjLockKernel(&hObj, pvSmallBuf, cb, fAccess);
+ if (RT_FAILURE(rc))
+ {
+ RTMemTmpFree(pvSmallBuf);
+ Log(("GstHGCMCall: RTR0MemObjLockKernel failed for small buffer: rc=%Rrc pvSmallBuf=%p cb=%#x\n",
+ rc, pvSmallBuf, cb));
+ return rc;
+ }
+ Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p small buffer %p -> %p\n",
+ iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, pvSmallBuf, hObj));
+ }
+ else
+ {
+ rc = RTR0MemObjAllocPage(&hObj, cb, false /*fExecutable*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!fCopyIn)
+ memset(RTR0MemObjAddress(hObj), '\0', cb);
+ else
+ {
+ rc = RTR0MemUserCopyFrom(RTR0MemObjAddress(hObj), pSrcParm->u.Pointer.u.linearAddr, cb);
+ if (RT_FAILURE(rc))
+ {
+ RTR0MemObjFree(hObj, false /*fFreeMappings*/);
+ Log(("GstHGCMCall: id=%#x fn=%u parm=%u RTR0MemUserCopyFrom(,%p,%#x) -> %Rrc\n",
+ pCallInfo->u32ClientID, pCallInfo->u32Function, iParm,
+ pSrcParm->u.Pointer.u.linearAddr, cb, rc));
+ return rc;
+ }
+ }
+ Log3(("GstHGCMCall: parm=%u type=%#x: cb=%#010x pv=%p big buffer -> %p\n",
+ iParm, pSrcParm->type, cb, pSrcParm->u.Pointer.u.linearAddr, hObj));
+ }
+#endif /* USE_BOUNCE_BUFFERS */
+ }
+
+ pParmInfo->aLockBufs[iLockBuf].iParm = iParm;
+ pParmInfo->aLockBufs[iLockBuf].hObj = hObj;
+#ifdef USE_BOUNCE_BUFFERS
+ pParmInfo->aLockBufs[iLockBuf].pvSmallBuf = pvSmallBuf;
+#endif
+ pParmInfo->cLockBufs = iLockBuf + 1;
+
+ if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false))
+ {
+ size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT;
+ *pcbExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]);
+ }
+ }
+ else
+ Log4(("GstHGCMCall: parm=%u type=%#x: cb=0\n", iParm, pSrcParm->type));
+ break;
+
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Translates locked linear address to the normal type.
+ * The locked types are only for the guest side and not handled by the host.
+ *
+ * @returns normal linear address type.
+ * @param enmType The type.
+ */
+static HGCMFunctionParameterType vbglR0HGCMInternalConvertLinAddrType(HGCMFunctionParameterType enmType)
+{
+ switch (enmType)
+ {
+ case VMMDevHGCMParmType_LinAddr_Locked_In:
+ return VMMDevHGCMParmType_LinAddr_In;
+ case VMMDevHGCMParmType_LinAddr_Locked_Out:
+ return VMMDevHGCMParmType_LinAddr_Out;
+ case VMMDevHGCMParmType_LinAddr_Locked:
+ return VMMDevHGCMParmType_LinAddr;
+ default:
+ return enmType;
+ }
+}
+
+
+/**
+ * Translates linear address types to page list direction flags.
+ *
+ * @returns page list flags.
+ * @param enmType The type.
+ */
+static uint32_t vbglR0HGCMInternalLinAddrTypeToPageListFlags(HGCMFunctionParameterType enmType)
+{
+ switch (enmType)
+ {
+ case VMMDevHGCMParmType_LinAddr_In:
+ case VMMDevHGCMParmType_LinAddr_Locked_In:
+ return VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
+
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr_Locked_Out:
+ return VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+
+ default: AssertFailed(); RT_FALL_THRU();
+ case VMMDevHGCMParmType_LinAddr:
+ case VMMDevHGCMParmType_LinAddr_Locked:
+ return VBOX_HGCM_F_PARM_DIRECTION_BOTH;
+ }
+}
+
+
+/**
+ * Initializes the call request that we're sending to the host.
+ *
+ * @returns VBox status code.
+ *
+ * @param pCallInfo The call info.
+ * @param cbCallInfo The size of the call info structure.
+ * @param fRequestor VMMDEV_REQUESTOR_XXX.
+ * @param fIsUser Is it a user request or kernel request.
+ * @param pcbExtra Where to return the extra request space needed for
+ * physical page lists.
+ */
+static void vbglR0HGCMInternalInitCall(VMMDevHGCMCall *pHGCMCall, PCVBGLIOCHGCMCALL pCallInfo,
+ uint32_t cbCallInfo, uint32_t fRequestor, bool fIsUser, struct VbglR0ParmInfo *pParmInfo)
+{
+ HGCMFunctionParameter const *pSrcParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo);
+ HGCMFunctionParameter *pDstParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
+ uint32_t const cParms = pCallInfo->cParms;
+ uint32_t offExtra = (uint32_t)((uintptr_t)(pDstParm + cParms) - (uintptr_t)pHGCMCall);
+ uint32_t iLockBuf = 0;
+ uint32_t iParm;
+ RT_NOREF1(cbCallInfo);
+#ifndef USE_BOUNCE_BUFFERS
+ RT_NOREF1(fIsUser);
+#endif
+
+ /*
+ * The call request headers.
+ */
+ pHGCMCall->header.header.fRequestor = !fIsUser || (fRequestor & VMMDEV_REQUESTOR_USERMODE) ? fRequestor
+ : VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_USR_NOT_GIVEN
+ | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN | VMMDEV_REQUESTOR_CON_DONT_KNOW;
+
+ pHGCMCall->header.fu32Flags = 0;
+ pHGCMCall->header.result = VINF_SUCCESS;
+
+ pHGCMCall->u32ClientID = pCallInfo->u32ClientID;
+ pHGCMCall->u32Function = pCallInfo->u32Function;
+ pHGCMCall->cParms = cParms;
+
+ /*
+ * The parameters.
+ */
+ for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++)
+ {
+ switch (pSrcParm->type)
+ {
+ case VMMDevHGCMParmType_32bit:
+ case VMMDevHGCMParmType_64bit:
+ *pDstParm = *pSrcParm;
+ break;
+
+ case VMMDevHGCMParmType_PageList:
+ case VMMDevHGCMParmType_ContiguousPageList:
+ pDstParm->type = pSrcParm->type;
+ pDstParm->u.PageList.size = pSrcParm->u.PageList.size;
+ if (pSrcParm->u.PageList.size)
+ {
+ HGCMPageListInfo const *pSrcPgLst = (HGCMPageListInfo *)((uint8_t *)pCallInfo + pSrcParm->u.PageList.offset);
+ HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra);
+ uint32_t const cPages = pSrcPgLst->cPages;
+ uint32_t iPage;
+
+ pDstParm->u.PageList.offset = offExtra;
+ pDstPgLst->flags = pSrcPgLst->flags;
+ pDstPgLst->offFirstPage = pSrcPgLst->offFirstPage;
+ pDstPgLst->cPages = (uint16_t)cPages;
+ for (iPage = 0; iPage < cPages; iPage++)
+ pDstPgLst->aPages[iPage] = pSrcPgLst->aPages[iPage];
+
+ offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]);
+ }
+ else
+ pDstParm->u.PageList.offset = 0; /** @todo will fail on the host side now */
+ break;
+
+ case VMMDevHGCMParmType_Embedded:
+ {
+ uint32_t const cb = pSrcParm->u.Embedded.cbData;
+ pDstParm->type = VMMDevHGCMParmType_Embedded;
+ pDstParm->u.Embedded.cbData = cb;
+ pDstParm->u.Embedded.offData = offExtra;
+ if (cb > 0)
+ {
+ uint8_t *pbDst = (uint8_t *)pHGCMCall + offExtra;
+ if (pSrcParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
+ {
+ memcpy(pbDst, (uint8_t const *)pCallInfo + pSrcParm->u.Embedded.offData, cb);
+ if (RT_ALIGN(cb, 8) != cb)
+ memset(pbDst + cb, 0, RT_ALIGN(cb, 8) - cb);
+ }
+ else
+ RT_BZERO(pbDst, RT_ALIGN(cb, 8));
+ offExtra += RT_ALIGN(cb, 8);
+ }
+ break;
+ }
+
+ case VMMDevHGCMParmType_LinAddr_Locked_In:
+ case VMMDevHGCMParmType_LinAddr_Locked_Out:
+ case VMMDevHGCMParmType_LinAddr_Locked:
+ if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true))
+ {
+ *pDstParm = *pSrcParm;
+ pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type);
+ break;
+ }
+ RT_FALL_THRU();
+
+ case VMMDevHGCMParmType_LinAddr_In:
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr:
+ if (pSrcParm->u.Pointer.size != 0)
+ {
+#ifdef USE_BOUNCE_BUFFERS
+ void *pvSmallBuf = pParmInfo->aLockBufs[iLockBuf].pvSmallBuf;
+#endif
+ RTR0MEMOBJ hObj = pParmInfo->aLockBufs[iLockBuf].hObj;
+ Assert(iParm == pParmInfo->aLockBufs[iLockBuf].iParm);
+
+ if (VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false))
+ {
+ HGCMPageListInfo *pDstPgLst = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offExtra);
+ size_t const cPages = RTR0MemObjSize(hObj) >> PAGE_SHIFT;
+ size_t iPage;
+
+ pDstParm->type = VMMDevHGCMParmType_PageList;
+ pDstParm->u.PageList.size = pSrcParm->u.Pointer.size;
+ pDstParm->u.PageList.offset = offExtra;
+ pDstPgLst->flags = vbglR0HGCMInternalLinAddrTypeToPageListFlags(pSrcParm->type);
+#ifdef USE_BOUNCE_BUFFERS
+ if (fIsUser)
+ pDstPgLst->offFirstPage = (uintptr_t)pvSmallBuf & PAGE_OFFSET_MASK;
+ else
+#endif
+ pDstPgLst->offFirstPage = (uint16_t)(pSrcParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK);
+ pDstPgLst->cPages = (uint16_t)cPages; Assert(pDstPgLst->cPages == cPages);
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ pDstPgLst->aPages[iPage] = RTR0MemObjGetPagePhysAddr(hObj, iPage);
+ Assert(pDstPgLst->aPages[iPage] != NIL_RTHCPHYS);
+ }
+
+ offExtra += RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]);
+ }
+ else
+ {
+ pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type);
+ pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size;
+#ifdef USE_BOUNCE_BUFFERS
+ if (fIsUser)
+ pDstParm->u.Pointer.u.linearAddr = pvSmallBuf
+ ? (uintptr_t)pvSmallBuf
+ : (uintptr_t)RTR0MemObjAddress(hObj);
+ else
+#endif
+ pDstParm->u.Pointer.u.linearAddr = pSrcParm->u.Pointer.u.linearAddr;
+ }
+ iLockBuf++;
+ }
+ else
+ {
+ pDstParm->type = vbglR0HGCMInternalConvertLinAddrType(pSrcParm->type);
+ pDstParm->u.Pointer.size = 0;
+ pDstParm->u.Pointer.u.linearAddr = 0;
+ }
+ break;
+
+ default:
+ AssertFailed();
+ pDstParm->type = VMMDevHGCMParmType_Invalid;
+ break;
+ }
+ }
+}
+
+
+/**
+ * Performs the call and completion wait.
+ *
+ * @returns VBox status code of this operation, not necessarily the call.
+ *
+ * @param pHGCMCall The HGCM call info.
+ * @param pfnAsyncCallback The async callback that will wait for the call
+ * to complete.
+ * @param pvAsyncData Argument for the callback.
+ * @param u32AsyncData Argument for the callback.
+ * @param pfLeakIt Where to return the leak it / free it,
+ * indicator. Cancellation fun.
+ */
+static int vbglR0HGCMInternalDoCall(VMMDevHGCMCall *pHGCMCall, PFNVBGLHGCMCALLBACK pfnAsyncCallback,
+ void *pvAsyncData, uint32_t u32AsyncData, bool *pfLeakIt)
+{
+ int rc;
+
+ Log(("calling VbglR0GRPerform\n"));
+ rc = VbglR0GRPerform(&pHGCMCall->header.header);
+ Log(("VbglR0GRPerform rc = %Rrc (header rc=%d)\n", rc, pHGCMCall->header.result));
+
+ /*
+ * If the call failed, but as a result of the request itself, then pretend
+ * success. Upper layers will interpret the result code in the packet.
+ */
+ if ( RT_FAILURE(rc)
+ && rc == pHGCMCall->header.result)
+ {
+ Assert(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE);
+ rc = VINF_SUCCESS;
+ }
+
+ /*
+ * Check if host decides to process the request asynchronously,
+ * if so, we wait for it to complete using the caller supplied callback.
+ */
+ *pfLeakIt = false;
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ Log(("Processing HGCM call asynchronously\n"));
+ rc = pfnAsyncCallback(&pHGCMCall->header, pvAsyncData, u32AsyncData);
+ if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
+ {
+ Assert(!(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED));
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * The request didn't complete in time or the call was interrupted,
+ * the RC from the callback indicates which. Try cancel the request.
+ *
+ * This is a bit messy because we're racing request completion. Sorry.
+ */
+ /** @todo It would be nice if we could use the waiter callback to do further
+ * waiting in case of a completion race. If it wasn't for WINNT having its own
+ * version of all that stuff, I would've done it already. */
+ VMMDevHGCMCancel2 *pCancelReq;
+ int rc2 = VbglR0GRAlloc((VMMDevRequestHeader **)&pCancelReq, sizeof(*pCancelReq), VMMDevReq_HGCMCancel2);
+ if (RT_SUCCESS(rc2))
+ {
+ pCancelReq->physReqToCancel = VbglR0PhysHeapGetPhysAddr(pHGCMCall);
+ rc2 = VbglR0GRPerform(&pCancelReq->header);
+ VbglR0GRFree(&pCancelReq->header);
+ }
+#if 1 /** @todo ADDVER: Remove this on next minor version change. */
+ if (rc2 == VERR_NOT_IMPLEMENTED)
+ {
+ /* host is too old, or we're out of heap. */
+ pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED;
+ pHGCMCall->header.header.requestType = VMMDevReq_HGCMCancel;
+ rc2 = VbglR0GRPerform(&pHGCMCall->header.header);
+ if (rc2 == VERR_INVALID_PARAMETER)
+ rc2 = VERR_NOT_FOUND;
+ else if (RT_SUCCESS(rc))
+ RTThreadSleep(1);
+ }
+#endif
+ if (RT_SUCCESS(rc)) rc = VERR_INTERRUPTED; /** @todo weed this out from the WINNT VBoxGuest code. */
+ if (RT_SUCCESS(rc2))
+ {
+ Log(("vbglR0HGCMInternalDoCall: successfully cancelled\n"));
+ pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED;
+ }
+ else
+ {
+ /*
+ * Wait for a bit while the host (hopefully) completes it.
+ */
+ uint64_t u64Start = RTTimeSystemMilliTS();
+ uint32_t cMilliesToWait = rc2 == VERR_NOT_FOUND || rc2 == VERR_SEM_DESTROYED ? 500 : 2000;
+ uint64_t cElapsed = 0;
+ if (rc2 != VERR_NOT_FOUND)
+ {
+ static unsigned s_cErrors = 0;
+ if (s_cErrors++ < 32)
+ LogRel(("vbglR0HGCMInternalDoCall: Failed to cancel the HGCM call on %Rrc: rc2=%Rrc\n", rc, rc2));
+ }
+ else
+ Log(("vbglR0HGCMInternalDoCall: Cancel race rc=%Rrc rc2=%Rrc\n", rc, rc2));
+
+ do
+ {
+ ASMCompilerBarrier(); /* paranoia */
+ if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
+ break;
+ RTThreadSleep(1);
+ cElapsed = RTTimeSystemMilliTS() - u64Start;
+ } while (cElapsed < cMilliesToWait);
+
+ ASMCompilerBarrier(); /* paranoia^2 */
+ if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
+ rc = VINF_SUCCESS;
+ else
+ {
+ LogRel(("vbglR0HGCMInternalDoCall: Leaking %u bytes. Pending call to %u with %u parms. (rc2=%Rrc)\n",
+ pHGCMCall->header.header.size, pHGCMCall->u32Function, pHGCMCall->cParms, rc2));
+ *pfLeakIt = true;
+ }
+ Log(("vbglR0HGCMInternalDoCall: Cancel race ended with rc=%Rrc (rc2=%Rrc) after %llu ms\n", rc, rc2, cElapsed));
+ }
+ }
+ }
+
+ Log(("GstHGCMCall: rc=%Rrc result=%Rrc fu32Flags=%#x fLeakIt=%d\n",
+ rc, pHGCMCall->header.result, pHGCMCall->header.fu32Flags, *pfLeakIt));
+ return rc;
+}
+
+
+/**
+ * Copies the result of the call back to the caller info structure and user
+ * buffers (if using bounce buffers).
+ *
+ * @returns rc, unless RTR0MemUserCopyTo fails.
+ * @param pCallInfo Call info structure to update.
+ * @param cbCallInfo The size of the client request.
+ * @param pHGCMCall HGCM call request.
+ * @param cbHGCMCall The size of the HGCM call request.
+ * @param pParmInfo Parameter locking/buffering info.
+ * @param fIsUser Is it a user (true) or kernel request.
+ * @param rc The current result code. Passed along to
+ * preserve informational status codes.
+ */
+static int vbglR0HGCMInternalCopyBackResult(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo,
+ VMMDevHGCMCall const *pHGCMCall, uint32_t cbHGCMCall,
+ struct VbglR0ParmInfo *pParmInfo, bool fIsUser, int rc)
+{
+ HGCMFunctionParameter const *pSrcParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
+ HGCMFunctionParameter *pDstParm = VBGL_HGCM_GET_CALL_PARMS(pCallInfo);
+ uint32_t const cParms = pCallInfo->cParms;
+#ifdef USE_BOUNCE_BUFFERS
+ uint32_t iLockBuf = 0;
+#endif
+ uint32_t iParm;
+ RT_NOREF1(pParmInfo);
+#ifndef USE_BOUNCE_BUFFERS
+ RT_NOREF1(fIsUser);
+#endif
+
+ /*
+ * The call result.
+ */
+ pCallInfo->Hdr.rc = pHGCMCall->header.result;
+
+ /*
+ * Copy back parameters.
+ */
+ /** @todo This is assuming user data (pDstParm) is buffered. Not true
+ * on OS/2, though I'm not sure we care... */
+ for (iParm = 0; iParm < cParms; iParm++, pSrcParm++, pDstParm++)
+ {
+ switch (pDstParm->type)
+ {
+ case VMMDevHGCMParmType_32bit:
+ case VMMDevHGCMParmType_64bit:
+ *pDstParm = *pSrcParm;
+ break;
+
+ case VMMDevHGCMParmType_PageList:
+ case VMMDevHGCMParmType_ContiguousPageList:
+ pDstParm->u.PageList.size = pSrcParm->u.PageList.size;
+ break;
+
+ case VMMDevHGCMParmType_Embedded:
+ {
+ uint32_t const cbDst = pDstParm->u.Embedded.cbData;
+ uint32_t cbSrc;
+ pDstParm->u.Embedded.cbData = cbSrc = pSrcParm->u.Embedded.cbData;
+ if ( cbSrc > 0
+ && (pDstParm->u.Embedded.fFlags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST))
+ {
+ uint32_t const offDst = pDstParm->u.Embedded.offData;
+ uint32_t const offSrc = pSrcParm->u.Embedded.offData;
+
+ AssertReturn(offDst < cbCallInfo, VERR_INTERNAL_ERROR_2);
+ AssertReturn(offDst >= sizeof(*pCallInfo) + cParms * sizeof(*pDstParm), VERR_INTERNAL_ERROR_2);
+ AssertReturn(cbDst <= cbCallInfo - offDst , VERR_INTERNAL_ERROR_2);
+
+ AssertReturn(offSrc < cbCallInfo, VERR_INTERNAL_ERROR_2);
+ AssertReturn(offSrc >= sizeof(*pHGCMCall) + cParms * sizeof(*pSrcParm), VERR_INTERNAL_ERROR_2);
+ if (cbSrc <= cbHGCMCall - offSrc)
+ { /* likely */ }
+ else
+ {
+ /* Special case: Buffer overflow w/ correct size given. */
+ AssertReturn(RT_FAILURE_NP(rc), VERR_INTERNAL_ERROR_2);
+ cbSrc = cbHGCMCall - offSrc;
+ }
+ memcpy((uint8_t *)pCallInfo + offDst, (uint8_t const *)pHGCMCall + offSrc, RT_MIN(cbSrc, cbDst));
+ }
+ break;
+ }
+
+ case VMMDevHGCMParmType_LinAddr_Locked_In:
+ case VMMDevHGCMParmType_LinAddr_In:
+#ifdef USE_BOUNCE_BUFFERS
+ if ( fIsUser
+ && iLockBuf < pParmInfo->cLockBufs
+ && iParm == pParmInfo->aLockBufs[iLockBuf].iParm)
+ iLockBuf++;
+#endif
+ pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size;
+ break;
+
+ case VMMDevHGCMParmType_LinAddr_Locked_Out:
+ case VMMDevHGCMParmType_LinAddr_Locked:
+ if (!VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ true))
+ {
+ pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size;
+ break;
+ }
+ RT_FALL_THRU();
+
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr:
+ {
+#ifdef USE_BOUNCE_BUFFERS
+ if (fIsUser)
+ {
+ size_t cbOut = RT_MIN(pSrcParm->u.Pointer.size, pDstParm->u.Pointer.size);
+ if (cbOut)
+ {
+ int rc2;
+ Assert(pParmInfo->aLockBufs[iLockBuf].iParm == iParm);
+ rc2 = RTR0MemUserCopyTo((RTR3PTR)pDstParm->u.Pointer.u.linearAddr,
+ pParmInfo->aLockBufs[iLockBuf].pvSmallBuf
+ ? pParmInfo->aLockBufs[iLockBuf].pvSmallBuf
+ : RTR0MemObjAddress(pParmInfo->aLockBufs[iLockBuf].hObj),
+ cbOut);
+ if (RT_FAILURE(rc2))
+ return rc2;
+ iLockBuf++;
+ }
+ else if ( iLockBuf < pParmInfo->cLockBufs
+ && iParm == pParmInfo->aLockBufs[iLockBuf].iParm)
+ iLockBuf++;
+ }
+#endif
+ pDstParm->u.Pointer.size = pSrcParm->u.Pointer.size;
+ break;
+ }
+
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_4;
+ break;
+ }
+ }
+
+#ifdef USE_BOUNCE_BUFFERS
+ Assert(!fIsUser || pParmInfo->cLockBufs == iLockBuf);
+#endif
+ return rc;
+}
+
+
+DECLR0VBGL(int) VbglR0HGCMInternalCall(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor,
+ PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
+{
+ bool fIsUser = (fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_USER;
+ struct VbglR0ParmInfo ParmInfo;
+ size_t cbExtra;
+ int rc;
+
+ /*
+ * Basic validation.
+ */
+ AssertMsgReturn( !pCallInfo
+ || !pfnAsyncCallback
+ || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS
+ || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
+ ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags),
+ VERR_INVALID_PARAMETER);
+ AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL)
+ || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter),
+ VERR_INVALID_PARAMETER);
+
+ Log(("GstHGCMCall: u32ClientID=%#x u32Function=%u cParms=%u cbCallInfo=%#x fFlags=%#x\n",
+ pCallInfo->u32ClientID, pCallInfo->u32ClientID, pCallInfo->u32Function, pCallInfo->cParms, cbCallInfo, fFlags));
+
+ /*
+ * Validate, lock and buffer the parameters for the call.
+ * This will calculate the amount of extra space for physical page list.
+ */
+ rc = vbglR0HGCMInternalPreprocessCall(pCallInfo, cbCallInfo, fIsUser, &ParmInfo, &cbExtra);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate the request buffer and recreate the call request.
+ */
+ VMMDevHGCMCall *pHGCMCall;
+ uint32_t const cbHGCMCall = sizeof(VMMDevHGCMCall) + pCallInfo->cParms * sizeof(HGCMFunctionParameter) + (uint32_t)cbExtra;
+ rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pHGCMCall, cbHGCMCall, VMMDevReq_HGCMCall);
+ if (RT_SUCCESS(rc))
+ {
+ bool fLeakIt;
+ vbglR0HGCMInternalInitCall(pHGCMCall, pCallInfo, cbCallInfo, fRequestor, fIsUser, &ParmInfo);
+
+ /*
+ * Perform the call.
+ */
+ rc = vbglR0HGCMInternalDoCall(pHGCMCall, pfnAsyncCallback, pvAsyncData, u32AsyncData, &fLeakIt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy back the result (parameters and buffers that changed).
+ */
+ rc = vbglR0HGCMInternalCopyBackResult(pCallInfo, cbCallInfo, pHGCMCall, cbHGCMCall, &ParmInfo, fIsUser, rc);
+ }
+ else
+ {
+ if ( rc != VERR_INTERRUPTED
+ && rc != VERR_TIMEOUT)
+ {
+ static unsigned s_cErrors = 0;
+ if (s_cErrors++ < 32)
+ LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalDoCall failed. rc=%Rrc\n", rc));
+ }
+ }
+
+ if (!fLeakIt)
+ VbglR0GRFree(&pHGCMCall->header.header);
+ }
+ }
+ else
+ LogRel(("VbglR0HGCMInternalCall: vbglR0HGCMInternalPreprocessCall failed. rc=%Rrc\n", rc));
+
+ /*
+ * Release locks and free bounce buffers.
+ */
+ if (ParmInfo.cLockBufs)
+ while (ParmInfo.cLockBufs-- > 0)
+ {
+ RTR0MemObjFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].hObj, false /*fFreeMappings*/);
+#ifdef USE_BOUNCE_BUFFERS
+ RTMemTmpFree(ParmInfo.aLockBufs[ParmInfo.cLockBufs].pvSmallBuf);
+#endif
+ }
+
+ return rc;
+}
+
+
+#if ARCH_BITS == 64
+DECLR0VBGL(int) VbglR0HGCMInternalCall32(PVBGLIOCHGCMCALL pCallInfo, uint32_t cbCallInfo, uint32_t fFlags, uint32_t fRequestor,
+ PFNVBGLHGCMCALLBACK pfnAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
+{
+ PVBGLIOCHGCMCALL pCallInfo64 = NULL;
+ HGCMFunctionParameter *pParm64 = NULL;
+ HGCMFunctionParameter32 *pParm32 = NULL;
+ uint32_t cParms = 0;
+ uint32_t iParm = 0;
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Input validation.
+ */
+ AssertMsgReturn( !pCallInfo
+ || !pfnAsyncCallback
+ || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS
+ || !(fFlags & ~VBGLR0_HGCMCALL_F_MODE_MASK),
+ ("pCallInfo=%p pfnAsyncCallback=%p fFlags=%#x\n", pCallInfo, pfnAsyncCallback, fFlags),
+ VERR_INVALID_PARAMETER);
+ AssertReturn( cbCallInfo >= sizeof(VBGLIOCHGCMCALL)
+ || cbCallInfo >= pCallInfo->cParms * sizeof(HGCMFunctionParameter32),
+ VERR_INVALID_PARAMETER);
+
+ /* This Assert does not work on Solaris/Windows 64/32 mixed mode, not sure why, skipping for now */
+#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_WINDOWS)
+ AssertReturn((fFlags & VBGLR0_HGCMCALL_F_MODE_MASK) == VBGLR0_HGCMCALL_F_KERNEL, VERR_WRONG_ORDER);
+#endif
+
+ cParms = pCallInfo->cParms;
+ Log(("VbglR0HGCMInternalCall32: cParms=%d, u32Function=%d, fFlags=%#x\n", cParms, pCallInfo->u32Function, fFlags));
+
+ /*
+ * The simple approach, allocate a temporary request and convert the parameters.
+ */
+ pCallInfo64 = (PVBGLIOCHGCMCALL)RTMemTmpAllocZ(sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter));
+ if (!pCallInfo64)
+ return VERR_NO_TMP_MEMORY;
+
+ *pCallInfo64 = *pCallInfo;
+ pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo);
+ pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64);
+ for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++)
+ {
+ switch (pParm32->type)
+ {
+ case VMMDevHGCMParmType_32bit:
+ pParm64->type = VMMDevHGCMParmType_32bit;
+ pParm64->u.value32 = pParm32->u.value32;
+ break;
+
+ case VMMDevHGCMParmType_64bit:
+ pParm64->type = VMMDevHGCMParmType_64bit;
+ pParm64->u.value64 = pParm32->u.value64;
+ break;
+
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr:
+ case VMMDevHGCMParmType_LinAddr_In:
+ pParm64->type = pParm32->type;
+ pParm64->u.Pointer.size = pParm32->u.Pointer.size;
+ pParm64->u.Pointer.u.linearAddr = pParm32->u.Pointer.u.linearAddr;
+ break;
+
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ LogRel(("VbglR0HGCMInternalCall32: pParm32 type %#x invalid.\n", pParm32->type));
+ break;
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0HGCMInternalCall(pCallInfo64, sizeof(*pCallInfo64) + cParms * sizeof(HGCMFunctionParameter), fFlags,
+ fRequestor, pfnAsyncCallback, pvAsyncData, u32AsyncData);
+
+ if (RT_SUCCESS(rc))
+ {
+ *pCallInfo = *pCallInfo64;
+
+ /*
+ * Copy back.
+ */
+ pParm32 = VBGL_HGCM_GET_CALL_PARMS32(pCallInfo);
+ pParm64 = VBGL_HGCM_GET_CALL_PARMS(pCallInfo64);
+ for (iParm = 0; iParm < cParms; iParm++, pParm32++, pParm64++)
+ {
+ switch (pParm64->type)
+ {
+ case VMMDevHGCMParmType_32bit:
+ pParm32->u.value32 = pParm64->u.value32;
+ break;
+
+ case VMMDevHGCMParmType_64bit:
+ pParm32->u.value64 = pParm64->u.value64;
+ break;
+
+ case VMMDevHGCMParmType_LinAddr_Out:
+ case VMMDevHGCMParmType_LinAddr:
+ case VMMDevHGCMParmType_LinAddr_In:
+ pParm32->u.Pointer.size = pParm64->u.Pointer.size;
+ break;
+
+ default:
+ LogRel(("VbglR0HGCMInternalCall32: failed invalid pParm32 type %d\n", pParm32->type));
+ rc = VERR_INTERNAL_ERROR_3;
+ break;
+ }
+ }
+ }
+ else
+ {
+ static unsigned s_cErrors = 0;
+ if (s_cErrors++ < 32)
+ LogRel(("VbglR0HGCMInternalCall32: VbglR0HGCMInternalCall failed. rc=%Rrc\n", rc));
+ }
+ }
+ else
+ {
+ static unsigned s_cErrors = 0;
+ if (s_cErrors++ < 32)
+ LogRel(("VbglR0HGCMInternalCall32: failed. rc=%Rrc\n", rc));
+ }
+
+ RTMemTmpFree(pCallInfo64);
+ return rc;
+}
+#endif /* ARCH_BITS == 64 */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp
new file mode 100644
index 00000000..828dea2e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-os2.cpp
@@ -0,0 +1,85 @@
+/* $Id: VBoxGuestR0LibIdc-os2.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, OS/2 specific.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN /* for watcom */
+/* This is defined in some assembly file. The AttachDD operation
+ is done in the driver init code. */
+extern VBGLOS2ATTACHDD g_VBoxGuestIDC;
+RT_C_DECLS_END
+
+
+
+int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq)
+{
+ /*
+ * Just check whether the connection was made or not.
+ */
+ if ( g_VBoxGuestIDC.u32Version == VBGL_IOC_VERSION
+ && RT_VALID_PTR(g_VBoxGuestIDC.u32Session)
+ && RT_VALID_PTR(g_VBoxGuestIDC.pfnServiceEP))
+ return g_VBoxGuestIDC.pfnServiceEP(g_VBoxGuestIDC.u32Session, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq));
+ Log(("vbglDriverOpen: failed\n"));
+ RT_NOREF(pHandle);
+ return VERR_FILE_NOT_FOUND;
+}
+
+
+int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq)
+{
+ return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq));
+}
+
+
+/**
+ * Makes an IDC call, returning only the I/O control status code.
+ *
+ * @returns VBox status code (the I/O control failure status).
+ * @param pHandle The IDC handle.
+ * @param uReq The request number.
+ * @param pReqHdr The request header.
+ * @param cbReq The request size.
+ */
+DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq)
+{
+ return g_VBoxGuestIDC.pfnServiceEP((uintptr_t)pHandle->s.pvSession, uReq, pReqHdr, cbReq);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp
new file mode 100644
index 00000000..760f060c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-solaris.cpp
@@ -0,0 +1,97 @@
+/* $Id: VBoxGuestR0LibIdc-solaris.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Solaris specific.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <sys/conf.h>
+#include <sys/sunldi.h>
+#include <sys/file.h>
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+#include "VBoxGuestR0LibInternal.h"
+#include <VBox/err.h>
+
+
+int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq)
+{
+ ldi_handle_t hDev = NULL;
+ ldi_ident_t hIdent = ldi_ident_from_anon();
+ int rc = ldi_open_by_name((char *)VBOXGUEST_DEVICE_NAME, FREAD, kcred, &hDev, hIdent);
+ ldi_ident_release(hIdent);
+ if (rc == 0)
+ {
+ pHandle->s.hDev = hDev;
+ rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq));
+ if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc))
+ return VINF_SUCCESS;
+ ldi_close(hDev, FREAD, kcred);
+ }
+ else
+ rc = VERR_OPEN_FAILED;
+ pHandle->s.hDev = NULL;
+ return rc;
+}
+
+
+int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq)
+{
+ int rc = VbglR0IdcCallRaw(pHandle, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq));
+ if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc))
+ {
+ ldi_close(pHandle->s.hDev, FREAD, kcred);
+ pHandle->s.hDev = NULL;
+ }
+ return rc;
+}
+
+
+/**
+ * Makes an IDC call, returning only the I/O control status code.
+ *
+ * @returns VBox status code (the I/O control failure status).
+ * @param pHandle The IDC handle.
+ * @param uReq The request number.
+ * @param pReqHdr The request header.
+ * @param cbReq The request size.
+ */
+DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq)
+{
+#if 0
+ return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq);
+#else
+ int iIgn;
+ int rc = ldi_ioctl(pHandle->s.hDev, uReq, (intptr_t)pReqHdr, FKIOCTL | FNATIVE, kcred, &iIgn);
+ if (rc == 0)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(rc);
+#endif
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp
new file mode 100644
index 00000000..8aefb725
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp
@@ -0,0 +1,64 @@
+/* $Id: VBoxGuestR0LibIdc-unix.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, UNIX-like OSes.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+
+int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq)
+{
+ RT_NOREF(pHandle);
+ return VBoxGuestIDC(NULL /*pvSession*/, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr, sizeof(*pReq));
+}
+
+
+int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq)
+{
+ return VBoxGuestIDC(pHandle->s.pvSession, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr, sizeof(*pReq));
+}
+
+
+/**
+ * Makes an IDC call, returning only the I/O control status code.
+ *
+ * @returns VBox status code (the I/O control failure status).
+ * @param pHandle The IDC handle.
+ * @param uReq The request number.
+ * @param pReqHdr The request header.
+ * @param cbReq The request size.
+ */
+DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq)
+{
+ return VBoxGuestIDC(pHandle->s.pvSession, uReq, pReqHdr, cbReq);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp
new file mode 100644
index 00000000..395667e0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-win.cpp
@@ -0,0 +1,208 @@
+/* $Id: VBoxGuestR0LibIdc-win.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC, Windows specific.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt.h>
+#include "VBoxGuestR0LibInternal.h"
+#include <VBox/VBoxGuest.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+
+
+/**
+ * Internal I/O Control call worker.
+ *
+ * @returns VBox status code.
+ * @param pDeviceObject The device object to call.
+ * @param pFileObject The file object for the connection.
+ * @param uReq The request.
+ * @param pReq The request packet.
+ */
+static int vbglR0IdcNtCallInternal(PDEVICE_OBJECT pDeviceObject, PFILE_OBJECT pFileObject, uint32_t uReq, PVBGLREQHDR pReq)
+{
+ int rc;
+ NTSTATUS rcNt;
+
+ /*
+ * Build the request.
+ *
+ * We want to avoid double buffering of the request, therefore we don't
+ * specify any request pointers or sizes when asking the kernel to build
+ * the IRP for us, but instead do that part our selves.
+ *
+ * See https://www.osr.com/blog/2018/02/14/beware-iobuilddeviceiocontrolrequest/
+ * for how fun this is when we're not at IRQL PASSIVE (HACK ALERT futher down).
+ * Ran into this little issue when LoadLibraryEx on a .NET DLL using the
+ * LOAD_LIBRARY_AS_DATAFILE and LOAD_LIBRARY_AS_IMAGE_RESOURCE flags.
+ */
+ KEVENT Event;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ IO_STATUS_BLOCK IoStatusBlock = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+#if 0
+ PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */
+ pDeviceObject,
+ pReq, /* InputBuffer */
+ pReq->cbIn, /* InputBufferLength */
+ pReq, /* OutputBuffer */
+ pReq->cbOut, /* OutputBufferLength */
+ TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */
+ &Event, /* Event */
+ &IoStatusBlock); /* IoStatusBlock */
+#else
+ PIRP pIrp = IoBuildDeviceIoControlRequest(uReq, /* IoControlCode */
+ pDeviceObject,
+ NULL, /* InputBuffer */
+ 0, /* InputBufferLength */
+ NULL, /* OutputBuffer */
+ 0, /* OutputBufferLength */
+ TRUE, /* InternalDeviceIoControl (=> IRP_MJ_INTERNAL_DEVICE_CONTROL) */
+ &Event, /* Event */
+ &IoStatusBlock); /* IoStatusBlock */
+#endif
+ if (pIrp)
+ {
+#if 0
+ IoGetNextIrpStackLocation(pIrp)->FileObject = pFileObject;
+#else
+ //w2k3sp1+: if (!KeAreAllApcsDisabled())
+ //w2k3sp1+: pIrp->Flags |= IRP_SYNCHRONOUS_API;
+ //w2k3sp1+: else
+ {
+ /* HACK ALERT! Causes IoCompleteRequest to update UserIosb and free the IRP w/o any APC happening. */
+ pIrp->Flags |= IRP_SYNCHRONOUS_API | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO;
+ KIRQL bIrqlSaved;
+ KeRaiseIrql(APC_LEVEL, &bIrqlSaved);
+ RemoveEntryList(&pIrp->ThreadListEntry);
+ InitializeListHead(&pIrp->ThreadListEntry);
+ KeLowerIrql(bIrqlSaved);
+ }
+ pIrp->UserBuffer = pReq;
+ pIrp->AssociatedIrp.SystemBuffer = pReq;
+ PIO_STACK_LOCATION pStack = IoGetNextIrpStackLocation(pIrp);
+ pStack->FileObject = pFileObject;
+ pStack->Parameters.DeviceIoControl.OutputBufferLength = pReq->cbOut;
+ pStack->Parameters.DeviceIoControl.InputBufferLength = pReq->cbIn;
+#endif
+
+ /*
+ * Call the driver, wait for an async request to complete (should never happen).
+ */
+ rcNt = IoCallDriver(pDeviceObject, pIrp);
+ if (rcNt == STATUS_PENDING)
+ rcNt = KeWaitForSingleObject(&Event, /* Object */
+ Executive, /* WaitReason */
+ KernelMode, /* WaitMode */
+ FALSE, /* Alertable */
+ NULL); /* TimeOut */
+ if (NT_SUCCESS(rcNt))
+ rcNt = IoStatusBlock.Status;
+ if (NT_SUCCESS(rcNt))
+ rc = pReq->rc;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq)
+{
+ PDEVICE_OBJECT pDeviceObject = NULL;
+ PFILE_OBJECT pFileObject = NULL;
+ UNICODE_STRING wszDeviceName;
+ NTSTATUS rcNt;
+ int rc;
+
+ /*
+ * Get the device object pointer.
+ */
+ RtlInitUnicodeString(&wszDeviceName, VBOXGUEST_DEVICE_NAME_NT);
+ rcNt = IoGetDeviceObjectPointer(&wszDeviceName, FILE_ALL_ACCESS, &pFileObject, &pDeviceObject);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Make the connection call.
+ */
+ rc = vbglR0IdcNtCallInternal(pDeviceObject, pFileObject, VBGL_IOCTL_IDC_CONNECT, &pReq->Hdr);
+ if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc))
+ {
+ pHandle->s.pDeviceObject = pDeviceObject;
+ pHandle->s.pFileObject = pFileObject;
+ return rc;
+ }
+
+ /* only the file object. */
+ ObDereferenceObject(pFileObject);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ pHandle->s.pDeviceObject = NULL;
+ pHandle->s.pFileObject = NULL;
+ return rc;
+}
+
+
+int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq)
+{
+ PFILE_OBJECT pFileObject = pHandle->s.pFileObject;
+ int rc = vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pFileObject, VBGL_IOCTL_IDC_DISCONNECT, &pReq->Hdr);
+ if (RT_SUCCESS(rc) && RT_SUCCESS(pReq->Hdr.rc))
+ {
+ pHandle->s.pDeviceObject = NULL;
+ pHandle->s.pFileObject = NULL;
+ ObDereferenceObject(pFileObject);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Makes an IDC call, returning only the I/O control status code.
+ *
+ * @returns VBox status code (the I/O control failure status).
+ * @param pHandle The IDC handle.
+ * @param uReq The request number.
+ * @param pReqHdr The request header.
+ * @param cbReq The request size.
+ */
+DECLR0VBGL(int) VbglR0IdcCallRaw(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq)
+{
+ NOREF(cbReq);
+ return vbglR0IdcNtCallInternal(pHandle->s.pDeviceObject, pHandle->s.pFileObject, (uint32_t)uReq, pReqHdr);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp
new file mode 100644
index 00000000..c0b0ac53
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp
@@ -0,0 +1,205 @@
+/* $Id: VBoxGuestR0LibIdc.cpp $ */
+/** @file
+ * VBoxGuestLib - Ring-0 Support Library for VBoxGuest, IDC.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+#include <iprt/errcore.h>
+#include <VBox/VBoxGuest.h>
+/*#include <iprt/asm.h>*/
+
+#ifdef VBGL_VBOXGUEST
+# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code."
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/*static PVBGLIDCHANDLE volatile g_pMainHandle = NULL;*/
+
+
+/**
+ * Opens the IDC interface of the support driver.
+ *
+ * This will perform basic version negotiations and fail if the
+ * minimum requirements aren't met.
+ *
+ * @returns VBox status code.
+ * @param pHandle The handle structure (output).
+ * @param uReqVersion The requested version. Pass 0 for default.
+ * @param uMinVersion The minimum required version. Pass 0 for default.
+ * @param puSessionVersion Where to store the session version. Optional.
+ * @param puDriverVersion Where to store the session version. Optional.
+ * @param puDriverRevision Where to store the SVN revision of the driver. Optional.
+ */
+DECLR0VBGL(int) VbglR0IdcOpen(PVBGLIDCHANDLE pHandle, uint32_t uReqVersion, uint32_t uMinVersion,
+ uint32_t *puSessionVersion, uint32_t *puDriverVersion, uint32_t *puDriverRevision)
+{
+ unsigned uDefaultMinVersion;
+ VBGLIOCIDCCONNECT Req;
+ int rc;
+
+ /*
+ * Validate and set failure return values.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
+ pHandle->s.pvSession = NULL;
+
+ AssertPtrNullReturn(puSessionVersion, VERR_INVALID_POINTER);
+ if (puSessionVersion)
+ *puSessionVersion = 0;
+
+ AssertPtrNullReturn(puDriverVersion, VERR_INVALID_POINTER);
+ if (puDriverVersion)
+ *puDriverVersion = 0;
+
+ AssertPtrNullReturn(puDriverRevision, VERR_INVALID_POINTER);
+ if (puDriverRevision)
+ *puDriverRevision = 0;
+
+ AssertReturn(!uMinVersion || (uMinVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER);
+ AssertReturn(!uReqVersion || (uReqVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)), VERR_INVALID_PARAMETER);
+
+ /*
+ * Handle default version input and enforce minimum requirements made
+ * by this library.
+ *
+ * The clients will pass defaults (0), and only in the case that some
+ * special API feature was just added will they set an actual version.
+ * So, this is the place where can easily enforce a minimum IDC version
+ * on bugs and similar. It corresponds a bit to what SUPR3Init is
+ * responsible for.
+ */
+ uDefaultMinVersion = VBGL_IOC_VERSION & UINT32_C(0xffff0000);
+ if (!uMinVersion || uMinVersion < uDefaultMinVersion)
+ uMinVersion = uDefaultMinVersion;
+ if (!uReqVersion || uReqVersion < uDefaultMinVersion)
+ uReqVersion = uDefaultMinVersion;
+
+ /*
+ * Setup the connect request packet and call the OS specific function.
+ */
+ VBGLREQHDR_INIT(&Req.Hdr, IDC_CONNECT);
+ Req.u.In.u32MagicCookie = VBGL_IOCTL_IDC_CONNECT_MAGIC_COOKIE;
+ Req.u.In.uMinVersion = uMinVersion;
+ Req.u.In.uReqVersion = uReqVersion;
+ Req.u.In.uReserved = 0;
+ rc = vbglR0IdcNativeOpen(pHandle, &Req);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pvSession = Req.u.Out.pvSession;
+ if (puSessionVersion)
+ *puSessionVersion = Req.u.Out.uSessionVersion;
+ if (puDriverVersion)
+ *puDriverVersion = Req.u.Out.uDriverVersion;
+ if (puDriverRevision)
+ *puDriverRevision = Req.u.Out.uDriverRevision;
+
+ /*
+ * We don't really trust anyone, make sure the returned
+ * session and version values actually makes sense.
+ */
+ if ( RT_VALID_PTR(Req.u.Out.pvSession)
+ && Req.u.Out.uSessionVersion >= uMinVersion
+ && (Req.u.Out.uSessionVersion & UINT32_C(0xffff0000)) == (VBGL_IOC_VERSION & UINT32_C(0xffff0000)))
+ {
+ /*ASMAtomicCmpXchgPtr(&g_pMainHandle, pHandle, NULL);*/
+ return rc;
+ }
+
+ AssertMsgFailed(("pSession=%p uSessionVersion=0x%x (r%u)\n", Req.u.Out.pvSession, Req.u.Out.uSessionVersion, Req.u.Out.uDriverRevision));
+ rc = VERR_VERSION_MISMATCH;
+ VbglR0IdcClose(pHandle);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Closes a IDC connection established by VbglR0IdcOpen.
+ *
+ * @returns VBox status code.
+ * @param pHandle The IDC handle.
+ */
+DECLR0VBGL(int) VbglR0IdcClose(PVBGLIDCHANDLE pHandle)
+{
+ VBGLIOCIDCDISCONNECT Req;
+ int rc;
+
+ /*
+ * Catch closed handles and check that the session is valid.
+ */
+ AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
+ if (!pHandle->s.pvSession)
+ return VERR_INVALID_HANDLE;
+ AssertPtrReturn(pHandle->s.pvSession, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the request and hand it to the OS specific code.
+ */
+ VBGLREQHDR_INIT(&Req.Hdr, IDC_DISCONNECT);
+ Req.u.In.pvSession = pHandle->s.pvSession;
+ rc = vbglR0IdcNativeClose(pHandle, &Req);
+ if (RT_SUCCESS(rc))
+ rc = Req.Hdr.rc;
+ if (RT_SUCCESS(rc))
+ {
+ pHandle->s.pvSession = NULL;
+ /*ASMAtomicCmpXchgPtr(&g_pMainHandle, NULL, pHandle);*/
+ }
+ return rc;
+}
+
+
+/**
+ * Makes an IDC call, returning the request status.
+ *
+ * @returns VBox status code. Request status if the I/O control succeeds,
+ * otherwise the I/O control failure status.
+ * @param pHandle The IDC handle.
+ * @param uReq The request number.
+ * @param pReqHdr The request header.
+ * @param cbReq The request size.
+ */
+DECLR0VBGL(int) VbglR0IdcCall(PVBGLIDCHANDLE pHandle, uintptr_t uReq, PVBGLREQHDR pReqHdr, uint32_t cbReq)
+{
+ int rc = VbglR0IdcCallRaw(pHandle, uReq, pReqHdr, cbReq);
+ if (RT_SUCCESS(rc))
+ rc = pReqHdr->rc;
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp
new file mode 100644
index 00000000..0ed6c35f
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInit.cpp
@@ -0,0 +1,333 @@
+/* $Id: VBoxGuestR0LibInit.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Library initialization.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <VBox/err.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The global VBGL instance data. */
+VBGLDATA g_vbgldata;
+
+
+/**
+ * Used by vbglR0QueryDriverInfo and VbglInit to try get the host feature mask
+ * and version information (g_vbgldata::hostVersion).
+ *
+ * This was first implemented by the host in 3.1 and we quietly ignore failures
+ * for that reason.
+ */
+static void vbglR0QueryHostVersion(void)
+{
+ VMMDevReqHostVersion *pReq;
+ int rc = VbglR0GRAlloc((VMMDevRequestHeader **) &pReq, sizeof (*pReq), VMMDevReq_GetHostVersion);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0GRPerform(&pReq->header);
+ if (RT_SUCCESS(rc))
+ {
+ g_vbgldata.hostVersion = *pReq;
+ Log(("vbglR0QueryHostVersion: %u.%u.%ur%u %#x\n",
+ pReq->major, pReq->minor, pReq->build, pReq->revision, pReq->features));
+ }
+
+ VbglR0GRFree(&pReq->header);
+ }
+}
+
+
+#ifndef VBGL_VBOXGUEST
+/**
+ * The guest library uses lazy initialization for VMMDev port and memory,
+ * because these values are provided by the VBoxGuest driver and it might
+ * be loaded later than other drivers.
+ *
+ * The VbglEnter checks the current library status, tries to retrieve these
+ * values and fails if they are unavailable.
+ */
+static int vbglR0QueryDriverInfo(void)
+{
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ int rc = RTSemFastMutexRequest(g_vbgldata.hMtxIdcSetup);
+# else
+ int rc = RTSemMutexRequest(g_vbgldata.hMtxIdcSetup, RT_INDEFINITE_WAIT);
+# endif
+ if (RT_SUCCESS(rc))
+ {
+ if (g_vbgldata.status == VbglStatusReady)
+ { /* likely */ }
+ else
+ {
+ rc = VbglR0IdcOpen(&g_vbgldata.IdcHandle,
+ VBGL_IOC_VERSION /*uReqVersion*/,
+ VBGL_IOC_VERSION & UINT32_C(0xffff0000) /*uMinVersion*/,
+ NULL /*puSessionVersion*/, NULL /*puDriverVersion*/, NULL /*puDriverRevision*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try query the port info.
+ */
+ VBGLIOCGETVMMDEVIOINFO PortInfo;
+ RT_ZERO(PortInfo);
+ VBGLREQHDR_INIT(&PortInfo.Hdr, GET_VMMDEV_IO_INFO);
+ rc = VbglR0IdcCall(&g_vbgldata.IdcHandle, VBGL_IOCTL_GET_VMMDEV_IO_INFO, &PortInfo.Hdr, sizeof(PortInfo));
+ if (RT_SUCCESS(rc))
+ {
+ dprintf(("Port I/O = 0x%04x, MMIO = %p\n", PortInfo.u.Out.IoPort, PortInfo.u.Out.pvVmmDevMapping));
+
+ g_vbgldata.portVMMDev = PortInfo.u.Out.IoPort;
+ g_vbgldata.pVMMDevMemory = (VMMDevMemory *)PortInfo.u.Out.pvVmmDevMapping;
+ g_vbgldata.status = VbglStatusReady;
+
+ vbglR0QueryHostVersion();
+ }
+ }
+
+ dprintf(("vbglQueryDriverInfo rc = %Rrc\n", rc));
+ }
+
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ RTSemFastMutexRelease(g_vbgldata.hMtxIdcSetup);
+# else
+ RTSemMutexRelease(g_vbgldata.hMtxIdcSetup);
+# endif
+ }
+ return rc;
+}
+#endif /* !VBGL_VBOXGUEST */
+
+/**
+ * Checks if VBGL has been initialized.
+ *
+ * The client library, this will lazily complete the initialization.
+ *
+ * @return VINF_SUCCESS or VERR_VBGL_NOT_INITIALIZED.
+ */
+int vbglR0Enter(void)
+{
+ if (g_vbgldata.status == VbglStatusReady)
+ return VINF_SUCCESS;
+
+#ifndef VBGL_VBOXGUEST
+ if (g_vbgldata.status == VbglStatusInitializing)
+ {
+ vbglR0QueryDriverInfo();
+ if (g_vbgldata.status == VbglStatusReady)
+ return VINF_SUCCESS;
+ }
+#endif
+ return VERR_VBGL_NOT_INITIALIZED;
+}
+
+
+static int vbglR0InitCommon(void)
+{
+ int rc;
+
+ RT_ZERO(g_vbgldata);
+ g_vbgldata.status = VbglStatusInitializing;
+
+ rc = VbglR0PhysHeapInit();
+ if (RT_SUCCESS(rc))
+ {
+ dprintf(("vbglR0InitCommon: returns rc = %d\n", rc));
+ return rc;
+ }
+
+ LogRel(("vbglR0InitCommon: VbglR0PhysHeapInit failed: rc=%Rrc\n", rc));
+ g_vbgldata.status = VbglStatusNotInitialized;
+ return rc;
+}
+
+
+static void vbglR0TerminateCommon(void)
+{
+ VbglR0PhysHeapTerminate();
+ g_vbgldata.status = VbglStatusNotInitialized;
+}
+
+#ifdef VBGL_VBOXGUEST
+
+DECLR0VBGL(int) VbglR0InitPrimary(RTIOPORT portVMMDev, VMMDevMemory *pVMMDevMemory, uint32_t *pfFeatures)
+{
+ int rc;
+
+# ifdef RT_OS_WINDOWS /** @todo r=bird: this doesn't make sense. Is there something special going on on windows? */
+ dprintf(("vbglInit: starts g_vbgldata.status %d\n", g_vbgldata.status));
+
+ if ( g_vbgldata.status == VbglStatusInitializing
+ || g_vbgldata.status == VbglStatusReady)
+ {
+ /* Initialization is already in process. */
+ return VINF_SUCCESS;
+ }
+# else
+ dprintf(("vbglInit: starts\n"));
+# endif
+
+ rc = vbglR0InitCommon();
+ if (RT_SUCCESS(rc))
+ {
+ g_vbgldata.portVMMDev = portVMMDev;
+ g_vbgldata.pVMMDevMemory = pVMMDevMemory;
+ g_vbgldata.status = VbglStatusReady;
+
+ vbglR0QueryHostVersion();
+ *pfFeatures = g_vbgldata.hostVersion.features;
+ return VINF_SUCCESS;
+ }
+
+ g_vbgldata.status = VbglStatusNotInitialized;
+ return rc;
+}
+
+DECLR0VBGL(void) VbglR0TerminatePrimary(void)
+{
+ vbglR0TerminateCommon();
+}
+
+
+#else /* !VBGL_VBOXGUEST */
+
+DECLR0VBGL(int) VbglR0InitClient(void)
+{
+ int rc;
+
+ /** @todo r=bird: explain why we need to be doing this, please... */
+ if ( g_vbgldata.status == VbglStatusInitializing
+ || g_vbgldata.status == VbglStatusReady)
+ {
+ /* Initialization is already in process. */
+ return VINF_SUCCESS;
+ }
+
+ rc = vbglR0InitCommon();
+ if (RT_SUCCESS(rc))
+ {
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ rc = RTSemFastMutexCreate(&g_vbgldata.hMtxIdcSetup);
+# else
+ rc = RTSemMutexCreate(&g_vbgldata.hMtxIdcSetup);
+# endif
+ if (RT_SUCCESS(rc))
+ {
+ /* Try to obtain VMMDev port via IOCTL to VBoxGuest main driver. */
+ vbglR0QueryDriverInfo();
+
+# ifdef VBOX_WITH_HGCM
+ rc = VbglR0HGCMInit();
+# endif
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup);
+ g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX;
+# else
+ RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup);
+ g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX;
+# endif
+ }
+ vbglR0TerminateCommon();
+ }
+
+ return rc;
+}
+
+DECLR0VBGL(void) VbglR0TerminateClient(void)
+{
+# ifdef VBOX_WITH_HGCM
+ VbglR0HGCMTerminate();
+# endif
+
+ /* driver open could fail, which does not prevent VbglInit from succeeding,
+ * close the driver only if it is opened */
+ VbglR0IdcClose(&g_vbgldata.IdcHandle);
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ RTSemFastMutexDestroy(g_vbgldata.hMtxIdcSetup);
+ g_vbgldata.hMtxIdcSetup = NIL_RTSEMFASTMUTEX;
+# else
+ RTSemMutexDestroy(g_vbgldata.hMtxIdcSetup);
+ g_vbgldata.hMtxIdcSetup = NIL_RTSEMMUTEX;
+# endif
+
+ /* note: do vbglR0TerminateCommon as a last step since it zeroez up the g_vbgldata
+ * conceptually, doing vbglR0TerminateCommon last is correct
+ * since this is the reverse order to how init is done */
+ vbglR0TerminateCommon();
+}
+
+
+int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle)
+{
+ if (g_vbgldata.status == VbglStatusReady)
+ { /* likely */ }
+ else
+ {
+ vbglR0QueryDriverInfo();
+ if (g_vbgldata.status != VbglStatusReady)
+ {
+ *ppIdcHandle = NULL;
+ return VERR_TRY_AGAIN;
+ }
+ }
+
+ *ppIdcHandle = &g_vbgldata.IdcHandle;
+ return VINF_SUCCESS;
+}
+
+
+DECLR0VBGL(int) VbglR0QueryHostFeatures(uint32_t *pfHostFeatures)
+{
+ if (g_vbgldata.status == VbglStatusReady)
+ *pfHostFeatures = g_vbgldata.hostVersion.features;
+ else
+ {
+ int rc = vbglR0QueryDriverInfo();
+ if (g_vbgldata.status != VbglStatusReady)
+ return rc;
+ *pfHostFeatures = g_vbgldata.hostVersion.features;
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !VBGL_VBOXGUEST */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h
new file mode 100644
index 00000000..81657a8e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h
@@ -0,0 +1,212 @@
+/* $Id: VBoxGuestR0LibInternal.h $ */
+/** @file
+ * VBoxGuestLibR0 - Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h
+#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/*
+ * Define the private IDC handle structure before we include the VBoxGuestLib.h header.
+ */
+#include <iprt/types.h>
+#include <iprt/assert.h>
+RT_C_DECLS_BEGIN
+
+# ifndef VBGL_VBOXGUEST
+/**
+ * The hidden part of VBGLIDCHANDLE.
+ */
+struct VBGLIDCHANDLEPRIVATE
+{
+ /** Pointer to the session handle. */
+ void *pvSession;
+# if defined(RT_OS_WINDOWS) && (defined(IPRT_INCLUDED_nt_ntddk_h) || defined(IPRT_INCLUDED_nt_nt_h))
+ /** Pointer to the NT device object. */
+ PDEVICE_OBJECT pDeviceObject;
+ /** Pointer to the NT file object. */
+ PFILE_OBJECT pFileObject;
+# elif defined(RT_OS_SOLARIS) && defined(_SYS_SUNLDI_H)
+ /** LDI device handle to keep the device attached. */
+ ldi_handle_t hDev;
+# endif
+};
+/** Indicate that the VBGLIDCHANDLEPRIVATE structure is present. */
+# define VBGLIDCHANDLEPRIVATE_DECLARED 1
+#endif
+
+#include <VBox/VMMDev.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/VBoxGuestLib.h>
+
+#ifdef VBGLIDCHANDLEPRIVATE_DECLARED
+AssertCompile(RT_SIZEOFMEMB(VBGLIDCHANDLE, apvPadding) >= sizeof(struct VBGLIDCHANDLEPRIVATE));
+#endif
+
+
+/*
+ * Native IDC functions.
+ */
+int VBOXCALL vbglR0IdcNativeOpen(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCCONNECT pReq);
+int VBOXCALL vbglR0IdcNativeClose(PVBGLIDCHANDLE pHandle, PVBGLIOCIDCDISCONNECT pReq);
+
+
+/*
+ * Deprecated logging macro
+ */
+#include <VBox/log.h>
+#ifdef RT_OS_WINDOWS /** @todo dprintf() -> Log() */
+# if (defined(DEBUG) && !defined(NO_LOGGING)) || defined(LOG_ENABLED)
+# define dprintf(a) RTLogBackdoorPrintf a
+# else
+# define dprintf(a) do {} while (0)
+# endif
+#else
+# define dprintf(a) Log(a)
+#endif
+
+/*
+ * Lazy bird: OS/2 doesn't currently implement the RTSemMutex API in ring-0, so
+ * use a fast mutex instead. Unlike Windows, the OS/2 implementation
+ * doesn't have any nasty side effects on IRQL-like context properties, so the
+ * fast mutexes on OS/2 are identical to normal mutexes except for the missing
+ * timeout aspec. Fortunately we don't need timeouts here.
+ */
+#ifdef RT_OS_OS2
+# define VBGLDATA_USE_FAST_MUTEX
+#endif
+
+struct VBGLPHYSHEAPBLOCK;
+typedef struct VBGLPHYSHEAPBLOCK VBGLPHYSHEAPBLOCK;
+struct VBGLPHYSHEAPFREEBLOCK;
+typedef struct VBGLPHYSHEAPFREEBLOCK VBGLPHYSHEAPFREEBLOCK;
+struct VBGLPHYSHEAPCHUNK;
+typedef struct VBGLPHYSHEAPCHUNK VBGLPHYSHEAPCHUNK;
+
+enum VbglLibStatus
+{
+ VbglStatusNotInitialized = 0,
+ VbglStatusInitializing,
+ VbglStatusReady
+};
+
+/**
+ * Global VBGL ring-0 data.
+ * Lives in VBoxGuestR0LibInit.cpp.
+ */
+typedef struct VBGLDATA
+{
+ enum VbglLibStatus status;
+
+ RTIOPORT portVMMDev;
+
+ VMMDevMemory *pVMMDevMemory;
+
+ /** Physical memory heap data.
+ * @{
+ */
+ RTSEMFASTMUTEX hMtxHeap;
+ /** Head of the block list (both free and allocated blocks).
+ * This runs parallel to the chunk list and is sorted by address within each
+ * chunk. This allows merging with blocks both after and before when
+ * freeing. */
+ VBGLPHYSHEAPBLOCK *pBlockHead;
+ /** Head of the free list.
+ * This is not sorted and more on the most recently freed approach. */
+ VBGLPHYSHEAPFREEBLOCK *pFreeHead;
+ /** Number of block of any kind. */
+ int32_t cBlocks;
+ /** Number of free blocks. */
+ int32_t cFreeBlocks;
+ /** Head of the chunk list. */
+ VBGLPHYSHEAPCHUNK *pChunkHead;
+ /** @} */
+
+ /**
+ * The host version data.
+ */
+ VMMDevReqHostVersion hostVersion;
+
+
+#ifndef VBGL_VBOXGUEST
+ /** The IDC handle. This is used for talking to the main driver. */
+ VBGLIDCHANDLE IdcHandle;
+ /** Mutex used to serialize IDC setup. */
+# ifdef VBGLDATA_USE_FAST_MUTEX
+ RTSEMFASTMUTEX hMtxIdcSetup;
+# else
+ RTSEMMUTEX hMtxIdcSetup;
+# endif
+#endif
+} VBGLDATA;
+
+
+extern VBGLDATA g_vbgldata;
+
+/**
+ * Internal macro for checking whether we can pass physical page lists to the
+ * host.
+ *
+ * ASSUMES that vbglR0Enter has been called already.
+ *
+ * @param a_fLocked For the windows shared folders workarounds.
+ *
+ * @remarks Disabled the PageList feature for locked memory on Windows,
+ * because a new MDL is created by VBGL to get the page addresses
+ * and the pages from the MDL are marked as dirty when they should not.
+ */
+#if defined(RT_OS_WINDOWS)
+# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \
+ ( !(a_fLocked) && (g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) )
+#else
+# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \
+ ( !!(g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) )
+#endif
+
+int vbglR0Enter (void);
+
+#ifdef VBOX_WITH_HGCM
+struct VBGLHGCMHANDLEDATA *vbglR0HGCMHandleAlloc(void);
+void vbglR0HGCMHandleFree(struct VBGLHGCMHANDLEDATA *pHandle);
+#endif /* VBOX_WITH_HGCM */
+
+#ifndef VBGL_VBOXGUEST
+/**
+ * Get the IDC handle to the main VBoxGuest driver.
+ * @returns VERR_TRY_AGAIN if the main driver has not yet been loaded.
+ */
+int VBOXCALL vbglR0QueryIdcHandle(PVBGLIDCHANDLE *ppIdcHandle);
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR0LibInternal_h */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp
new file mode 100644
index 00000000..a0c9e1c5
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibMouse.cpp
@@ -0,0 +1,130 @@
+/* $Id: VBoxGuestR0LibMouse.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Mouse Integration.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+#ifdef VBGL_VBOXGUEST
+# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code."
+#endif
+
+
+/**
+ * Sets the function which is called back on each mouse pointer event. Only
+ * one callback can be active at once, so if you need several for any reason
+ * you must multiplex yourself. Call backs can be disabled by passing NULL
+ * as the function pointer.
+ *
+ * @remarks Ring-0.
+ * @returns iprt status code.
+ * @returns VERR_TRY_AGAIN if the main guest driver hasn't finished
+ * initialising.
+ *
+ * @param pfnNotify the function to call back. NULL to disable call backs.
+ * @param pvUser user supplied data/cookie to be passed to the function.
+ */
+DECLR0VBGL(int) VbglR0SetMouseNotifyCallback(PFNVBOXGUESTMOUSENOTIFY pfnNotify, void *pvUser)
+{
+ PVBGLIDCHANDLE pIdcHandle;
+ int rc = vbglR0QueryIdcHandle(&pIdcHandle);
+ if (RT_SUCCESS(rc))
+ {
+ VBGLIOCSETMOUSENOTIFYCALLBACK NotifyCallback;
+ VBGLREQHDR_INIT(&NotifyCallback.Hdr, SET_MOUSE_NOTIFY_CALLBACK);
+ NotifyCallback.u.In.pfnNotify = pfnNotify;
+ NotifyCallback.u.In.pvUser = pvUser;
+ rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_NOTIFY_CALLBACK, &NotifyCallback.Hdr, sizeof(NotifyCallback));
+ }
+ return rc;
+}
+
+
+/**
+ * Retrieve mouse coordinates and features from the host.
+ *
+ * @remarks Ring-0.
+ * @returns VBox status code.
+ *
+ * @param pfFeatures Where to store the mouse features.
+ * @param px Where to store the X co-ordinate.
+ * @param py Where to store the Y co-ordinate.
+ */
+DECLR0VBGL(int) VbglR0GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py)
+{
+ PVBGLIDCHANDLE pIdcHandle;
+ int rc = vbglR0QueryIdcHandle(&pIdcHandle);
+ if (RT_SUCCESS(rc))
+ {
+ VMMDevReqMouseStatus Req;
+ VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetMouseStatus);
+ Req.mouseFeatures = 0;
+ Req.pointerXPos = 0;
+ Req.pointerYPos = 0;
+ rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_VMMDEV_REQUEST(sizeof(Req)), (PVBGLREQHDR)&Req.header, sizeof(Req));
+ if (RT_SUCCESS(rc))
+ {
+ if (pfFeatures)
+ *pfFeatures = Req.mouseFeatures;
+ if (px)
+ *px = Req.pointerXPos;
+ if (py)
+ *py = Req.pointerYPos;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Send mouse features to the host.
+ *
+ * @remarks Ring-0.
+ * @returns VBox status code.
+ *
+ * @param fFeatures Supported mouse pointer features. The main guest driver
+ * will mediate different callers and show the host any
+ * feature enabled by any guest caller.
+ */
+DECLR0VBGL(int) VbglR0SetMouseStatus(uint32_t fFeatures)
+{
+ PVBGLIDCHANDLE pIdcHandle;
+ int rc = vbglR0QueryIdcHandle(&pIdcHandle);
+ if (RT_SUCCESS(rc))
+ {
+ VBGLIOCSETMOUSESTATUS Req;
+ VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
+ Req.u.In.fStatus = fFeatures;
+ rc = VbglR0IdcCall(pIdcHandle, VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp
new file mode 100644
index 00000000..b99f51ad
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp
@@ -0,0 +1,1197 @@
+/* $Id: VBoxGuestR0LibPhysHeap.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Physical memory heap.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @page pg_vbglr0_phys_heap VBoxGuestLibR0 - Physical memory heap.
+ *
+ * Traditional heap implementation keeping all blocks in a ordered list and
+ * keeping free blocks on additional list via pointers in the user area. This
+ * is similar to @ref grp_rt_heap_simple "RTHeapSimple" and
+ * @ref grp_rt_heap_offset "RTHeapOffset" in IPRT, except that this code handles
+ * mutiple chunks and has a physical address associated with each chunk and
+ * block. The alignment is fixed (VBGL_PH_ALLOC_ALIGN).
+ *
+ * When allocating memory, a free block is found that satisfies the request,
+ * extending the heap with another chunk if needed. The block is split if it's
+ * too large, and the tail end is put on the free list.
+ *
+ * When freeing memory, the block being freed is put back on the free list and
+ * we use the block list to check whether it can be merged with adjacent blocks.
+ *
+ * @note The original code managed the blocks in two separate lists for free and
+ * allocated blocks, which had the disadvantage only allowing merging with
+ * the block after the block being freed. On the plus side, it had the
+ * potential for slightly better locality when examining the free list,
+ * since the next pointer and block size members were closer to one
+ * another.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/memobj.h>
+#include <iprt/semaphore.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Enables heap dumping. */
+#if defined(DOXYGEN_RUNNING) || 0
+# define VBGL_PH_DUMPHEAP
+#endif
+
+#ifdef VBGL_PH_DUMPHEAP
+# define VBGL_PH_DPRINTF(a) RTAssertMsg2Weak a
+#else
+# define VBGL_PH_DPRINTF(a) do { } while (0)
+#endif
+
+/** Heap chunk signature */
+#define VBGL_PH_CHUNKSIGNATURE UINT32_C(0xADDCCCCC)
+/** Heap chunk allocation unit */
+#define VBGL_PH_CHUNKSIZE (0x10000)
+
+/** Heap block signature */
+#define VBGL_PH_BLOCKSIGNATURE UINT32_C(0xADDBBBBB)
+
+/** The allocation block alignment.
+ *
+ * This cannot be larger than VBGLPHYSHEAPBLOCK.
+ */
+#define VBGL_PH_ALLOC_ALIGN (sizeof(void *))
+
+/** Max number of free nodes to search before just using the best fit.
+ *
+ * This is used to limit the free list walking during allocation and just get
+ * on with the job. A low number should reduce the cache trashing at the
+ * possible cost of heap fragmentation.
+ *
+ * Picked 16 after comparing the tstVbglR0PhysHeap-1 results w/ uRandSeed=42 for
+ * different max values.
+ */
+#define VBGL_PH_MAX_FREE_SEARCH 16
+
+/** Threshold to stop the block search if a free block is at least this much too big.
+ *
+ * May cause more fragmation (depending on usage pattern), but should speed up
+ * allocation and hopefully reduce cache trashing.
+ *
+ * Since we merge adjacent free blocks when we can, free blocks should typically
+ * be a lot larger that what's requested. So, it is probably a good idea to
+ * just chop up a large block rather than keep searching for a perfect-ish
+ * match.
+ *
+ * Undefine this to disable this trick.
+ */
+#if defined(DOXYGEN_RUNNING) || 1
+# define VBGL_PH_STOP_SEARCH_AT_EXCESS _4K
+#endif
+
+/** Threshold at which to split out a tail free block when allocating.
+ *
+ * The value gives the amount of user space, i.e. excluding the header.
+ *
+ * Using 32 bytes based on VMMDev.h request sizes. The smallest requests are 24
+ * bytes, i.e. only the header, at least 4 of these. There are at least 10 with
+ * size 28 bytes and at least 11 with size 32 bytes. So, 32 bytes would fit
+ * some 25 requests out of about 60, which is reasonable.
+ */
+#define VBGL_PH_MIN_SPLIT_FREE_BLOCK 32
+
+
+/** The smallest amount of user data that can be allocated.
+ *
+ * This is to ensure that the block can be converted into a
+ * VBGLPHYSHEAPFREEBLOCK structure when freed. This must be smaller or equal
+ * to VBGL_PH_MIN_SPLIT_FREE_BLOCK.
+ */
+#define VBGL_PH_SMALLEST_ALLOC_SIZE 16
+
+/** The maximum allocation request size. */
+#define VBGL_PH_LARGEST_ALLOC_SIZE RT_ALIGN_32( _128M \
+ - sizeof(VBGLPHYSHEAPBLOCK) \
+ - sizeof(VBGLPHYSHEAPCHUNK) \
+ - VBGL_PH_ALLOC_ALIGN, \
+ VBGL_PH_ALLOC_ALIGN)
+
+/**
+ * Whether to use the RTR0MemObjAllocCont API or RTMemContAlloc for
+ * allocating chunks.
+ *
+ * This can be enabled on hosts where RTMemContAlloc is more complicated than
+ * RTR0MemObjAllocCont. This can also be done if we wish to save code space, as
+ * the latter is typically always dragged into the link on guests where the
+ * linker cannot eliminiate functions within objects. Only drawback is that
+ * RTR0MemObjAllocCont requires another heap allocation for the handle.
+ */
+#if defined(DOXYGEN_RUNNING) || (!defined(IN_TESTCASE) && 0)
+# define VBGL_PH_USE_MEMOBJ
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * A heap block (within a chunk).
+ *
+ * This is used to track a part of a heap chunk that's either free or
+ * allocated. The VBGLPHYSHEAPBLOCK::fAllocated member indicates which it is.
+ */
+struct VBGLPHYSHEAPBLOCK
+{
+ /** Magic value (VBGL_PH_BLOCKSIGNATURE). */
+ uint32_t u32Signature;
+
+ /** Size of user data in the block. Does not include this block header. */
+ uint32_t cbUser : 31;
+ /** The top bit indicates whether it's allocated or free. */
+ uint32_t fAllocated : 1;
+
+ /** Pointer to the next block on the list. */
+ VBGLPHYSHEAPBLOCK *pNext;
+ /** Pointer to the previous block on the list. */
+ VBGLPHYSHEAPBLOCK *pPrev;
+ /** Pointer back to the chunk. */
+ VBGLPHYSHEAPCHUNK *pChunk;
+};
+
+/**
+ * A free block.
+ */
+struct VBGLPHYSHEAPFREEBLOCK
+{
+ /** Core block data. */
+ VBGLPHYSHEAPBLOCK Core;
+ /** Pointer to the next free list entry. */
+ VBGLPHYSHEAPFREEBLOCK *pNextFree;
+ /** Pointer to the previous free list entry. */
+ VBGLPHYSHEAPFREEBLOCK *pPrevFree;
+};
+AssertCompile(VBGL_PH_SMALLEST_ALLOC_SIZE >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK));
+AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(VBGLPHYSHEAPFREEBLOCK) - sizeof(VBGLPHYSHEAPBLOCK));
+AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= VBGL_PH_SMALLEST_ALLOC_SIZE);
+
+/**
+ * A chunk of memory used by the heap for sub-allocations.
+ *
+ * There is a list of these.
+ */
+struct VBGLPHYSHEAPCHUNK
+{
+ /** Magic value (VBGL_PH_CHUNKSIGNATURE). */
+ uint32_t u32Signature;
+
+ /** Size of the chunk. Includes the chunk header. */
+ uint32_t cbChunk;
+
+ /** Physical address of the chunk (contiguous). */
+ uint32_t physAddr;
+
+#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32
+ uint32_t uPadding1;
+#endif
+
+ /** Number of block of any kind. */
+ int32_t cBlocks;
+ /** Number of free blocks. */
+ int32_t cFreeBlocks;
+
+ /** Pointer to the next chunk. */
+ VBGLPHYSHEAPCHUNK *pNext;
+ /** Pointer to the previous chunk. */
+ VBGLPHYSHEAPCHUNK *pPrev;
+
+#if defined(VBGL_PH_USE_MEMOBJ)
+ /** The allocation handle. */
+ RTR0MEMOBJ hMemObj;
+#endif
+
+#if ARCH_BITS == 64
+ /** Pad the size up to 64 bytes. */
+# ifdef VBGL_PH_USE_MEMOBJ
+ uintptr_t auPadding2[2];
+# else
+ uintptr_t auPadding2[3];
+# endif
+#endif
+};
+#if ARCH_BITS == 64
+AssertCompileSize(VBGLPHYSHEAPCHUNK, 64);
+#endif
+
+
+/**
+ * Debug function that dumps the heap.
+ */
+#ifndef VBGL_PH_DUMPHEAP
+# define dumpheap(pszWhere) do { } while (0)
+#else
+static void dumpheap(const char *pszWhere)
+{
+ VBGL_PH_DPRINTF(("VBGL_PH dump at '%s'\n", pszWhere));
+
+ VBGL_PH_DPRINTF(("Chunks:\n"));
+ for (VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead; pChunk; pChunk = pChunk->pNext)
+ VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, cBlocks = %8d, cFreeBlocks=%8d, phys = %08X\n",
+ pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbChunk,
+ pChunk->cBlocks, pChunk->cFreeBlocks, pChunk->physAddr));
+
+ VBGL_PH_DPRINTF(("Allocated blocks:\n"));
+ for (VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pBlockHead; pBlock; pBlock = pBlock->pNext)
+ VBGL_PH_DPRINTF(("%p: pNext = %p, pPrev = %p, size = %05x, sign = %08X, %s, pChunk = %p\n",
+ pBlock, pBlock->pNext, pBlock->pPrev, pBlock->cbUser,
+ pBlock->u32Signature, pBlock->fAllocated ? "allocated" : " free", pBlock->pChunk));
+
+ VBGL_PH_DPRINTF(("Free blocks:\n"));
+ for (VBGLPHYSHEAPFREEBLOCK *pBlock = g_vbgldata.pFreeHead; pBlock; pBlock = pBlock->pNextFree)
+ VBGL_PH_DPRINTF(("%p: pNextFree = %p, pPrevFree = %p, size = %05x, sign = %08X, pChunk = %p%s\n",
+ pBlock, pBlock->pNextFree, pBlock->pPrevFree, pBlock->Core.cbUser,
+ pBlock->Core.u32Signature, pBlock->Core.pChunk,
+ !pBlock->Core.fAllocated ? "" : " !!allocated-block-on-freelist!!"));
+
+ VBGL_PH_DPRINTF(("VBGL_PH dump at '%s' done\n", pszWhere));
+}
+#endif
+
+
+/**
+ * Initialize a free block
+ */
+static void vbglPhysHeapInitFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbUser)
+{
+ Assert(pBlock != NULL);
+ Assert(pChunk != NULL);
+
+ pBlock->Core.u32Signature = VBGL_PH_BLOCKSIGNATURE;
+ pBlock->Core.cbUser = cbUser;
+ pBlock->Core.fAllocated = false;
+ pBlock->Core.pNext = NULL;
+ pBlock->Core.pPrev = NULL;
+ pBlock->Core.pChunk = pChunk;
+ pBlock->pNextFree = NULL;
+ pBlock->pPrevFree = NULL;
+}
+
+
+/**
+ * Updates block statistics when a block is added.
+ */
+DECLINLINE(void) vbglPhysHeapStatsBlockAdded(VBGLPHYSHEAPBLOCK *pBlock)
+{
+ g_vbgldata.cBlocks += 1;
+ pBlock->pChunk->cBlocks += 1;
+ AssertMsg((uint32_t)pBlock->pChunk->cBlocks <= pBlock->pChunk->cbChunk / sizeof(VBGLPHYSHEAPFREEBLOCK),
+ ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks));
+}
+
+
+/**
+ * Links @a pBlock onto the head of block list.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapInsertBlock(VBGLPHYSHEAPBLOCK *pBlock)
+{
+ AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext));
+ AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev));
+
+ /* inserting to head of list */
+ VBGLPHYSHEAPBLOCK *pOldHead = g_vbgldata.pBlockHead;
+
+ pBlock->pNext = pOldHead;
+ pBlock->pPrev = NULL;
+
+ if (pOldHead)
+ pOldHead->pPrev = pBlock;
+ g_vbgldata.pBlockHead = pBlock;
+
+ /* Update the stats: */
+ vbglPhysHeapStatsBlockAdded(pBlock);
+}
+
+
+/**
+ * Links @a pBlock onto the block list after @a pInsertAfter.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapInsertBlockAfter(VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPBLOCK *pInsertAfter)
+{
+ AssertMsg(pBlock->pNext == NULL, ("pBlock->pNext = %p\n", pBlock->pNext));
+ AssertMsg(pBlock->pPrev == NULL, ("pBlock->pPrev = %p\n", pBlock->pPrev));
+
+ pBlock->pNext = pInsertAfter->pNext;
+ pBlock->pPrev = pInsertAfter;
+
+ if (pInsertAfter->pNext)
+ pInsertAfter->pNext->pPrev = pBlock;
+
+ pInsertAfter->pNext = pBlock;
+
+ /* Update the stats: */
+ vbglPhysHeapStatsBlockAdded(pBlock);
+}
+
+
+/**
+ * Unlinks @a pBlock from the block list.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapUnlinkBlock(VBGLPHYSHEAPBLOCK *pBlock)
+{
+ VBGLPHYSHEAPBLOCK *pOtherBlock = pBlock->pNext;
+ if (pOtherBlock)
+ pOtherBlock->pPrev = pBlock->pPrev;
+ /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */
+
+ pOtherBlock = pBlock->pPrev;
+ if (pOtherBlock)
+ pOtherBlock->pNext = pBlock->pNext;
+ else
+ {
+ Assert(g_vbgldata.pBlockHead == pBlock);
+ g_vbgldata.pBlockHead = pBlock->pNext;
+ }
+
+ pBlock->pNext = NULL;
+ pBlock->pPrev = NULL;
+
+ /* Update the stats: */
+ g_vbgldata.cBlocks -= 1;
+ pBlock->pChunk->cBlocks -= 1;
+ AssertMsg(pBlock->pChunk->cBlocks >= 0,
+ ("pChunk=%p: cbChunk=%#x cBlocks=%d\n", pBlock->pChunk, pBlock->pChunk->cbChunk, pBlock->pChunk->cBlocks));
+ Assert(g_vbgldata.cBlocks >= 0);
+}
+
+
+
+/**
+ * Updates statistics after adding a free block.
+ */
+DECLINLINE(void) vbglPhysHeapStatsFreeBlockAdded(VBGLPHYSHEAPFREEBLOCK *pBlock)
+{
+ g_vbgldata.cFreeBlocks += 1;
+ pBlock->Core.pChunk->cFreeBlocks += 1;
+}
+
+
+/**
+ * Links @a pBlock onto head of the free chain.
+ *
+ * This is used during block freeing and when adding a new chunk.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapInsertFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock)
+{
+ Assert(!pBlock->Core.fAllocated);
+ AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree));
+ AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree));
+
+ /* inserting to head of list */
+ VBGLPHYSHEAPFREEBLOCK *pOldHead = g_vbgldata.pFreeHead;
+
+ pBlock->pNextFree = pOldHead;
+ pBlock->pPrevFree = NULL;
+
+ if (pOldHead)
+ pOldHead->pPrevFree = pBlock;
+ g_vbgldata.pFreeHead = pBlock;
+
+ /* Update the stats: */
+ vbglPhysHeapStatsFreeBlockAdded(pBlock);
+}
+
+
+/**
+ * Links @a pBlock after @a pInsertAfter.
+ *
+ * This is used when splitting a free block during allocation to preserve the
+ * place in the free list.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapInsertFreeBlockAfter(VBGLPHYSHEAPFREEBLOCK *pBlock, VBGLPHYSHEAPFREEBLOCK *pInsertAfter)
+{
+ Assert(!pBlock->Core.fAllocated);
+ AssertMsg(pBlock->pNextFree == NULL, ("pBlock->pNextFree = %p\n", pBlock->pNextFree));
+ AssertMsg(pBlock->pPrevFree == NULL, ("pBlock->pPrevFree = %p\n", pBlock->pPrevFree));
+
+ /* inserting after the tiven node */
+ pBlock->pNextFree = pInsertAfter->pNextFree;
+ pBlock->pPrevFree = pInsertAfter;
+
+ if (pInsertAfter->pNextFree)
+ pInsertAfter->pNextFree->pPrevFree = pBlock;
+
+ pInsertAfter->pNextFree = pBlock;
+
+ /* Update the stats: */
+ vbglPhysHeapStatsFreeBlockAdded(pBlock);
+}
+
+
+/**
+ * Unlinks @a pBlock from the free list.
+ *
+ * This also update the per-chunk block counts.
+ */
+static void vbglPhysHeapUnlinkFreeBlock(VBGLPHYSHEAPFREEBLOCK *pBlock)
+{
+ Assert(!pBlock->Core.fAllocated);
+
+ VBGLPHYSHEAPFREEBLOCK *pOtherBlock = pBlock->pNextFree;
+ if (pOtherBlock)
+ pOtherBlock->pPrevFree = pBlock->pPrevFree;
+ /* else: this is tail of list but we do not maintain tails of block lists. so nothing to do. */
+
+ pOtherBlock = pBlock->pPrevFree;
+ if (pOtherBlock)
+ pOtherBlock->pNextFree = pBlock->pNextFree;
+ else
+ {
+ Assert(g_vbgldata.pFreeHead == pBlock);
+ g_vbgldata.pFreeHead = pBlock->pNextFree;
+ }
+
+ pBlock->pNextFree = NULL;
+ pBlock->pPrevFree = NULL;
+
+ /* Update the stats: */
+ g_vbgldata.cFreeBlocks -= 1;
+ pBlock->Core.pChunk->cFreeBlocks -= 1;
+ AssertMsg(pBlock->Core.pChunk->cFreeBlocks >= 0,
+ ("pChunk=%p: cbChunk=%#x cFreeBlocks=%d\n",
+ pBlock->Core.pChunk, pBlock->Core.pChunk->cbChunk, pBlock->Core.pChunk->cFreeBlocks));
+ Assert(g_vbgldata.cFreeBlocks >= 0);
+}
+
+
+/**
+ * Allocate another chunk and add it to the heap.
+ *
+ * @returns Pointer to the free block in the new chunk on success, NULL on
+ * allocation failure.
+ * @param cbMinBlock The size of the user block we need this chunk for.
+ */
+static VBGLPHYSHEAPFREEBLOCK *vbglPhysHeapChunkAlloc(uint32_t cbMinBlock)
+{
+ RTCCPHYS PhysAddr = NIL_RTHCPHYS;
+ VBGLPHYSHEAPCHUNK *pChunk;
+ uint32_t cbChunk;
+#ifdef VBGL_PH_USE_MEMOBJ
+ RTR0MEMOBJ hMemObj = NIL_RTR0MEMOBJ;
+ int rc;
+#endif
+
+ VBGL_PH_DPRINTF(("Allocating new chunk for %#x byte allocation\n", cbMinBlock));
+ AssertReturn(cbMinBlock <= VBGL_PH_LARGEST_ALLOC_SIZE, NULL); /* paranoia */
+
+ /*
+ * Compute the size of the new chunk, rounding up to next chunk size,
+ * which must be power of 2.
+ *
+ * Note! Using VBGLPHYSHEAPFREEBLOCK here means the minimum block size is
+ * 8 or 16 bytes too high, but safer this way since cbMinBlock is
+ * zero during the init code call.
+ */
+ Assert(RT_IS_POWER_OF_TWO(VBGL_PH_CHUNKSIZE));
+ cbChunk = cbMinBlock + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK);
+ cbChunk = RT_ALIGN_32(cbChunk, VBGL_PH_CHUNKSIZE);
+
+ /*
+ * This function allocates physical contiguous memory below 4 GB. This 4GB
+ * limitation stems from using a 32-bit OUT instruction to pass a block
+ * physical address to the host.
+ */
+#ifdef VBGL_PH_USE_MEMOBJ
+ rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/);
+ pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL);
+#else
+ pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk);
+#endif
+ if (!pChunk)
+ {
+ /* If the allocation fail, halv the size till and try again. */
+ uint32_t cbMinChunk = RT_MAX(cbMinBlock, PAGE_SIZE / 2) + sizeof(VBGLPHYSHEAPCHUNK) + sizeof(VBGLPHYSHEAPFREEBLOCK);
+ cbMinChunk = RT_ALIGN_32(cbMinChunk, PAGE_SIZE);
+ if (cbChunk > cbMinChunk)
+ do
+ {
+ cbChunk >>= 2;
+ cbChunk = RT_ALIGN_32(cbChunk, PAGE_SIZE);
+#ifdef VBGL_PH_USE_MEMOBJ
+ rc = RTR0MemObjAllocCont(&hMemObj, cbChunk, false /*fExecutable*/);
+ pChunk = (VBGLPHYSHEAPCHUNK *)(RT_SUCCESS(rc) ? RTR0MemObjAddress(hMemObj) : NULL);
+#else
+ pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc(&PhysAddr, cbChunk);
+#endif
+ } while (!pChunk && cbChunk > cbMinChunk);
+ }
+ if (pChunk)
+ {
+ VBGLPHYSHEAPCHUNK *pOldHeadChunk;
+ VBGLPHYSHEAPFREEBLOCK *pBlock;
+ AssertRelease(PhysAddr < _4G && PhysAddr + cbChunk <= _4G);
+
+ /*
+ * Init the new chunk.
+ */
+ pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
+ pChunk->cbChunk = cbChunk;
+ pChunk->physAddr = (uint32_t)PhysAddr;
+ pChunk->cBlocks = 0;
+ pChunk->cFreeBlocks = 0;
+ pChunk->pNext = NULL;
+ pChunk->pPrev = NULL;
+#ifdef VBGL_PH_USE_MEMOBJ
+ pChunk->hMemObj = hMemObj;
+#endif
+
+ /* Initialize the padding too: */
+#if !defined(VBGL_PH_USE_MEMOBJ) || ARCH_BITS != 32
+ pChunk->uPadding1 = UINT32_C(0xADDCAAA1);
+#endif
+#if ARCH_BITS == 64
+ pChunk->auPadding2[0] = UINT64_C(0xADDCAAA3ADDCAAA2);
+ pChunk->auPadding2[1] = UINT64_C(0xADDCAAA5ADDCAAA4);
+# ifndef VBGL_PH_USE_MEMOBJ
+ pChunk->auPadding2[2] = UINT64_C(0xADDCAAA7ADDCAAA6);
+# endif
+#endif
+
+ /*
+ * Initialize the free block, which now occupies entire chunk.
+ */
+ pBlock = (VBGLPHYSHEAPFREEBLOCK *)(pChunk + 1);
+ vbglPhysHeapInitFreeBlock(pBlock, pChunk, cbChunk - sizeof(VBGLPHYSHEAPCHUNK) - sizeof(VBGLPHYSHEAPBLOCK));
+ vbglPhysHeapInsertBlock(&pBlock->Core);
+ vbglPhysHeapInsertFreeBlock(pBlock);
+
+ /*
+ * Add the chunk to the list.
+ */
+ pOldHeadChunk = g_vbgldata.pChunkHead;
+ pChunk->pNext = pOldHeadChunk;
+ if (pOldHeadChunk)
+ pOldHeadChunk->pPrev = pChunk;
+ g_vbgldata.pChunkHead = pChunk;
+
+ VBGL_PH_DPRINTF(("Allocated chunk %p LB %#x, block %p LB %#x\n", pChunk, cbChunk, pBlock, pBlock->Core.cbUser));
+ return pBlock;
+ }
+ LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u (%#x) contiguous bytes.\n", cbChunk, cbChunk));
+ return NULL;
+}
+
+
+/**
+ * Deletes a chunk: Unlinking all its blocks and freeing its memory.
+ */
+static void vbglPhysHeapChunkDelete(VBGLPHYSHEAPCHUNK *pChunk)
+{
+ uintptr_t uEnd, uCur;
+ Assert(pChunk != NULL);
+ AssertMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE, ("pChunk->u32Signature = %08X\n", pChunk->u32Signature));
+
+ VBGL_PH_DPRINTF(("Deleting chunk %p size %x\n", pChunk, pChunk->cbChunk));
+
+ /*
+ * First scan the chunk and unlink all blocks from the lists.
+ *
+ * Note! We could do this by finding the first and last block list entries
+ * and just drop the whole chain relating to this chunk, rather than
+ * doing it one by one. But doing it one by one is simpler and will
+ * continue to work if the block list ends in an unsorted state.
+ */
+ uEnd = (uintptr_t)pChunk + pChunk->cbChunk;
+ uCur = (uintptr_t)(pChunk + 1);
+
+ while (uCur < uEnd)
+ {
+ VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)uCur;
+ Assert(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE);
+ Assert(pBlock->pChunk == pChunk);
+
+ uCur += pBlock->cbUser + sizeof(VBGLPHYSHEAPBLOCK);
+ Assert(uCur == (uintptr_t)pBlock->pNext || uCur >= uEnd);
+
+ if (!pBlock->fAllocated)
+ vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pBlock);
+ vbglPhysHeapUnlinkBlock(pBlock);
+ }
+
+ AssertMsg(uCur == uEnd, ("uCur = %p, uEnd = %p, pChunk->cbChunk = %08X\n", uCur, uEnd, pChunk->cbChunk));
+
+ /*
+ * Unlink the chunk from the chunk list.
+ */
+ if (pChunk->pNext)
+ pChunk->pNext->pPrev = pChunk->pPrev;
+ /* else: we do not maintain tail pointer. */
+
+ if (pChunk->pPrev)
+ pChunk->pPrev->pNext = pChunk->pNext;
+ else
+ {
+ Assert(g_vbgldata.pChunkHead == pChunk);
+ g_vbgldata.pChunkHead = pChunk->pNext;
+ }
+
+ /*
+ * Finally, free the chunk memory.
+ */
+#ifdef VBGL_PH_USE_MEMOBJ
+ RTR0MemObjFree(pChunk->hMemObj, true /*fFreeMappings*/);
+#else
+ RTMemContFree(pChunk, pChunk->cbChunk);
+#endif
+}
+
+
+DECLR0VBGL(void *) VbglR0PhysHeapAlloc(uint32_t cb)
+{
+ VBGLPHYSHEAPFREEBLOCK *pBlock;
+ VBGLPHYSHEAPFREEBLOCK *pIter;
+ int32_t cLeft;
+#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS
+ uint32_t cbAlwaysSplit;
+#endif
+ int rc;
+
+ /*
+ * Make sure we don't allocate anything too small to turn into a free node
+ * and align the size to prevent pointer misalignment and whatnot.
+ */
+ cb = RT_MAX(cb, VBGL_PH_SMALLEST_ALLOC_SIZE);
+ cb = RT_ALIGN_32(cb, VBGL_PH_ALLOC_ALIGN);
+ AssertCompile(VBGL_PH_ALLOC_ALIGN <= sizeof(pBlock->Core));
+
+ rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap);
+ AssertRCReturn(rc, NULL);
+
+ dumpheap("pre alloc");
+
+ /*
+ * Search the free list. We do this in linear fashion as we don't expect
+ * there to be many blocks in the heap.
+ */
+#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS
+ cbAlwaysSplit = cb + VBGL_PH_STOP_SEARCH_AT_EXCESS;
+#endif
+ cLeft = VBGL_PH_MAX_FREE_SEARCH;
+ pBlock = NULL;
+ if (cb <= PAGE_SIZE / 4 * 3)
+ {
+ /* Smaller than 3/4 page: Prefer a free block that can keep the request within a single page,
+ so HGCM processing in VMMDev can use page locks instead of several reads and writes. */
+ VBGLPHYSHEAPFREEBLOCK *pFallback = NULL;
+ for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--)
+ {
+ AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE);
+ if (pIter->Core.cbUser >= cb)
+ {
+ if (pIter->Core.cbUser == cb)
+ {
+ if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb)
+ {
+ pBlock = pIter;
+ break;
+ }
+ pFallback = pIter;
+ }
+ else
+ {
+ if (!pFallback || pIter->Core.cbUser < pFallback->Core.cbUser)
+ pFallback = pIter;
+ if (PAGE_SIZE - ((uintptr_t)(pIter + 1) & PAGE_OFFSET_MASK) >= cb)
+ {
+ if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser)
+ pBlock = pIter;
+#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS
+ else if (pIter->Core.cbUser >= cbAlwaysSplit)
+ {
+ pBlock = pIter;
+ break;
+ }
+#endif
+ }
+ }
+
+ if (cLeft > 0)
+ { /* likely */ }
+ else
+ break;
+ }
+ }
+
+ if (!pBlock)
+ pBlock = pFallback;
+ }
+ else
+ {
+ /* Large than 3/4 page: Find closest free list match. */
+ for (pIter = g_vbgldata.pFreeHead; pIter != NULL; pIter = pIter->pNextFree, cLeft--)
+ {
+ AssertBreak(pIter->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE);
+ if (pIter->Core.cbUser >= cb)
+ {
+ if (pIter->Core.cbUser == cb)
+ {
+ /* Exact match - we're done! */
+ pBlock = pIter;
+ break;
+ }
+
+#ifdef VBGL_PH_STOP_SEARCH_AT_EXCESS
+ if (pIter->Core.cbUser >= cbAlwaysSplit)
+ {
+ /* Really big block - no point continue searching! */
+ pBlock = pIter;
+ break;
+ }
+#endif
+ /* Looking for a free block with nearest size. */
+ if (!pBlock || pIter->Core.cbUser < pBlock->Core.cbUser)
+ pBlock = pIter;
+
+ if (cLeft > 0)
+ { /* likely */ }
+ else
+ break;
+ }
+ }
+ }
+
+ if (!pBlock)
+ {
+ /* No free blocks, allocate a new chunk, the only free block of the
+ chunk will be returned. */
+ pBlock = vbglPhysHeapChunkAlloc(cb);
+ }
+
+ if (pBlock)
+ {
+ /* We have a free block, either found or allocated. */
+ AssertMsg(pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE,
+ ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->Core.u32Signature));
+ AssertMsg(!pBlock->Core.fAllocated, ("pBlock = %p\n", pBlock));
+
+ /*
+ * If the block is too large, split off a free block with the unused space.
+ *
+ * We do this before unlinking the block so we can preserve the location
+ * in the free list.
+ *
+ * Note! We cannot split off and return the tail end here, because that may
+ * violate the same page requirements for requests smaller than 3/4 page.
+ */
+ AssertCompile(VBGL_PH_MIN_SPLIT_FREE_BLOCK >= sizeof(*pBlock) - sizeof(pBlock->Core));
+ if (pBlock->Core.cbUser >= sizeof(VBGLPHYSHEAPBLOCK) * 2 + VBGL_PH_MIN_SPLIT_FREE_BLOCK + cb)
+ {
+ pIter = (VBGLPHYSHEAPFREEBLOCK *)((uintptr_t)(&pBlock->Core + 1) + cb);
+ vbglPhysHeapInitFreeBlock(pIter, pBlock->Core.pChunk, pBlock->Core.cbUser - cb - sizeof(VBGLPHYSHEAPBLOCK));
+
+ pBlock->Core.cbUser = cb;
+
+ /* Insert the new 'pIter' block after the 'pBlock' in the block list
+ and in the free list. */
+ vbglPhysHeapInsertBlockAfter(&pIter->Core, &pBlock->Core);
+ vbglPhysHeapInsertFreeBlockAfter(pIter, pBlock);
+ }
+
+ /*
+ * Unlink the block from the free list and mark it as allocated.
+ */
+ vbglPhysHeapUnlinkFreeBlock(pBlock);
+ pBlock->Core.fAllocated = true;
+
+ dumpheap("post alloc");
+
+ /*
+ * Return success.
+ */
+ rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap);
+
+ VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns %p size %x\n", pBlock + 1, pBlock->Core.cbUser));
+ return &pBlock->Core + 1;
+ }
+
+ /*
+ * Return failure.
+ */
+ rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap);
+ AssertRC(rc);
+
+ VBGL_PH_DPRINTF(("VbglR0PhysHeapAlloc: returns NULL (requested %#x bytes)\n", cb));
+ return NULL;
+}
+
+
+DECLR0VBGL(uint32_t) VbglR0PhysHeapGetPhysAddr(void *pv)
+{
+ /*
+ * Validate the incoming pointer.
+ */
+ if (pv != NULL)
+ {
+ VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)pv - 1;
+ if ( pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE
+ && pBlock->fAllocated)
+ {
+ /*
+ * Calculate and return its physical address.
+ */
+ VBGLPHYSHEAPCHUNK *pChunk = pBlock->pChunk;
+ return pChunk->physAddr + (uint32_t)((uintptr_t)pv - (uintptr_t)pChunk);
+ }
+
+ AssertMsgFailed(("Use after free or corrupt pointer variable: pv=%p pBlock=%p: u32Signature=%#x cb=%#x fAllocated=%d\n",
+ pv, pBlock, pBlock->u32Signature, pBlock->cbUser, pBlock->fAllocated));
+ }
+ else
+ AssertMsgFailed(("Unexpected NULL pointer\n"));
+ return 0;
+}
+
+
+DECLR0VBGL(void) VbglR0PhysHeapFree(void *pv)
+{
+ if (pv != NULL)
+ {
+ VBGLPHYSHEAPFREEBLOCK *pBlock;
+
+ int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap);
+ AssertRCReturnVoid(rc);
+
+ dumpheap("pre free");
+
+ /*
+ * Validate the block header.
+ */
+ pBlock = (VBGLPHYSHEAPFREEBLOCK *)((VBGLPHYSHEAPBLOCK *)pv - 1);
+ if ( pBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE
+ && pBlock->Core.fAllocated
+ && pBlock->Core.cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE)
+ {
+ VBGLPHYSHEAPCHUNK *pChunk;
+ VBGLPHYSHEAPBLOCK *pNeighbour;
+
+ /*
+ * Change the block status to freeed.
+ */
+ VBGL_PH_DPRINTF(("VbglR0PhysHeapFree: %p size %#x\n", pv, pBlock->Core.cbUser));
+
+ pBlock->Core.fAllocated = false;
+ pBlock->pNextFree = pBlock->pPrevFree = NULL;
+ vbglPhysHeapInsertFreeBlock(pBlock);
+
+ dumpheap("post insert");
+
+ /*
+ * Check if the block after this one is also free and we can merge it into this one.
+ */
+ pChunk = pBlock->Core.pChunk;
+
+ pNeighbour = pBlock->Core.pNext;
+ if ( pNeighbour
+ && !pNeighbour->fAllocated
+ && pNeighbour->pChunk == pChunk)
+ {
+ Assert((uintptr_t)pBlock + sizeof(pBlock->Core) + pBlock->Core.cbUser == (uintptr_t)pNeighbour);
+
+ /* Adjust size of current memory block */
+ pBlock->Core.cbUser += pNeighbour->cbUser + sizeof(VBGLPHYSHEAPBLOCK);
+
+ /* Unlink the following node and invalid it. */
+ vbglPhysHeapUnlinkFreeBlock((VBGLPHYSHEAPFREEBLOCK *)pNeighbour);
+ vbglPhysHeapUnlinkBlock(pNeighbour);
+
+ pNeighbour->u32Signature = ~VBGL_PH_BLOCKSIGNATURE;
+ pNeighbour->cbUser = UINT32_MAX / 4;
+
+ dumpheap("post merge after");
+ }
+
+ /*
+ * Same check for the block before us. This invalidates pBlock.
+ */
+ pNeighbour = pBlock->Core.pPrev;
+ if ( pNeighbour
+ && !pNeighbour->fAllocated
+ && pNeighbour->pChunk == pChunk)
+ {
+ Assert((uintptr_t)pNeighbour + sizeof(*pNeighbour) + pNeighbour->cbUser == (uintptr_t)pBlock);
+
+ /* Adjust size of the block before us */
+ pNeighbour->cbUser += pBlock->Core.cbUser + sizeof(VBGLPHYSHEAPBLOCK);
+
+ /* Unlink this node and invalid it. */
+ vbglPhysHeapUnlinkFreeBlock(pBlock);
+ vbglPhysHeapUnlinkBlock(&pBlock->Core);
+
+ pBlock->Core.u32Signature = ~VBGL_PH_BLOCKSIGNATURE;
+ pBlock->Core.cbUser = UINT32_MAX / 8;
+
+ pBlock = NULL; /* invalid */
+
+ dumpheap("post merge before");
+ }
+
+ /*
+ * If this chunk is now completely unused, delete it if there are
+ * more completely free ones.
+ */
+ if ( pChunk->cFreeBlocks == pChunk->cBlocks
+ && (pChunk->pPrev || pChunk->pNext))
+ {
+ VBGLPHYSHEAPCHUNK *pCurChunk;
+ uint32_t cUnusedChunks = 0;
+ for (pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext)
+ {
+ AssertBreak(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE);
+ if (pCurChunk->cFreeBlocks == pCurChunk->cBlocks)
+ {
+ cUnusedChunks++;
+ if (cUnusedChunks > 1)
+ {
+ /* Delete current chunk, it will also unlink all free blocks
+ * remaining in the chunk from the free list, so the pBlock
+ * will also be invalid after this.
+ */
+ vbglPhysHeapChunkDelete(pChunk);
+ pBlock = NULL; /* invalid */
+ pChunk = NULL;
+ pNeighbour = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ dumpheap("post free");
+ }
+ else
+ AssertMsgFailed(("pBlock: %p: u32Signature=%#x cb=%#x fAllocated=%d - double free?\n",
+ pBlock, pBlock->Core.u32Signature, pBlock->Core.cbUser, pBlock->Core.fAllocated));
+
+ rc = RTSemFastMutexRelease(g_vbgldata.hMtxHeap);
+ AssertRC(rc);
+ }
+}
+
+#ifdef IN_TESTCASE /* For the testcase only */
+
+/**
+ * Returns the sum of all free heap blocks.
+ *
+ * This is the amount of memory you can theoretically allocate if you do
+ * allocations exactly matching the free blocks.
+ *
+ * @returns The size of the free blocks.
+ * @returns 0 if heap was safely detected as being bad.
+ */
+DECLVBGL(size_t) VbglR0PhysHeapGetFreeSize(void)
+{
+ int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap);
+ AssertRCReturn(rc, 0);
+
+ size_t cbTotal = 0;
+ for (VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead; pCurBlock; pCurBlock = pCurBlock->pNextFree)
+ {
+ Assert(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE);
+ Assert(!pCurBlock->Core.fAllocated);
+ cbTotal += pCurBlock->Core.cbUser;
+ }
+
+ RTSemFastMutexRelease(g_vbgldata.hMtxHeap);
+ return cbTotal;
+}
+
+
+/**
+ * Checks the heap, caller responsible for locking.
+ *
+ * @returns VINF_SUCCESS if okay, error status if not.
+ * @param pErrInfo Where to return more error details, optional.
+ */
+static int vbglR0PhysHeapCheckLocked(PRTERRINFO pErrInfo)
+{
+ /*
+ * Scan the blocks in each chunk, walking the block list in parallel.
+ */
+ const VBGLPHYSHEAPBLOCK *pPrevBlockListEntry = NULL;
+ const VBGLPHYSHEAPBLOCK *pCurBlockListEntry = g_vbgldata.pBlockHead;
+ unsigned acTotalBlocks[2] = { 0, 0 };
+ for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead, *pPrevChunk = NULL; pCurChunk; pCurChunk = pCurChunk->pNext)
+ {
+ AssertReturn(pCurChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
+ RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurChunk=%p: magic=%#x", pCurChunk, pCurChunk->u32Signature));
+ AssertReturn(pCurChunk->pPrev == pPrevChunk,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2,
+ "pCurChunk=%p: pPrev=%p, expected %p", pCurChunk, pCurChunk->pPrev, pPrevChunk));
+
+ const VBGLPHYSHEAPBLOCK *pCurBlock = (const VBGLPHYSHEAPBLOCK *)(pCurChunk + 1);
+ uintptr_t const uEnd = (uintptr_t)pCurChunk + pCurChunk->cbChunk;
+ unsigned acBlocks[2] = { 0, 0 };
+ while ((uintptr_t)pCurBlock < uEnd)
+ {
+ AssertReturn(pCurBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
+ RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC,
+ "pCurBlock=%p: magic=%#x", pCurBlock, pCurBlock->u32Signature));
+ AssertReturn(pCurBlock->pChunk == pCurChunk,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2,
+ "pCurBlock=%p: pChunk=%p, expected %p", pCurBlock, pCurBlock->pChunk, pCurChunk));
+ AssertReturn( pCurBlock->cbUser >= VBGL_PH_SMALLEST_ALLOC_SIZE
+ && pCurBlock->cbUser <= VBGL_PH_LARGEST_ALLOC_SIZE
+ && RT_ALIGN_32(pCurBlock->cbUser, VBGL_PH_ALLOC_ALIGN) == pCurBlock->cbUser,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3,
+ "pCurBlock=%p: cbUser=%#x", pCurBlock, pCurBlock->cbUser));
+ AssertReturn(pCurBlock == pCurBlockListEntry,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4,
+ "pCurChunk=%p: pCurBlock=%p, pCurBlockListEntry=%p\n",
+ pCurChunk, pCurBlock, pCurBlockListEntry));
+ AssertReturn(pCurBlock->pPrev == pPrevBlockListEntry,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5,
+ "pCurChunk=%p: pCurBlock->pPrev=%p, pPrevBlockListEntry=%p\n",
+ pCurChunk, pCurBlock->pPrev, pPrevBlockListEntry));
+
+ acBlocks[pCurBlock->fAllocated] += 1;
+
+ /* advance */
+ pPrevBlockListEntry = pCurBlock;
+ pCurBlockListEntry = pCurBlock->pNext;
+ pCurBlock = (const VBGLPHYSHEAPBLOCK *)((uintptr_t)(pCurBlock + 1) + pCurBlock->cbUser);
+ }
+ AssertReturn((uintptr_t)pCurBlock == uEnd,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4,
+ "pCurBlock=%p uEnd=%p", pCurBlock, uEnd));
+
+ acTotalBlocks[1] += acBlocks[1];
+ AssertReturn(acBlocks[0] + acBlocks[1] == (uint32_t)pCurChunk->cBlocks,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_4,
+ "pCurChunk=%p: cBlocks=%u, expected %u",
+ pCurChunk, pCurChunk->cBlocks, acBlocks[0] + acBlocks[1]));
+
+ acTotalBlocks[0] += acBlocks[0];
+ AssertReturn(acBlocks[0] == (uint32_t)pCurChunk->cFreeBlocks,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_5,
+ "pCurChunk=%p: cFreeBlocks=%u, expected %u",
+ pCurChunk, pCurChunk->cFreeBlocks, acBlocks[0]));
+
+ pPrevChunk = pCurChunk;
+ }
+
+ AssertReturn(acTotalBlocks[0] == (uint32_t)g_vbgldata.cFreeBlocks,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR,
+ "g_vbgldata.cFreeBlocks=%u, expected %u", g_vbgldata.cFreeBlocks, acTotalBlocks[0]));
+ AssertReturn(acTotalBlocks[0] + acTotalBlocks[1] == (uint32_t)g_vbgldata.cBlocks,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR,
+ "g_vbgldata.cBlocks=%u, expected %u", g_vbgldata.cBlocks, acTotalBlocks[0] + acTotalBlocks[1]));
+
+ /*
+ * Check that the free list contains the same number of blocks as we
+ * encountered during the above scan.
+ */
+ {
+ unsigned cFreeListBlocks = 0;
+ for (const VBGLPHYSHEAPFREEBLOCK *pCurBlock = g_vbgldata.pFreeHead, *pPrevBlock = NULL;
+ pCurBlock;
+ pCurBlock = pCurBlock->pNextFree)
+ {
+ AssertReturn(pCurBlock->Core.u32Signature == VBGL_PH_BLOCKSIGNATURE,
+ RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC,
+ "pCurBlock=%p/free: magic=%#x", pCurBlock, pCurBlock->Core.u32Signature));
+ AssertReturn(pCurBlock->pPrevFree == pPrevBlock,
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_2,
+ "pCurBlock=%p/free: pPrev=%p, expected %p", pCurBlock, pCurBlock->pPrevFree, pPrevBlock));
+ AssertReturn(pCurBlock->Core.pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
+ RTErrInfoSetF(pErrInfo, VERR_INVALID_MAGIC, "pCurBlock=%p/free: chunk (%p) magic=%#x",
+ pCurBlock, pCurBlock->Core.pChunk, pCurBlock->Core.pChunk->u32Signature));
+ cFreeListBlocks++;
+ pPrevBlock = pCurBlock;
+ }
+
+ AssertReturn(cFreeListBlocks == acTotalBlocks[0],
+ RTErrInfoSetF(pErrInfo, VERR_INTERNAL_ERROR_3,
+ "Found %u in free list, expected %u", cFreeListBlocks, acTotalBlocks[0]));
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Performs a heap check.
+ *
+ * @returns Problem description on failure, NULL on success.
+ * @param pErrInfo Where to return more error details, optional.
+ */
+DECLVBGL(int) VbglR0PhysHeapCheck(PRTERRINFO pErrInfo)
+{
+ int rc = RTSemFastMutexRequest(g_vbgldata.hMtxHeap);
+ AssertRCReturn(rc, 0);
+
+ rc = vbglR0PhysHeapCheckLocked(pErrInfo);
+
+ RTSemFastMutexRelease(g_vbgldata.hMtxHeap);
+ return rc;
+}
+
+#endif /* IN_TESTCASE */
+
+DECLR0VBGL(int) VbglR0PhysHeapInit(void)
+{
+ g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX;
+
+ /* Allocate the first chunk of the heap. */
+ VBGLPHYSHEAPFREEBLOCK *pBlock = vbglPhysHeapChunkAlloc(0);
+ if (pBlock)
+ return RTSemFastMutexCreate(&g_vbgldata.hMtxHeap);
+ return VERR_NO_CONT_MEMORY;
+}
+
+DECLR0VBGL(void) VbglR0PhysHeapTerminate(void)
+{
+ while (g_vbgldata.pChunkHead)
+ vbglPhysHeapChunkDelete(g_vbgldata.pChunkHead);
+
+ RTSemFastMutexDestroy(g_vbgldata.hMtxHeap);
+ g_vbgldata.hMtxHeap = NIL_RTSEMFASTMUTEX;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c
new file mode 100644
index 00000000..02a5c470
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c
@@ -0,0 +1,716 @@
+/* $Id: VBoxGuestR0LibSharedFolders.c $ */
+/** @file
+ * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxGuestR0LibInternal.h"
+#include <VBox/VBoxGuestLibSharedFolders.h>
+#include <VBox/log.h>
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#ifdef VBGL_VBOXGUEST
+# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest. It's client code."
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_INIT_CALL(a, b, c) \
+ LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \
+ VBGL_HGCM_HDR_INIT(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b); \
+ (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */
+
+#define VBOX_INIT_CALL_EX(a, b, c, a_cbReq) \
+ LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \
+ VBGL_HGCM_HDR_INIT_EX(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b, a_cbReq); \
+ (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */
+
+
+
+DECLVBGL(int) VbglR0SfInit(void)
+{
+ return VbglR0InitClient();
+}
+
+DECLVBGL(void) VbglR0SfTerm(void)
+{
+ VbglR0TerminateClient();
+}
+
+DECLVBGL(int) VbglR0SfConnect(PVBGLSFCLIENT pClient)
+{
+ int rc = VbglR0HGCMConnect(&pClient->handle, "VBoxSharedFolders", &pClient->idClient);
+ if (RT_SUCCESS(rc))
+ LogFunc(("idClient=%d\n", pClient->idClient));
+ else
+ LogFunc(("VbglR0HGCMConnect failed -> rc=%Rrc\n", rc));
+ return rc;
+}
+
+DECLVBGL(void) VbglR0SfDisconnect(PVBGLSFCLIENT pClient)
+{
+ int rc;
+ LogFunc(("u32ClientID=%d\n", pClient->idClient));
+ if (pClient->handle == NULL)
+ return; /* not connected */
+
+ rc = VbglR0HGCMDisconnect(pClient->handle, pClient->idClient);
+ NOREF(rc);
+/* Log(("VBOXSF: VbglR0SfDisconnect: VbglR0HGCMDisconnect -> %#x\n", rc)); */
+ pClient->idClient = 0;
+ pClient->handle = NULL;
+ return;
+}
+
+#if !defined(RT_OS_LINUX)
+
+# ifndef RT_OS_WINDOWS
+
+DECLVBGL(int) VbglR0SfSetUtf8(PVBGLSFCLIENT pClient)
+{
+ int rc;
+ VBGLIOCHGCMCALL callInfo;
+
+ VBOX_INIT_CALL(&callInfo, SET_UTF8, pClient);
+ rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo));
+/* Log(("VBOXSF: VbglR0SfSetUtf8: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+# endif /* !RT_OS_WINDOWS */
+
+/** @name Deprecated VBGL shared folder helpers.
+ *
+ * @deprecated These are all use the slow VbglR0HGCMCall interface, that
+ * basically treat ring-0 and user land callers much the same.
+ * Since 6.0 there is VbglR0HGCMFastCall() that does not bother with
+ * repacking the request and locking/duplicating parameter buffers,
+ * but just passes it along to the host and handles the waiting.
+ * Also new in 6.0 is embedded buffers which saves a bit time on
+ * guest and host by embedding parameter buffers into the request.
+ *
+ * @{
+ */
+
+DECLVBGL(int) VbglR0SfQueryMappings(PVBGLSFCLIENT pClient, SHFLMAPPING paMappings[], uint32_t *pcMappings)
+{
+ int rc;
+ VBoxSFQueryMappings data;
+
+ VBOX_INIT_CALL(&data.callInfo, QUERY_MAPPINGS, pClient);
+
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = SHFL_MF_UCS2;
+
+ data.numberOfMappings.type = VMMDevHGCMParmType_32bit;
+ data.numberOfMappings.u.value32 = *pcMappings;
+
+ data.mappings.type = VMMDevHGCMParmType_LinAddr;
+ data.mappings.u.Pointer.size = sizeof(SHFLMAPPING) * *pcMappings;
+ data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&paMappings[0];
+
+/* Log(("VBOXSF: in ifs difference %d\n", (char *)&data.flags.type - (char *)&data.callInfo.cParms)); */
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfQueryMappings: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.result)); */
+ if (RT_SUCCESS(rc))
+ *pcMappings = data.numberOfMappings.u.value32;
+
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfQueryMapName(PVBGLSFCLIENT pClient, SHFLROOT root, SHFLSTRING *pString, uint32_t size)
+{
+ int rc;
+ VBoxSFQueryMapName data;
+
+ VBOX_INIT_CALL(&data.callInfo, QUERY_MAP_NAME, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = root;
+
+ data.name.type = VMMDevHGCMParmType_LinAddr;
+ data.name.u.Pointer.size = size;
+ data.name.u.Pointer.u.linearAddr = (uintptr_t)pString;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfQueryMapName: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfMapFolder(PVBGLSFCLIENT pClient, PSHFLSTRING szFolderName, PVBGLSFMAP pMap)
+{
+ int rc;
+ VBoxSFMapFolder data;
+
+ VBOX_INIT_CALL(&data.callInfo, MAP_FOLDER, pClient);
+
+ data.path.type = VMMDevHGCMParmType_LinAddr;
+ data.path.u.Pointer.size = ShflStringSizeOfBuffer(szFolderName);
+ data.path.u.Pointer.u.linearAddr = (uintptr_t)szFolderName;
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = 0;
+
+ data.delimiter.type = VMMDevHGCMParmType_32bit;
+ data.delimiter.u.value32 = RTPATH_DELIMITER;
+
+ data.fCaseSensitive.type = VMMDevHGCMParmType_32bit;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ data.fCaseSensitive.u.value32 = 0;
+#else
+ data.fCaseSensitive.u.value32 = 1;
+#endif
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfMapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ pMap->root = data.root.u.value32;
+ rc = data.callInfo.Hdr.rc;
+ }
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfUnmapFolder(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap)
+{
+ int rc;
+ VBoxSFUnmapFolder data;
+
+ VBOX_INIT_CALL(&data.callInfo, UNMAP_FOLDER, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfUnmapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+# if !defined(RT_OS_WINDOWS)
+
+DECLVBGL(int) VbglR0SfCreate(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, PSHFLCREATEPARMS pCreateParms)
+{
+ /** @todo copy buffers to physical or mapped memory. */
+ int rc;
+ VBoxSFCreate data;
+
+ VBOX_INIT_CALL(&data.callInfo, CREATE, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.path.type = VMMDevHGCMParmType_LinAddr;
+ data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath);
+ data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath;
+
+ data.parms.type = VMMDevHGCMParmType_LinAddr;
+ data.parms.u.Pointer.size = sizeof(SHFLCREATEPARMS);
+ data.parms.u.Pointer.u.linearAddr = (uintptr_t)pCreateParms;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfCreate: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfClose(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE Handle)
+{
+ int rc;
+ VBoxSFClose data;
+
+ VBOX_INIT_CALL(&data.callInfo, CLOSE, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = Handle;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfClose: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+
+DECLVBGL(int) VbglR0SfRemove(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ VBoxSFRemove data;
+
+ VBOX_INIT_CALL(&data.callInfo, REMOVE, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.path.type = VMMDevHGCMParmType_LinAddr_In;
+ data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath);
+ data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath;
+
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = flags;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfRemove: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfRename(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, uint32_t flags)
+{
+ int rc;
+ VBoxSFRename data;
+
+ VBOX_INIT_CALL(&data.callInfo, RENAME, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.src.type = VMMDevHGCMParmType_LinAddr_In;
+ data.src.u.Pointer.size = ShflStringSizeOfBuffer(pSrcPath);
+ data.src.u.Pointer.u.linearAddr = (uintptr_t)pSrcPath;
+
+ data.dest.type = VMMDevHGCMParmType_LinAddr_In;
+ data.dest.u.Pointer.size = ShflStringSizeOfBuffer(pDestPath);
+ data.dest.u.Pointer.u.linearAddr = (uintptr_t)pDestPath;
+
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = flags;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfRename: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfRead(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
+ uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked)
+{
+ int rc;
+ VBoxSFRead data;
+
+ VBOX_INIT_CALL(&data.callInfo, READ, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+ data.offset.type = VMMDevHGCMParmType_64bit;
+ data.offset.u.value64 = offset;
+ data.cb.type = VMMDevHGCMParmType_32bit;
+ data.cb.u.value32 = *pcbBuffer;
+ data.buffer.type = (fLocked) ? VMMDevHGCMParmType_LinAddr_Locked_Out : VMMDevHGCMParmType_LinAddr_Out;
+ data.buffer.u.Pointer.size = *pcbBuffer;
+ data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfRead: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = data.callInfo.Hdr.rc;
+ *pcbBuffer = data.cb.u.value32;
+ }
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfReadPageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer,
+ uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages)
+{
+ uint32_t cbToRead = *pcbBuffer;
+ uint32_t cbData = (uint32_t)(sizeof(VBoxSFRead) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
+ VBoxSFRead *pData = (VBoxSFRead *)RTMemTmpAlloc(cbData);
+ HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1);
+ uint16_t iPage;
+ int rc;
+
+ if (RT_UNLIKELY(!pData))
+ return VERR_NO_TMP_MEMORY;
+
+ VBOX_INIT_CALL_EX(&pData->callInfo, READ, pClient, cbData);
+
+ pData->root.type = VMMDevHGCMParmType_32bit;
+ pData->root.u.value32 = pMap->root;
+
+ pData->handle.type = VMMDevHGCMParmType_64bit;
+ pData->handle.u.value64 = hFile;
+ pData->offset.type = VMMDevHGCMParmType_64bit;
+ pData->offset.u.value64 = offset;
+ pData->cb.type = VMMDevHGCMParmType_32bit;
+ pData->cb.u.value32 = cbToRead;
+ pData->buffer.type = VMMDevHGCMParmType_PageList;
+ pData->buffer.u.PageList.size = cbToRead;
+ pData->buffer.u.PageList.offset = sizeof(VBoxSFRead);
+
+ pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+ pPgLst->offFirstPage = offFirstPage;
+ pPgLst->cPages = cPages;
+ for (iPage = 0; iPage < cPages; iPage++)
+ pPgLst->aPages[iPage] = paPages[iPage];
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
+/* Log(("VBOXSF: VbglR0SfReadPageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = pData->callInfo.Hdr.rc;
+ *pcbBuffer = pData->cb.u.value32;
+ }
+
+ RTMemTmpFree(pData);
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfWrite(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
+ uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked)
+{
+ int rc;
+ VBoxSFWrite data;
+
+ VBOX_INIT_CALL(&data.callInfo, WRITE, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+ data.offset.type = VMMDevHGCMParmType_64bit;
+ data.offset.u.value64 = offset;
+ data.cb.type = VMMDevHGCMParmType_32bit;
+ data.cb.u.value32 = *pcbBuffer;
+ data.buffer.type = fLocked ? VMMDevHGCMParmType_LinAddr_Locked_In : VMMDevHGCMParmType_LinAddr_In;
+ data.buffer.u.Pointer.size = *pcbBuffer;
+ data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfWrite: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = data.callInfo.Hdr.rc;
+ *pcbBuffer = data.cb.u.value32;
+ }
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfWritePhysCont(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset,
+ uint32_t *pcbBuffer, RTCCPHYS PhysBuffer)
+{
+ uint32_t cbToWrite = *pcbBuffer;
+ uint32_t cPages = RT_ALIGN_32((PhysBuffer & PAGE_OFFSET_MASK) + cbToWrite, PAGE_SIZE) >> PAGE_SHIFT;
+ uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
+ VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData);
+ HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1);
+ uint32_t iPage;
+ int rc;
+
+ if (RT_UNLIKELY(!pData))
+ return VERR_NO_TMP_MEMORY;
+
+ VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData);
+
+ pData->root.type = VMMDevHGCMParmType_32bit;
+ pData->root.u.value32 = pMap->root;
+
+ pData->handle.type = VMMDevHGCMParmType_64bit;
+ pData->handle.u.value64 = hFile;
+ pData->offset.type = VMMDevHGCMParmType_64bit;
+ pData->offset.u.value64 = offset;
+ pData->cb.type = VMMDevHGCMParmType_32bit;
+ pData->cb.u.value32 = cbToWrite;
+ pData->buffer.type = VMMDevHGCMParmType_PageList;
+ pData->buffer.u.PageList.size = cbToWrite;
+ pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite);
+
+ pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
+ pPgLst->offFirstPage = (uint16_t)(PhysBuffer & PAGE_OFFSET_MASK);
+ pPgLst->cPages = cPages;
+ PhysBuffer &= ~(RTCCPHYS)PAGE_OFFSET_MASK;
+ for (iPage = 0; iPage < cPages; iPage++, PhysBuffer += PAGE_SIZE)
+ pPgLst->aPages[iPage] = PhysBuffer;
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
+/* Log(("VBOXSF: VbglR0SfWritePhysCont: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = pData->callInfo.Hdr.rc;
+ *pcbBuffer = pData->cb.u.value32;
+ }
+
+ RTMemTmpFree(pData);
+ return rc;
+
+}
+
+DECLVBGL(int) VbglR0SfWritePageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer,
+ uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages)
+{
+ uint32_t cbToWrite = *pcbBuffer;
+ uint32_t cbData = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
+ VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData);
+ HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1);
+ uint16_t iPage;
+ int rc;
+
+ if (RT_UNLIKELY(!pData))
+ return VERR_NO_TMP_MEMORY;
+
+ VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData);
+
+ pData->root.type = VMMDevHGCMParmType_32bit;
+ pData->root.u.value32 = pMap->root;
+
+ pData->handle.type = VMMDevHGCMParmType_64bit;
+ pData->handle.u.value64 = hFile;
+ pData->offset.type = VMMDevHGCMParmType_64bit;
+ pData->offset.u.value64 = offset;
+ pData->cb.type = VMMDevHGCMParmType_32bit;
+ pData->cb.u.value32 = cbToWrite;
+ pData->buffer.type = VMMDevHGCMParmType_PageList;
+ pData->buffer.u.PageList.size = cbToWrite;
+ pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite);
+
+ pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
+ pPgLst->offFirstPage = offFirstPage;
+ pPgLst->cPages = cPages;
+ for (iPage = 0; iPage < cPages; iPage++)
+ pPgLst->aPages[iPage] = paPages[iPage];
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
+/* Log(("VBOXSF: VbglR0SfWritePageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = pData->callInfo.Hdr.rc;
+ *pcbBuffer = pData->cb.u.value32;
+ }
+
+ RTMemTmpFree(pData);
+ return rc;
+}
+
+# endif /* !RT_OS_WINDOWS */
+
+DECLVBGL(int) VbglR0SfFlush(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile)
+{
+ int rc;
+ VBoxSFFlush data;
+
+ VBOX_INIT_CALL(&data.callInfo, FLUSH, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfFlush: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfDirInfo(
+ PVBGLSFCLIENT pClient,
+ PVBGLSFMAP pMap,
+ SHFLHANDLE hFile,
+ PSHFLSTRING ParsedPath,
+ uint32_t flags,
+ uint32_t index,
+ uint32_t *pcbBuffer,
+ PSHFLDIRINFO pBuffer,
+ uint32_t *pcFiles)
+{
+ int rc;
+ VBoxSFList data;
+
+ VBOX_INIT_CALL(&data.callInfo, LIST, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = flags;
+ data.cb.type = VMMDevHGCMParmType_32bit;
+ data.cb.u.value32 = *pcbBuffer;
+ data.path.type = VMMDevHGCMParmType_LinAddr_In;
+ data.path.u.Pointer.size = ParsedPath ? ShflStringSizeOfBuffer(ParsedPath) : 0;
+ data.path.u.Pointer.u.linearAddr = (uintptr_t) ParsedPath;
+
+ data.buffer.type = VMMDevHGCMParmType_LinAddr_Out;
+ data.buffer.u.Pointer.size = *pcbBuffer;
+ data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ data.resumePoint.type = VMMDevHGCMParmType_32bit;
+ data.resumePoint.u.value32 = index;
+ data.cFiles.type = VMMDevHGCMParmType_32bit;
+ data.cFiles.u.value32 = 0; /* out parameters only */
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfDirInfo: rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ *pcbBuffer = data.cb.u.value32;
+ *pcFiles = data.cFiles.u.value32;
+ return rc;
+}
+
+# ifndef RT_OS_WINDOWS
+
+DECLVBGL(int) VbglR0SfFsInfo(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
+ uint32_t flags, uint32_t *pcbBuffer, PSHFLDIRINFO pBuffer)
+{
+ int rc;
+ VBoxSFInformation data;
+
+ VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = flags;
+ data.cb.type = VMMDevHGCMParmType_32bit;
+ data.cb.u.value32 = *pcbBuffer;
+ data.info.type = VMMDevHGCMParmType_LinAddr;
+ data.info.u.Pointer.size = *pcbBuffer;
+ data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfFsInfo: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ if (RT_SUCCESS(rc))
+ {
+ rc = data.callInfo.Hdr.rc;
+ *pcbBuffer = data.cb.u.value32;
+ }
+ return rc;
+}
+
+# endif /* !RT_OS_WINDOWS */
+
+DECLVBGL(int) VbglR0SfLock(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
+ uint64_t offset, uint64_t cbSize, uint32_t fLock)
+{
+ int rc;
+ VBoxSFLock data;
+
+ VBOX_INIT_CALL(&data.callInfo, LOCK, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.handle.type = VMMDevHGCMParmType_64bit;
+ data.handle.u.value64 = hFile;
+ data.offset.type = VMMDevHGCMParmType_64bit;
+ data.offset.u.value64 = offset;
+ data.length.type = VMMDevHGCMParmType_64bit;
+ data.length.u.value64 = cbSize;
+
+ data.flags.type = VMMDevHGCMParmType_32bit;
+ data.flags.u.value32 = fLock;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfLock: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+# ifndef RT_OS_WINDOWS
+
+DECLVBGL(int) VbglR0SfReadLink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t cbBuffer, uint8_t *pBuffer)
+{
+ int rc;
+ VBoxSFReadLink data;
+
+ VBOX_INIT_CALL(&data.callInfo, READLINK, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.path.type = VMMDevHGCMParmType_LinAddr_In;
+ data.path.u.Pointer.size = ShflStringSizeOfBuffer (pParsedPath);
+ data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath;
+
+ data.buffer.type = VMMDevHGCMParmType_LinAddr_Out;
+ data.buffer.u.Pointer.size = cbBuffer;
+ data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfReadLink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfSymlink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath,
+ PSHFLFSOBJINFO pBuffer)
+{
+ int rc;
+ VBoxSFSymlink data;
+
+ VBOX_INIT_CALL(&data.callInfo, SYMLINK, pClient);
+
+ data.root.type = VMMDevHGCMParmType_32bit;
+ data.root.u.value32 = pMap->root;
+
+ data.newPath.type = VMMDevHGCMParmType_LinAddr_In;
+ data.newPath.u.Pointer.size = ShflStringSizeOfBuffer (pNewPath);
+ data.newPath.u.Pointer.u.linearAddr = (uintptr_t)pNewPath;
+
+ data.oldPath.type = VMMDevHGCMParmType_LinAddr_In;
+ data.oldPath.u.Pointer.size = ShflStringSizeOfBuffer (pOldPath);
+ data.oldPath.u.Pointer.u.linearAddr = (uintptr_t)pOldPath;
+
+ data.info.type = VMMDevHGCMParmType_LinAddr_Out;
+ data.info.u.Pointer.size = sizeof(SHFLFSOBJINFO);
+ data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer;
+
+ rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
+/* Log(("VBOXSF: VbglR0SfSymlink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+DECLVBGL(int) VbglR0SfSetSymlinks(PVBGLSFCLIENT pClient)
+{
+ int rc;
+ VBGLIOCHGCMCALL callInfo;
+
+ VBOX_INIT_CALL(&callInfo, SET_SYMLINKS, pClient);
+ rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo));
+/* Log(("VBOXSF: VbglR0SfSetSymlinks: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
+ return rc;
+}
+
+# endif /* !RT_OS_WINDOWS */
+
+#endif /* !RT_OS_LINUX */
+
+/** @} */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp
new file mode 100644
index 00000000..5909fd2c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibVMMDev.cpp
@@ -0,0 +1,51 @@
+/* $Id: VBoxGuestR0LibVMMDev.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - VMMDev device related functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+
+DECLVBGL(int) VbglR0QueryVMMDevMemory(VMMDevMemory **ppVMMDevMemory)
+{
+ int rc = vbglR0Enter();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* If the memory was not found, return an error. */
+ if (!g_vbgldata.pVMMDevMemory)
+ return VERR_NOT_SUPPORTED;
+
+ *ppVMMDevMemory = g_vbgldata.pVMMDevMemory;
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp
new file mode 100644
index 00000000..4298b742
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp
@@ -0,0 +1,485 @@
+/* $Id: VBoxGuestR3Lib.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(RT_OS_WINDOWS)
+# include <iprt/nt/nt-and-windows.h>
+
+#elif defined(RT_OS_OS2)
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+
+#elif defined(RT_OS_DARWIN) \
+ || defined(RT_OS_FREEBSD) \
+ || defined(RT_OS_HAIKU) \
+ || defined(RT_OS_LINUX) \
+ || defined(RT_OS_NETBSD) \
+ || defined(RT_OS_SOLARIS)
+# include <sys/types.h>
+# include <sys/stat.h>
+# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_NETBSD)
+ /** @todo check this on solaris+freebsd as well. */
+# include <sys/ioctl.h>
+# endif
+# if defined(RT_OS_DARWIN)
+# include <mach/mach_port.h>
+# include <IOKit/IOKitLib.h>
+# endif
+# include <errno.h>
+# include <unistd.h>
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <VBox/log.h>
+#include "VBoxGuestR3LibInternal.h"
+
+#ifdef VBOX_VBGLR3_XFREE86
+/* Rather than try to resolve all the header file conflicts, I will just
+ prototype what we need here. */
+# define XF86_O_RDWR 0x0002
+typedef void *pointer;
+extern "C" int xf86open(const char *, int, ...);
+extern "C" int xf86close(int);
+extern "C" int xf86ioctl(int, unsigned long, pointer);
+# define VBOX_VBGLR3_XSERVER
+#elif defined(VBOX_VBGLR3_XORG)
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <sys/ioctl.h>
+# define xf86open open
+# define xf86close close
+# define xf86ioctl ioctl
+# define XF86_O_RDWR O_RDWR
+# define VBOX_VBGLR3_XSERVER
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The VBoxGuest device handle. */
+#ifdef VBOX_VBGLR3_XSERVER
+static int g_File = -1;
+#elif defined(RT_OS_WINDOWS)
+static HANDLE g_hFile = INVALID_HANDLE_VALUE;
+#else
+static RTFILE g_File = NIL_RTFILE;
+#endif
+/** User counter.
+ * A counter of the number of times the library has been initialised, for use with
+ * X.org drivers, where the library may be shared by multiple independent modules
+ * inside a single process space.
+ */
+static uint32_t volatile g_cInits = 0;
+#ifdef RT_OS_DARWIN
+/** I/O Kit connection handle. */
+static io_connect_t g_uConnection = 0;
+#endif
+
+
+
+/**
+ * Implementation of VbglR3Init and VbglR3InitUser
+ */
+static int vbglR3Init(const char *pszDeviceName)
+{
+ int rc2;
+ uint32_t cInits = ASMAtomicIncU32(&g_cInits);
+ Assert(cInits > 0);
+ if (cInits > 1)
+ {
+ /*
+ * This will fail if two (or more) threads race each other calling VbglR3Init.
+ * However it will work fine for single threaded or otherwise serialized
+ * processed calling us more than once.
+ */
+#ifdef RT_OS_WINDOWS
+ if (g_hFile == INVALID_HANDLE_VALUE)
+#elif !defined (VBOX_VBGLR3_XSERVER)
+ if (g_File == NIL_RTFILE)
+#else
+ if (g_File == -1)
+#endif
+ return VERR_INTERNAL_ERROR;
+ return VINF_SUCCESS;
+ }
+#if defined(RT_OS_WINDOWS)
+ if (g_hFile != INVALID_HANDLE_VALUE)
+#elif !defined(VBOX_VBGLR3_XSERVER)
+ if (g_File != NIL_RTFILE)
+#else
+ if (g_File != -1)
+#endif
+ return VERR_INTERNAL_ERROR;
+
+#if defined(RT_OS_WINDOWS)
+ /*
+ * Have to use CreateFile here as we want to specify FILE_FLAG_OVERLAPPED
+ * and possible some other bits not available thru iprt/file.h.
+ */
+ HANDLE hFile = CreateFile(pszDeviceName,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return VERR_OPEN_FAILED;
+ g_hFile = hFile;
+
+#elif defined(RT_OS_OS2)
+ /*
+ * We might wish to compile this with Watcom, so stick to
+ * the OS/2 APIs all the way. And in any case we have to use
+ * DosDevIOCtl for the requests, why not use Dos* for everything.
+ */
+ HFILE hf = NULLHANDLE;
+ ULONG ulAction = 0;
+ APIRET rc = DosOpen((PCSZ)pszDeviceName, &hf, &ulAction, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE,
+ NULL);
+ if (rc)
+ return RTErrConvertFromOS2(rc);
+
+ if (hf < 16)
+ {
+ HFILE ahfs[16];
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(ahfs); i++)
+ {
+ ahfs[i] = 0xffffffff;
+ rc = DosDupHandle(hf, &ahfs[i]);
+ if (rc)
+ break;
+ }
+
+ if (i-- > 1)
+ {
+ ULONG fulState = 0;
+ rc = DosQueryFHState(ahfs[i], &fulState);
+ if (!rc)
+ {
+ fulState |= OPEN_FLAGS_NOINHERIT;
+ fulState &= OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT; /* Turn off non-participating bits. */
+ rc = DosSetFHState(ahfs[i], fulState);
+ }
+ if (!rc)
+ {
+ rc = DosClose(hf);
+ AssertMsg(!rc, ("%ld\n", rc));
+ hf = ahfs[i];
+ }
+ else
+ i++;
+ while (i-- > 0)
+ DosClose(ahfs[i]);
+ }
+ }
+ g_File = (RTFILE)hf;
+
+#elif defined(RT_OS_DARWIN)
+ /*
+ * Darwin is kind of special we need to engage the device via I/O first
+ * before we open it via the BSD device node.
+ */
+ /* IOKit */
+ mach_port_t MasterPort;
+ kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("IOMasterPort -> %d\n", kr));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ CFDictionaryRef ClassToMatch = IOServiceMatching("org_virtualbox_VBoxGuest");
+ if (!ClassToMatch)
+ {
+ LogRel(("IOServiceMatching(\"org_virtualbox_VBoxGuest\") failed.\n"));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ io_service_t ServiceObject = IOServiceGetMatchingService(kIOMasterPortDefault, ClassToMatch);
+ if (!ServiceObject)
+ {
+ LogRel(("IOServiceGetMatchingService returned NULL\n"));
+ return VERR_NOT_FOUND;
+ }
+
+ io_connect_t uConnection;
+ kr = IOServiceOpen(ServiceObject, mach_task_self(), VBOXGUEST_DARWIN_IOSERVICE_COOKIE, &uConnection);
+ IOObjectRelease(ServiceObject);
+ if (kr != kIOReturnSuccess)
+ {
+ LogRel(("IOServiceOpen returned %d. Driver open failed.\n", kr));
+ return VERR_OPEN_FAILED;
+ }
+
+ /* Regular unix FD. */
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("RTFileOpen(%s) returned %Rrc. Driver open failed.\n", pszDeviceName, rc));
+ IOServiceClose(uConnection);
+ return rc;
+ }
+ g_File = hFile;
+ g_uConnection = uConnection;
+
+#elif defined(VBOX_VBGLR3_XSERVER)
+ int File = xf86open(pszDeviceName, XF86_O_RDWR);
+ if (File == -1)
+ return VERR_OPEN_FAILED;
+ g_File = File;
+
+#else
+
+ /* The default implementation. (linux, solaris, freebsd, netbsd, haiku) */
+ RTFILE File;
+ int rc = RTFileOpen(&File, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ return rc;
+ g_File = File;
+
+#endif
+
+ /*
+ * Adjust the I/O control interface version.
+ */
+ {
+ VBGLIOCDRIVERVERSIONINFO VerInfo;
+ VBGLREQHDR_INIT(&VerInfo.Hdr, DRIVER_VERSION_INFO);
+ VerInfo.u.In.uMinVersion = VBGL_IOC_VERSION & UINT32_C(0xffff0000);
+ VerInfo.u.In.uReqVersion = VBGL_IOC_VERSION;
+ VerInfo.u.In.uReserved1 = 0;
+ VerInfo.u.In.uReserved2 = 0;
+ rc2 = vbglR3DoIOCtl(VBGL_IOCTL_DRIVER_VERSION_INFO, &VerInfo.Hdr, sizeof(VerInfo));
+#ifndef VBOX_VBGLR3_XSERVER
+ AssertRC(rc2); /* otherwise ignored for now*/
+#endif
+ }
+
+
+#ifndef VBOX_VBGLR3_XSERVER
+ /*
+ * Create release logger
+ */
+ PRTLOGGER pReleaseLogger;
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ rc2 = RTLogCreate(&pReleaseLogger, 0, "all", "VBOX_RELEASE_LOG",
+ RT_ELEMENTS(s_apszGroups), &s_apszGroups[0], RTLOGDEST_USER, NULL);
+ /* This may legitimately fail if we are using the mini-runtime. */
+ if (RT_SUCCESS(rc2))
+ RTLogRelSetDefaultInstance(pReleaseLogger);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Open the VBox R3 Guest Library. This should be called by system daemons
+ * and processes.
+ */
+VBGLR3DECL(int) VbglR3Init(void)
+{
+ return vbglR3Init(VBOXGUEST_DEVICE_NAME);
+}
+
+
+/**
+ * Open the VBox R3 Guest Library. Equivalent to VbglR3Init, but for user
+ * session processes.
+ */
+VBGLR3DECL(int) VbglR3InitUser(void)
+{
+ return vbglR3Init(VBOXGUEST_USER_DEVICE_NAME);
+}
+
+
+VBGLR3DECL(void) VbglR3Term(void)
+{
+ /*
+ * Decrement the reference count and see if we're the last one out.
+ */
+ uint32_t cInits = ASMAtomicDecU32(&g_cInits);
+ if (cInits > 0)
+ return;
+#if !defined(VBOX_VBGLR3_XSERVER)
+ AssertReturnVoid(!cInits);
+
+# if defined(RT_OS_WINDOWS)
+ HANDLE hFile = g_hFile;
+ g_hFile = INVALID_HANDLE_VALUE;
+ AssertReturnVoid(hFile != INVALID_HANDLE_VALUE);
+ BOOL fRc = CloseHandle(hFile);
+ Assert(fRc); NOREF(fRc);
+
+# elif defined(RT_OS_OS2)
+ RTFILE File = g_File;
+ g_File = NIL_RTFILE;
+ AssertReturnVoid(File != NIL_RTFILE);
+ APIRET rc = DosClose((uintptr_t)File);
+ AssertMsg(!rc, ("%ld\n", rc));
+
+#elif defined(RT_OS_DARWIN)
+ io_connect_t uConnection = g_uConnection;
+ RTFILE hFile = g_File;
+ g_uConnection = 0;
+ g_File = NIL_RTFILE;
+ kern_return_t kr = IOServiceClose(uConnection);
+ AssertMsg(kr == kIOReturnSuccess, ("%#x (%d)\n", kr, kr)); NOREF(kr);
+ int rc = RTFileClose(hFile);
+ AssertRC(rc);
+
+# else /* The IPRT case. */
+ RTFILE File = g_File;
+ g_File = NIL_RTFILE;
+ AssertReturnVoid(File != NIL_RTFILE);
+ int rc = RTFileClose(File);
+ AssertRC(rc);
+# endif
+
+#else /* VBOX_VBGLR3_XSERVER */
+ int File = g_File;
+ g_File = -1;
+ if (File == -1)
+ return;
+ xf86close(File);
+#endif /* VBOX_VBGLR3_XSERVER */
+}
+
+
+/**
+ * Internal wrapper around various OS specific ioctl implementations.
+ *
+ * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or
+ * an failure returned by the OS specific ioctl APIs.
+ *
+ * @param uFunction The requested function.
+ * @param pHdr The input and output request buffer.
+ * @param cbReq The size of the request buffer.
+ */
+int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq)
+{
+ Assert(cbReq == RT_MAX(pHdr->cbIn, pHdr->cbOut)); RT_NOREF1(cbReq);
+ Assert(pHdr->cbOut != 0);
+
+#if defined(RT_OS_WINDOWS)
+# if 0 /*def USE_NT_DEVICE_IO_CONTROL_FILE*/
+ IO_STATUS_BLOCK Ios;
+ Ios.Status = -1;
+ Ios.Information = 0;
+ NTSTATUS rcNt = NtDeviceIoControlFile(g_hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/, &Ios,
+ (ULONG)uFunction,
+ pHdr /*pvInput */, pHdr->cbIn /* cbInput */,
+ pHdr /*pvOutput*/, pHdr->cbOut /* cbOutput */);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (NT_SUCCESS(Ios.Status))
+ return VINF_SUCCESS;
+ rcNt = Ios.Status;
+ }
+ return RTErrConvertFromNtStatus(rcNt);
+
+# else
+ DWORD cbReturned = (ULONG)pHdr->cbOut;
+ if (DeviceIoControl(g_hFile, uFunction, pHdr, pHdr->cbIn, pHdr, cbReturned, &cbReturned, NULL))
+ return 0;
+ return RTErrConvertFromWin32(GetLastError());
+# endif
+
+#elif defined(RT_OS_OS2)
+ ULONG cbOS2Parm = cbReq;
+ APIRET rc = DosDevIOCtl((uintptr_t)g_File, VBGL_IOCTL_CATEGORY, uFunction, pHdr, cbReq, &cbOS2Parm, NULL, 0, NULL);
+ if (RT_LIKELY(rc == NO_ERROR))
+ return VINF_SUCCESS;
+ return RTErrConvertFromOS2(rc);
+
+#elif defined(VBOX_VBGLR3_XSERVER)
+ if (g_File != -1)
+ {
+ if (RT_LIKELY(xf86ioctl((int)g_File, uFunction, pHdr) >= 0))
+ return VINF_SUCCESS;
+ return VERR_FILE_IO_ERROR;
+ }
+ return VERR_INVALID_HANDLE;
+
+#else
+ if (g_File != NIL_RTFILE)
+ {
+ if (RT_LIKELY(ioctl((int)(intptr_t)g_File, uFunction, pHdr) >= 0))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+ }
+ return VERR_INVALID_HANDLE;
+#endif
+}
+
+
+/**
+ * Internal wrapper around various OS specific ioctl implementations, that
+ * returns the status from the header.
+ *
+ * @returns VBox status code as returned by VBoxGuestCommonIOCtl, or
+ * an failure returned by the OS specific ioctl APIs.
+ *
+ * @param uFunction The requested function.
+ * @param pHdr The input and output request buffer.
+ * @param cbReq The size of the request buffer.
+ */
+int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pHdr, size_t cbReq)
+{
+ int rc = vbglR3DoIOCtlRaw(uFunction, pHdr, cbReq);
+ if (RT_SUCCESS(rc))
+ rc = pHdr->rc;
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp
new file mode 100644
index 00000000..2ee88509
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAdditions.cpp
@@ -0,0 +1,363 @@
+/* $Id: VBoxGuestR3LibAdditions.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Additions Info.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/utf16.h>
+#endif
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+
+#ifdef RT_OS_WINDOWS
+
+/**
+ * Opens the "VirtualBox Guest Additions" registry key.
+ *
+ * @returns IPRT status code
+ * @param phKey Receives key handle on success. The returned handle must
+ * be closed by calling vbglR3WinCloseRegKey.
+ */
+static int vbglR3WinOpenAdditionRegisterKey(PHKEY phKey)
+{
+ /*
+ * Current vendor first. We keep the older ones just for the case that
+ * the caller isn't actually installed yet (no real use case AFAIK).
+ */
+ static PCRTUTF16 s_apwszKeys[] =
+ {
+ L"SOFTWARE\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions",
+#ifdef RT_ARCH_AMD64
+ L"SOFTWARE\\Wow6432Node\\" RT_LSTR(VBOX_VENDOR_SHORT) L"\\VirtualBox Guest Additions",
+#endif
+ L"SOFTWARE\\Sun\\VirtualBox Guest Additions",
+#ifdef RT_ARCH_AMD64
+ L"SOFTWARE\\Wow6432Node\\Sun\\VirtualBox Guest Additions",
+#endif
+ L"SOFTWARE\\Sun\\xVM VirtualBox Guest Additions",
+#ifdef RT_ARCH_AMD64
+ L"SOFTWARE\\Wow6432Node\\Sun\\xVM VirtualBox Guest Additions",
+#endif
+ };
+ int rc = VERR_NOT_FOUND;
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apwszKeys); i++)
+ {
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_apwszKeys[i], 0 /* ulOptions*/, KEY_READ, phKey);
+ if (lrc == ERROR_SUCCESS)
+ return VINF_SUCCESS;
+ if (i == 0)
+ rc = RTErrConvertFromWin32(lrc);
+ }
+ return rc;
+}
+
+
+/**
+ * Closes the registry handle returned by vbglR3WinOpenAdditionRegisterKey().
+ *
+ * @returns @a rc or IPRT failure status.
+ * @param hKey Handle to close.
+ * @param rc The current IPRT status of the operation. Error
+ * condition takes precedence over errors from this call.
+ */
+static int vbglR3WinCloseRegKey(HKEY hKey, int rc)
+{
+ LSTATUS lrc = RegCloseKey(hKey);
+ if ( lrc == ERROR_SUCCESS
+ || RT_FAILURE(rc))
+ return rc;
+ return RTErrConvertFromWin32(lrc);
+}
+
+
+/**
+ * Queries a string value from a specified registry key.
+ *
+ * @return IPRT status code.
+ * @param hKey Handle of registry key to use.
+ * @param pwszValueName The name of the value to query.
+ * @param cbHint Size hint.
+ * @param ppszValue Where to return value string on success. Free
+ * with RTStrFree.
+ */
+static int vbglR3QueryRegistryString(HKEY hKey, PCRTUTF16 pwszValueName, uint32_t cbHint, char **ppszValue)
+{
+ AssertPtr(pwszValueName);
+ AssertPtrReturn(ppszValue, VERR_INVALID_POINTER);
+
+ /*
+ * First try.
+ */
+ int rc;
+ DWORD dwType;
+ DWORD cbTmp = cbHint;
+ PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16));
+ if (pwszTmp)
+ {
+ LSTATUS lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp);
+ if (lrc == ERROR_MORE_DATA)
+ {
+ /*
+ * Allocate larger buffer and try again.
+ */
+ RTMemTmpFree(pwszTmp);
+ cbTmp += 16;
+ pwszTmp = (PRTUTF16)RTMemTmpAllocZ(cbTmp + sizeof(RTUTF16));
+ if (!pwszTmp)
+ {
+ *ppszValue = NULL;
+ return VERR_NO_TMP_MEMORY;
+ }
+ lrc = RegQueryValueExW(hKey, pwszValueName, NULL, &dwType, (BYTE *)pwszTmp, &cbTmp);
+ }
+ if (lrc == ERROR_SUCCESS)
+ {
+ /*
+ * Check the type and convert to UTF-8.
+ */
+ if (dwType == REG_SZ)
+ rc = RTUtf16ToUtf8(pwszTmp, ppszValue);
+ else
+ rc = VERR_WRONG_TYPE;
+ }
+ else
+ rc = RTErrConvertFromWin32(lrc);
+ RTMemTmpFree(pwszTmp);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ if (RT_SUCCESS(rc))
+ return rc;
+ *ppszValue = NULL;
+ return rc;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+
+/**
+ * Fallback for VbglR3GetAdditionsVersion.
+ *
+ * @copydoc VbglR3GetAdditionsVersion
+ */
+static int vbglR3GetAdditionsCompileTimeVersion(char **ppszVer, char **ppszVerExt, char **ppszRev)
+{
+ int rc = VINF_SUCCESS;
+ if (ppszVer)
+ rc = RTStrDupEx(ppszVer, VBOX_VERSION_STRING_RAW);
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszVerExt)
+ rc = RTStrDupEx(ppszVerExt, VBOX_VERSION_STRING);
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszRev)
+ rc = RTStrDupEx(ppszRev, RT_XSTR(VBOX_SVN_REV));
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ /* bail out: */
+ }
+ if (ppszVerExt)
+ {
+ RTStrFree(*ppszVerExt);
+ *ppszVerExt = NULL;
+ }
+ }
+ if (ppszVer)
+ {
+ RTStrFree(*ppszVer);
+ *ppszVer = NULL;
+ }
+ return rc;
+}
+
+
+/**
+ * Retrieves the installed Guest Additions version and/or revision.
+ *
+ * @returns IPRT status code
+ * @param ppszVer Receives pointer of allocated raw version string
+ * (major.minor.build). NULL is accepted. The returned
+ * pointer must be freed using RTStrFree().
+ * @param ppszVerExt Receives pointer of allocated full version string
+ * (raw version + vendor suffix(es)). NULL is
+ * accepted. The returned pointer must be freed using
+ * RTStrFree().
+ * @param ppszRev Receives pointer of allocated revision string. NULL is
+ * accepted. The returned pointer must be freed using
+ * RTStrFree().
+ */
+VBGLR3DECL(int) VbglR3GetAdditionsVersion(char **ppszVer, char **ppszVerExt, char **ppszRev)
+{
+ /*
+ * Zap the return value up front.
+ */
+ if (ppszVer)
+ *ppszVer = NULL;
+ if (ppszVerExt)
+ *ppszVerExt = NULL;
+ if (ppszRev)
+ *ppszRev = NULL;
+
+#ifdef RT_OS_WINDOWS
+ HKEY hKey;
+ int rc = vbglR3WinOpenAdditionRegisterKey(&hKey);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Version.
+ */
+ if (ppszVer)
+ rc = vbglR3QueryRegistryString(hKey, L"Version", 64, ppszVer);
+
+ if ( RT_SUCCESS(rc)
+ && ppszVerExt)
+ rc = vbglR3QueryRegistryString(hKey, L"VersionExt", 128, ppszVerExt);
+
+ /*
+ * Revision.
+ */
+ if ( RT_SUCCESS(rc)
+ && ppszRev)
+ rc = vbglR3QueryRegistryString(hKey, L"Revision", 64, ppszRev);
+
+ rc = vbglR3WinCloseRegKey(hKey, rc);
+
+ /* Clean up allocated strings on error. */
+ if (RT_FAILURE(rc))
+ {
+ if (ppszVer)
+ {
+ RTStrFree(*ppszVer);
+ *ppszVer = NULL;
+ }
+ if (ppszVerExt)
+ {
+ RTStrFree(*ppszVerExt);
+ *ppszVerExt = NULL;
+ }
+ if (ppszRev)
+ {
+ RTStrFree(*ppszRev);
+ *ppszRev = NULL;
+ }
+ }
+ }
+ /*
+ * No registry entries found, return the version string compiled into this binary.
+ */
+ else
+ rc = vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev);
+ return rc;
+
+#else /* !RT_OS_WINDOWS */
+ /*
+ * On non-Windows platforms just return the compile-time version string.
+ */
+ return vbglR3GetAdditionsCompileTimeVersion(ppszVer, ppszVerExt, ppszRev);
+#endif /* !RT_OS_WINDOWS */
+}
+
+
+/**
+ * Retrieves the installation path of Guest Additions.
+ *
+ * @returns IPRT status code
+ * @param ppszPath Receives pointer of allocated installation path string.
+ * The returned pointer must be freed using
+ * RTStrFree().
+ */
+VBGLR3DECL(int) VbglR3GetAdditionsInstallationPath(char **ppszPath)
+{
+ int rc;
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Get it from the registry.
+ */
+ HKEY hKey;
+ rc = vbglR3WinOpenAdditionRegisterKey(&hKey);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbglR3QueryRegistryString(hKey, L"InstallDir", MAX_PATH * sizeof(RTUTF16), ppszPath);
+ if (RT_SUCCESS(rc))
+ RTPathChangeToUnixSlashes(*ppszPath, true /*fForce*/);
+ rc = vbglR3WinCloseRegKey(hKey, rc);
+ }
+#else
+ /** @todo implement me */
+ rc = VERR_NOT_IMPLEMENTED;
+ RT_NOREF1(ppszPath);
+#endif
+ return rc;
+}
+
+
+/**
+ * Reports the Guest Additions status of a certain facility to the host.
+ *
+ * @returns IPRT status code
+ * @param enmFacility The facility to report the status on.
+ * @param enmStatus The new status of the facility.
+ * @param fReserved Flags reserved for future hacks.
+ */
+VBGLR3DECL(int) VbglR3ReportAdditionsStatus(VBoxGuestFacilityType enmFacility, VBoxGuestFacilityStatus enmStatus,
+ uint32_t fReserved)
+{
+ VMMDevReportGuestStatus Report;
+ RT_ZERO(Report);
+ int rc = vmmdevInitRequest(&Report.header, VMMDevReq_ReportGuestStatus);
+ if (RT_SUCCESS(rc))
+ {
+ Report.guestStatus.facility = enmFacility;
+ Report.guestStatus.status = enmStatus;
+ Report.guestStatus.flags = fReserved;
+
+ rc = vbglR3GRPerform(&Report.header);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp
new file mode 100644
index 00000000..680e6a5c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibAutoLogon.cpp
@@ -0,0 +1,130 @@
+/* $Id: VBoxGuestR3LibAutoLogon.cpp $ */
+/** @file
+ * VBoxGuestR3LibAutoLogon - Ring-3 utility functions for auto-logon modules
+ * (VBoxGINA / VBoxCredProv / pam_vbox).
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#endif
+
+#include "VBoxGuestR3LibInternal.h"
+#include <iprt/errcore.h>
+
+
+/**
+ * Reports the current auto-logon status to the host.
+ *
+ * This makes sure that the Failed state is sticky.
+ *
+ * @return IPRT status code.
+ * @param enmStatus Status to report to the host.
+ */
+VBGLR3DECL(int) VbglR3AutoLogonReportStatus(VBoxGuestFacilityStatus enmStatus)
+{
+ /*
+ * VBoxGuestFacilityStatus_Failed is sticky.
+ */
+ static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
+ if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
+ {
+ int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_AutoLogon, enmStatus, 0 /* Flags */);
+ if (rc == VERR_NOT_SUPPORTED)
+ {
+ /*
+ * To maintain backwards compatibility to older hosts which don't have
+ * VMMDevReportGuestStatus implemented we set the appropriate status via
+ * guest property to have at least something.
+ */
+#ifdef VBOX_WITH_GUEST_PROPS
+ HGCMCLIENTID idClient = 0;
+ rc = VbglR3GuestPropConnect(&idClient);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszStatus;
+ switch (enmStatus)
+ {
+ case VBoxGuestFacilityStatus_Inactive: pszStatus = "Inactive"; break;
+ case VBoxGuestFacilityStatus_Paused: pszStatus = "Disabled"; break;
+ case VBoxGuestFacilityStatus_PreInit: pszStatus = "PreInit"; break;
+ case VBoxGuestFacilityStatus_Init: pszStatus = "Init"; break;
+ case VBoxGuestFacilityStatus_Active: pszStatus = "Active"; break;
+ case VBoxGuestFacilityStatus_Terminating: pszStatus = "Terminating"; break;
+ case VBoxGuestFacilityStatus_Terminated: pszStatus = "Terminated"; break;
+ case VBoxGuestFacilityStatus_Failed: pszStatus = "Failed"; break;
+ default: pszStatus = NULL;
+ }
+ if (pszStatus)
+ {
+ /*
+ * Use TRANSRESET when possible, fall back to TRANSIENT
+ * (generally sufficient unless the guest misbehaves).
+ */
+ static const char s_szPath[] = "/VirtualBox/GuestInfo/OS/AutoLogonStatus";
+ rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSRESET");
+ if (rc == VERR_PARSE_ERROR)
+ rc = VbglR3GuestPropWrite(idClient, s_szPath, pszStatus, "TRANSIENT");
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ VbglR3GuestPropDisconnect(idClient);
+ }
+#endif
+ }
+
+ s_enmLastStatus = enmStatus;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Detects whether our process is running in a remote session or not.
+ *
+ * @return bool true if running in a remote session, false if not.
+ */
+VBGLR3DECL(bool) VbglR3AutoLogonIsRemoteSession(void)
+{
+#ifdef RT_OS_WINDOWS
+ return GetSystemMetrics(SM_REMOTESESSION) != 0 ? true : false;
+#else
+ return false; /* Not implemented. */
+#endif
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp
new file mode 100644
index 00000000..b7b2a8f8
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibBalloon.cpp
@@ -0,0 +1,83 @@
+/* $Id: VBoxGuestR3LibBalloon.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Ballooning.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+#include <iprt/string.h>
+
+
+/**
+ * Refresh the memory balloon after a change.
+ *
+ * @returns IPRT status code.
+ * @param pcChunks The size of the balloon in chunks of 1MB (out).
+ * @param pfHandleInR3 Allocating of memory in R3 required (out).
+ */
+VBGLR3DECL(int) VbglR3MemBalloonRefresh(uint32_t *pcChunks, bool *pfHandleInR3)
+{
+ VBGLIOCCHECKBALLOON Info;
+ VBGLREQHDR_INIT(&Info.Hdr, CHECK_BALLOON);
+ int rc = vbglR3DoIOCtl(VBGL_IOCTL_CHECK_BALLOON, &Info.Hdr, sizeof(Info));
+ if (RT_SUCCESS(rc))
+ {
+ *pcChunks = Info.u.Out.cBalloonChunks;
+ *pfHandleInR3 = Info.u.Out.fHandleInR3 != false;
+ }
+ return rc;
+}
+
+
+/**
+ * Change the memory by granting/reclaiming memory to/from R0.
+ *
+ * @returns IPRT status code.
+ * @param pv Memory chunk (1MB).
+ * @param fInflate true = inflate balloon (grant memory).
+ * false = deflate balloon (reclaim memory).
+ */
+VBGLR3DECL(int) VbglR3MemBalloonChange(void *pv, bool fInflate)
+{
+ VBGLIOCCHANGEBALLOON Info;
+ VBGLREQHDR_INIT(&Info.Hdr, CHANGE_BALLOON);
+ Info.u.In.pvChunk = pv;
+ Info.u.In.fInflate = fInflate;
+ RT_ZERO(Info.u.In.abPadding);
+ return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_BALLOON, &Info.Hdr, sizeof(Info));
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp
new file mode 100644
index 00000000..f44b923a
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp
@@ -0,0 +1,2609 @@
+/* $Id: VBoxGuestR3LibClipboard.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared Clipboard.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include <VBox/GuestHost/SharedClipboard-transfers.h>
+#endif
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include <iprt/dir.h>
+# include <iprt/file.h>
+# include <iprt/path.h>
+#endif
+#include <iprt/string.h>
+#include <iprt/cpp/ministring.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/*
+ * Function naming convention:
+ *
+ * FunctionNameRecv = Receives a host message (request).
+ * FunctionNameReply = Replies to a host message (request).
+ * FunctionNameSend = Sends a guest message to the host.
+ */
+
+
+/*********************************************************************************************************************************
+* Prototypes *
+*********************************************************************************************************************************/
+
+
+/**
+ * Connects to the Shared Clipboard service, legacy version, do not use anymore.
+ *
+ * @returns VBox status code
+ * @param pidClient Where to put the client id on success. The client id
+ * must be passed to all the other clipboard calls.
+ */
+VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient)
+{
+ int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
+ LogRel(("Shared Clipboard: Unabled to connect, as host service was not found, skipping\n"));
+ else
+ LogRel(("Shared Clipboard: Unabled to connect to host service, rc=%Rrc\n", rc));
+ }
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Connects to the Shared Clipboard service, extended version.
+ *
+ * @returns VBox status code.
+ * @param pCtx Command context. This will be initialized by this
+ * call.
+ * @param fGuestFeatures The guest features supported by this client,
+ * VBOX_SHCL_GF_0_XXX.
+ */
+VBGLR3DECL(int) VbglR3ClipboardConnectEx(PVBGLR3SHCLCMDCTX pCtx, uint64_t fGuestFeatures)
+{
+ /*
+ * Intialize the context structure.
+ */
+ pCtx->idClient = 0;
+ pCtx->fGuestFeatures = fGuestFeatures;
+ pCtx->fHostFeatures = 0;
+ pCtx->fUseLegacyProtocol = true;
+ pCtx->cParmsRecived = 0;
+ pCtx->idContext = 0;
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ /* Init callback table. */
+ RT_ZERO(pCtx->Transfers.Callbacks);
+ /* Indicate that this guest supports Shared Clipboard file transfers. */
+ pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS;
+# ifdef RT_OS_WINDOWS
+ /* Indicate that on Windows guest OSes we have our own IDataObject implementation which
+ * integrates nicely into the guest's Windows Explorer showing / handling the Shared Clipboard file transfers. */
+ pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS_FRONTEND;
+# endif
+ pCtx->Transfers.cbChunkSize = VBOX_SHCL_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */
+ pCtx->Transfers.cbMaxChunkSize = VBOX_SHCL_MAX_CHUNK_SIZE; /** @todo Ditto. */
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+
+ /*
+ * First step is connecting to the HGCM service.
+ */
+ int rc = VbglR3ClipboardConnect(&pCtx->idClient);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Next is reporting our features. If this fails, assume older host.
+ */
+ rc = VbglR3ClipboardReportFeatures(pCtx->idClient, pCtx->fGuestFeatures, &pCtx->fHostFeatures);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel2(("Shared Clipboard: Guest features: %#RX64 - Host features: %#RX64\n",
+ pCtx->fGuestFeatures, pCtx->fHostFeatures));
+
+ if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID)
+ && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_CONTEXT_ID) )
+ {
+ pCtx->fUseLegacyProtocol = false;
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS)
+ && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_TRANSFERS) )
+ {
+ VBoxShClParmNegotiateChunkSize MsgChunkSize;
+ do
+ {
+ VBGL_HGCM_HDR_INIT(&MsgChunkSize.hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE,
+ VBOX_SHCL_CPARMS_NEGOTIATE_CHUNK_SIZE);
+ MsgChunkSize.cb32MaxChunkSize.SetUInt32(pCtx->Transfers.cbMaxChunkSize);
+ MsgChunkSize.cb32ChunkSize.SetUInt32(0); /* If set to 0, let the host choose. */
+ rc = VbglR3HGCMCall(&MsgChunkSize.hdr, sizeof(MsgChunkSize));
+ } while (rc == VERR_INTERRUPTED);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(MsgChunkSize.cb32ChunkSize.type == VMMDevHGCMParmType_32bit);
+ pCtx->Transfers.cbChunkSize = RT_MIN(MsgChunkSize.cb32ChunkSize.u.value32, pCtx->Transfers.cbChunkSize);
+ Assert(MsgChunkSize.cb32MaxChunkSize.type == VMMDevHGCMParmType_32bit);
+ pCtx->Transfers.cbMaxChunkSize = RT_MIN(MsgChunkSize.cb32MaxChunkSize.u.value32, pCtx->Transfers.cbMaxChunkSize);
+
+ LogRel2(("Shared Clipboard: Using chunk size %RU32 (maximum is %RU32)\n",
+ pCtx->Transfers.cbChunkSize, pCtx->Transfers.cbMaxChunkSize));
+ }
+ }
+ else
+ {
+ if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS))
+ LogRel2(("Shared Clipboard: Host does not support transfers\n"));
+ }
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+ }
+ else
+ {
+ if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID))
+ LogRel(("Shared Clipboard: Host does not support context IDs, using legacy protocol\n"));
+
+ pCtx->fUseLegacyProtocol = true;
+ }
+ }
+ else
+ {
+ AssertLogRelMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED,
+ ("Reporting features failed: %Rrc\n", rc));
+ pCtx->fUseLegacyProtocol = true;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Reports features to the host and retrieve host feature set.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3ClipboardConnect().
+ * @param fGuestFeatures Features to report, VBOX_SHCL_GF_XXX.
+ * @param pfHostFeatures Where to store the features VBOX_SHCL_HF_XXX.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter f64Features0;
+ HGCMFunctionParameter f64Features1;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FEATURES, 2);
+ VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures);
+ VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_SHCL_GF_1_MUST_BE_ONE);
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
+ Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
+ if (Msg.f64Features1.u.value64 & VBOX_SHCL_GF_1_MUST_BE_ONE)
+ rc = VERR_NOT_SUPPORTED;
+ else if (pfHostFeatures)
+ *pfHostFeatures = Msg.f64Features0.u.value64;
+ break;
+ }
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+
+}
+
+
+/**
+ * Disconnects from the Shared Clipboard service, legacy version, do not use anymore.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ */
+VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient)
+{
+ return VbglR3HGCMDisconnect(idClient);
+}
+
+
+/**
+ * Disconnects from the Shared Clipboard service, extended version.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ */
+VBGLR3DECL(int) VbglR3ClipboardDisconnectEx(PVBGLR3SHCLCMDCTX pCtx)
+{
+ int rc = VbglR3ClipboardDisconnect(pCtx->idClient);
+ if (RT_SUCCESS(rc))
+ {
+ pCtx->idClient = 0;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Receives reported formats from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the
+ * connection.
+ * @param pfFormats Where to store the received formats from the host.
+ */
+static int vbglR3ClipboardFormatsReportRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMATS pfFormats)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFormats, VERR_INVALID_POINTER);
+
+ *pfFormats = 0;
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter id64Context;
+ HGCMFunctionParameter f32Formats;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2);
+ Msg.id64Context.SetUInt32(VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
+ Msg.f32Formats.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.f32Formats.GetUInt32(pfFormats);
+ AssertRC(rc);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA_CID message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pfFormat Where to return the requested format.
+ */
+static int vbglR3ClipboardFetchReadDataCid(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFormat, VERR_INVALID_POINTER);
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter id64Context;
+ HGCMFunctionParameter f32Format;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2);
+ Msg.id64Context.SetUInt64(VBOX_SHCL_HOST_MSG_READ_DATA_CID);
+ Msg.f32Format.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.id64Context.GetUInt64(&pCtx->idContext);
+ AssertRC(rc);
+ int rc2 = Msg.f32Format.GetUInt32(pfFormat);
+ AssertRCStmt(rc2, rc = rc2);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pfFormat Where to return the requested format.
+ */
+static int vbglR3ClipboardFetchReadData(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFormat, VERR_INVALID_POINTER);
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter id32Msg;
+ HGCMFunctionParameter f32Format;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2);
+ Msg.id32Msg.SetUInt32(VBOX_SHCL_HOST_MSG_READ_DATA);
+ Msg.f32Format.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.f32Format.GetUInt32(pfFormat);
+ AssertRC(rc);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Get a host message, legacy version (which does not have VBOX_SHCL_GUEST_FN_MSG_GET). Do not use anymore.
+ *
+ * Note: This is the old message which still is being used for the non-URI Shared Clipboard transfers,
+ * to not break compatibility with older additions / VBox versions.
+ *
+ * This will block until a message becomes available.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param pidMsg Where to store the message id.
+ * @param pfFormats Where to store the format(s) the message applies to.
+ */
+VBGLR3DECL(int) VbglR3ClipboardGetHostMsgOld(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats)
+{
+ VBoxShClGetHostMsgOld Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD);
+ VbglHGCMParmUInt32Set(&Msg.msg, 0);
+ VbglHGCMParmUInt32Set(&Msg.formats, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg);
+ if (RT_SUCCESS(rc))
+ {
+ rc2 = VbglHGCMParmUInt32Get(&Msg.formats, pfFormats);
+ if (RT_SUCCESS(rc2))
+ return rc;
+ }
+ rc = rc2;
+ }
+ *pidMsg = UINT32_MAX - 1;
+ *pfFormats = UINT32_MAX;
+ return rc;
+}
+
+
+/**
+ * Reads data from the host clipboard.
+ *
+ * Legacy function, do not use anymore.
+ *
+ * @returns VBox status code.
+ * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for.
+ *
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param fFormat The format we're requesting the data in.
+ * @param pvData Where to store the data.
+ * @param cbData The size of the buffer pointed to by \a pvData.
+ * @param pcbRead The actual size of the host clipboard data. May be larger than \a cbData.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pvData, uint32_t cbData,
+ uint32_t *pcbRead)
+{
+ LogFlowFuncEnter();
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ VBoxShClParmDataRead Parms;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_READ, VBOX_SHCL_CPARMS_DATA_READ);
+ VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat);
+ VbglHGCMParmPtrSet( &Msg.Parms.pData, pvData, cbData);
+ VbglHGCMParmUInt32Set(&Msg.Parms.cb32Needed, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbRead;
+ rc = VbglHGCMParmUInt32Get(&Msg.Parms.cb32Needed, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("cbRead=%RU32\n", cbRead));
+
+ if (cbRead > cbData)
+ rc = VINF_BUFFER_OVERFLOW;
+
+ *pcbRead = cbRead;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Reads clipboard data from the host clipboard.
+ *
+ * @returns VBox status code.
+ * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for.
+ *
+ * @param pCtx The command context returned by VbglR3ClipboardConnectEx().
+ * @param uFormat Clipboard format of clipboard data to be read.
+ * @param pvData Buffer where to store the read data.
+ * @param cbData Size (in bytes) of data buffer where to store the read data.
+ * @param pcbRead The actual size of the host clipboard data.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReadDataEx(PVBGLR3SHCLCMDCTX pCtx,
+ SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbRead)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ return VbglR3ClipboardReadData(pCtx->idClient, uFormat, pvData, cbData, pcbRead);
+}
+
+
+/**
+ * Query the host features.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3ClipboardConnect().
+ * @param pfHostFeatures Where to store the host feature, VBOX_SHCL_HF_XXX.
+ */
+VBGLR3DECL(int) VbglR3ClipboardQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter f64Features0;
+ HGCMFunctionParameter f64Features1;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_QUERY_FEATURES, 2);
+ VbglHGCMParmUInt64Set(&Msg.f64Features0, 0);
+ VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63));
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
+ Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
+ if (Msg.f64Features1.u.value64 & RT_BIT_64(63))
+ rc = VERR_NOT_SUPPORTED;
+ else if (pfHostFeatures)
+ *pfHostFeatures = Msg.f64Features0.u.value64;
+ break;
+ }
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+
+}
+
+/**
+ * Peeks at the next host message, waiting for one to turn up.
+ *
+ * This glosses over the difference between new (6.1) and old (1.3.2) host
+ * service versions, however it does so by abusing @a pcParameters, so don't use
+ * it directly when in legacy mode, always pass it on to
+ * VbglR3ClipboardEventGetNext() or VbglR3ClipboardEventGetNextEx().
+ *
+ * @returns VBox status code.
+ * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so
+ * caller just have to repeat this call.
+ * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck).
+ *
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pidMsg Where to store the message id.
+ * @param pcParameters Where to store the number of parameters which will
+ * be received in a second call to the host.
+ * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use
+ * for the VM restore check. Optional.
+ *
+ * @note Restore check is only performed optimally with a 6.0 host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardMsgPeekWait(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pidMsg,
+ uint32_t *pcParameters, uint64_t *pidRestoreCheck)
+{
+ AssertPtrReturn(pidMsg, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParameters, VERR_INVALID_POINTER);
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */
+ HGCMFunctionParameter cParameters;
+ } Msg;
+ int rc;
+ if (!pCtx->fUseLegacyProtocol)
+ {
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT, 2);
+ VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0);
+ VbglHGCMParmUInt32Set(&Msg.cParameters, 0);
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ LogFlowFunc(("VbglR3HGCMCall -> %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ {
+ AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit
+ && Msg.cParameters.type == VMMDevHGCMParmType_32bit,
+ ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type),
+ VERR_INTERNAL_ERROR_3);
+
+ *pidMsg = (uint32_t)Msg.idMsg.u.value64;
+ *pcParameters = Msg.cParameters.u.value32;
+ return rc;
+ }
+
+ /*
+ * If restored, update pidRestoreCheck.
+ */
+ if (rc == VERR_VM_RESTORED && pidRestoreCheck)
+ *pidRestoreCheck = Msg.idMsg.u.value64;
+ }
+ else
+ {
+ /*
+ * We do some crude stuff here by putting the 2nd parameter (foramts) in the parameter count,
+ * however it's supposed to be passed directly to VbglR3ClipboardEventGetNext or
+ * VbglR3ClipboardEventGetNextEx, so that's fine...
+ */
+ rc = VbglR3ClipboardGetHostMsgOld(pCtx->idClient, pidMsg, pcParameters);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+
+ /*
+ * If interrupted we must cancel the call so it doesn't prevent us from making another one.
+ */
+ if (rc == VERR_INTERRUPTED)
+ {
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_CANCEL, 0);
+ int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr));
+ AssertRC(rc2);
+ }
+
+ *pidMsg = UINT32_MAX - 1;
+ *pcParameters = UINT32_MAX - 2;
+ return rc;
+}
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+
+/**
+ * Reads a root list header from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pRootListHdr Where to store the received root list header.
+ */
+static int vbglR3ClipboardRootListHdrRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER);
+
+ VBoxShClRootListHdrMsg Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.fRoots.SetUInt32(0);
+
+ Msg.cRoots.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.fRoots.GetUInt32(&pRootListHdr->fRoots); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.cRoots.GetUInt32(&pRootListHdr->cRoots);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Reads a root list entry from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param uIndex Index of root list entry to read.
+ * @param pRootListEntry Where to store the root list entry read from the host.
+ */
+static int vbglR3ClipboardRootListEntryRead(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pRootListEntry)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pRootListEntry, VERR_INVALID_POINTER);
+
+ VBoxShClRootListEntryMsg Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ);
+
+ Msg.Parms.uContext.SetUInt64(pCtx->idContext);
+ Msg.Parms.fInfo.SetUInt32(pRootListEntry->fInfo);
+ Msg.Parms.uIndex.SetUInt32(uIndex);
+
+ Msg.szName.SetPtr(pRootListEntry->pszName, pRootListEntry->cbName);
+ Msg.cbInfo.SetUInt32(pRootListEntry->cbInfo);
+ Msg.pvInfo.SetPtr(pRootListEntry->pvInfo, pRootListEntry->cbInfo);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.Parms.fInfo.GetUInt32(&pRootListEntry->fInfo); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbInfo = 0;
+ rc = Msg.cbInfo.GetUInt32(&cbInfo); AssertRC(rc);
+ if (pRootListEntry->cbInfo != cbInfo)
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Reads the root list from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param ppRootList Where to store the (allocated) root list. Must be free'd by the caller with
+ * SharedClipboardTransferRootListFree().
+ */
+VBGLR3DECL(int) VbglR3ClipboardRootListRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLIST *ppRootList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
+
+ int rc;
+
+ PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
+ if (pRootList)
+ {
+ SHCLROOTLISTHDR srcRootListHdr;
+ rc = vbglR3ClipboardRootListHdrRead(pCtx, &srcRootListHdr);
+ if (RT_SUCCESS(rc))
+ {
+ pRootList->Hdr.cRoots = srcRootListHdr.cRoots;
+ pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
+
+ if (srcRootListHdr.cRoots)
+ {
+ pRootList->paEntries =
+ (PSHCLROOTLISTENTRY)RTMemAllocZ(srcRootListHdr.cRoots * sizeof(SHCLROOTLISTENTRY));
+ if (pRootList->paEntries)
+ {
+ for (uint32_t i = 0; i < srcRootListHdr.cRoots; i++)
+ {
+ SHCLROOTLISTENTRY *pEntry = &pRootList->paEntries[i];
+ AssertPtr(pEntry);
+
+ rc = ShClTransferRootListEntryInit(pEntry);
+ if (RT_SUCCESS(rc))
+ rc = vbglR3ClipboardRootListEntryRead(pCtx, i, pEntry);
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppRootList = pRootList;
+ }
+ else
+ ShClTransferRootListFree(pRootList);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a transfer status from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pEnmDir Where to store the transfer direction for the reported transfer.
+ * @param pReport Where to store the transfer (status) report.
+ */
+VBGLR3DECL(int) VbglR3ClipboarTransferStatusRecv(PVBGLR3SHCLCMDCTX pCtx,
+ PSHCLTRANSFERDIR pEnmDir, PSHCLTRANSFERREPORT pReport)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pReport, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEnmDir, VERR_INVALID_POINTER);
+
+ VBoxShClTransferStatusMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_TRANSFER_STATUS);
+
+ Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS);
+ Msg.enmDir.SetUInt32(0);
+ Msg.enmStatus.SetUInt32(0);
+ Msg.rc.SetUInt32(0);
+ Msg.fFlags.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.enmDir.GetUInt32((uint32_t *)pEnmDir);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.enmStatus.GetUInt32(&pReport->uStatus);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.rc.GetUInt32((uint32_t *)&pReport->rc);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.fFlags.GetUInt32(&pReport->fFlags);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to a transfer report from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pTransfer Transfer of report to reply to.
+ * @param uStatus Tranfer status to reply.
+ * @param rcTransfer Result code (rc) to reply.
+ */
+VBGLR3DECL(int) VbglR3ClipboardTransferStatusReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLTRANSFER pTransfer,
+ SHCLTRANSFERSTATUS uStatus, int rcTransfer)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ RT_NOREF(pTransfer);
+
+ VBoxShClReplyMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS);
+ Msg.rc.SetUInt32((uint32_t )rcTransfer); /* int vs. uint32_t */
+ Msg.pvPayload.SetPtr(NULL, 0);
+
+ Msg.u.TransferStatus.enmStatus.SetUInt32((uint32_t)uStatus);
+
+ LogFlowFunc(("%s\n", ShClTransferStatusToStr(uStatus)));
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to read a root list header from the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pfRoots Where to store the root list header flags to use, requested by the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pfRoots)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfRoots, VERR_INVALID_POINTER);
+
+ VBoxShClRootListReadReqMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ);
+
+ Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ);
+ Msg.ReqParms.fRoots.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.fRoots.GetUInt32(pfRoots);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to a root list header request.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pRootListHdr Root lsit header to reply to the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER);
+
+ VBoxShClRootListHdrMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.fRoots.SetUInt32(pRootListHdr->fRoots);
+
+ Msg.cRoots.SetUInt32(pRootListHdr->cRoots);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to read a root list entry from the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param puIndex Where to return the index of the root list entry the host wants to read.
+ * @param pfInfo Where to return the read flags the host wants to use.
+ */
+VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *puIndex, uint32_t *pfInfo)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(puIndex, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfInfo, VERR_INVALID_POINTER);
+
+ VBoxShClRootListEntryReadReqMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ);
+
+ Msg.Parms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ);
+ Msg.Parms.fInfo.SetUInt32(0);
+ Msg.Parms.uIndex.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.Parms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.Parms.fInfo.GetUInt32(pfInfo);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.Parms.uIndex.GetUInt32(puIndex);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to a root list entry read request from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param uIndex Index of root list entry to reply.
+ * @param pEntry Actual root list entry to reply.
+ */
+VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReply(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pEntry)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
+
+ VBoxShClRootListEntryMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE);
+
+ Msg.Parms.uContext.SetUInt64(pCtx->idContext);
+ Msg.Parms.fInfo.SetUInt32(0);
+ Msg.Parms.uIndex.SetUInt32(uIndex);
+
+ Msg.szName.SetPtr(pEntry->pszName, pEntry->cbName);
+ Msg.cbInfo.SetUInt32(pEntry->cbInfo);
+ Msg.pvInfo.SetPtr(pEntry->pvInfo, pEntry->cbInfo);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to open a list handle to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pOpenParms List open parameters to use for the open request.
+ * @param phList Where to return the list handle received from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms,
+ PSHCLLISTHANDLE phList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
+ AssertPtrReturn(phList, VERR_INVALID_POINTER);
+
+ VBoxShClListOpenMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_OPEN, VBOX_SHCL_CPARMS_LIST_OPEN);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.fList.SetUInt32(0);
+ Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter);
+ Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath);
+ Msg.uHandle.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uHandle.GetUInt64(phList); AssertRC(rc);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to open a list handle on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pOpenParms Where to store the open parameters the host wants to use for opening the list handle.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
+
+ VBoxShClListOpenMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_OPEN);
+
+ Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN);
+ Msg.fList.SetUInt32(0);
+ Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath);
+ Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter);
+ Msg.uHandle.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ rc = Msg.fList.GetUInt32(&pOpenParms->fList);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to a list open request from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param rcReply Return code to reply to the host.
+ * @param hList List handle of (guest) list to reply to the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClReplyMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN);
+ Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */
+ Msg.pvPayload.SetPtr(NULL, 0);
+
+ Msg.u.ListOpen.uHandle.SetUInt64(hList);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to close a list handle on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param phList Where to store the list handle to close, received from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(phList, VERR_INVALID_POINTER);
+
+ VBoxShClListCloseMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_CLOSE);
+
+ Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE);
+ Msg.uHandle.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uHandle.GetUInt64(phList);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to a list handle close request from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param rcReply Return code to reply to the host.
+ * @param hList List handle the send the close reply for.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClReplyMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE);
+ Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */
+ Msg.pvPayload.SetPtr(NULL, 0);
+
+ Msg.u.ListOpen.uHandle.SetUInt64(hList);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to close a list handle to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hList List handle to request for closing on the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClListCloseMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_CLOSE, VBOX_SHCL_CPARMS_LIST_CLOSE);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.uHandle.SetUInt64(hList);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to read a list header to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hList List handle to read list header for.
+ * @param fFlags List header read flags to use.
+ * @param pListHdr Where to return the list header received from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListHdrRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, uint32_t fFlags,
+ PSHCLLISTHDR pListHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pListHdr, VERR_INVALID_POINTER);
+
+ VBoxShClListHdrMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_HDR_READ, VBOX_SHCL_CPARMS_LIST_HDR);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.uHandle.SetUInt64(hList);
+ Msg.ReqParms.fFlags.SetUInt32(fFlags);
+
+ Msg.fFeatures.SetUInt32(0);
+ Msg.cbTotalSize.SetUInt32(0);
+ Msg.cTotalObjects.SetUInt64(0);
+ Msg.cbTotalSize.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.fFeatures.GetUInt32(&pListHdr->fFeatures);
+ if (RT_SUCCESS(rc))
+ rc = Msg.cTotalObjects.GetUInt64(&pListHdr->cTotalObjects);
+ if (RT_SUCCESS(rc))
+ rc = Msg.cbTotalSize.GetUInt64(&pListHdr->cbTotalSize);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to read a list header on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param phList Where to return the list handle to read list header for.
+ * @param pfFlags Where to return the List header read flags to use.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListHdrReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(phList, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ VBoxShClListHdrReadReqMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ);
+
+ Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ);
+ Msg.ReqParms.uHandle.SetUInt64(0);
+ Msg.ReqParms.fFlags.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ rc = Msg.ReqParms.uHandle.GetUInt64(phList);
+ if (RT_SUCCESS(rc))
+ rc = Msg.ReqParms.fFlags.GetUInt32(pfFlags);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends (writes) a list header to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hList List handle to write list header for.
+ * @param pListHdr List header to write.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListHdrWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList,
+ PSHCLLISTHDR pListHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pListHdr, VERR_INVALID_POINTER);
+
+ VBoxShClListHdrMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_LIST_HDR);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.uHandle.SetUInt64(hList);
+ Msg.ReqParms.fFlags.SetUInt32(0);
+
+ Msg.fFeatures.SetUInt32(0);
+ Msg.cbTotalSize.SetUInt32(pListHdr->fFeatures);
+ Msg.cTotalObjects.SetUInt64(pListHdr->cTotalObjects);
+ Msg.cbTotalSize.SetUInt64(pListHdr->cbTotalSize);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to read a list entry from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hList List handle to request to read a list entry for.
+ * @param pListEntry Where to return the list entry read from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListEntryRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList,
+ PSHCLLISTENTRY pListEntry)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pListEntry, VERR_INVALID_POINTER);
+
+ VBoxShClListEntryMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_LIST_ENTRY);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.uHandle.SetUInt64(hList);
+ Msg.ReqParms.fInfo.SetUInt32(0);
+
+ Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName);
+ Msg.cbInfo.SetUInt32(pListEntry->cbInfo);
+ Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.cbInfo.GetUInt32(&pListEntry->cbInfo); AssertRC(rc);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to read a list entry from the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param phList Where to return the list handle to read a list entry for.
+ * @param pfInfo Where to return the list read flags.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListEntryReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfInfo)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(phList, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfInfo, VERR_INVALID_POINTER);
+
+ VBoxShClListEntryReadReqMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_ENTRY_READ);
+
+ Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ);
+ Msg.ReqParms.uHandle.SetUInt64(0);
+ Msg.ReqParms.fInfo.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.uHandle.GetUInt64(phList);
+ AssertRC(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.fInfo.GetUInt32(pfInfo);
+ AssertRC(rc);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends (writes) a list entry to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hList List handle to write a list etnry for.
+ * @param pListEntry List entry to write.
+ */
+VBGLR3DECL(int) VbglR3ClipboardListEntryWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList,
+ PSHCLLISTENTRY pListEntry)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pListEntry, VERR_INVALID_POINTER);
+
+ VBoxShClListEntryMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_LIST_ENTRY);
+
+ Msg.ReqParms.uContext.SetUInt64(pCtx->idContext);
+ Msg.ReqParms.uHandle.SetUInt64(hList);
+ Msg.ReqParms.fInfo.SetUInt32(pListEntry->fInfo);
+
+ Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName);
+ Msg.cbInfo.SetUInt32(pListEntry->cbInfo);
+ Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to open an object on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pCreateParms Where to store the object open/create parameters received from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER);
+
+ VBoxShClObjOpenMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_OPEN);
+
+ Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN);
+ Msg.uHandle.SetUInt64(0);
+ Msg.szPath.SetPtr(pCreateParms->pszPath, pCreateParms->cbPath);
+ Msg.fCreate.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ rc = Msg.fCreate.GetUInt32(&pCreateParms->fCreate);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies a host request to open an object.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param rcReply Return code to reply to the host.
+ * @param hObj Object handle of opened object to reply to the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClReplyMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN);
+ Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */
+ Msg.pvPayload.SetPtr(NULL, 0);
+
+ Msg.u.ObjOpen.uHandle.SetUInt64(hObj);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends an object open request to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param pCreateParms Object open/create parameters to use for opening the object on the host.
+ * @param phObj Where to return the object handle from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms,
+ PSHCLOBJHANDLE phObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER);
+ AssertPtrReturn(phObj, VERR_INVALID_POINTER);
+
+ VBoxShClObjOpenMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_OBJ_OPEN, VBOX_SHCL_CPARMS_OBJ_OPEN);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.uHandle.SetUInt64(0);
+ Msg.szPath.SetPtr((void *)pCreateParms->pszPath, pCreateParms->cbPath);
+ Msg.fCreate.SetUInt32(pCreateParms->fCreate);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.uHandle.GetUInt64(phObj);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to close an object on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param phObj Where to return the object handle to close from the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(phObj, VERR_INVALID_POINTER);
+
+ VBoxShClObjCloseMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_CLOSE);
+
+ Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE);
+ Msg.uHandle.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ rc = Msg.uHandle.GetUInt64(phObj);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Replies to an object open request from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param rcReply Return code to reply to the host.
+ * @param hObj Object handle to reply to the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClReplyMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE);
+ Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */
+ Msg.pvPayload.SetPtr(NULL, 0);
+
+ Msg.u.ObjClose.uHandle.SetUInt64(hObj);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to close an object to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hObj Object handle to close on the host.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ VBoxShClObjCloseMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_OBJ_CLOSE, VBOX_SHCL_CPARMS_OBJ_CLOSE);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.uHandle.SetUInt64(hObj);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Receives a host request to read from an object on the guest.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param phObj Where to return the object handle to read from.
+ * @param pcbToRead Where to return the amount (in bytes) to read.
+ * @param pfFlags Where to return the read flags.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjReadRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj, uint32_t *pcbToRead,
+ uint32_t *pfFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(phObj, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbToRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ VBoxShClObjReadReqMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_READ_REQ);
+
+ Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ);
+ Msg.ReqParms.uHandle.SetUInt64(0);
+ Msg.ReqParms.cbToRead.SetUInt32(0);
+ Msg.ReqParms.fRead.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext);
+ if (RT_SUCCESS(rc))
+ rc = Msg.ReqParms.uHandle.GetUInt64(phObj);
+ if (RT_SUCCESS(rc))
+ rc = Msg.ReqParms.cbToRead.GetUInt32(pcbToRead);
+ if (RT_SUCCESS(rc))
+ rc = Msg.ReqParms.fRead.GetUInt32(pfFlags);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to read from an object to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hObj Object handle of object to read from.
+ * @param pvData Buffer where to store the read object data.
+ * @param cbData Size (in bytes) of buffer.
+ * @param pcbRead Where to store the amount (in bytes) read from the object.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjReadSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj,
+ void *pvData, uint32_t cbData, uint32_t *pcbRead)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ /* pcbRead is optional. */
+
+ VBoxShClObjReadWriteMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_OBJ_READ, VBOX_SHCL_CPARMS_OBJ_READ);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.uHandle.SetUInt64(hObj);
+ Msg.cbData.SetUInt32(cbData);
+ Msg.pvData.SetPtr(pvData, cbData);
+ Msg.cbChecksum.SetUInt32(0);
+ Msg.pvChecksum.SetPtr(NULL, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Add checksum support. */
+
+ if (pcbRead)
+ {
+ rc = Msg.cbData.GetUInt32(pcbRead); AssertRC(rc);
+ AssertReturn(cbData >= *pcbRead, VERR_TOO_MUCH_DATA);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends a request to write to an object to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Shared Clipboard command context to use for the connection.
+ * @param hObj Object handle of object to write to.
+ * @param pvData Buffer of data to write to object.
+ * @param cbData Size (in bytes) of buffer.
+ * @param pcbWritten Where to store the amount (in bytes) written to the object.
+ */
+VBGLR3DECL(int) VbglR3ClipboardObjWriteSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj,
+ void *pvData, uint32_t cbData, uint32_t *pcbWritten)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ /* cbData can be 0. */
+ /* pcbWritten is optional. */
+
+ VBoxShClObjReadWriteMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient,
+ VBOX_SHCL_GUEST_FN_OBJ_WRITE, VBOX_SHCL_CPARMS_OBJ_WRITE);
+
+ Msg.uContext.SetUInt64(pCtx->idContext);
+ Msg.uHandle.SetUInt64(hObj);
+ Msg.pvData.SetPtr(pvData, cbData);
+ Msg.pvChecksum.SetPtr(NULL, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Add checksum support. */
+
+ if (pcbWritten)
+ *pcbWritten = cbData; /** @todo For now return all as being written. */
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Transfer interface implementations *
+*********************************************************************************************************************************/
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceGetRoots(PSHCLTXPROVIDERCTX pCtx, PSHCLROOTLIST *ppRootList)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardRootListRead(pCmdCtx, ppRootList);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms,
+ PSHCLLISTHANDLE phList)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardListOpenSend(pCmdCtx, pOpenParms, phList);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardListCloseSend(pCmdCtx, hList);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx,
+ SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = ShClTransferListHdrInit(pListHdr);
+ if (RT_SUCCESS(rc))
+ {
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3ClipboardListHdrRead(pCmdCtx, hList, 0 /* fFlags */, pListHdr);
+ }
+ else
+ ShClTransferListHdrDestroy(pListHdr);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx,
+ SHCLLISTHANDLE hList, PSHCLLISTENTRY pEntry)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardListEntryRead(pCmdCtx, hList, pEntry);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx,
+ PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardObjOpenSend(pCmdCtx, pCreateParms, phObj);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ int rc = VbglR3ClipboardObjCloseSend(pCmdCtx, hObj);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx,
+ SHCLOBJHANDLE hObj, void *pvData, uint32_t cbData,
+ uint32_t fFlags, uint32_t *pcbRead)
+{
+ LogFlowFuncEnter();
+
+ PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser;
+ AssertPtr(pCmdCtx);
+
+ RT_NOREF(fFlags); /* Not used yet. */
+
+ int rc = VbglR3ClipboardObjReadSend(pCmdCtx, hObj, pvData, cbData, pcbRead);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Starts a transfer on the guest side.
+ *
+ * @returns VBox status code.
+ * @param pCmdCtx Command context to use.
+ * @param pTransferCtx Transfer context to create transfer for.
+ * @param uTransferID ID to use for transfer to start.
+ * @param enmDir Direction of transfer to start.
+ * @param enmSource Source of transfer to start.
+ * @param ppTransfer Where to return the transfer object on success. Optional.
+ */
+static int vbglR3ClipboardTransferStart(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx,
+ SHCLTRANSFERID uTransferID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource,
+ PSHCLTRANSFER *ppTransfer)
+{
+ PSHCLTRANSFER pTransfer;
+ int rc = ShClTransferCreate(&pTransfer);
+ if (RT_SUCCESS(rc))
+ {
+ ShClTransferSetCallbacks(pTransfer, &pCmdCtx->Transfers.Callbacks);
+
+ rc = ShClTransferInit(pTransfer, enmDir, enmSource);
+ if (RT_SUCCESS(rc))
+ {
+ rc = ShClTransferCtxTransferRegisterById(pTransferCtx, pTransfer, uTransferID);
+ if (RT_SUCCESS(rc))
+ {
+ /* If this is a read transfer (reading data from host), set the interface to use
+ * our VbglR3 routines here. */
+ if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE)
+ {
+ SHCLTXPROVIDERCREATIONCTX creationCtx;
+ RT_ZERO(creationCtx);
+
+ creationCtx.Interface.pfnRootsGet = vbglR3ClipboardTransferIfaceGetRoots;
+
+ creationCtx.Interface.pfnListOpen = vbglR3ClipboardTransferIfaceListOpen;
+ creationCtx.Interface.pfnListClose = vbglR3ClipboardTransferIfaceListClose;
+ creationCtx.Interface.pfnListHdrRead = vbglR3ClipboardTransferIfaceListHdrRead;
+ creationCtx.Interface.pfnListEntryRead = vbglR3ClipboardTransferIfaceListEntryRead;
+
+ creationCtx.Interface.pfnObjOpen = vbglR3ClipboardTransferIfaceObjOpen;
+ creationCtx.Interface.pfnObjClose = vbglR3ClipboardTransferIfaceObjClose;
+ creationCtx.Interface.pfnObjRead = vbglR3ClipboardTransferIfaceObjRead;
+
+ creationCtx.pvUser = pCmdCtx;
+
+ rc = ShClTransferSetProviderIface(pTransfer, &creationCtx);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = ShClTransferStart(pTransfer);
+ }
+
+ if (RT_FAILURE(rc))
+ ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ppTransfer)
+ *ppTransfer = pTransfer;
+
+ LogRel2(("Shared Clipboard: Transfer ID=%RU32 (%s %s) successfully started\n",
+ uTransferID,
+ enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "reading from" : "writing to",
+ enmSource == SHCLSOURCE_LOCAL ? "local" : "remote"));
+ }
+ else
+ LogRel(("Shared Clipboard: Unable to start transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc));
+
+ /* Send a reply in any case. */
+ int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer,
+ RT_SUCCESS(rc)
+ ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ if (RT_FAILURE(rc))
+ {
+ ShClTransferDestroy(pTransfer);
+ pTransfer = NULL;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Stops a transfer on the guest side.
+ *
+ * @returns VBox status code, or VERR_NOT_FOUND if transfer has not been found.
+ * @param pCmdCtx Command context to use.
+ * @param pTransferCtx Transfer context to stop transfer for.
+ * @param uTransferID ID of transfer to stop.
+ */
+static int vbglR3ClipboardTransferStop(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx,
+ SHCLTRANSFERID uTransferID)
+{
+ int rc;
+
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, uTransferID);
+ if (pTransfer)
+ {
+ rc = ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel2(("Shared Clipboard: Transfer ID=%RU32 successfully stopped\n", uTransferID));
+ }
+ else
+ LogRel(("Shared Clipboard: Unable to stop transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc));
+
+ /* Send a reply in any case. */
+ int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer,
+ RT_SUCCESS(rc)
+ ? SHCLTRANSFERSTATUS_STOPPED : SHCLTRANSFERSTATUS_ERROR, rc);
+ AssertRC(rc2);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sets transfer callbacks of a Shared Clipboard command context.
+ *
+ * @param pCmdCtx Command context to set callbacks for.
+ * @param pCallbacks Pointer to callback table to set.
+ */
+VBGLR3DECL(void) VbglR3ClipboardTransferSetCallbacks(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCALLBACKTABLE pCallbacks)
+{
+ AssertPtrReturnVoid(pCmdCtx);
+ AssertPtrReturnVoid(pCallbacks);
+
+ ShClTransferCopyCallbacks(&pCmdCtx->Transfers.Callbacks, pCallbacks);
+}
+
+VBGLR3DECL(int) VbglR3ClipboardEventGetNextEx(uint32_t idMsg, uint32_t cParms,
+ PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx,
+ PVBGLR3CLIPBOARDEVENT pEvent)
+{
+ AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ LogFunc(("Handling idMsg=%RU32 (%s), cParms=%RU32\n", idMsg, ShClHostMsgToStr(idMsg), cParms));
+
+ int rc;
+ if (!pCmdCtx->fUseLegacyProtocol)
+ {
+ bool fErrorSent = false; /* Whether an error has been reported back to the host already. */
+
+ switch (idMsg)
+ {
+ case VBOX_SHCL_HOST_MSG_TRANSFER_STATUS:
+ {
+ SHCLTRANSFERDIR enmDir;
+ SHCLTRANSFERREPORT transferReport;
+ rc = VbglR3ClipboarTransferStatusRecv(pCmdCtx, &enmDir, &transferReport);
+ if (RT_SUCCESS(rc))
+ {
+ const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext);
+
+ LogFlowFunc(("[Transfer %RU32] enmDir=%RU32, status=%s\n",
+ uTransferID, enmDir, ShClTransferStatusToStr(transferReport.uStatus)));
+
+ switch (transferReport.uStatus)
+ {
+ case SHCLTRANSFERSTATUS_INITIALIZED:
+ RT_FALL_THROUGH();
+ case SHCLTRANSFERSTATUS_STARTED:
+ {
+ SHCLSOURCE enmSource = SHCLSOURCE_INVALID;
+
+ /* The host announces the transfer direction from its point of view, so inverse the direction here. */
+ if (enmDir == SHCLTRANSFERDIR_TO_REMOTE)
+ {
+ enmDir = SHCLTRANSFERDIR_FROM_REMOTE;
+ enmSource = SHCLSOURCE_REMOTE;
+ }
+ else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE)
+ {
+ enmDir = SHCLTRANSFERDIR_TO_REMOTE;
+ enmSource = SHCLSOURCE_LOCAL;
+ }
+ else
+ AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER);
+
+ rc = vbglR3ClipboardTransferStart(pCmdCtx, pTransferCtx, uTransferID,
+ enmDir, enmSource, NULL /* ppTransfer */);
+ break;
+ }
+
+ case SHCLTRANSFERSTATUS_STOPPED:
+ RT_FALL_THROUGH();
+ case SHCLTRANSFERSTATUS_CANCELED:
+ RT_FALL_THROUGH();
+ case SHCLTRANSFERSTATUS_KILLED:
+ RT_FALL_THROUGH();
+ case SHCLTRANSFERSTATUS_ERROR:
+ {
+ rc = vbglR3ClipboardTransferStop(pCmdCtx, pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pEvent->u.TransferStatus.enmDir = enmDir;
+ pEvent->u.TransferStatus.Report = transferReport;
+ pEvent->u.TransferStatus.uID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext);
+
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS;
+
+ LogRel2(("Shared Clipboard: Received status %s (rc=%Rrc) for transfer ID=%RU32\n",
+ ShClTransferStatusToStr(pEvent->u.TransferStatus.Report.uStatus), pEvent->u.TransferStatus.Report.rc,
+ pEvent->u.TransferStatus.uID));
+ }
+ }
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ:
+ {
+ uint32_t fRoots;
+ rc = VbglR3ClipboardRootListHdrReadReq(pCmdCtx, &fRoots);
+
+ /** @todo Validate / handle fRoots. */
+
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ SHCLROOTLISTHDR rootListHdr;
+ RT_ZERO(rootListHdr);
+
+ rootListHdr.cRoots = ShClTransferRootsCount(pTransfer);
+
+ LogFlowFunc(("cRoots=%RU32\n", rootListHdr.cRoots));
+
+ rc = VbglR3ClipboardRootListHdrReadReply(pCmdCtx, &rootListHdr);
+ }
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ:
+ {
+ uint32_t uIndex;
+ uint32_t fInfo;
+ rc = VbglR3ClipboardRootListEntryReadReq(pCmdCtx, &uIndex, &fInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ SHCLROOTLISTENTRY rootListEntry;
+ rc = ShClTransferRootsEntry(pTransfer, uIndex, &rootListEntry);
+ if (RT_SUCCESS(rc))
+ rc = VbglR3ClipboardRootListEntryReadReply(pCmdCtx, uIndex, &rootListEntry);
+ }
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN:
+ {
+ SHCLLISTOPENPARMS openParmsList;
+ rc = ShClTransferListOpenParmsInit(&openParmsList);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3ClipboardListOpenRecv(pCmdCtx, &openParmsList);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ LogFlowFunc(("pszPath=%s\n", openParmsList.pszPath));
+
+ SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID;
+ rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList);
+
+ /* Reply in any case. */
+ int rc2 = VbglR3ClipboardListOpenReply(pCmdCtx, rc, hList);
+ AssertRC(rc2);
+ }
+
+ ShClTransferListOpenParmsDestroy(&openParmsList);
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE:
+ {
+ SHCLLISTHANDLE hList;
+ rc = VbglR3ClipboardListCloseRecv(pCmdCtx, &hList);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ rc = ShClTransferListClose(pTransfer, hList);
+
+ /* Reply in any case. */
+ int rc2 = VbglR3ClipboardListCloseReply(pCmdCtx, rc, hList);
+ AssertRC(rc2);
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ:
+ {
+ /** @todo Handle filter + list features. */
+
+ SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID;
+ uint32_t fFlags = 0;
+ rc = VbglR3ClipboardListHdrReadRecvReq(pCmdCtx, &hList, &fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ SHCLLISTHDR hdrList;
+ rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3ClipboardListHdrWrite(pCmdCtx, hList, &hdrList);
+
+ ShClTransferListHdrDestroy(&hdrList);
+ }
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ:
+ {
+ LogFlowFunc(("VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ\n"));
+
+ SHCLLISTENTRY entryList;
+ rc = ShClTransferListEntryInit(&entryList);
+ if (RT_SUCCESS(rc))
+ {
+ SHCLLISTHANDLE hList;
+ uint32_t fInfo;
+ rc = VbglR3ClipboardListEntryReadRecvReq(pCmdCtx, &hList, &fInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ rc = ShClTransferListRead(pTransfer, hList, &entryList);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLFSOBJINFO pObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo;
+ Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO));
+
+ RT_NOREF(pObjInfo);
+
+ LogFlowFunc(("\t%s (%RU64 bytes)\n", entryList.pszName, pObjInfo->cbObject));
+
+ rc = VbglR3ClipboardListEntryWrite(pCmdCtx, hList, &entryList);
+ }
+ }
+
+ ShClTransferListEntryDestroy(&entryList);
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN:
+ {
+ SHCLOBJOPENCREATEPARMS openParms;
+ rc = ShClTransferObjOpenParmsInit(&openParms);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3ClipboardObjOpenRecv(pCmdCtx, &openParms);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ SHCLOBJHANDLE hObj;
+ rc = ShClTransferObjOpen(pTransfer, &openParms, &hObj);
+
+ /* Reply in any case. */
+ int rc2 = VbglR3ClipboardObjOpenReply(pCmdCtx, rc, hObj);
+ AssertRC(rc2);
+ }
+
+ ShClTransferObjOpenParmsDestroy(&openParms);
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE:
+ {
+ SHCLOBJHANDLE hObj;
+ rc = VbglR3ClipboardObjCloseRecv(pCmdCtx, &hObj);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ rc = ShClTransferObjClose(pTransfer, hObj);
+
+ /* Reply in any case. */
+ int rc2 = VbglR3ClipboardObjCloseReply(pCmdCtx, rc, hObj);
+ AssertRC(rc2);
+ }
+
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ:
+ {
+ SHCLOBJHANDLE hObj;
+ uint32_t cbBuf;
+ uint32_t fFlags;
+ rc = VbglR3ClipboardObjReadRecv(pCmdCtx, &hObj, &cbBuf, &fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx,
+ VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext));
+ AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND);
+
+ AssertBreakStmt(pCmdCtx->Transfers.cbChunkSize, rc = VERR_INVALID_PARAMETER);
+
+ const uint32_t cbToRead = RT_MIN(cbBuf, pCmdCtx->Transfers.cbChunkSize);
+
+ LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, fFlags=0x%x -> cbChunkSize=%RU32, cbToRead=%RU32\n",
+ hObj, cbBuf, fFlags, pCmdCtx->Transfers.cbChunkSize, cbToRead));
+
+ void *pvBuf = RTMemAlloc(cbToRead);
+ if (pvBuf)
+ {
+ uint32_t cbRead;
+ rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, fFlags, &cbRead);
+ if (RT_SUCCESS(rc))
+ rc = VbglR3ClipboardObjWriteSend(pCmdCtx, hObj, pvBuf, cbRead, NULL /* pcbWritten */);
+
+ RTMemFree(pvBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ rc = VbglR3ClipboardEventGetNext(idMsg, cParms, pCmdCtx, pEvent);
+ if (RT_FAILURE(rc))
+ fErrorSent = true;
+ break;
+ }
+ }
+
+ if ( !fErrorSent
+ && RT_FAILURE(rc))
+ {
+ /* Report error back to the host. */
+ int rc2 = VbglR3ClipboardWriteError(pCmdCtx->idClient, rc);
+ AssertRC(rc2);
+ }
+ }
+ else
+ {
+ /*
+ * This builds on what we did in VbglR3ClipboardMsgPeekWait, so
+ * !HACK ALERT! cParms is the format flag or flags.
+ */
+ rc = VINF_SUCCESS;
+ switch (idMsg)
+ {
+ case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS;
+ pEvent->u.fReportedFormats = cParms;
+ break;
+
+ case VBOX_SHCL_HOST_MSG_READ_DATA:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA;
+ pEvent->u.fReadData = cParms;
+ break;
+
+ case VBOX_SHCL_HOST_MSG_QUIT:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT;
+ break;
+
+ default:
+ AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+
+VBGLR3DECL(int) VbglR3ClipboardEventGetNext(uint32_t idMsg, uint32_t cParms, PVBGLR3SHCLCMDCTX pCtx, PVBGLR3CLIPBOARDEVENT pEvent)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ RT_NOREF(cParms);
+
+ int rc;
+ if (!pCtx->fUseLegacyProtocol)
+ {
+ LogFunc(("Handling idMsg=%RU32 (%s)\n", idMsg, ShClHostMsgToStr(idMsg)));
+ switch (idMsg)
+ {
+ case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
+ {
+ rc = vbglR3ClipboardFormatsReportRecv(pCtx, &pEvent->u.fReportedFormats);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS;
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_READ_DATA_CID:
+ {
+ rc = vbglR3ClipboardFetchReadDataCid(pCtx, &pEvent->u.fReadData);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA;
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_READ_DATA:
+ {
+ rc = vbglR3ClipboardFetchReadData(pCtx, &pEvent->u.fReadData);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA;
+ break;
+ }
+
+ case VBOX_SHCL_HOST_MSG_QUIT:
+ {
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ /** @todo r=bird: BUGBUG - need a skip command here! */
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Copy over our command context to the event. */
+ pEvent->cmdCtx = *pCtx;
+ }
+ else
+ {
+ /* Report error back to the host. */
+ int rc2 = VbglR3ClipboardWriteError(pCtx->idClient, rc);
+ AssertRC(rc2);
+ }
+ }
+ else
+ {
+ /*
+ * This builds on what we did in VbglR3ClipboardMsgPeekWait, so
+ * !HACK ALERT! cParms is the format flag or flags.
+ */
+ rc = VINF_SUCCESS;
+ switch (idMsg)
+ {
+ case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS;
+ pEvent->u.fReportedFormats = cParms;
+ break;
+
+ case VBOX_SHCL_HOST_MSG_READ_DATA:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA;
+ pEvent->u.fReadData = cParms;
+ break;
+
+ case VBOX_SHCL_HOST_MSG_QUIT:
+ pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT;
+ break;
+
+ default:
+ AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ pEvent->cmdCtx = *pCtx;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Frees (destroys) a formerly allocated Shared Clipboard event.
+ *
+ * @returns IPRT status code.
+ * @param pEvent Event to free (destroy).
+ */
+VBGLR3DECL(void) VbglR3ClipboardEventFree(PVBGLR3CLIPBOARDEVENT pEvent)
+{
+ if (!pEvent)
+ return;
+
+ /* Some messages require additional cleanup. */
+ switch (pEvent->enmType)
+ {
+ default:
+ break;
+ }
+
+ RTMemFree(pEvent);
+ pEvent = NULL;
+}
+
+/**
+ * Reports (advertises) guest clipboard formats to the host.
+ *
+ * Legacy function, do not use anymore.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param fFormats The formats to report.
+ */
+VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats)
+{
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ VBoxShClParmReportFormats Parms;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FORMATS, VBOX_SHCL_CPARMS_REPORT_FORMATS);
+ VbglHGCMParmUInt32Set(&Msg.Parms.f32Formats, fFormats);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends guest clipboard data to the host.
+ *
+ * Legacy function kept for compatibility, do not use anymore.
+ *
+ * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message
+ * from the host.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param fFormat The format of the data.
+ * @param pvData Pointer to the data to send. Can be NULL if @a cbData
+ * is zero.
+ * @param cbData Number of bytes of data to send. Zero is valid.
+ */
+VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb)
+{
+ LogFlowFuncEnter();
+
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ VBoxShClParmDataWriteOld Parms;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE_OLD);
+ VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat);
+ VbglHGCMParmPtrSet(&Msg.Parms.pData, pv, cb);
+
+ int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sends guest clipboard data to the host.
+ *
+ * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message
+ * from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx The command context returned by VbglR3ClipboardConnectEx().
+ * @param fFormat Clipboard format to send.
+ * @param pvData Pointer to the data to send. Can be NULL if @a cbData
+ * is zero.
+ * @param cbData Number of bytes of data to send. Zero is valid.
+ */
+VBGLR3DECL(int) VbglR3ClipboardWriteDataEx(PVBGLR3SHCLCMDCTX pCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData)
+{
+ LogFlowFunc(("ENTER: fFormat=%#x pvData=%p cbData=%#x\n", fFormat, pvData, cbData));
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ if (cbData > 0)
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+
+ int rc;
+ if (pCtx->fUseLegacyProtocol)
+ rc = VbglR3ClipboardWriteData(pCtx->idClient, fFormat, pvData, cbData);
+ else
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ VBoxShClParmDataWrite Parms;
+ } Msg;
+
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE);
+ Msg.Parms.id64Context.SetUInt64(pCtx->idContext);
+ Msg.Parms.f32Format.SetUInt32(fFormat);
+ Msg.Parms.pData.SetPtr(pvData, cbData);
+
+ LogFlowFunc(("CID=%RU32\n", pCtx->idContext));
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Writes an error to the host.
+ *
+ * @returns IPRT status code.
+ * @param idClient The client id returned by VbglR3ClipboardConnect().
+ * @param rcErr Error (IPRT-style) to send.
+ */
+VBGLR3DECL(int) VbglR3ClipboardWriteError(HGCMCLIENTID idClient, int rcErr)
+{
+ AssertReturn(idClient, VERR_INVALID_PARAMETER);
+
+ VBoxShClWriteErrorMsg Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_ERROR, VBOX_SHCL_CPARMS_ERROR);
+
+ /** @todo Context ID not used yet. */
+ Msg.uContext.SetUInt64(0);
+ Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ if (RT_FAILURE(rc))
+ LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
+ if (rc == VERR_NOT_SUPPORTED)
+ rc = VINF_SUCCESS;
+
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Reporting error %Rrc to the host failed with %Rrc\n", rcErr, rc));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp
new file mode 100644
index 00000000..35971006
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCoreDump.cpp
@@ -0,0 +1,56 @@
+/* $Id: VBoxGuestR3LibCoreDump.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Core Dumps.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Write guest core dump.
+ *
+ * @returns IPRT status code.
+ */
+VBGLR3DECL(int) VbglR3WriteCoreDump(void)
+{
+ VBGLIOCWRITECOREDUMP Req;
+ VBGLREQHDR_INIT(&Req.Hdr, WRITE_CORE_DUMP);
+ Req.u.In.fFlags = 0;
+ return vbglR3DoIOCtl(VBGL_IOCTL_WRITE_CORE_DUMP, &Req.Hdr, sizeof(Req));
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp
new file mode 100644
index 00000000..00aa0257
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCpuHotPlug.cpp
@@ -0,0 +1,133 @@
+/* $Id: VBoxGuestR3LibCpuHotPlug.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, CPU Hot Plugging.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+/**
+ * Initialize CPU hot plugging.
+ *
+ * This will enable the CPU hot plugging events.
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3CpuHotPlugInit(void)
+{
+ int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_CPU_HOTPLUG, 0);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ VMMDevCpuHotPlugStatusRequest Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus);
+ Req.enmStatusType = VMMDevCpuStatusType_Enable;
+ rc = vbglR3GRPerform(&Req.header);
+ if (RT_FAILURE(rc))
+ VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG);
+
+ return rc;
+}
+
+
+/**
+ * Terminate CPU hot plugging.
+ *
+ * This will disable the CPU hot plugging events.
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3CpuHotPlugTerm(void)
+{
+ /* Clear the events. */
+ VbglR3CtlFilterMask(0, VMMDEV_EVENT_CPU_HOTPLUG);
+
+ VMMDevCpuHotPlugStatusRequest Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_SetCpuHotPlugStatus);
+ Req.enmStatusType = VMMDevCpuStatusType_Disable;
+ return vbglR3GRPerform(&Req.header);
+}
+
+
+/**
+ * Waits for a CPU hot plugging event and retrieve the data associated with it.
+ *
+ * @returns VBox status code.
+ * @param penmEventType Where to store the event type on success.
+ * @param pidCpuCore Where to store the CPU core ID on success.
+ * @param pidCpuPackage Where to store the CPU package ID on success.
+ */
+VBGLR3DECL(int) VbglR3CpuHotPlugWaitForEvent(VMMDevCpuEventType *penmEventType, uint32_t *pidCpuCore, uint32_t *pidCpuPackage)
+{
+ AssertPtrReturn(penmEventType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pidCpuCore, VERR_INVALID_POINTER);
+ AssertPtrReturn(pidCpuPackage, VERR_INVALID_POINTER);
+
+ uint32_t fEvents = 0;
+ int rc = VbglR3WaitEvent(VMMDEV_EVENT_CPU_HOTPLUG, RT_INDEFINITE_WAIT, &fEvents);
+ if (RT_SUCCESS(rc))
+ {
+ /* did we get the right event? */
+ if (fEvents & VMMDEV_EVENT_CPU_HOTPLUG)
+ {
+ VMMDevGetCpuHotPlugRequest Req;
+
+ /* get the CPU hot plugging request */
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetCpuHotPlugRequest);
+ Req.idCpuCore = UINT32_MAX;
+ Req.idCpuPackage = UINT32_MAX;
+ Req.enmEventType = VMMDevCpuEventType_None;
+ rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ *penmEventType = Req.enmEventType;
+ *pidCpuCore = Req.idCpuCore;
+ *pidCpuPackage = Req.idCpuPackage;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ rc = VERR_TRY_AGAIN;
+ }
+ else if (rc == VERR_TIMEOUT) /* just in case */
+ rc = VERR_TRY_AGAIN;
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp
new file mode 100644
index 00000000..b4d795f7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibCredentials.cpp
@@ -0,0 +1,222 @@
+/* $Id: VBoxGuestR3LibCredentials.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, user credentials.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <VBox/log.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Checks whether user credentials are available to the guest or not.
+ *
+ * @returns IPRT status value; VINF_SUCCESS if credentials are available,
+ * VERR_NOT_FOUND if not. Otherwise an error is occurred.
+ */
+VBGLR3DECL(int) VbglR3CredentialsQueryAvailability(void)
+{
+ VMMDevCredentials Req;
+ RT_ZERO(Req);
+ vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials);
+ Req.u32Flags |= VMMDEV_CREDENTIALS_QUERYPRESENCE;
+
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ if ((Req.u32Flags & VMMDEV_CREDENTIALS_PRESENT) == 0)
+ rc = VERR_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/**
+ * Retrieves and clears the user credentials for logging into the guest OS.
+ *
+ * @returns IPRT status value
+ * @param ppszUser Receives pointer of allocated user name string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroy().
+ * @param ppszPassword Receives pointer of allocated user password string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroy().
+ * @param ppszDomain Receives pointer of allocated domain name string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroy().
+ */
+VBGLR3DECL(int) VbglR3CredentialsRetrieve(char **ppszUser, char **ppszPassword, char **ppszDomain)
+{
+ AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppszPassword, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppszDomain, VERR_INVALID_POINTER);
+
+ VMMDevCredentials Req;
+ RT_ZERO(Req);
+ vmmdevInitRequest((VMMDevRequestHeader*)&Req, VMMDevReq_QueryCredentials);
+ Req.u32Flags |= VMMDEV_CREDENTIALS_READ | VMMDEV_CREDENTIALS_CLEAR;
+
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrDupEx(ppszUser, Req.szUserName);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrDupEx(ppszPassword, Req.szPassword);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrDupEx(ppszDomain, Req.szDomain);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTStrFree(*ppszPassword);
+ }
+ RTStrFree(*ppszUser);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Retrieves and clears the user credentials for logging into the guest OS.
+ * UTF-16 version.
+ *
+ * @returns IPRT status value
+ * @param ppwszUser Receives pointer of allocated user name string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16().
+ * @param ppwszPassword Receives pointer of allocated user password string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16().
+ * @param ppwszDomain Receives pointer of allocated domain name string.
+ * The returned pointer must be freed using VbglR3CredentialsDestroyUtf16().
+ */
+VBGLR3DECL(int) VbglR3CredentialsRetrieveUtf16(PRTUTF16 *ppwszUser, PRTUTF16 *ppwszPassword, PRTUTF16 *ppwszDomain)
+{
+ AssertPtrReturn(ppwszUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppwszPassword, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppwszDomain, VERR_INVALID_POINTER);
+
+ char *pszUser, *pszPassword, *pszDomain;
+ int rc = VbglR3CredentialsRetrieve(&pszUser, &pszPassword, &pszDomain);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszUser = NULL;
+ PRTUTF16 pwszPassword = NULL;
+ PRTUTF16 pwszDomain = NULL;
+
+ rc = RTStrToUtf16(pszUser, &pwszUser);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrToUtf16(pszPassword, &pwszPassword);
+ if (RT_SUCCESS(rc))
+ rc = RTStrToUtf16(pszDomain, &pwszDomain);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppwszUser = pwszUser;
+ *ppwszPassword = pwszPassword;
+ *ppwszDomain = pwszDomain;
+ }
+ else
+ VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* Passes */);
+ VbglR3CredentialsDestroy(pszUser, pszPassword, pszDomain, 3 /* Passes */);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Clears and frees the three strings.
+ *
+ * @param pszUser Receives pointer of the user name string to destroy.
+ * Optional.
+ * @param pszPassword Receives pointer of the password string to destroy.
+ * Optional.
+ * @param pszDomain Receives pointer of allocated domain name string.
+ * Optional.
+ * @param cPasses Number of wipe passes. The more the better + slower.
+ */
+VBGLR3DECL(void) VbglR3CredentialsDestroy(char *pszUser, char *pszPassword, char *pszDomain, uint32_t cPasses)
+{
+ /* wipe first */
+ if (pszUser)
+ RTMemWipeThoroughly(pszUser, strlen(pszUser) + 1, cPasses);
+ if (pszPassword)
+ RTMemWipeThoroughly(pszPassword, strlen(pszPassword) + 1, cPasses);
+ if (pszDomain)
+ RTMemWipeThoroughly(pszDomain, strlen(pszDomain) + 1, cPasses);
+
+ /* then free. */
+ RTStrFree(pszUser);
+ RTStrFree(pszPassword);
+ RTStrFree(pszDomain);
+}
+
+
+/**
+ * Clears and frees the three strings. UTF-16 version.
+ *
+ * @param pwszUser Receives pointer of the user name string to destroy.
+ * Optional.
+ * @param pwszPassword Receives pointer of the password string to destroy.
+ * Optional.
+ * @param pwszDomain Receives pointer of allocated domain name string.
+ * Optional.
+ * @param cPasses Number of wipe passes. The more the better + slower.
+ */
+VBGLR3DECL(void) VbglR3CredentialsDestroyUtf16(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
+ uint32_t cPasses)
+{
+ /* wipe first */
+ if (pwszUser)
+ RTMemWipeThoroughly(pwszUser, (RTUtf16Len(pwszUser) + 1) * sizeof(RTUTF16), cPasses);
+ if (pwszPassword)
+ RTMemWipeThoroughly(pwszPassword, (RTUtf16Len(pwszPassword) + 1) * sizeof(RTUTF16), cPasses);
+ if (pwszDomain)
+ RTMemWipeThoroughly(pwszDomain, (RTUtf16Len(pwszDomain) + 1) * sizeof(RTUTF16), cPasses);
+
+ /* then free. */
+ RTUtf16Free(pwszUser);
+ RTUtf16Free(pwszPassword);
+ RTUtf16Free(pwszDomain);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp
new file mode 100644
index 00000000..2aad8c5c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDaemonize.cpp
@@ -0,0 +1,326 @@
+/** $Id: VBoxGuestR3LibDaemonize.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, daemonize a process.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if defined(RT_OS_OS2)
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+
+# include <iprt/alloca.h>
+# include <iprt/string.h>
+
+#elif defined(RT_OS_WINDOWS)
+# error "PORTME"
+
+#else /* the unices */
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/wait.h>
+# include <stdio.h>
+# include <fcntl.h>
+# include <stdlib.h>
+# include <unistd.h>
+# include <signal.h>
+# include <errno.h>
+#endif
+
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Daemonize the process for running in the background.
+ *
+ * This is supposed to do the same job as the BSD daemon() call.
+ *
+ * @returns 0 on success
+ *
+ * @param fNoChDir Pass false to change working directory to root.
+ * @param fNoClose Pass false to redirect standard file streams to /dev/null.
+ * @param fRespawn Restart the daemonised process after five seconds if it
+ * terminates abnormally.
+ * @param pcRespawn Where to store a count of how often we have respawned,
+ * intended for avoiding error spamming. Optional.
+ * @param fReturnOnUpdate If True, this function will return control to caller when
+ * child process will terminate with exit code of VBGLR3EXITCODERELOAD,
+ * indicating that Guest Additions update has been started and this running
+ * process will be asked to be restarted by arrival of the next SIGUSR1
+ * signal (caller should wait for SIGUSR1). If False, this functions will
+ * never return, but rather exit() when child process terminates with
+ * exit code 0.
+ * @param pfUpdateStarted A flag which passed to caller if fReturnOnUpdate is True (can be NULL).
+ * @param szPidfile Optional path to parent process' pidfile (can be NULL).
+ * @param phPidfile Optional path to parent process' pidfile handle (can not be NULL if
+ * szPidfile was specified).
+ *
+ * @todo Use RTProcDaemonize instead of this.
+ * @todo Implement fRespawn on OS/2.
+ * @todo Make the respawn interval configurable. But not until someone
+ * actually needs that.
+ */
+VBGLR3DECL(int) VbglR3DaemonizeEx(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn,
+ bool fReturnOnUpdate, bool *pfUpdateStarted, const char *szPidfile,
+ RTFILE *phPidfile)
+{
+#if defined(RT_OS_OS2)
+ PPIB pPib;
+ PTIB pTib;
+ DosGetInfoBlocks(&pTib, &pPib);
+
+ RT_NOREF(fReturnOnUpdate);
+ RT_NOREF(pfUpdateStarted);
+ RT_NOREF(szPidfile);
+ RT_NOREF(phPidfile);
+
+ AssertRelease(!fRespawn);
+ /* Get the full path to the executable. */
+ char szExe[CCHMAXPATH];
+ APIRET rc = DosQueryModuleName(pPib->pib_hmte, sizeof(szExe), szExe);
+ if (rc)
+ return RTErrConvertFromOS2(rc);
+
+ /* calc the length of the command line. */
+ char *pch = pPib->pib_pchcmd;
+ size_t cch0 = strlen(pch);
+ pch += cch0 + 1;
+ size_t cch1 = strlen(pch);
+ pch += cch1 + 1;
+ char *pchArgs;
+ if (cch1 && *pch)
+ {
+ do pch = strchr(pch, '\0') + 1;
+ while (*pch);
+
+ size_t cchTotal = pch - pPib->pib_pchcmd;
+ pchArgs = (char *)alloca(cchTotal + sizeof("--daemonized\0\0"));
+ memcpy(pchArgs, pPib->pib_pchcmd, cchTotal - 1);
+ memcpy(pchArgs + cchTotal - 1, "--daemonized\0\0", sizeof("--daemonized\0\0"));
+ }
+ else
+ {
+ size_t cchTotal = pch - pPib->pib_pchcmd + 1;
+ pchArgs = (char *)alloca(cchTotal + sizeof(" --daemonized "));
+ memcpy(pchArgs, pPib->pib_pchcmd, cch0 + 1);
+ pch = pchArgs + cch0 + 1;
+ memcpy(pch, " --daemonized ", sizeof(" --daemonized ") - 1);
+ pch += sizeof(" --daemonized ") - 1;
+ if (cch1)
+ memcpy(pch, pPib->pib_pchcmd + cch0 + 1, cch1 + 2);
+ else
+ pch[0] = pch[1] = '\0';
+ }
+
+ /* spawn a detach process */
+ char szObj[128];
+ RESULTCODES ResCodes = { 0, 0 };
+ szObj[0] = '\0';
+ rc = DosExecPgm(szObj, sizeof(szObj), EXEC_BACKGROUND, (PCSZ)pchArgs, NULL, &ResCodes, (PCSZ)szExe);
+ if (rc)
+ {
+ /** @todo Change this to some standard log/print error?? */
+ /* VBoxServiceError("DosExecPgm failed with rc=%d and szObj='%s'\n", rc, szObj); */
+ return RTErrConvertFromOS2(rc);
+ }
+ DosExit(EXIT_PROCESS, 0);
+ return VERR_GENERAL_FAILURE;
+
+#elif defined(RT_OS_WINDOWS)
+# error "PORTME"
+
+#else /* the unices */
+ /*
+ * Fork the child process in a new session and quit the parent.
+ *
+ * - fork once and create a new session (setsid). This will detach us
+ * from the controlling tty meaning that we won't receive the SIGHUP
+ * (or any other signal) sent to that session.
+ * - The SIGHUP signal is ignored because the session/parent may throw
+ * us one before we get to the setsid.
+ * - When the parent exit(0) we will become an orphan and re-parented to
+ * the init process.
+ * - Because of the Linux / System V semantics of assigning the controlling
+ * tty automagically when a session leader first opens a tty, we will
+ * fork() once more on Linux to get rid of the session leadership role.
+ */
+
+ struct sigaction OldSigAct;
+ struct sigaction SigAct;
+ RT_ZERO(SigAct);
+ SigAct.sa_handler = SIG_IGN;
+ int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
+
+ pid_t pid = fork();
+ if (pid == -1)
+ return RTErrConvertFromErrno(errno);
+ if (pid != 0)
+ exit(0);
+
+ /*
+ * The orphaned child becomes is reparented to the init process.
+ * We create a new session for it (setsid), point the standard
+ * file descriptors to /dev/null, and change to the root directory.
+ */
+ pid_t newpgid = setsid();
+ int SavedErrno = errno;
+ if (rcSigAct != -1)
+ sigaction(SIGHUP, &OldSigAct, NULL);
+ if (newpgid == -1)
+ return RTErrConvertFromErrno(SavedErrno);
+
+ if (!fNoClose)
+ {
+ /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
+ int fd = open("/dev/null", O_RDWR);
+ if (fd == -1) /* paranoia */
+ {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ fd = open("/dev/null", O_RDWR);
+ }
+ if (fd != -1)
+ {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ close(fd);
+ }
+ }
+
+ if (!fNoChDir)
+ {
+ int rcShutUpGcc = chdir("/");
+ RT_NOREF_PV(rcShutUpGcc);
+ }
+
+ /*
+ * Change the umask - this is non-standard daemon() behavior.
+ */
+ umask(027);
+
+# ifdef RT_OS_LINUX
+ /*
+ * And fork again to lose session leader status (non-standard daemon()
+ * behaviour).
+ */
+ pid = fork();
+ if (pid == -1)
+ return RTErrConvertFromErrno(errno);
+ if (pid != 0)
+ exit(0);
+# endif /* RT_OS_LINUX */
+
+ /* Check if another instance is already running. */
+ if (szPidfile != NULL)
+ {
+ if (phPidfile != NULL)
+ {
+ int rc = VbglR3PidfileWait(szPidfile, phPidfile, 5000);
+
+ /* Another instance of process is already running. */
+ if (rc == VERR_FILE_LOCK_VIOLATION)
+ {
+ LogRel(("cannot aquire pidfile %s, exitting\n", szPidfile));
+ exit(1);
+ }
+
+ /* Unable to lock on pidfile. */
+ if (RT_FAILURE(rc))
+ exit(1);
+ }
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (fRespawn)
+ {
+ /* We implement re-spawning as a third fork(), with the parent process
+ * monitoring the child and re-starting it after a delay if it exits
+ * abnormally. */
+ unsigned cRespawn = 0;
+ for (;;)
+ {
+ int iStatus, rcWait;
+
+ if (pcRespawn != NULL)
+ *pcRespawn = cRespawn;
+ pid = fork();
+ if (pid == -1)
+ return RTErrConvertFromErrno(errno);
+ if (pid == 0)
+ return VINF_SUCCESS;
+ do
+ rcWait = waitpid(pid, &iStatus, 0);
+ while (rcWait == -1 && errno == EINTR);
+ if (rcWait == -1)
+ exit(1);
+ if (WIFEXITED(iStatus))
+ {
+ if (WEXITSTATUS(iStatus) == 0)
+ exit(0);
+ else if (fReturnOnUpdate && WEXITSTATUS(iStatus) == VBGLR3EXITCODERELOAD)
+ {
+ /* Tell caller that update has been started. */
+ if (pfUpdateStarted != NULL)
+ *pfUpdateStarted = true;
+
+ return VINF_SUCCESS;
+ }
+ }
+ sleep(5);
+ ++cRespawn;
+ }
+ }
+ return VINF_SUCCESS;
+#endif
+}
+
+/**
+ * A wrapper function for VbglR3DaemonizeEx.
+ */
+VBGLR3DECL(int) VbglR3Daemonize(bool fNoChDir, bool fNoClose, bool fRespawn, unsigned *pcRespawn)
+{
+ return VbglR3DaemonizeEx(fNoChDir, fNoClose, fRespawn, pcRespawn, false, NULL, NULL, NULL);
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp
new file mode 100644
index 00000000..68ba038b
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp
@@ -0,0 +1,1948 @@
+/* $Id: VBoxGuestR3LibDragAndDrop.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/path.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/uri.h>
+#include <iprt/thread.h>
+
+#include <iprt/cpp/list.h>
+#include <iprt/cpp/ministring.h>
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/DragAndDrop.h>
+#include <VBox/HostServices/DragAndDropSvc.h>
+
+using namespace DragAndDropSvc;
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Private internal functions *
+*********************************************************************************************************************************/
+
+/**
+ * Receives the next upcoming message for a given DnD context.
+ *
+ * @returns IPRT status code.
+ * Will return VERR_CANCELLED (implemented by the host service) if we need to bail out.
+ * @param pCtx DnD context to use.
+ * @param puMsg Where to store the message type.
+ * @param pcParms Where to store the number of parameters required for receiving the message.
+ * @param fWait Whether to wait (block) for a new message to arrive or not.
+ */
+static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
+
+ int rc;
+
+ do
+ {
+ HGCMMsgGetNext Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GET_NEXT_HOST_MSG, 3);
+ Msg.uMsg.SetUInt32(0);
+ Msg.cParms.SetUInt32(0);
+ Msg.fBlock.SetUInt32(fWait ? 1 : 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
+ rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
+ }
+
+ LogRel(("DnD: Received message %s (%#x) from host\n", DnDHostMsgToStr(*puMsg), *puMsg));
+
+ } while (rc == VERR_INTERRUPTED);
+
+ return rc;
+}
+
+
+/**
+ * Sends a DnD error back to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param rcErr Error (IPRT-style) to send.
+ */
+VBGLR3DECL(int) VbglR3DnDSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgGHError Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_EVT_ERROR, 2);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ /*
+ * Never return an error if the host did not accept the error at the current
+ * time. This can be due to the host not having any appropriate callbacks
+ * set which would handle that error.
+ *
+ * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
+ * doesn't an appropriate callback. The code used to ignore ALL errors
+ * the host would return, also relevant ones.
+ */
+ if (RT_FAILURE(rc))
+ LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
+ if (rc == VERR_NOT_SUPPORTED)
+ rc = VINF_SUCCESS;
+
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive a so-called "action message" from the host.
+ * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages".
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param uMsg Which kind of message to receive.
+ * @param puScreenID Where to store the host screen ID the message is bound to. Optional.
+ * @param puX Where to store the absolute X coordinates. Optional.
+ * @param puY Where to store the absolute Y coordinates. Optional.
+ * @param puDefAction Where to store the default action to perform. Optional.
+ * @param puAllActions Where to store the available actions. Optional.
+ * @param ppszFormats Where to store List of formats. Optional.
+ * @param pcbFormats Size (in bytes) of where to store the list of formats. Optional.
+ *
+ * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1.
+ * This was part of the initial protocol and needs to go.
+ */
+static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
+ uint32_t uMsg,
+ uint32_t *puScreenID,
+ uint32_t *puX,
+ uint32_t *puY,
+ uint32_t *puDefAction,
+ uint32_t *puAllActions,
+ char **ppszFormats,
+ uint32_t *pcbFormats)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ /* The rest is optional. */
+
+ const uint32_t cbFormatsTmp = pCtx->cbMaxChunkSize;
+
+ char *pszFormatsTmp = static_cast<char *>(RTMemAlloc(cbFormatsTmp));
+ if (!pszFormatsTmp)
+ return VERR_NO_MEMORY;
+
+ HGCMMsgHGAction Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 8);
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uScreenId.SetUInt32(0);
+ Msg.u.v3.uX.SetUInt32(0);
+ Msg.u.v3.uY.SetUInt32(0);
+ Msg.u.v3.uDefAction.SetUInt32(0);
+ Msg.u.v3.uAllActions.SetUInt32(0);
+ Msg.u.v3.pvFormats.SetPtr(pszFormatsTmp, cbFormatsTmp);
+ Msg.u.v3.cbFormats.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Context ID not used yet. */
+ if (RT_SUCCESS(rc) && puScreenID)
+ rc = Msg.u.v3.uScreenId.GetUInt32(puScreenID);
+ if (RT_SUCCESS(rc) && puX)
+ rc = Msg.u.v3.uX.GetUInt32(puX);
+ if (RT_SUCCESS(rc) && puY)
+ rc = Msg.u.v3.uY.GetUInt32(puY);
+ if (RT_SUCCESS(rc) && puDefAction)
+ rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction);
+ if (RT_SUCCESS(rc) && puAllActions)
+ rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions);
+ if (RT_SUCCESS(rc) && pcbFormats)
+ rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormats);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszFormats)
+ {
+ *ppszFormats = RTStrDup(pszFormatsTmp);
+ if (!*ppszFormats)
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ RTStrFree(pszFormatsTmp);
+
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive a HOST_DND_FN_HG_EVT_LEAVE message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ */
+static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgHGLeave Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_EVT_LEAVE, 1);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive a HOST_DND_FN_HG_EVT_CANCEL message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ */
+static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgHGCancel Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_CANCEL, 1);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive a HOST_DND_FN_HG_SND_DIR message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pszDirname Where to store the directory name of the directory being created.
+ * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created.
+ * @param pcbDirnameRecv Size (in bytes) of the actual directory name received.
+ * @param pfMode Where to store the directory creation mode.
+ */
+static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
+ char *pszDirname,
+ uint32_t cbDirname,
+ uint32_t *pcbDirnameRecv,
+ uint32_t *pfMode)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
+ AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
+
+ HGCMMsgHGSendDir Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DIR, 4);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
+ Msg.u.v3.cbName.SetUInt32(cbDirname);
+ Msg.u.v3.fMode.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Context ID not used yet. */
+ rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
+ rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
+
+ AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
+ }
+
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive a HOST_DND_FN_HG_SND_FILE_DATA message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pvData Where to store the file data chunk.
+ * @param cbData Size (in bytes) of where to store the data chunk.
+ * @param pcbDataRecv Size (in bytes) of the actual data chunk size received.
+ */
+static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pcbDataRecv)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
+
+ HGCMMsgHGSendFileData Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_DATA, 5);
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvData.SetPtr(pvData, cbData);
+ Msg.u.v3.cbData.SetUInt32(0);
+ Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
+ Msg.u.v3.cbChecksum.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Context ID not used yet. */
+ rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
+ AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
+ /** @todo Add checksum support. */
+ }
+
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive the HOST_DND_FN_HG_SND_FILE_HDR message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pszFilename Where to store the file name of the file being transferred.
+ * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred.
+ * @param puFlags File transfer flags. Currently not being used.
+ * @param pfMode Where to store the file creation mode.
+ * @param pcbTotal Where to store the file size (in bytes).
+ */
+static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
+ char *pszFilename,
+ uint32_t cbFilename,
+ uint32_t *puFlags,
+ uint32_t *pfMode,
+ uint64_t *pcbTotal)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
+ AssertReturn(pcbTotal, VERR_INVALID_POINTER);
+
+ HGCMMsgHGSendFileHdr Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_HDR, 6);
+ Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
+ Msg.pvName.SetPtr(pszFilename, cbFilename);
+ Msg.cbName.SetUInt32(cbFilename);
+ Msg.uFlags.SetUInt32(0);
+ Msg.fMode.SetUInt32(0);
+ Msg.cbTotal.SetUInt64(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Get context ID. */
+ rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
+ rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
+ rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
+ }
+
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Helper function for receiving URI data from the host. Do not call directly.
+ * This function also will take care of the file creation / locking on the guest.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_CANCELLED if the transfer was cancelled by the host.
+ * @param pCtx DnD context to use.
+ * @param pDataHdr DnD data header to use. Needed for accounting.
+ * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking.
+ */
+static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES pDroppedFiles)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
+
+ /* Only count the raw data minus the already received meta data. */
+ Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
+ uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
+ uint64_t cToRecvObjs = pDataHdr->cObjects;
+
+ LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
+ cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
+
+ /* Anything to do at all? */
+ /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just
+ * a bunch of 0-byte files to be transferred. */
+ if (!cToRecvObjs)
+ return VINF_SUCCESS;
+
+ LogRel2(("DnD: Receiving URI data started\n"));
+
+ /*
+ * Allocate temporary chunk buffer.
+ */
+ uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
+ void *pvChunk = RTMemAlloc(cbChunkMax);
+ if (!pvChunk)
+ return VERR_NO_MEMORY;
+ uint32_t cbChunkRead = 0;
+
+ uint64_t cbFileSize = 0; /* Total file size (in bytes). */
+ uint64_t cbFileWritten = 0; /* Written bytes. */
+
+ const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles);
+ AssertPtr(pszDropDir);
+
+ int rc;
+
+ /*
+ * Enter the main loop of retieving files + directories.
+ */
+ DNDTRANSFEROBJECT objCur;
+ RT_ZERO(objCur);
+
+ char szPathName[RTPATH_MAX] = { 0 };
+ uint32_t cbPathName = 0;
+ uint32_t fFlags = 0;
+ uint32_t fMode = 0;
+
+ do
+ {
+ LogFlowFunc(("Waiting for new message ...\n"));
+
+ uint32_t uNextMsg;
+ uint32_t cNextParms;
+ rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, true /* fWait */);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
+
+ switch (uNextMsg)
+ {
+ case HOST_DND_FN_HG_SND_DIR:
+ {
+ rc = vbglR3DnDHGRecvDir(pCtx,
+ szPathName,
+ sizeof(szPathName),
+ &cbPathName,
+ &fMode);
+ LogFlowFunc(("HOST_DND_FN_HG_SND_DIR: "
+ "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
+ szPathName, cbPathName, fMode, rc));
+
+ char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
+ if (pszPathAbs)
+ {
+#ifdef RT_OS_WINDOWS
+ uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
+#else
+ uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
+#endif
+ rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
+ if (RT_SUCCESS(rc))
+ rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs);
+
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cToRecvObjs);
+ cToRecvObjs--;
+ }
+
+ RTStrFree(pszPathAbs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ case HOST_DND_FN_HG_SND_FILE_HDR:
+ RT_FALL_THROUGH();
+ case HOST_DND_FN_HG_SND_FILE_DATA:
+ {
+ if (uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
+ {
+ rc = vbglR3DnDHGRecvFileHdr(pCtx,
+ szPathName,
+ sizeof(szPathName),
+ &fFlags,
+ &fMode,
+ &cbFileSize);
+ LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_HDR: "
+ "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
+ szPathName, fFlags, fMode, cbFileSize, rc));
+ }
+ else
+ {
+ rc = vbglR3DnDHGRecvFileData(pCtx,
+ pvChunk,
+ cbChunkMax,
+ &cbChunkRead);
+ LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
+ "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc));
+ }
+
+ if ( RT_SUCCESS(rc)
+ && uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
+ {
+ char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
+ if (pszPathAbs)
+ {
+ LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
+ szPathName, cbPathName, fMode, cbFileSize));
+
+ uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE
+ | RTFILE_O_CREATE_REPLACE;
+
+ /* Is there already a file open, e.g. in transfer? */
+ if (!DnDTransferObjectIsOpen(&objCur))
+ {
+#ifdef RT_OS_WINDOWS
+ uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
+#else
+ uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
+#endif
+ rc = DnDTransferObjectInitEx(&objCur, DNDTRANSFEROBJTYPE_FILE,
+ pszDropDir /* Source (base) path */, szPathName /* Destination path */);
+ if (RT_SUCCESS(rc))
+ {
+ rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs);
+ if (RT_SUCCESS(rc))
+ {
+ cbFileWritten = 0;
+ DnDTransferObjectSetSize(&objCur, cbFileSize);
+ }
+ }
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur)));
+ rc = VERR_WRONG_ORDER;
+ }
+
+ RTStrFree(pszPathAbs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && uNextMsg == HOST_DND_FN_HG_SND_FILE_DATA
+ && cbChunkRead)
+ {
+ uint32_t cbChunkWritten;
+ rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
+ "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
+ cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
+
+ cbFileWritten += cbChunkWritten;
+
+ Assert(cbChunkRead <= cbToRecvBytes);
+ cbToRecvBytes -= cbChunkRead;
+ }
+ }
+
+ /* Data transfer complete? Close the file. */
+ bool fClose = DnDTransferObjectIsComplete(&objCur);
+ if (fClose)
+ {
+ Assert(cToRecvObjs);
+ cToRecvObjs--;
+ }
+
+ /* Only since protocol v2 we know the file size upfront. */
+ Assert(cbFileWritten <= cbFileSize);
+
+ if (fClose)
+ {
+ LogFlowFunc(("Closing file\n"));
+ DnDTransferObjectDestroy(&objCur);
+ }
+
+ break;
+ }
+ case HOST_DND_FN_CANCEL:
+ {
+ rc = vbglR3DnDHGRecvCancel(pCtx);
+ if (RT_SUCCESS(rc))
+ rc = VERR_CANCELLED;
+ break;
+ }
+ default:
+ {
+ LogRel(("DnD: Warning: Message %s (%#x) from host not supported or in wrong order\n", DnDHostMsgToStr(uNextMsg), uNextMsg));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+
+ LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
+ if ( !cbToRecvBytes
+ && !cToRecvObjs)
+ {
+ break;
+ }
+
+ } while (RT_SUCCESS(rc));
+
+ LogFlowFunc(("Loop ended with %Rrc\n", rc));
+
+ /* All URI data processed? */
+ if (rc == VERR_NO_DATA)
+ rc = VINF_SUCCESS;
+
+ /* Delete temp buffer again. */
+ if (pvChunk)
+ RTMemFree(pvChunk);
+
+ /* Cleanup on failure or if the user has canceled the operation or
+ * something else went wrong. */
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_CANCELLED)
+ LogRel2(("DnD: Receiving URI data was cancelled by the host\n"));
+ else
+ LogRel(("DnD: Receiving URI data failed with %Rrc\n", rc));
+
+ DnDTransferObjectDestroy(&objCur);
+ DnDDroppedFilesRollback(pDroppedFiles);
+ }
+ else
+ {
+ LogRel2(("DnD: Receiving URI data finished\n"));
+
+ /** @todo Compare the transfer list with the dirs/files we really transferred. */
+ /** @todo Implement checksum verification, if any. */
+ }
+
+ /*
+ * Close the dropped files directory.
+ * Don't try to remove it here, however, as the files are being needed
+ * by the client's drag'n drop operation lateron.
+ */
+ int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */);
+ if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
+ LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive the HOST_DND_FN_HG_SND_DATA message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pDataHdr DnD data header to use. Need for accounting and stuff.
+ * @param pvData Where to store the received data from the host.
+ * @param cbData Size (in bytes) of where to store the received data.
+ * @param pcbDataRecv Where to store the received amount of data (in bytes).
+ */
+static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
+ void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
+
+ HGCMMsgHGSendData Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA, 5);
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvData.SetPtr(pvData, cbData);
+ Msg.u.v3.cbData.SetUInt32(0);
+ Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
+ Msg.u.v3.cbChecksum.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbDataRecv;
+ rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Use checksum for validating the received data. */
+ if (pcbDataRecv)
+ *pcbDataRecv = cbDataRecv;
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+ }
+
+ /* failure */
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Utility function to receive the HOST_DND_FN_HG_SND_DATA_HDR message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pDataHdr Where to store the receivd DnD data header.
+ */
+static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
+
+ Assert(pCtx->uProtocolDeprecated >= 3); /* Only for protocol v3 and up. */
+
+ HGCMMsgHGSendDataHdr Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA_HDR, 12);
+ Msg.uContext.SetUInt32(0);
+ Msg.uFlags.SetUInt32(0);
+ Msg.uScreenId.SetUInt32(0);
+ Msg.cbTotal.SetUInt64(0);
+ Msg.cbMeta.SetUInt32(0);
+ Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
+ Msg.cbMetaFmt.SetUInt32(0);
+ Msg.cObjects.SetUInt64(0);
+ Msg.enmCompression.SetUInt32(0);
+ Msg.enmChecksumType.SetUInt32(0);
+ Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
+ Msg.cbChecksum.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /* Msg.uContext not needed here. */
+ Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
+ Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
+ Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
+ Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
+ Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
+ Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
+ Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
+ Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
+ Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Helper function for receiving the actual DnD data from the host. Do not call directly.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pDataHdr Where to store the data header data.
+ * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
+ * @param pcbData Where to store the size (in bytes) of the received meta data.
+ */
+static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
+ void **ppvData, uint64_t *pcbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
+
+ int rc;
+ uint32_t cbDataRecv;
+
+ LogFlowFuncEnter();
+
+ rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
+ if (pDataHdr->cbMeta)
+ {
+ uint64_t cbDataTmp = 0;
+ void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
+ if (!pvDataTmp)
+ rc = VERR_NO_MEMORY;
+
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
+ while (cbDataTmp < pDataHdr->cbMeta)
+ {
+ rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
+ pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
+ &cbDataRecv);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
+ Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
+ cbDataTmp += cbDataRecv;
+ pvDataOff += cbDataRecv;
+ }
+ else
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbDataTmp == pDataHdr->cbMeta);
+
+ LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
+
+ *ppvData = pvDataTmp;
+ *pcbData = cbDataTmp;
+ }
+ else
+ RTMemFree(pvDataTmp);
+ }
+ }
+ else
+ {
+ *ppvData = NULL;
+ *pcbData = 0;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Host -> Guest
+ * Main function for receiving the actual DnD data from the host.
+ *
+ * @returns VBox status code.
+ * @retval VERR_CANCELLED if cancelled by the host.
+ * @param pCtx DnD context to use.
+ * @param pMeta Where to store the actual meta data received from the host.
+ */
+static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
+ PVBGLR3GUESTDNDMETADATA pMeta)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
+
+ AssertMsgReturn(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"), VERR_INVALID_PARAMETER);
+
+ VBOXDNDDATAHDR dataHdr;
+ RT_ZERO(dataHdr);
+ dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize;
+ dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
+ if (!dataHdr.pvMetaFmt)
+ return VERR_NO_MEMORY;
+
+ void *pvData = NULL;
+ uint64_t cbData = 0;
+ int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel2(("DnD: Received %RU64 bytes meta data in format '%s'\n", cbData, (char *)dataHdr.pvMetaFmt));
+
+ /**
+ * Check if this is an URI event. If so, let VbglR3 do all the actual
+ * data transfer + file/directory creation internally without letting
+ * the caller know.
+ *
+ * This keeps the actual (guest OS-)dependent client (like VBoxClient /
+ * VBoxTray) small by not having too much redundant code.
+ */
+ Assert(dataHdr.cbMetaFmt);
+ AssertPtr(dataHdr.pvMetaFmt);
+ if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */
+ {
+ DNDDROPPEDFILES droppedFiles;
+ RT_ZERO(droppedFiles);
+
+ rc = DnDDroppedFilesInit(&droppedFiles);
+ if (RT_SUCCESS(rc))
+ rc = DnDDroppedFilesOpenTemp(&droppedFiles, DNDURIDROPPEDFILE_FLAGS_NONE);
+
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("DnD: Initializing dropped files directory failed with %Rrc\n", rc));
+ }
+ else
+ {
+ AssertPtr(pvData);
+ Assert(cbData);
+
+ /* Use the dropped files directory as the root directory for the current transfer. */
+ rc = DnDTransferListInitEx(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles),
+ DNDTRANSFERLISTFMT_NATIVE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = DnDTransferListAppendRootsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
+ DND_PATH_SEPARATOR_STR, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
+ if (RT_SUCCESS(rc))
+ {
+ pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
+ }
+ }
+ }
+ }
+ }
+ else /* Raw data. */
+ {
+ pMeta->u.Raw.cbMeta = cbData;
+ pMeta->u.Raw.pvMeta = pvData;
+
+ pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
+ }
+
+ if (pvData)
+ RTMemFree(pvData);
+ }
+
+ if (dataHdr.pvMetaFmt)
+ RTMemFree(dataHdr.pvMetaFmt);
+
+ if (RT_FAILURE(rc))
+ {
+ if (rc != VERR_CANCELLED)
+ {
+ LogRel(("DnD: Receiving data failed with %Rrc\n", rc));
+
+ int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
+ if (RT_FAILURE(rc2))
+ LogRel(("DnD: Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+/**
+ * Guest -> Host
+ * Utility function to receive the HOST_DND_FN_GH_REQ_PENDING message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param puScreenID For which screen on the host the request is for. Optional.
+ */
+static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ /* pScreenID is optional. */
+
+ HGCMMsgGHReqPending Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_REQ_PENDING, 2);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uScreenId.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Context ID not used yet. */
+ if (puScreenID)
+ rc = Msg.u.v3.uContext.GetUInt32(puScreenID);
+ }
+
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Utility function to receive the HOST_DND_FN_GH_EVT_DROPPED message from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param ppszFormat Requested data format from the host. Optional.
+ * @param pcbFormat Size of requested data format (in bytes). Optional.
+ * @param puAction Requested action from the host. Optional.
+ */
+static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
+ char **ppszFormat,
+ uint32_t *pcbFormat,
+ uint32_t *puAction)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ /* The rest is optional. */
+
+ const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize;
+
+ char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp));
+ if (!pszFormatTmp)
+ return VERR_NO_MEMORY;
+
+ HGCMMsgGHDropped Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_EVT_DROPPED, 4);
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp);
+ Msg.u.v3.cbFormat.SetUInt32(0);
+ Msg.u.v3.uAction.SetUInt32(0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Context ID not used yet. */
+ if (pcbFormat)
+ rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat);
+ if (RT_SUCCESS(rc) && puAction)
+ rc = Msg.u.v3.uAction.GetUInt32(puAction);
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppszFormat = RTStrDup(pszFormatTmp);
+ if (!*ppszFormat)
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ RTMemFree(pszFormatTmp);
+
+ return rc;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+
+/*********************************************************************************************************************************
+* Public functions *
+*********************************************************************************************************************************/
+
+/**
+ * Connects a DnD context to the DnD host service.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to connect.
+ */
+VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ /* Initialize header */
+ int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(pCtx->uClientID);
+
+ /* Set the default protocol version we would like to use.
+ * Deprecated since VBox 6.1.x, but let this set to 3 to (hopefully) not break things. */
+ pCtx->uProtocolDeprecated = 3;
+
+ pCtx->fHostFeatures = VBOX_DND_HF_NONE;
+ pCtx->fGuestFeatures = VBOX_DND_GF_NONE;
+
+ /*
+ * Get the VM's session ID.
+ * This is not fatal in case we're running with an ancient VBox version.
+ */
+ pCtx->uSessionID = 0;
+ int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2);
+ LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
+
+ /*
+ * Try sending the connect message to tell the protocol version to use.
+ * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
+ * does not implement this command.
+ */
+ HGCMMsgConnect Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_CONNECT, 3);
+ Msg.u.v3.uContext.SetUInt32(0); /** @todo Context ID not used yet. */
+ Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocolDeprecated); /* Deprecated since VBox 6.1.x. */
+ Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /* Set the protocol version we're going to use as told by the host. */
+ rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocolDeprecated); AssertRC(rc);
+
+ /*
+ * Next is reporting our features. If this fails, assume older host.
+ */
+ rc2 = VbglR3DnDReportFeatures(pCtx->uClientID, pCtx->fGuestFeatures, &pCtx->fHostFeatures);
+ if (RT_SUCCESS(rc2))
+ {
+ LogRel2(("DnD: Guest features: %#RX64 - Host features: %#RX64\n",
+ pCtx->fGuestFeatures, pCtx->fHostFeatures));
+ }
+ else /* Failing here is not fatal; might be running with an older host. */
+ {
+ AssertLogRelMsg(rc2 == VERR_NOT_SUPPORTED || rc2 == VERR_NOT_IMPLEMENTED,
+ ("Reporting features failed: %Rrc\n", rc2));
+ }
+
+ pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */
+ }
+ else
+ pCtx->uProtocolDeprecated = 0; /* We're using protocol v0 (initial draft) as a fallback. */
+
+ LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocolDeprecated, rc));
+ return rc;
+}
+
+/**
+ * Disconnects a given DnD context from the DnD host service.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to disconnect.
+ * The context is invalid afterwards on successful disconnection.
+ */
+VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ if (!pCtx->uClientID) /* Already disconnected? Bail out early. */
+ return VINF_SUCCESS;
+
+ int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
+ if (RT_SUCCESS(rc))
+ pCtx->uClientID = 0;
+
+ return rc;
+}
+
+/**
+ * Reports features to the host and retrieve host feature set.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3DnDConnect().
+ * @param fGuestFeatures Features to report, VBOX_DND_GF_XXX.
+ * @param pfHostFeatures Where to store the features VBOX_DND_HF_XXX.
+ */
+VBGLR3DECL(int) VbglR3DnDReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter f64Features0;
+ HGCMFunctionParameter f64Features1;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_DND_FN_REPORT_FEATURES, 2);
+ VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures);
+ VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_DND_GF_1_MUST_BE_ONE);
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
+ Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
+ if (Msg.f64Features1.u.value64 & VBOX_DND_GF_1_MUST_BE_ONE)
+ rc = VERR_NOT_SUPPORTED;
+ else if (pfHostFeatures)
+ *pfHostFeatures = Msg.f64Features0.u.value64;
+ break;
+ }
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+
+}
+
+/**
+ * Receives the next upcoming DnD event.
+ *
+ * This is the main function DnD clients call in order to implement any DnD functionality.
+ * The purpose of it is to abstract the actual DnD protocol handling as much as possible from
+ * the clients -- those only need to react to certain events, regardless of how the underlying
+ * protocol actually is working.
+ *
+ * @returns VBox status code.
+ * @param pCtx DnD context to work with.
+ * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling
+ * VbglR3DnDEventFree() when done.
+ */
+VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
+
+ PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT));
+ if (!pEvent)
+ return VERR_NO_MEMORY;
+
+ uint32_t uMsg = 0;
+ uint32_t cParms = 0;
+ int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check for VM session change. */
+ uint64_t uSessionID;
+ int rc2 = VbglR3GetSessionId(&uSessionID);
+ if ( RT_SUCCESS(rc2)
+ && (uSessionID != pCtx->uSessionID))
+ {
+ LogRel2(("DnD: VM session ID changed to %RU64\n", uSessionID));
+ rc = VbglR3DnDDisconnect(pCtx);
+ if (RT_SUCCESS(rc))
+ rc = VbglR3DnDConnect(pCtx);
+ }
+ }
+
+ if (rc == VERR_CANCELLED) /* Host service told us that we have to bail out. */
+ {
+ LogRel2(("DnD: Host service requested termination\n"));
+
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_QUIT;
+ *ppEvent = pEvent;
+
+ return VINF_SUCCESS;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFunc(("Handling uMsg=%RU32\n", uMsg));
+
+ switch(uMsg)
+ {
+ case HOST_DND_FN_HG_EVT_ENTER:
+ {
+ rc = vbglR3DnDHGRecvAction(pCtx,
+ uMsg,
+ &pEvent->u.HG_Enter.uScreenID,
+ NULL /* puXPos */,
+ NULL /* puYPos */,
+ NULL /* uDefAction */,
+ &pEvent->u.HG_Enter.dndLstActionsAllowed,
+ &pEvent->u.HG_Enter.pszFormats,
+ &pEvent->u.HG_Enter.cbFormats);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
+ break;
+ }
+ case HOST_DND_FN_HG_EVT_MOVE:
+ {
+ rc = vbglR3DnDHGRecvAction(pCtx,
+ uMsg,
+ NULL /* puScreenId */,
+ &pEvent->u.HG_Move.uXpos,
+ &pEvent->u.HG_Move.uYpos,
+ &pEvent->u.HG_Move.dndActionDefault,
+ NULL /* puAllActions */,
+ NULL /* pszFormats */,
+ NULL /* pcbFormats */);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
+ break;
+ }
+ case HOST_DND_FN_HG_EVT_DROPPED:
+ {
+ rc = vbglR3DnDHGRecvAction(pCtx,
+ uMsg,
+ NULL /* puScreenId */,
+ &pEvent->u.HG_Drop.uXpos,
+ &pEvent->u.HG_Drop.uYpos,
+ &pEvent->u.HG_Drop.dndActionDefault,
+ NULL /* puAllActions */,
+ NULL /* pszFormats */,
+ NULL /* pcbFormats */);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
+ break;
+ }
+ case HOST_DND_FN_HG_EVT_LEAVE:
+ {
+ rc = vbglR3DnDHGRecvLeave(pCtx);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
+ break;
+ }
+ case HOST_DND_FN_HG_SND_DATA_HDR:
+ {
+ rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
+ break;
+ }
+ case HOST_DND_FN_HG_SND_DIR:
+ RT_FALL_THROUGH();
+ case HOST_DND_FN_HG_SND_FILE_HDR:
+ RT_FALL_THROUGH();
+ case HOST_DND_FN_HG_SND_FILE_DATA:
+ {
+ /*
+ * All messages for this block are handled internally
+ * by vbglR3DnDHGRecvDataMain(), see above.
+ *
+ * So if we land here our code is buggy.
+ */
+ rc = VERR_WRONG_ORDER;
+ break;
+ }
+ case HOST_DND_FN_CANCEL:
+ {
+ rc = vbglR3DnDHGRecvCancel(pCtx);
+ if (RT_SUCCESS(rc))
+ rc = VERR_CANCELLED; /* Will emit a cancel event below. */
+ break;
+ }
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ case HOST_DND_FN_GH_REQ_PENDING:
+ {
+ rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
+ break;
+ }
+ case HOST_DND_FN_GH_EVT_DROPPED:
+ {
+ rc = vbglR3DnDGHRecvDropped(pCtx,
+ &pEvent->u.GH_Drop.pszFormat,
+ &pEvent->u.GH_Drop.cbFormat,
+ &pEvent->u.GH_Drop.dndActionRequested);
+ if (RT_SUCCESS(rc))
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
+ break;
+ }
+#endif
+ default:
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ /* Current operation cancelled? Set / overwrite event type and tell the caller. */
+ if (rc == VERR_CANCELLED)
+ {
+ pEvent->enmType = VBGLR3DNDEVENTTYPE_CANCEL;
+ rc = VINF_SUCCESS; /* Deliver the event to the caller. */
+ }
+ else
+ {
+ VbglR3DnDEventFree(pEvent);
+ LogRel(("DnD: Handling message %s (%#x) failed with %Rrc\n", DnDHostMsgToStr(uMsg), uMsg, rc));
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppEvent = pEvent;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Frees (destroys) a formerly allocated DnD event.
+ *
+ * @returns IPRT status code.
+ * @param pEvent Event to free (destroy).
+ */
+VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
+{
+ if (!pEvent)
+ return;
+
+ /* Some messages require additional cleanup. */
+ switch (pEvent->enmType)
+ {
+ case VBGLR3DNDEVENTTYPE_HG_ENTER:
+ {
+ if (pEvent->u.HG_Enter.pszFormats)
+ RTStrFree(pEvent->u.HG_Enter.pszFormats);
+ break;
+ }
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ case VBGLR3DNDEVENTTYPE_GH_DROP:
+ {
+ if (pEvent->u.GH_Drop.pszFormat)
+ RTStrFree(pEvent->u.GH_Drop.pszFormat);
+ break;
+ }
+#endif
+ case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
+ {
+ PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
+ switch (pMeta->enmType)
+ {
+ case VBGLR3GUESTDNDMETADATATYPE_RAW:
+ {
+ if (pMeta->u.Raw.pvMeta)
+ {
+ Assert(pMeta->u.Raw.cbMeta);
+ RTMemFree(pMeta->u.Raw.pvMeta);
+ pMeta->u.Raw.cbMeta = 0;
+ }
+ break;
+ }
+
+ case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
+ {
+ DnDTransferListDestroy(&pMeta->u.URI.Transfer);
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ RTMemFree(pEvent);
+ pEvent = NULL;
+}
+
+VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgHGAck Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_ACK_OP, 2);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uAction.SetUInt32(dndAction);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Host -> Guest
+ * Requests the actual DnD data to be sent from the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pcszFormat Format to request the data from the host in.
+ */
+VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
+ if (!RTStrIsValidEncoding(pcszFormat))
+ return VERR_INVALID_PARAMETER;
+
+ const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
+
+ HGCMMsgHGReqData Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_REQ_DATA, 3);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
+ Msg.u.v3.cbFormat.SetUInt32(cbFormat);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Host -> Guest
+ * Reports back its progress back to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param uStatus DnD status to report.
+ * @param uPercent Overall progress (in percent) to report.
+ * @param rcErr Error code (IPRT-style) to report.
+ */
+VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
+
+ HGCMMsgHGProgress Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_EVT_PROGRESS, 4);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uStatus.SetUInt32(uStatus);
+ Msg.u.v3.uPercent.SetUInt32(uPercent);
+ Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+/**
+ * Guest -> Host
+ * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
+ * which eventually could be dragged over to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param dndActionDefault Default action for the operation to report.
+ * @param dndLstActionsAllowed All available actions for the operation to report.
+ * @param pcszFormats Available formats for the operation to report.
+ * @param cbFormats Size (in bytes) of formats to report.
+ */
+VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
+ VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
+ const char* pcszFormats, uint32_t cbFormats)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
+ AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
+
+ if (!RTStrIsValidEncoding(pcszFormats))
+ return VERR_INVALID_UTF8_ENCODING;
+
+ HGCMMsgGHAckPending Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_ACK_PENDING, 5);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
+ Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
+ Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
+ Msg.u.v3.cbFormats.SetUInt32(cbFormats);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send DnD data from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pvData Data block to send.
+ * @param cbData Size (in bytes) of data block to send.
+ * @param pDataHdr Data header to use -- needed for accounting.
+ */
+static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
+ void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
+
+ HGCMMsgGHSendDataHdr MsgHdr;
+ VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA_HDR, 12);
+ MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
+ MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
+ MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
+ MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
+ MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
+ MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
+ MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
+ MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
+ MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
+ MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
+ MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
+ MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
+
+ int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
+
+ LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
+ pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ HGCMMsgGHSendData MsgData;
+ VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA, 5);
+ MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
+ MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
+ MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
+
+ uint32_t cbCurChunk;
+ const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
+ uint32_t cbSent = 0;
+
+ while (cbSent < cbData)
+ {
+ cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
+ MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
+ MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
+
+ rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
+ if (RT_FAILURE(rc))
+ break;
+
+ cbSent += cbCurChunk;
+ }
+
+ LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
+ cbMaxChunk, cbData, cbSent, rc));
+
+ if (RT_SUCCESS(rc))
+ Assert(cbSent == cbData);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send a guest directory to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pObj transfer object containing the directory to send.
+ */
+static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj)
+{
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER);
+
+ const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
+ const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */;
+ const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
+
+ LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode));
+
+ if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */
+ return VERR_INVALID_PARAMETER;
+
+ HGCMMsgGHSendDir Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DIR, 4);
+ /** @todo Context ID not used yet. */
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath);
+ Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
+ Msg.u.v3.fMode.SetUInt32(fMode);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send a file from the guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pObj Transfer object containing the file to send.
+ */
+static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+ AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE);
+ AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
+
+ uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE;
+
+ int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t cbBuf = pCtx->cbMaxChunkSize;
+ AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+ void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
+ if (!pvBuf)
+ {
+ int rc2 = DnDTransferObjectClose(pObj);
+ AssertRC(rc2);
+ return VERR_NO_MEMORY;
+ }
+
+ const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
+ const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX);
+ const uint64_t cbSize = DnDTransferObjectGetSize(pObj);
+ const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
+
+ LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode));
+
+ HGCMMsgGHSendFileHdr MsgHdr;
+ VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_HDR, 6);
+ MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
+ MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */
+ MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */
+ MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
+ MsgHdr.fMode.SetUInt32(fMode); /* File mode */
+ MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */
+
+ rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
+
+ LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Send the actual file data, chunk by chunk.
+ */
+ HGCMMsgGHSendFileData Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_DATA, 5);
+ Msg.u.v3.uContext.SetUInt32(0);
+ Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
+ Msg.u.v3.cbChecksum.SetUInt32(0);
+
+ uint64_t cbToReadTotal = cbSize;
+ uint64_t cbWrittenTotal = 0;
+ while (cbToReadTotal)
+ {
+ uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
+ uint32_t cbRead = 0;
+ if (cbToRead)
+ rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead);
+
+ LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
+ cbToReadTotal, cbToRead, cbRead, rc));
+
+ if ( RT_SUCCESS(rc)
+ && cbRead)
+ {
+ Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
+ Msg.u.v3.cbData.SetUInt32(cbRead);
+ /** @todo Calculate + set checksums. */
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
+ break;
+ }
+
+ Assert(cbRead <= cbToReadTotal);
+ cbToReadTotal -= cbRead;
+ cbWrittenTotal += cbRead;
+
+ LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize));
+ };
+ }
+
+ RTMemFree(pvBuf);
+ int rc2 = DnDTransferObjectClose(pObj);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send a transfer object from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pObj Transfer object to send from guest to the host.
+ */
+static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pObj, VERR_INVALID_POINTER);
+
+ int rc;
+
+ const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj);
+
+ switch (enmType)
+ {
+ case DNDTRANSFEROBJTYPE_DIRECTORY:
+ rc = vbglR3DnDGHSendDir(pCtx, pObj);
+ break;
+
+ case DNDTRANSFEROBJTYPE_FILE:
+ rc = vbglR3DnDGHSendFile(pCtx, pObj);
+ break;
+
+ default:
+ AssertMsgFailed(("Object type %ld not implemented\n", enmType));
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send raw data from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pvData Block to raw data to send.
+ * @param cbData Size (in bytes) of raw data to send.
+ */
+static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ /* cbData can be 0. */
+
+ VBOXDNDDATAHDR dataHdr;
+ RT_ZERO(dataHdr);
+
+ dataHdr.cbMeta = (uint32_t)cbData;
+ dataHdr.cbTotal = cbData;
+
+ return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
+}
+
+/**
+ * Guest -> Host
+ * Utility function to send transfer data from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pTransferList Dnd transfer list to send.
+ */
+static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList)
+{
+ AssertPtrReturn(pCtx,VERR_INVALID_POINTER);
+ AssertPtrReturn(pTransferList, VERR_INVALID_POINTER);
+
+ /*
+ * Send the (meta) data; in case of URIs it's the root entries of a
+ * transfer list the host needs to know upfront to set up the drag'n drop operation.
+ */
+ char *pszList = NULL;
+ size_t cbList;
+ int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ void *pvURIList = (void *)pszList;
+ uint32_t cbURLIist = (uint32_t)cbList;
+
+ /* The total size also contains the size of the meta data. */
+ uint64_t cbTotal = cbURLIist;
+ cbTotal += DnDTransferListObjTotalBytes(pTransferList);
+
+ /* We're going to send a transfer list in text format. */
+ const char szMetaFmt[] = "text/uri-list";
+ const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
+
+ VBOXDNDDATAHDR dataHdr;
+ dataHdr.uFlags = 0; /* Flags not used yet. */
+ dataHdr.cbTotal = cbTotal;
+ dataHdr.cbMeta = cbURLIist;
+ dataHdr.pvMetaFmt = (void *)szMetaFmt;
+ dataHdr.cbMetaFmt = cbMetaFmt;
+ dataHdr.cObjects = DnDTransferListObjCount(pTransferList);
+
+ rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr);
+
+ if (RT_SUCCESS(rc))
+ {
+ while (DnDTransferListObjCount(pTransferList))
+ {
+ PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList);
+
+ rc = vbglR3DnDGHSendURIObject(pCtx, pObj);
+ if (RT_FAILURE(rc))
+ break;
+
+ DnDTransferListObjRemoveFirst(pTransferList);
+ }
+
+ Assert(DnDTransferListObjCount(pTransferList) == 0);
+ }
+
+ return rc;
+}
+
+/**
+ * Guest -> Host
+ * Sends data, which either can be raw or URI data, from guest to the host. This function
+ * initiates the actual data transfer from guest to the host.
+ *
+ * @returns IPRT status code.
+ * @param pCtx DnD context to use.
+ * @param pszFormat In which format the data will be sent.
+ * @param pvData Data block to send.
+ * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR_STR.
+ * @param cbData Size (in bytes) of data block to send.
+ */
+VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+ LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
+
+ LogRel2(("DnD: Sending %RU32 bytes meta data in format '%s'\n", cbData, pszFormat));
+
+ int rc;
+ if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
+ {
+ DNDTRANSFERLIST lstTransfer;
+ RT_ZERO(lstTransfer);
+
+ rc = DnDTransferListInit(&lstTransfer);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */
+ /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */
+ const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE;
+
+ rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
+ DND_PATH_SEPARATOR_STR, fFlags);
+ if (RT_SUCCESS(rc))
+ rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer);
+ DnDTransferListDestroy(&lstTransfer);
+ }
+ }
+ else
+ rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
+
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("DnD: Sending data failed with rc=%Rrc\n", rc));
+
+ if (rc != VERR_CANCELLED)
+ {
+ int rc2 = VbglR3DnDSendError(pCtx, rc);
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
+ }
+ }
+
+ return rc;
+}
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp
new file mode 100644
index 00000000..66a8bca7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDrmClient.cpp
@@ -0,0 +1,199 @@
+/* $Id: VBoxGuestR3LibDrmClient.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, DRM client handling.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+
+#include <iprt/env.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+
+#if defined(RT_OS_LINUX)
+# include <VBox/HostServices/GuestPropertySvc.h>
+
+/** Defines the DRM client executable (image). */
+# define VBOX_DRMCLIENT_EXECUTABLE "/usr/bin/VBoxDRMClient"
+# define VBOX_DRMCLIENT_LEGACY_EXECUTABLE "/usr/bin/VBoxClient"
+/** Defines the guest property that defines if the DRM resizing client needs to be active or not. */
+# define VBOX_DRMCLIENT_GUEST_PROP_RESIZE "/VirtualBox/GuestAdd/DRMResize"
+
+/**
+ * Check if specified guest property exist.
+ *
+ * @returns \c true if the property exists and its flags do match, \c false otherwise.
+ * @param pszPropName Guest property name.
+ * @param fPropFlags Guest property flags mask to verify if property exist.
+ * If \p fPropFlags is 0, flags verification is omitted.
+ */
+static bool vbglR3DrmClientCheckProp(const char *pszPropName, uint32_t fPropFlags)
+{
+ bool fExist = false;
+# if defined(VBOX_WITH_GUEST_PROPS)
+ uint32_t idClient;
+
+ int rc = VbglR3GuestPropConnect(&idClient);
+ if (RT_SUCCESS(rc))
+ {
+ char *pcszFlags = NULL;
+
+ rc = VbglR3GuestPropReadEx(idClient, pszPropName, NULL /* ppszValue */, &pcszFlags, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check property flags match. */
+ if (fPropFlags)
+ {
+ uint32_t fFlags = 0;
+
+ rc = GuestPropValidateFlags(pcszFlags, &fFlags);
+ fExist = RT_SUCCESS(rc) && (fFlags == fPropFlags);
+ }
+ else
+ fExist = true;
+
+ RTStrFree(pcszFlags);
+ }
+
+ VbglR3GuestPropDisconnect(idClient);
+ }
+# endif /* VBOX_WITH_GUEST_PROPS */
+ return fExist;
+}
+#endif /* RT_OS_LINUX */
+
+/**
+ * Returns true if the DRM resizing client is needed.
+ * This is achieved by querying existence of a guest property.
+ *
+ * @returns \c true if the DRM resizing client is needed, \c false if not.
+ */
+VBGLR3DECL(bool) VbglR3DrmClientIsNeeded(void)
+{
+#if defined(RT_OS_LINUX)
+ return vbglR3DrmClientCheckProp(VBOX_DRMCLIENT_GUEST_PROP_RESIZE, 0);
+#else
+ return false;
+#endif
+}
+
+/**
+ * Returns true if the DRM IPC server socket access should be restricted.
+ *
+ * Restricted access means that only users from a certain group should
+ * be granted with read and write access permission to IPC socket. Check
+ * is done by examining \c VBGLR3DRMIPCPROPRESTRICT guest property. Property
+ * is only considered valid if is read-only for guest. I.e., the following
+ * property should be set on the host side:
+ *
+ * VBoxManage guestproperty set <VM> /VirtualBox/GuestAdd/DRMIpcRestricted 1 --flags RDONLYGUEST
+ *
+ * @returns \c true if restricted socket access is required, \c false otherwise.
+ */
+VBGLR3DECL(bool) VbglR3DrmRestrictedIpcAccessIsNeeded(void)
+{
+#if defined(RT_OS_LINUX)
+ return vbglR3DrmClientCheckProp(VBGLR3DRMIPCPROPRESTRICT, GUEST_PROP_F_RDONLYGUEST);
+#else
+ return false;
+#endif
+}
+
+/**
+ * Returns true if the DRM resizing client already is running.
+ * This is achieved by querying existence of a guest property.
+ *
+ * @returns \c true if the DRM resizing client is running, \c false if not.
+ */
+VBGLR3DECL(bool) VbglR3DrmClientIsRunning(void)
+{
+ return VbglR3DrmClientIsNeeded();
+}
+
+#if defined(RT_OS_LINUX)
+static int VbglR3DrmStart(const char *pszCmd, const char **apszArgs)
+{
+ return RTProcCreate(pszCmd, apszArgs, RTENV_DEFAULT,
+ RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL);
+}
+#endif
+
+/**
+ * Starts (executes) the DRM resizing client process ("VBoxDRMClient").
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3DrmClientStart(void)
+{
+#if defined(RT_OS_LINUX)
+ const char *apszArgs[2] = { VBOX_DRMCLIENT_EXECUTABLE, NULL }; /** @todo r=andy Pass path + process name as argv0? */
+ return VbglR3DrmStart(VBOX_DRMCLIENT_EXECUTABLE, apszArgs);
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+/**
+ * Starts (executes) the legacy DRM resizing client process ("VBoxClient --vmsvga").
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3DrmLegacyClientStart(void)
+{
+#if defined(RT_OS_LINUX)
+ const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--vmsvga", NULL };
+ return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs);
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+/**
+ * Starts (executes) the legacy X11 resizing agent process ("VBoxClient --display").
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3DrmLegacyX11AgentStart(void)
+{
+#if defined(RT_OS_LINUX)
+ const char *apszArgs[3] = { VBOX_DRMCLIENT_LEGACY_EXECUTABLE, "--display", NULL };
+ return VbglR3DrmStart(VBOX_DRMCLIENT_LEGACY_EXECUTABLE, apszArgs);
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp
new file mode 100644
index 00000000..f459f607
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibEvent.cpp
@@ -0,0 +1,103 @@
+/* $Id: VBoxGuestR3LibEvent.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Events.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Wait for the host to signal one or more events and return which.
+ *
+ * The events will only be delivered by the host if they have been enabled
+ * previously using @a VbglR3CtlFilterMask. If one or several of the events
+ * have already been signalled but not yet waited for, this function will return
+ * immediately and return those events.
+ *
+ * @returns IPRT status code.
+ *
+ * @param fMask The events we want to wait for, or-ed together.
+ * @param cMillies How long to wait before giving up and returning
+ * (VERR_TIMEOUT). Use RT_INDEFINITE_WAIT to wait until we
+ * are interrupted or one of the events is signalled.
+ * @param pfEvents Where to store the events signalled. Optional.
+ */
+VBGLR3DECL(int) VbglR3WaitEvent(uint32_t fMask, uint32_t cMillies, uint32_t *pfEvents)
+{
+ LogFlow(("VbglR3WaitEvent: fMask=%#x, cMillies=%u, pfEvents=%p\n", fMask, cMillies, pfEvents));
+ AssertReturn((fMask & ~VMMDEV_EVENT_VALID_EVENT_MASK) == 0, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfEvents, VERR_INVALID_POINTER);
+
+ VBGLIOCWAITFOREVENTS WaitEvents;
+ VBGLREQHDR_INIT(&WaitEvents.Hdr, WAIT_FOR_EVENTS);
+ WaitEvents.u.In.fEvents = fMask;
+ WaitEvents.u.In.cMsTimeOut = cMillies;
+ int rc = vbglR3DoIOCtl(VBGL_IOCTL_WAIT_FOR_EVENTS, &WaitEvents.Hdr, sizeof(WaitEvents));
+ if (pfEvents)
+ {
+ if (RT_SUCCESS(rc))
+ *pfEvents = WaitEvents.u.Out.fEvents;
+ else
+ *pfEvents = 0;
+ }
+
+ LogFlow(("VbglR3WaitEvent: rc=%Rrc fEvents=%#x\n", rc, WaitEvents.u.Out.fEvents));
+ return rc;
+}
+
+
+/**
+ * Causes any pending VbglR3WaitEvent calls (VBGL_IOCTL_WAIT_FOR_EVENTS) to
+ * return with a VERR_INTERRUPTED status.
+ *
+ * Can be used in combination with a termination flag variable for interrupting
+ * event loops. After calling this, VBGL_IOCTL_WAIT_FOR_EVENTS should no longer
+ * be called in the same session. At the time of writing this is not enforced;
+ * at the time of reading it may be.
+ *
+ * @returns IPRT status code.
+ */
+VBGLR3DECL(int) VbglR3InterruptEventWaits(void)
+{
+ VBGLREQHDR Req;
+ VBGLREQHDR_INIT(&Req, INTERRUPT_ALL_WAIT_FOR_EVENTS);
+ return vbglR3DoIOCtl(VBGL_IOCTL_INTERRUPT_ALL_WAIT_FOR_EVENTS, &Req, sizeof(Req));
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp
new file mode 100644
index 00000000..e80f1f55
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp
@@ -0,0 +1,90 @@
+/* $Id: VBoxGuestR3LibGR.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, GR.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType)
+{
+ VMMDevRequestHeader *pReq;
+
+ AssertPtrReturn(ppReq, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(cb >= sizeof(VMMDevRequestHeader) && cb < _1G, ("%#zx vs %#zx\n", cb, sizeof(VMMDevRequestHeader)),
+ VERR_INVALID_PARAMETER);
+
+ pReq = (VMMDevRequestHeader *)RTMemTmpAlloc(cb);
+ if (RT_LIKELY(pReq))
+ {
+ pReq->size = (uint32_t)cb;
+ pReq->version = VMMDEV_REQUEST_HEADER_VERSION;
+ pReq->requestType = enmReqType;
+ pReq->rc = VERR_GENERAL_FAILURE;
+ pReq->reserved1 = 0;
+ pReq->fRequestor = 0;
+
+ *ppReq = pReq;
+
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+int vbglR3GRPerform(VMMDevRequestHeader *pReq)
+{
+ PVBGLREQHDR pReqHdr = (PVBGLREQHDR)pReq;
+ uint32_t const cbReq = pReqHdr->cbIn;
+ Assert(pReqHdr->cbOut == 0 || pReqHdr->cbOut == cbReq);
+ pReqHdr->cbOut = cbReq;
+ if (pReq->size < _1K)
+ return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST(cbReq), pReqHdr, cbReq);
+ return vbglR3DoIOCtl(VBGL_IOCTL_VMMDEV_REQUEST_BIG, pReqHdr, cbReq);
+}
+
+
+void vbglR3GRFree(VMMDevRequestHeader *pReq)
+{
+ RTMemTmpFree(pReq);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp
new file mode 100644
index 00000000..fc29936c
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp
@@ -0,0 +1,2214 @@
+/* $Id: VBoxGuestR3LibGuestCtrl.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest control.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/cpp/autores.h>
+#include <iprt/stdarg.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/GuestHost/GuestControl.h>
+#include <VBox/HostServices/GuestControlSvc.h>
+
+#ifndef RT_OS_WINDOWS
+# include <signal.h>
+# ifdef RT_OS_DARWIN
+# include <pthread.h>
+# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */
+# endif
+#endif
+
+#include "VBoxGuestR3LibInternal.h"
+
+using namespace guestControl;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Set if GUEST_MSG_PEEK_WAIT and friends are supported. */
+static int g_fVbglR3GuestCtrlHavePeekGetCancel = -1;
+
+
+/**
+ * Connects to the guest control service.
+ *
+ * @returns VBox status code
+ * @param pidClient Where to put The client ID on success. The client ID
+ * must be passed to all the other calls to the service.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pidClient)
+{
+ return VbglR3HGCMConnect("VBoxGuestControlSvc", pidClient);
+}
+
+
+/**
+ * Disconnect from the guest control service.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t idClient)
+{
+ return VbglR3HGCMDisconnect(idClient);
+}
+
+
+/**
+ * Waits until a new host message arrives.
+ * This will block until a message becomes available.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param pidMsg Where to store the message id.
+ * @param pcParameters Where to store the number of parameters which will
+ * be received in a second call to the host.
+ */
+static int vbglR3GuestCtrlMsgWaitFor(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters)
+{
+ AssertPtrReturn(pidMsg, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParameters, VERR_INVALID_POINTER);
+
+ HGCMMsgWaitFor Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient,
+ GUEST_MSG_WAIT, /* Tell the host we want our next message. */
+ 2); /* Just peek for the next message! */
+ VbglHGCMParmUInt32Set(&Msg.msg, 0);
+ VbglHGCMParmUInt32Set(&Msg.num_parms, 0);
+
+ /*
+ * We should always get a VERR_TOO_MUCH_DATA response here, see
+ * guestControl::HostMessage::Peek() and its caller ClientState::SendReply().
+ * We accept success too here, in case someone decide to make the protocol
+ * slightly more sane.
+ *
+ * Note! A really sane protocol design would have a separate call for getting
+ * info about a pending message (returning VINF_SUCCESS), and a separate
+ * one for retriving the actual message parameters. Not this weird
+ * stuff, to put it rather bluntly.
+ *
+ * Note! As a result of this weird design, we are not able to correctly
+ * retrieve message if we're interrupted by a signal, like SIGCHLD.
+ * Because IPRT wants to use waitpid(), we're forced to have a handler
+ * installed for SIGCHLD, so when working with child processes there
+ * will be signals in the air and we will get VERR_INTERRUPTED returns.
+ * The way HGCM handles interrupted calls is to silently (?) drop them
+ * as they complete (see VMMDev), so the server knows little about it
+ * and just goes on to the next message inline.
+ *
+ * So, as a "temporary" mesasure, we block SIGCHLD here while waiting,
+ * because it will otherwise be impossible do simple stuff like 'mkdir'
+ * on a mac os x guest, and probably most other unix guests.
+ */
+#ifdef RT_OS_WINDOWS
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+#else
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ sigprocmask(SIG_UNBLOCK, &SigSet, NULL);
+#endif
+ if ( rc == VERR_TOO_MUCH_DATA
+ || RT_SUCCESS(rc))
+ {
+ int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg);
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = VbglHGCMParmUInt32Get(&Msg.num_parms, pcParameters);
+ if (RT_SUCCESS(rc2))
+ {
+ /* Ok, so now we know what message type and how much parameters there are. */
+ return rc;
+ }
+ }
+ rc = rc2;
+ }
+ *pidMsg = UINT32_MAX - 1;
+ *pcParameters = UINT32_MAX - 2;
+ return rc;
+}
+
+
+/**
+ * Determins the value of g_fVbglR3GuestCtrlHavePeekGetCancel.
+ *
+ * @returns true if supported, false if not.
+ * @param idClient The client ID to use for the testing.
+ */
+DECL_NO_INLINE(static, bool) vbglR3GuestCtrlDetectPeekGetCancelSupport(uint32_t idClient)
+{
+ /*
+ * Seems we get VINF_SUCCESS back from the host if we try unsupported
+ * guest control messages, so we need to supply some random message
+ * parameters and check that they change.
+ */
+ uint32_t const idDummyMsg = UINT32_C(0x8350bdca);
+ uint32_t const cDummyParmeters = UINT32_C(0x7439604f);
+ uint32_t const cbDummyMask = UINT32_C(0xc0ffe000);
+ Assert(cDummyParmeters > VMMDEV_MAX_HGCM_PARMS);
+
+ int rc;
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idMsg;
+ HGCMFunctionParameter cParams;
+ HGCMFunctionParameter acbParams[14];
+ } PeekCall;
+ Assert(RT_ELEMENTS(PeekCall.acbParams) + 2 < VMMDEV_MAX_HGCM_PARMS);
+
+ do
+ {
+ memset(&PeekCall, 0xf6, sizeof(PeekCall));
+ VBGL_HGCM_HDR_INIT(&PeekCall.Hdr, idClient, GUEST_MSG_PEEK_NOWAIT, 16);
+ VbglHGCMParmUInt32Set(&PeekCall.idMsg, idDummyMsg);
+ VbglHGCMParmUInt32Set(&PeekCall.cParams, cDummyParmeters);
+ for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++)
+ VbglHGCMParmUInt32Set(&PeekCall.acbParams[i], i | cbDummyMask);
+
+ rc = VbglR3HGCMCall(&PeekCall.Hdr, sizeof(PeekCall));
+ } while (rc == VERR_INTERRUPTED);
+
+ LogRel2(("vbglR3GuestCtrlDetectPeekGetCancelSupport: rc=%Rrc %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x %#x\n",
+ rc, PeekCall.idMsg.u.value32, PeekCall.cParams.u.value32,
+ PeekCall.acbParams[ 0].u.value32, PeekCall.acbParams[ 1].u.value32,
+ PeekCall.acbParams[ 2].u.value32, PeekCall.acbParams[ 3].u.value32,
+ PeekCall.acbParams[ 4].u.value32, PeekCall.acbParams[ 5].u.value32,
+ PeekCall.acbParams[ 6].u.value32, PeekCall.acbParams[ 7].u.value32,
+ PeekCall.acbParams[ 8].u.value32, PeekCall.acbParams[ 9].u.value32,
+ PeekCall.acbParams[10].u.value32, PeekCall.acbParams[11].u.value32,
+ PeekCall.acbParams[12].u.value32, PeekCall.acbParams[13].u.value32));
+
+ /*
+ * VERR_TRY_AGAIN is likely and easy.
+ */
+ if ( rc == VERR_TRY_AGAIN
+ && PeekCall.idMsg.u.value32 == 0
+ && PeekCall.cParams.u.value32 == 0
+ && PeekCall.acbParams[0].u.value32 == 0
+ && PeekCall.acbParams[1].u.value32 == 0
+ && PeekCall.acbParams[2].u.value32 == 0
+ && PeekCall.acbParams[3].u.value32 == 0)
+ {
+ g_fVbglR3GuestCtrlHavePeekGetCancel = 1;
+ LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#1)\n"));
+ return true;
+ }
+
+ /*
+ * VINF_SUCCESS is annoying but with 16 parameters we've got plenty to check.
+ */
+ if ( rc == VINF_SUCCESS
+ && PeekCall.idMsg.u.value32 != idDummyMsg
+ && PeekCall.idMsg.u.value32 != 0
+ && PeekCall.cParams.u.value32 <= VMMDEV_MAX_HGCM_PARMS)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(PeekCall.acbParams); i++)
+ if (PeekCall.acbParams[i].u.value32 != (i | cbDummyMask))
+ {
+ g_fVbglR3GuestCtrlHavePeekGetCancel = 0;
+ LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#1)\n"));
+ return false;
+ }
+ g_fVbglR3GuestCtrlHavePeekGetCancel = 1;
+ LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Supported (#2)\n"));
+ return true;
+ }
+
+ /*
+ * Okay, pretty sure it's not supported then.
+ */
+ LogRel(("vbglR3GuestCtrlDetectPeekGetCancelSupport: Not supported (#3)\n"));
+ g_fVbglR3GuestCtrlHavePeekGetCancel = 0;
+ return false;
+}
+
+
+/**
+ * Reads g_fVbglR3GuestCtrlHavePeekGetCancel and resolved -1.
+ *
+ * @returns true if supported, false if not.
+ * @param idClient The client ID to use for the testing.
+ */
+DECLINLINE(bool) vbglR3GuestCtrlSupportsPeekGetCancel(uint32_t idClient)
+{
+ int fState = g_fVbglR3GuestCtrlHavePeekGetCancel;
+ if (RT_LIKELY(fState != -1))
+ return fState != 0;
+ return vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient);
+}
+
+
+/**
+ * Figures which getter function to use to retrieve the message.
+ */
+DECLINLINE(uint32_t) vbglR3GuestCtrlGetMsgFunctionNo(uint32_t idClient)
+{
+ return vbglR3GuestCtrlSupportsPeekGetCancel(idClient) ? GUEST_MSG_GET : GUEST_MSG_WAIT;
+}
+
+
+/**
+ * Checks if the host supports the optimizes message and session functions.
+ *
+ * @returns true / false.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * We may need to use this for checking.
+ * @since 6.0
+ */
+VBGLR3DECL(bool) VbglR3GuestCtrlSupportsOptimizations(uint32_t idClient)
+{
+ return vbglR3GuestCtrlSupportsPeekGetCancel(idClient);
+}
+
+
+/**
+ * Make us the guest control master client.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMakeMeMaster(uint32_t idClient)
+{
+ int rc;
+ do
+ {
+ VBGLIOCHGCMCALL Hdr;
+ VBGL_HGCM_HDR_INIT(&Hdr, idClient, GUEST_MSG_MAKE_ME_MASTER, 0);
+ rc = VbglR3HGCMCall(&Hdr, sizeof(Hdr));
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+}
+
+
+/**
+ * Reports features to the host and retrieve host feature set.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param fGuestFeatures Features to report, VBOX_GUESTCTRL_GF_XXX.
+ * @param pfHostFeatures Where to store the features VBOX_GUESTCTRL_HF_XXX.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter f64Features0;
+ HGCMFunctionParameter f64Features1;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_REPORT_FEATURES, 2);
+ VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures);
+ VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_GUESTCTRL_GF_1_MUST_BE_ONE);
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
+ Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
+ if (Msg.f64Features1.u.value64 & VBOX_GUESTCTRL_GF_1_MUST_BE_ONE)
+ rc = VERR_NOT_SUPPORTED;
+ else if (pfHostFeatures)
+ *pfHostFeatures = Msg.f64Features0.u.value64;
+ break;
+ }
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+
+}
+
+
+/**
+ * Query the host features.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param pfHostFeatures Where to store the host feature, VBOX_GUESTCTRL_HF_XXX.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter f64Features0;
+ HGCMFunctionParameter f64Features1;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_QUERY_FEATURES, 2);
+ VbglHGCMParmUInt64Set(&Msg.f64Features0, 0);
+ VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63));
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
+ Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
+ if (Msg.f64Features1.u.value64 & RT_BIT_64(63))
+ rc = VERR_NOT_SUPPORTED;
+ else if (pfHostFeatures)
+ *pfHostFeatures = Msg.f64Features0.u.value64;
+ break;
+ }
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+
+}
+
+
+/**
+ * Peeks at the next host message, waiting for one to turn up.
+ *
+ * @returns VBox status code.
+ * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so
+ * caller just have to repeat this call.
+ * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck).
+ *
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param pidMsg Where to store the message id.
+ * @param pcParameters Where to store the number of parameters which will
+ * be received in a second call to the host.
+ * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use
+ * for the VM restore check. Optional.
+ *
+ * @note Restore check is only performed optimally with a 6.0 host.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgPeekWait(uint32_t idClient, uint32_t *pidMsg, uint32_t *pcParameters, uint64_t *pidRestoreCheck)
+{
+ AssertPtrReturn(pidMsg, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParameters, VERR_INVALID_POINTER);
+
+ int rc;
+ if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient))
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */
+ HGCMFunctionParameter cParameters;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_PEEK_WAIT, 2);
+ VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0);
+ VbglHGCMParmUInt32Set(&Msg.cParameters, 0);
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ LogRel2(("VbglR3GuestCtrlMsgPeekWait -> %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ {
+ AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit
+ && Msg.cParameters.type == VMMDevHGCMParmType_32bit,
+ ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type),
+ VERR_INTERNAL_ERROR_3);
+
+ *pidMsg = (uint32_t)Msg.idMsg.u.value64;
+ *pcParameters = Msg.cParameters.u.value32;
+ return rc;
+ }
+
+ /*
+ * If interrupted we must cancel the call so it doesn't prevent us from making another one.
+ */
+ if (rc == VERR_INTERRUPTED)
+ {
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_CANCEL, 0);
+ int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr));
+ AssertRC(rc2);
+ }
+
+ /*
+ * If restored, update pidRestoreCheck.
+ */
+ if (rc == VERR_VM_RESTORED && pidRestoreCheck)
+ *pidRestoreCheck = Msg.idMsg.u.value64;
+
+ *pidMsg = UINT32_MAX - 1;
+ *pcParameters = UINT32_MAX - 2;
+ return rc;
+ }
+
+ /*
+ * Fallback if host < v6.0.
+ *
+ * Note! The restore check isn't perfect. Would require checking afterwards
+ * and stash the result if we were restored during the call. Too much
+ * hazzle for a downgrade scenario.
+ */
+ if (pidRestoreCheck)
+ {
+ uint64_t idRestoreCur = *pidRestoreCheck;
+ rc = VbglR3GetSessionId(&idRestoreCur);
+ if (RT_SUCCESS(rc) && idRestoreCur != *pidRestoreCheck)
+ {
+ *pidRestoreCheck = idRestoreCur;
+ return VERR_VM_RESTORED;
+ }
+ }
+
+ rc = vbglR3GuestCtrlMsgWaitFor(idClient, pidMsg, pcParameters);
+ if (rc == VERR_TOO_MUCH_DATA)
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+/**
+ * Asks the host guest control service to set a message filter to this
+ * client so that it only will receive certain messages in the future.
+ * The filter(s) are a bitmask for the context IDs, served from the host.
+ *
+ * @return IPRT status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param uValue The value to filter messages for.
+ * @param uMaskAdd Filter mask to add.
+ * @param uMaskRemove Filter mask to remove.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgFilterSet(uint32_t idClient, uint32_t uValue, uint32_t uMaskAdd, uint32_t uMaskRemove)
+{
+ HGCMMsgFilterSet Msg;
+
+ /* Tell the host we want to set a filter. */
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_FILTER_SET, 4);
+ VbglHGCMParmUInt32Set(&Msg.value, uValue);
+ VbglHGCMParmUInt32Set(&Msg.mask_add, uMaskAdd);
+ VbglHGCMParmUInt32Set(&Msg.mask_remove, uMaskRemove);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Replies to a message from the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param rc Guest rc to reply.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ int rc)
+{
+ return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */,
+ NULL /* pvPayload */, 0 /* cbPayload */);
+}
+
+
+/**
+ * Replies to a message from the host, extended version.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param rc Guest rc to reply.
+ * @param uType Reply type; not used yet and must be 0.
+ * @param pvPayload Pointer to data payload to reply. Optional.
+ * @param cbPayload Size of data payload (in bytes) to reply.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgReplyEx(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ int rc, uint32_t uType,
+ void *pvPayload, uint32_t cbPayload)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ /* Everything else is optional. */
+
+ HGCMMsgReply Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_REPLY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, uType);
+ VbglHGCMParmUInt32Set(&Msg.rc, (uint32_t)rc); /* int vs. uint32_t */
+ VbglHGCMParmPtrSet(&Msg.payload, pvPayload, cbPayload);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Tell the host to skip the current message replying VERR_NOT_SUPPORTED
+ *
+ * @return IPRT status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ * @param rcSkip The status code to pass back to Main when skipping.
+ * @param idMsg The message ID to skip, pass UINT32_MAX to pass any.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t idClient, int rcSkip, uint32_t idMsg)
+{
+ if (vbglR3GuestCtrlSupportsPeekGetCancel(idClient))
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter rcSkip;
+ HGCMFunctionParameter idMsg;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SKIP, 2);
+ VbglHGCMParmUInt32Set(&Msg.rcSkip, (uint32_t)rcSkip);
+ VbglHGCMParmUInt32Set(&Msg.idMsg, idMsg);
+ return VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ }
+
+ /* This is generally better than nothing... */
+ return VbglR3GuestCtrlMsgSkipOld(idClient);
+}
+
+
+/**
+ * Tells the host service to skip the current message returned by
+ * VbglR3GuestCtrlMsgWaitFor().
+ *
+ * @return IPRT status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgSkipOld(uint32_t idClient)
+{
+ HGCMMsgSkip Msg;
+
+ /* Tell the host we want to skip the current assigned message. */
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_SKIP_OLD, 1);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */);
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Asks the host to cancel (release) all pending waits which were deferred.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID returned by VbglR3GuestCtrlConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t idClient)
+{
+ HGCMMsgCancelPendingWaits Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_MSG_CANCEL, 0);
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Prepares a session.
+ * @since 6.0
+ * @sa GUEST_SESSION_PREPARE
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionPrepare(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idSession;
+ HGCMFunctionParameter pKey;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_PREPARE, 2);
+ VbglHGCMParmUInt32Set(&Msg.idSession, idSession);
+ VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey);
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+}
+
+
+/**
+ * Accepts a session.
+ * @since 6.0
+ * @sa GUEST_SESSION_ACCEPT
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionAccept(uint32_t idClient, uint32_t idSession, void const *pvKey, uint32_t cbKey)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idSession;
+ HGCMFunctionParameter pKey;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_ACCEPT, 2);
+ VbglHGCMParmUInt32Set(&Msg.idSession, idSession);
+ VbglHGCMParmPtrSet(&Msg.pKey, (void *)pvKey, cbKey);
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+}
+
+
+/**
+ * Cancels a prepared session.
+ * @since 6.0
+ * @sa GUEST_SESSION_CANCEL_PREPARED
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionCancelPrepared(uint32_t idClient, uint32_t idSession)
+{
+ int rc;
+ do
+ {
+ struct
+ {
+ VBGLIOCHGCMCALL Hdr;
+ HGCMFunctionParameter idSession;
+ } Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_MSG_SESSION_CANCEL_PREPARED, 1);
+ VbglHGCMParmUInt32Set(&Msg.idSession, idSession);
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ } while (rc == VERR_INTERRUPTED);
+ return rc;
+}
+
+
+/**
+ * Invalidates the internal state because the (VM) session has been changed (i.e. restored).
+ *
+ * @returns VBox status code.
+ * @param idClient Client ID to use for invalidating state.
+ * @param idNewControlSession New control session ID. Currently unused.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionHasChanged(uint32_t idClient, uint64_t idNewControlSession)
+{
+ RT_NOREF(idNewControlSession);
+
+ vbglR3GuestCtrlDetectPeekGetCancelSupport(idClient);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Asks a specific guest session to close.
+ *
+ * @return IPRT status code.
+ * @param pCtx Guest control command context to use.
+ * @param fFlags Some kind of flag. Figure it out yourself.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t fFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+
+ HGCMMsgSessionClose Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_CLOSE, pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.flags, fFlags);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Notifies a guest session.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uType Notification type of type GUEST_SESSION_NOTIFYTYPE_XXX.
+ * @param iResult Result code (rc) to notify.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionNotify(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uType, int32_t iResult)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgSessionNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_SESSION_NOTIFY, 3);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, uType);
+ VbglHGCMParmUInt32Set(&Msg.result, (uint32_t)iResult);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+/**
+ * Initializes a session startup info, extended version.
+ *
+ * @returns VBox status code.
+ * @param pStartupInfo Session startup info to initializes.
+ * @param cbUser Size (in bytes) to use for the user name buffer.
+ * @param cbPassword Size (in bytes) to use for the password buffer.
+ * @param cbDomain Size (in bytes) to use for the domain name buffer.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInitEx(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo,
+ size_t cbUser, size_t cbPassword, size_t cbDomain)
+{
+ AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
+
+ RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO));
+
+#define ALLOC_STR(a_Str, a_cb) \
+ if ((a_cb) > 0) \
+ { \
+ pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \
+ AssertPtrBreak(pStartupInfo->psz##a_Str); \
+ pStartupInfo->cb##a_Str = (uint32_t)a_cb; \
+ }
+
+ do
+ {
+ ALLOC_STR(User, cbUser);
+ ALLOC_STR(Password, cbPassword);
+ ALLOC_STR(Domain, cbDomain);
+
+ return VINF_SUCCESS;
+
+ } while (0);
+
+#undef ALLOC_STR
+
+ VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo);
+ return VERR_NO_MEMORY;
+}
+
+/**
+ * Initializes a session startup info.
+ *
+ * @returns VBox status code.
+ * @param pStartupInfo Session startup info to initializes.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionStartupInfoInit(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo)
+{
+ return VbglR3GuestCtrlSessionStartupInfoInitEx(pStartupInfo,
+ GUEST_PROC_DEF_USER_LEN, GUEST_PROC_DEF_PASSWORD_LEN,
+ GUEST_PROC_DEF_DOMAIN_LEN);
+}
+
+/**
+ * Destroys a session startup info.
+ *
+ * @param pStartupInfo Session startup info to destroy.
+ */
+VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoDestroy(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo)
+{
+ if (!pStartupInfo)
+ return;
+
+ RTStrFree(pStartupInfo->pszUser);
+ RTStrFree(pStartupInfo->pszPassword);
+ RTStrFree(pStartupInfo->pszDomain);
+
+ RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO));
+}
+
+/**
+ * Free's a session startup info.
+ *
+ * @param pStartupInfo Session startup info to free.
+ * The pointer will not be valid anymore after return.
+ */
+VBGLR3DECL(void) VbglR3GuestCtrlSessionStartupInfoFree(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo)
+{
+ if (!pStartupInfo)
+ return;
+
+ VbglR3GuestCtrlSessionStartupInfoDestroy(pStartupInfo);
+
+ RTMemFree(pStartupInfo);
+ pStartupInfo = NULL;
+}
+
+/**
+ * Duplicates a session startup info.
+ *
+ * @returns Duplicated session startup info on success, or NULL on error.
+ * @param pStartupInfo Session startup info to duplicate.
+ */
+VBGLR3DECL(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO) VbglR3GuestCtrlSessionStartupInfoDup(PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo)
+{
+ AssertPtrReturn(pStartupInfo, NULL);
+
+ PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO)
+ RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO));
+ if (pStartupInfoDup)
+ {
+ do
+ {
+ pStartupInfoDup->pszUser = NULL;
+ pStartupInfoDup->pszPassword = NULL;
+ pStartupInfoDup->pszDomain = NULL;
+
+#define DUP_STR(a_Str) \
+ if (pStartupInfo->cb##a_Str) \
+ { \
+ pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \
+ AssertPtrBreak(pStartupInfoDup->psz##a_Str); \
+ pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \
+ }
+ DUP_STR(User);
+ DUP_STR(Password);
+ DUP_STR(Domain);
+
+#undef DUP_STR
+
+ return pStartupInfoDup;
+
+ } while (0); /* To use break macros above. */
+
+ VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfoDup);
+ }
+
+ return NULL;
+}
+
+/**
+ * Retrieves a HOST_SESSION_CREATE message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param ppStartupInfo Where to store the allocated session startup info.
+ * Needs to be free'd by VbglR3GuestCtrlSessionStartupInfoFree().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLSESSIONSTARTUPINFO *ppStartupInfo)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER);
+
+ PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo
+ = (PVBGLR3GUESTCTRLSESSIONSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLSESSIONSTARTUPINFO));
+ if (!pStartupInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = VbglR3GuestCtrlSessionStartupInfoInit(pStartupInfo);
+ if (RT_FAILURE(rc))
+ {
+ VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo);
+ return rc;
+ }
+
+ do
+ {
+ HGCMMsgSessionOpen Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CREATE);
+ VbglHGCMParmUInt32Set(&Msg.protocol, 0);
+ VbglHGCMParmPtrSet(&Msg.username, pStartupInfo->pszUser, pStartupInfo->cbUser);
+ VbglHGCMParmPtrSet(&Msg.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword);
+ VbglHGCMParmPtrSet(&Msg.domain, pStartupInfo->pszDomain, pStartupInfo->cbDomain);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.protocol.GetUInt32(&pStartupInfo->uProtocol);
+ Msg.flags.GetUInt32(&pStartupInfo->fFlags);
+
+ pStartupInfo->uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID);
+ }
+
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppStartupInfo = pStartupInfo;
+ }
+ else
+ VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_SESSION_CLOSE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfFlags, uint32_t *pidSession)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgSessionClose Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SESSION_CLOSE);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.flags.GetUInt32(pfFlags);
+
+ if (pidSession)
+ *pidSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_PATH_RENAME message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlPathGetRename(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ char *pszSource, uint32_t cbSource,
+ char *pszDest, uint32_t cbDest,
+ uint32_t *pfFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
+ AssertReturn(cbSource, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
+ AssertReturn(cbDest, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgPathRename Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_RENAME);
+ VbglHGCMParmPtrSet(&Msg.source, pszSource, cbSource);
+ VbglHGCMParmPtrSet(&Msg.dest, pszDest, cbDest);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.flags.GetUInt32(pfFlags);
+ }
+
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_PATH_USER_DOCUMENTS message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserDocuments(PVBGLR3GUESTCTRLCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER);
+
+ int rc;
+ do
+ {
+ HGCMMsgPathUserDocuments Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_DOCUMENTS);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_PATH_USER_HOME message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlPathGetUserHome(PVBGLR3GUESTCTRLCMDCTX pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 1, VERR_INVALID_PARAMETER);
+
+ int rc;
+ do
+ {
+ HGCMMsgPathUserHome Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_PATH_USER_HOME);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+/**
+ * Retrieves a HOST_MSG_SHUTDOWN message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param pfAction Where to store the action flags on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlGetShutdown(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *pfAction)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfAction, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgShutdown Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_SHUTDOWN);
+ VbglHGCMParmUInt32Set(&Msg.action, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.action.GetUInt32(pfAction);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+/**
+ * Initializes a process startup info, extended version.
+ *
+ * @returns VBox status code.
+ * @param pStartupInfo Process startup info to initializes.
+ * @param cbCmd Size (in bytes) to use for the command buffer.
+ * @param cbUser Size (in bytes) to use for the user name buffer.
+ * @param cbPassword Size (in bytes) to use for the password buffer.
+ * @param cbDomain Size (in bytes) to use for the domain buffer.
+ * @param cbArgs Size (in bytes) to use for the arguments buffer.
+ * @param cbEnv Size (in bytes) to use for the environment buffer.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInitEx(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo,
+ size_t cbCmd,
+ size_t cbUser, size_t cbPassword, size_t cbDomain,
+ size_t cbArgs, size_t cbEnv)
+{
+ AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER);
+ AssertReturn(cbCmd, VERR_INVALID_PARAMETER);
+ AssertReturn(cbUser, VERR_INVALID_PARAMETER);
+ AssertReturn(cbPassword, VERR_INVALID_PARAMETER);
+ AssertReturn(cbDomain, VERR_INVALID_PARAMETER);
+ AssertReturn(cbArgs, VERR_INVALID_PARAMETER);
+ AssertReturn(cbEnv, VERR_INVALID_PARAMETER);
+
+ RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO));
+
+#define ALLOC_STR(a_Str, a_cb) \
+ if ((a_cb) > 0) \
+ { \
+ pStartupInfo->psz##a_Str = RTStrAlloc(a_cb); \
+ AssertPtrBreak(pStartupInfo->psz##a_Str); \
+ pStartupInfo->cb##a_Str = (uint32_t)a_cb; \
+ }
+
+ do
+ {
+ ALLOC_STR(Cmd, cbCmd);
+ ALLOC_STR(Args, cbArgs);
+ ALLOC_STR(Env, cbEnv);
+ ALLOC_STR(User, cbUser);
+ ALLOC_STR(Password, cbPassword);
+ ALLOC_STR(Domain, cbDomain);
+
+ return VINF_SUCCESS;
+
+ } while (0);
+
+#undef ALLOC_STR
+
+ VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo);
+ return VERR_NO_MEMORY;
+}
+
+/**
+ * Initializes a process startup info with default values.
+ *
+ * @param pStartupInfo Process startup info to initializes.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcStartupInfoInit(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo)
+{
+ return VbglR3GuestCtrlProcStartupInfoInitEx(pStartupInfo,
+ GUEST_PROC_DEF_CMD_LEN,
+ GUEST_PROC_DEF_USER_LEN /* Deprecated, now handled via session creation. */,
+ GUEST_PROC_DEF_PASSWORD_LEN /* Ditto. */,
+ GUEST_PROC_DEF_DOMAIN_LEN /* Ditto. */,
+ GUEST_PROC_DEF_ARGS_LEN, GUEST_PROC_DEF_ENV_LEN);
+}
+
+/**
+ * Destroys a process startup info.
+ *
+ * @param pStartupInfo Process startup info to destroy.
+ */
+VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoDestroy(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo)
+{
+ if (!pStartupInfo)
+ return;
+
+ RTStrFree(pStartupInfo->pszCmd);
+ RTStrFree(pStartupInfo->pszArgs);
+ RTStrFree(pStartupInfo->pszEnv);
+ RTStrFree(pStartupInfo->pszUser);
+ RTStrFree(pStartupInfo->pszPassword);
+ RTStrFree(pStartupInfo->pszDomain);
+
+ RT_BZERO(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO));
+}
+
+/**
+ * Free's a process startup info.
+ *
+ * @param pStartupInfo Process startup info to free.
+ * The pointer will not be valid anymore after return.
+ */
+VBGLR3DECL(void) VbglR3GuestCtrlProcStartupInfoFree(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo)
+{
+ if (!pStartupInfo)
+ return;
+
+ VbglR3GuestCtrlProcStartupInfoDestroy(pStartupInfo);
+
+ RTMemFree(pStartupInfo);
+ pStartupInfo = NULL;
+}
+
+/**
+ * Duplicates a process startup info.
+ *
+ * @returns Duplicated process startup info on success, or NULL on error.
+ * @param pStartupInfo Process startup info to duplicate.
+ */
+VBGLR3DECL(PVBGLR3GUESTCTRLPROCSTARTUPINFO) VbglR3GuestCtrlProcStartupInfoDup(PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo)
+{
+ AssertPtrReturn(pStartupInfo, NULL);
+
+ PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfoDup = (PVBGLR3GUESTCTRLPROCSTARTUPINFO)
+ RTMemDup(pStartupInfo, sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO));
+ if (pStartupInfoDup)
+ {
+ do
+ {
+ pStartupInfoDup->pszCmd = NULL;
+ pStartupInfoDup->pszArgs = NULL;
+ pStartupInfoDup->pszEnv = NULL;
+ pStartupInfoDup->pszUser = NULL;
+ pStartupInfoDup->pszPassword = NULL;
+ pStartupInfoDup->pszDomain = NULL;
+
+#define DUP_STR(a_Str) \
+ if (pStartupInfo->cb##a_Str) \
+ { \
+ pStartupInfoDup->psz##a_Str = (char *)RTStrDup(pStartupInfo->psz##a_Str); \
+ AssertPtrBreak(pStartupInfoDup->psz##a_Str); \
+ pStartupInfoDup->cb##a_Str = (uint32_t)strlen(pStartupInfoDup->psz##a_Str) + 1 /* Include terminator */; \
+ }
+
+#define DUP_MEM(a_Str) \
+ if (pStartupInfo->cb##a_Str) \
+ { \
+ pStartupInfoDup->psz##a_Str = (char *)RTMemDup(pStartupInfo->psz##a_Str, pStartupInfo->cb##a_Str); \
+ AssertPtrBreak(pStartupInfoDup->psz##a_Str); \
+ pStartupInfoDup->cb##a_Str = (uint32_t)pStartupInfo->cb##a_Str; \
+ }
+
+ DUP_STR(Cmd);
+ DUP_MEM(Args);
+ DUP_MEM(Env);
+ DUP_STR(User);
+ DUP_STR(Password);
+ DUP_STR(Domain);
+
+#undef DUP_STR
+#undef DUP_MEM
+
+ return pStartupInfoDup;
+
+ } while (0); /* To use break macros above. */
+
+ VbglR3GuestCtrlProcStartupInfoFree(pStartupInfoDup);
+ }
+
+ return NULL;
+}
+
+/**
+ * Retrieves a HOST_EXEC_CMD message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param ppStartupInfo Where to store the allocated session startup info.
+ * Needs to be free'd by VbglR3GuestCtrlProcStartupInfoFree().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx, PVBGLR3GUESTCTRLPROCSTARTUPINFO *ppStartupInfo)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppStartupInfo, VERR_INVALID_POINTER);
+
+ PVBGLR3GUESTCTRLPROCSTARTUPINFO pStartupInfo
+ = (PVBGLR3GUESTCTRLPROCSTARTUPINFO)RTMemAlloc(sizeof(VBGLR3GUESTCTRLPROCSTARTUPINFO));
+ if (!pStartupInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = VbglR3GuestCtrlProcStartupInfoInit(pStartupInfo);
+ if (RT_FAILURE(rc))
+ {
+ VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo);
+ return rc;
+ }
+
+ unsigned cRetries = 0;
+ const unsigned cMaxRetries = 32; /* Should be enough for now. */
+ const unsigned cGrowthFactor = 2; /* By how much the buffers will grow if they're too small yet. */
+
+ do
+ {
+ LogRel(("VbglR3GuestCtrlProcGetStart: Retrieving\n"));
+
+ HGCMMsgProcExec Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_CMD);
+ VbglHGCMParmPtrSet(&Msg.cmd, pStartupInfo->pszCmd, pStartupInfo->cbCmd);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+ VbglHGCMParmUInt32Set(&Msg.num_args, 0);
+ VbglHGCMParmPtrSet(&Msg.args, pStartupInfo->pszArgs, pStartupInfo->cbArgs);
+ VbglHGCMParmUInt32Set(&Msg.num_env, 0);
+ VbglHGCMParmUInt32Set(&Msg.cb_env, 0);
+ VbglHGCMParmPtrSet(&Msg.env, pStartupInfo->pszEnv, pStartupInfo->cbEnv);
+ if (pCtx->uProtocol < 2)
+ {
+ VbglHGCMParmPtrSet(&Msg.u.v1.username, pStartupInfo->pszUser, pStartupInfo->cbUser);
+ VbglHGCMParmPtrSet(&Msg.u.v1.password, pStartupInfo->pszPassword, pStartupInfo->cbPassword);
+ VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0);
+ }
+ else
+ {
+ VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0);
+ VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0);
+ VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0);
+ VbglHGCMParmPtrSet(&Msg.u.v2.affinity, pStartupInfo->uAffinity, sizeof(pStartupInfo->uAffinity));
+ }
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VbglR3GuestCtrlProcGetStart: 1 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n",
+ rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv));
+
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && cRetries++ < cMaxRetries)
+ {
+#define GROW_STR(a_Str, a_cbMax) \
+ pStartupInfo->psz##a_Str = (char *)RTMemRealloc(pStartupInfo->psz##a_Str, \
+ RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax)); \
+ AssertPtrBreakStmt(pStartupInfo->psz##a_Str, VERR_NO_MEMORY); \
+ pStartupInfo->cb##a_Str = RT_MIN(pStartupInfo->cb##a_Str * cGrowthFactor, a_cbMax);
+
+ /* We can't tell which parameter doesn't fit, so we have to resize all. */
+ GROW_STR(Cmd , GUEST_PROC_MAX_CMD_LEN);
+ GROW_STR(Args, GUEST_PROC_MAX_ARGS_LEN);
+ GROW_STR(Env, GUEST_PROC_MAX_ENV_LEN);
+
+#undef GROW_STR
+ LogRel(("VbglR3GuestCtrlProcGetStart: 2 - %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n",
+ rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv));
+ LogRel(("g_fVbglR3GuestCtrlHavePeekGetCancel=%RTbool\n", RT_BOOL(g_fVbglR3GuestCtrlHavePeekGetCancel)));
+ }
+ else
+ break;
+ }
+ else
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.flags.GetUInt32(&pStartupInfo->fFlags);
+ Msg.num_args.GetUInt32(&pStartupInfo->cArgs);
+ Msg.num_env.GetUInt32(&pStartupInfo->cEnvVars);
+ Msg.cb_env.GetUInt32(&pStartupInfo->cbEnv);
+ if (pCtx->uProtocol < 2)
+ Msg.u.v1.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS);
+ else
+ {
+ Msg.u.v2.timeout.GetUInt32(&pStartupInfo->uTimeLimitMS);
+ Msg.u.v2.priority.GetUInt32(&pStartupInfo->uPriority);
+ Msg.u.v2.num_affinity.GetUInt32(&pStartupInfo->cAffinity);
+ }
+ }
+ } while (( rc == VERR_INTERRUPTED
+ || rc == VERR_BUFFER_OVERFLOW) && g_fVbglR3GuestCtrlHavePeekGetCancel);
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppStartupInfo = pStartupInfo;
+ }
+ else
+ VbglR3GuestCtrlProcStartupInfoFree(pStartupInfo);
+
+ LogRel(("VbglR3GuestCtrlProcGetStart: Returning %Rrc (retry %u, cbCmd=%RU32, cbArgs=%RU32, cbEnv=%RU32)\n",
+ rc, cRetries, pStartupInfo->cbCmd, pStartupInfo->cbArgs, pStartupInfo->cbEnv));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Allocates and gets host data, based on the message ID.
+ *
+ * This will block until data becomes available.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param puPID Where to return the guest PID to retrieve output from on success.
+ * @param puHandle Where to return the guest process handle to retrieve output from on success.
+ * @param pfFlags Where to return the output flags on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetOutput(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puPID, uint32_t *puHandle, uint32_t *pfFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(puPID, VERR_INVALID_POINTER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgProcOutput Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_GET_OUTPUT);
+ VbglHGCMParmUInt32Set(&Msg.pid, 0);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMMsgProcOutput, data));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.pid.GetUInt32(puPID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.flags.GetUInt32(pfFlags);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves the input data from host which then gets sent to the started
+ * process (HOST_EXEC_SET_INPUT).
+ *
+ * This will block until data becomes available.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetInput(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puPID, uint32_t *pfFlags,
+ void *pvData, uint32_t cbData,
+ uint32_t *pcbSize)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(puPID, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgProcInput Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_SET_INPUT);
+ VbglHGCMParmUInt32Set(&Msg.pid, 0);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+ VbglHGCMParmPtrSet(&Msg.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.pid.GetUInt32(puPID);
+ Msg.flags.GetUInt32(pfFlags);
+ Msg.size.GetUInt32(pcbSize);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+
+ if ( rc != VERR_TOO_MUCH_DATA
+ || g_fVbglR3GuestCtrlHavePeekGetCancel)
+ return rc;
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+/**
+ * Retrieves a HOST_DIR_REMOVE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlDirGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ char *pszPath, uint32_t cbPath,
+ uint32_t *pfFlags)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cbPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfFlags, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgDirRemove Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_DIR_REMOVE);
+ VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.flags.GetUInt32(pfFlags);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_OPEN message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ char *pszFileName, uint32_t cbFileName,
+ char *pszAccess, uint32_t cbAccess,
+ char *pszDisposition, uint32_t cbDisposition,
+ char *pszSharing, uint32_t cbSharing,
+ uint32_t *puCreationMode,
+ uint64_t *poffAt)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+ AssertReturn(pCtx->uNumParms == 7, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
+ AssertReturn(cbFileName, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszAccess, VERR_INVALID_POINTER);
+ AssertReturn(cbAccess, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszDisposition, VERR_INVALID_POINTER);
+ AssertReturn(cbDisposition, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszSharing, VERR_INVALID_POINTER);
+ AssertReturn(cbSharing, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puCreationMode, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffAt, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileOpen Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_OPEN);
+ VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName);
+ VbglHGCMParmPtrSet(&Msg.openmode, pszAccess, cbAccess);
+ VbglHGCMParmPtrSet(&Msg.disposition, pszDisposition, cbDisposition);
+ VbglHGCMParmPtrSet(&Msg.sharing, pszSharing, cbSharing);
+ VbglHGCMParmUInt32Set(&Msg.creationmode, 0);
+ VbglHGCMParmUInt64Set(&Msg.offset, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.creationmode.GetUInt32(puCreationMode);
+ Msg.offset.GetUInt64(poffAt);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_CLOSE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileClose Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_CLOSE);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_READ message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint32_t *puToRead)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(puToRead, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileRead Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.size.GetUInt32(puToRead);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_READ_AT message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetReadAt(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puHandle, uint32_t *puToRead, uint64_t *poffAt)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(puToRead, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileReadAt Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_READ_AT);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmUInt64Set(&Msg.offset, 0);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.offset.GetUInt64(poffAt);
+ Msg.size.GetUInt32(puToRead);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_WRITE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle,
+ void *pvData, uint32_t cbData, uint32_t *pcbSize)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileWrite Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmPtrSet(&Msg.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.size.GetUInt32(pcbSize);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+
+ if ( rc != VERR_TOO_MUCH_DATA
+ || g_fVbglR3GuestCtrlHavePeekGetCancel)
+ return rc;
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_WRITE_AT message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetWriteAt(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle,
+ void *pvData, uint32_t cbData, uint32_t *pcbSize, uint64_t *poffAt)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileWriteAt Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_WRITE_AT);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmPtrSet(&Msg.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+ VbglHGCMParmUInt64Set(&Msg.offset, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.size.GetUInt32(pcbSize);
+ Msg.offset.GetUInt64(poffAt);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+
+ if ( rc != VERR_TOO_MUCH_DATA
+ || g_fVbglR3GuestCtrlHavePeekGetCancel)
+ return rc;
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_SEEK message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetSeek(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puHandle, uint32_t *puSeekMethod, uint64_t *poffAt)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(puSeekMethod, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffAt, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileSeek Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_SEEK);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+ VbglHGCMParmUInt32Set(&Msg.method, 0);
+ VbglHGCMParmUInt64Set(&Msg.offset, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ Msg.method.GetUInt32(puSeekMethod);
+ Msg.offset.GetUInt64(poffAt);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_TELL message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileTell Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_FILE_TELL);
+ VbglHGCMParmUInt32Set(&Msg.handle, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.handle.GetUInt32(puHandle);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_FILE_SET_SIZE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileGetSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, uint64_t *pcbNew)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbNew, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgFileSetSize Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.id32Context, HOST_MSG_FILE_SET_SIZE);
+ VbglHGCMParmUInt32Set(&Msg.id32Handle, 0);
+ VbglHGCMParmUInt64Set(&Msg.cb64NewSize, 0);
+
+ rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.id32Context.GetUInt32(&pCtx->uContextID);
+ Msg.id32Handle.GetUInt32(puHandle);
+ Msg.cb64NewSize.GetUInt64(pcbNew);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_EXEC_TERMINATE message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetTerminate(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puPID)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puPID, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgProcTerminate Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_TERMINATE);
+ VbglHGCMParmUInt32Set(&Msg.pid, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.pid.GetUInt32(puPID);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Retrieves a HOST_EXEC_WAIT_FOR message.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t *puPID, uint32_t *puWaitFlags, uint32_t *puTimeoutMS)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puPID, VERR_INVALID_POINTER);
+
+ int rc;
+ do
+ {
+ HGCMMsgProcWaitFor Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, vbglR3GuestCtrlGetMsgFunctionNo(pCtx->uClientID), pCtx->uNumParms);
+ VbglHGCMParmUInt32Set(&Msg.context, HOST_MSG_EXEC_WAIT_FOR);
+ VbglHGCMParmUInt32Set(&Msg.pid, 0);
+ VbglHGCMParmUInt32Set(&Msg.flags, 0);
+ VbglHGCMParmUInt32Set(&Msg.timeout, 0);
+
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ Msg.context.GetUInt32(&pCtx->uContextID);
+ Msg.pid.GetUInt32(puPID);
+ Msg.flags.GetUInt32(puWaitFlags);
+ Msg.timeout.GetUInt32(puTimeoutMS);
+ }
+ } while (rc == VERR_INTERRUPTED && g_fVbglR3GuestCtrlHavePeekGetCancel);
+ return rc;
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_OPEN message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param uFileHandle File handle of opened file on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc, uint32_t uFileHandle)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_OPEN);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt32Set(&Msg.u.open.handle, uFileHandle);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.open));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_CLOSE message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_CLOSE);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u));
+}
+
+
+/**
+ * Sends an unexpected file handling error to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbError(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 3);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_ERROR);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSETOF(HGCMReplyFileNotify, u));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_READ message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param pvData Pointer to read file data from guest on success.
+ * @param cbData Size (in bytes) of read file data from guest on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uRc,
+ void *pvData, uint32_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmPtrSet(&Msg.u.read.data, pvData, cbData);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.read));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_READ_AT message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param pvData Pointer to read file data from guest on success.
+ * @param cbData Size (in bytes) of read file data from guest on success.
+ * @param offNew New offset (in bytes) the guest file pointer points at on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbReadOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc,
+ void *pvData, uint32_t cbData, int64_t offNew)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ_OFFSET);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmPtrSet(&Msg.u.ReadOffset.pvData, pvData, cbData);
+ VbglHGCMParmUInt64Set(&Msg.u.ReadOffset.off64New, (uint64_t)offNew);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.ReadOffset));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_WRITE message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt32Set(&Msg.u.write.written, cbWritten);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.write));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_WRITE_AT message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param cbWritten Size (in bytes) of file data successfully written to guest file. Can be partial.
+ * @param offNew New offset (in bytes) the guest file pointer points at on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbWriteOffset(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint32_t cbWritten, int64_t offNew)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 5);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt32Set(&Msg.u.WriteOffset.cb32Written, cbWritten);
+ VbglHGCMParmUInt64Set(&Msg.u.WriteOffset.off64New, (uint64_t)offNew);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.WriteOffset));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_SEEK message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param offCurrent New offset (in bytes) the guest file pointer points at on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SEEK);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt64Set(&Msg.u.seek.offset, offCurrent);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.seek));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_TELL message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param offCurrent Current offset (in bytes) the guest file pointer points at on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t offCurrent)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_TELL);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt64Set(&Msg.u.tell.offset, offCurrent);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.tell));
+}
+
+
+/**
+ * Replies to a HOST_MSG_FILE_SET_SIZE message.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uRc Guest rc of operation (note: IPRT-style signed int).
+ * @param cbNew New file size (in bytes) of the guest file on success.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlFileCbSetSize(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc, uint64_t cbNew)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMReplyFileNotify Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_FILE_NOTIFY, 4);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SET_SIZE);
+ VbglHGCMParmUInt32Set(&Msg.rc, uRc);
+ VbglHGCMParmUInt64Set(&Msg.u.SetSize.cb64Size, cbNew);
+
+ return VbglR3HGCMCall(&Msg.hdr, RT_UOFFSET_AFTER(HGCMReplyFileNotify, u.SetSize));
+}
+
+
+/**
+ * Callback for reporting a guest process status (along with some other stuff) to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uPID Guest process PID to report status for.
+ * @param uStatus Status to report. Of type PROC_STS_XXX.
+ * @param fFlags Additional status flags, depending on the reported status. See RTPROCSTATUS.
+ * @param pvData Pointer to additional status data. Optional.
+ * @param cbData Size (in bytes) of additional status data.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uPID, uint32_t uStatus, uint32_t fFlags,
+ void *pvData, uint32_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgProcStatus Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_STATUS, 5);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.pid, uPID);
+ VbglHGCMParmUInt32Set(&Msg.status, uStatus);
+ VbglHGCMParmUInt32Set(&Msg.flags, fFlags);
+ VbglHGCMParmPtrSet(&Msg.data, pvData, cbData);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Sends output (from stdout/stderr) from a running process.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uPID Guest process PID to report status for.
+ * @param uHandle Guest process handle the output belong to.
+ * @param fFlags Additional output flags.
+ * @param pvData Pointer to actual output data.
+ * @param cbData Size (in bytes) of output data.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uPID,uint32_t uHandle, uint32_t fFlags,
+ void *pvData, uint32_t cbData)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgProcOutput Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_OUTPUT, 5);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.pid, uPID);
+ VbglHGCMParmUInt32Set(&Msg.handle, uHandle);
+ VbglHGCMParmUInt32Set(&Msg.flags, fFlags);
+ VbglHGCMParmPtrSet(&Msg.data, pvData, cbData);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Callback for reporting back the input status of a guest process to the host.
+ *
+ * @returns VBox status code.
+ * @param pCtx Guest control command context to use.
+ * @param uPID Guest process PID to report status for.
+ * @param uStatus Status to report. Of type INPUT_STS_XXX.
+ * @param fFlags Additional input flags.
+ * @param cbWritten Size (in bytes) of input data handled.
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx,
+ uint32_t uPID, uint32_t uStatus,
+ uint32_t fFlags, uint32_t cbWritten)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ HGCMMsgProcStatusInput Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_MSG_EXEC_INPUT_STATUS, 5);
+ VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID);
+ VbglHGCMParmUInt32Set(&Msg.pid, uPID);
+ VbglHGCMParmUInt32Set(&Msg.status, uStatus);
+ VbglHGCMParmUInt32Set(&Msg.flags, fFlags);
+ VbglHGCMParmUInt32Set(&Msg.written, cbWritten);
+
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp
new file mode 100644
index 00000000..106be26e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestProp.cpp
@@ -0,0 +1,1032 @@
+/* $Id: VBoxGuestR3LibGuestProp.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, guest properties.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#if defined(VBOX_VBGLR3_XFREE86) || defined(VBOX_VBGLR3_XORG)
+# define VBOX_VBGLR3_XSERVER
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#ifndef VBOX_VBGLR3_XSERVER
+# include <iprt/mem.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/stdarg.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/HostServices/GuestPropertySvc.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+#ifdef VBOX_VBGLR3_XFREE86
+/* Rather than try to resolve all the header file conflicts, I will just
+ prototype what we need here. */
+extern "C" char* xf86strcpy(char*,const char*);
+# undef strcpy
+# define strcpy xf86strcpy
+extern "C" void* xf86memchr(const void*,int,xf86size_t);
+# undef memchr
+# define memchr xf86memchr
+extern "C" void* xf86memset(const void*,int,xf86size_t);
+# undef memset
+# define memset xf86memset
+
+#endif /* VBOX_VBGLR3_XFREE86 */
+
+#ifdef VBOX_VBGLR3_XSERVER
+
+# undef RTStrEnd
+# define RTStrEnd xf86RTStrEnd
+
+DECLINLINE(char const *) RTStrEnd(char const *pszString, size_t cchMax)
+{
+ /* Avoid potential issues with memchr seen in glibc.
+ * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */
+ while (cchMax > RTSTR_MEMCHR_MAX)
+ {
+ char const *pszRet = (char const *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX);
+ if (RT_LIKELY(pszRet))
+ return pszRet;
+ pszString += RTSTR_MEMCHR_MAX;
+ cchMax -= RTSTR_MEMCHR_MAX;
+ }
+ return (char const *)memchr(pszString, '\0', cchMax);
+}
+
+DECLINLINE(char *) RTStrEnd(char *pszString, size_t cchMax)
+{
+ /* Avoid potential issues with memchr seen in glibc.
+ * See sysdeps/x86_64/memchr.S in glibc versions older than 2.11 */
+ while (cchMax > RTSTR_MEMCHR_MAX)
+ {
+ char *pszRet = (char *)memchr(pszString, '\0', RTSTR_MEMCHR_MAX);
+ if (RT_LIKELY(pszRet))
+ return pszRet;
+ pszString += RTSTR_MEMCHR_MAX;
+ cchMax -= RTSTR_MEMCHR_MAX;
+ }
+ return (char *)memchr(pszString, '\0', cchMax);
+}
+
+#endif /* VBOX_VBGLR3_XSERVER */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Structure containing information needed to enumerate through guest
+ * properties.
+ *
+ * @remarks typedef in VBoxGuestLib.h.
+ */
+struct VBGLR3GUESTPROPENUM
+{
+ /** @todo add a magic and validate the handle. */
+ /** The buffer containing the raw enumeration data */
+ char *pchBuf;
+ /** The end of the buffer */
+ char *pchBufEnd;
+ /** Pointer to the next entry to enumerate inside the buffer */
+ char *pchNext;
+};
+
+
+
+/**
+ * Connects to the guest property service.
+ *
+ * @returns VBox status code
+ * @returns VERR_NOT_SUPPORTED if guest properties are not available on the host.
+ * @param pidClient Where to put the client ID on success. The client ID
+ * must be passed to all the other calls to the service.
+ */
+VBGLR3DECL(int) VbglR3GuestPropConnect(HGCMCLIENTID *pidClient)
+{
+ int rc = VbglR3HGCMConnect("VBoxGuestPropSvc", pidClient);
+ if (rc == VERR_NOT_IMPLEMENTED || rc == VERR_HGCM_SERVICE_NOT_FOUND)
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+/**
+ * Disconnect from the guest property service.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestPropDisconnect(HGCMCLIENTID idClient)
+{
+ return VbglR3HGCMDisconnect(idClient);
+}
+
+
+/**
+ * Checks if @a pszPropName exists.
+ *
+ * @returns \c true if the guest property exists, \c false if not.
+ * @param idClient The HGCM client ID for the guest property session.
+ * @param pszPropName The property name.
+ */
+VBGLR3DECL(bool) VbglR3GuestPropExist(uint32_t idClient, const char *pszPropName)
+{
+ return RT_SUCCESS(VbglR3GuestPropReadEx(idClient, pszPropName, NULL /*ppszValue*/, NULL /* ppszFlags */, NULL /* puTimestamp */));
+}
+
+
+/**
+ * Write a property value.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3InvsSvcConnect().
+ * @param pszName The property to save to. Utf8
+ * @param pszValue The value to store. Utf8. If this is NULL then
+ * the property will be removed.
+ * @param pszFlags The flags for the property
+ */
+VBGLR3DECL(int) VbglR3GuestPropWrite(HGCMCLIENTID idClient, const char *pszName, const char *pszValue, const char *pszFlags)
+{
+ int rc;
+
+ if (pszValue != NULL)
+ {
+ GuestPropMsgSetProperty Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 3);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ VbglHGCMParmPtrSetString(&Msg.value, pszValue);
+ VbglHGCMParmPtrSetString(&Msg.flags, pszFlags);
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ }
+ else
+ {
+ GuestPropMsgDelProperty Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ }
+ return rc;
+}
+
+
+/**
+ * Write a property value.
+ *
+ * @returns VBox status code.
+ *
+ * @param idClient The client id returned by VbglR3InvsSvcConnect().
+ * @param pszName The property to save to. Must be valid UTF-8.
+ * @param pszValue The value to store. Must be valid UTF-8.
+ * If this is NULL then the property will be removed.
+ *
+ * @note if the property already exists and pszValue is not NULL then the
+ * property's flags field will be left unchanged
+ */
+VBGLR3DECL(int) VbglR3GuestPropWriteValue(HGCMCLIENTID idClient, const char *pszName, const char *pszValue)
+{
+ int rc;
+
+ if (pszValue != NULL)
+ {
+ GuestPropMsgSetPropertyValue Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_SET_PROP_VALUE, 2);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ VbglHGCMParmPtrSetString(&Msg.value, pszValue);
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ }
+ else
+ {
+ GuestPropMsgDelProperty Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ }
+ return rc;
+}
+
+#ifndef VBOX_VBGLR3_XSERVER
+/**
+ * Write a property value where the value is formatted in RTStrPrintfV fashion.
+ *
+ * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
+ *
+ * @param idClient The client ID returned by VbglR3InvsSvcConnect().
+ * @param pszName The property to save to. Must be valid UTF-8.
+ * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
+ * @param va The format arguments.
+ */
+VBGLR3DECL(int) VbglR3GuestPropWriteValueV(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, va_list va)
+{
+ /*
+ * Format the value and pass it on to the setter.
+ */
+ int rc = VERR_NO_STR_MEMORY;
+ char *pszValue;
+ if (RTStrAPrintfV(&pszValue, pszValueFormat, va) >= 0)
+ {
+ rc = VbglR3GuestPropWriteValue(idClient, pszName, pszValue);
+ RTStrFree(pszValue);
+ }
+ return rc;
+}
+
+
+/**
+ * Write a property value where the value is formatted in RTStrPrintf fashion.
+ *
+ * @returns The same as VbglR3GuestPropWriteValue with the addition of VERR_NO_STR_MEMORY.
+ *
+ * @param idClient The client ID returned by VbglR3InvsSvcConnect().
+ * @param pszName The property to save to. Must be valid UTF-8.
+ * @param pszValueFormat The value format. This must be valid UTF-8 when fully formatted.
+ * @param ... The format arguments.
+ */
+VBGLR3DECL(int) VbglR3GuestPropWriteValueF(HGCMCLIENTID idClient, const char *pszName, const char *pszValueFormat, ...)
+{
+ va_list va;
+ va_start(va, pszValueFormat);
+ int rc = VbglR3GuestPropWriteValueV(idClient, pszName, pszValueFormat, va);
+ va_end(va);
+ return rc;
+}
+#endif /* VBOX_VBGLR3_XSERVER */
+
+/**
+ * Retrieve a property.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success, pszValue, pu64Timestamp and pszFlags
+ * containing valid data.
+ * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pcBuf is not large
+ * enough. In this case the size needed will be placed in
+ * @a pcbBufActual if it is not NULL.
+ * @retval VERR_NOT_FOUND if the key wasn't found.
+ *
+ * @param idClient The client id returned by VbglR3GuestPropConnect().
+ * @param pszName The value to read. Utf8
+ * @param pvBuf A scratch buffer to store the data retrieved into.
+ * The returned data is only valid for it's lifetime.
+ * @a ppszValue will point to the start of this buffer.
+ * @param cbBuf The size of @a pcBuf
+ * @param ppszValue Where to store the pointer to the value retrieved.
+ * Optional.
+ * @param pu64Timestamp Where to store the timestamp. Optional.
+ * @param ppszFlags Where to store the pointer to the flags. Optional.
+ * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
+ * Optional.
+ */
+VBGLR3DECL(int) VbglR3GuestPropRead(HGCMCLIENTID idClient, const char *pszName,
+ void *pvBuf, uint32_t cbBuf,
+ char **ppszValue, uint64_t *pu64Timestamp,
+ char **ppszFlags,
+ uint32_t *pcbBufActual)
+{
+ /*
+ * Create the GET_PROP message and call the host.
+ */
+ GuestPropMsgGetProperty Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_GET_PROP, 4);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf);
+ VbglHGCMParmUInt64Set(&Msg.timestamp, 0);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ /*
+ * The cbBufActual parameter is also returned on overflow so the call can
+ * adjust his/her buffer.
+ */
+ if ( rc == VERR_BUFFER_OVERFLOW
+ || pcbBufActual != NULL)
+ {
+ int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
+ AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Buffer layout: Value\0Flags\0.
+ *
+ * If the caller cares about any of these strings, make sure things are
+ * properly terminated (paranoia).
+ */
+ if ( RT_SUCCESS(rc)
+ && (ppszValue != NULL || ppszFlags != NULL))
+ {
+ /* Validate / skip 'Name'. */
+ char *pszFlags = RTStrEnd((char *)pvBuf, cbBuf) + 1;
+ AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
+ if (ppszValue)
+ *ppszValue = (char *)pvBuf;
+
+ if (ppszFlags)
+ {
+ /* Validate 'Flags'. */
+ char *pszEos = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf));
+ AssertPtrReturn(pszEos, VERR_TOO_MUCH_DATA);
+ *ppszFlags = pszFlags;
+ }
+ }
+
+ /* And the timestamp, if requested. */
+ if (pu64Timestamp != NULL)
+ {
+ rc = VbglHGCMParmUInt64Get(&Msg.timestamp, pu64Timestamp);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Reads a guest property by returning allocated values.
+ *
+ * @returns VBox status code, fully bitched.
+ *
+ * @param idClient The HGCM client ID for the guest property session.
+ * @param pszPropName The property name.
+ * @param ppszValue Where to return the value. This is always set
+ * to NULL. Needs to be free'd using RTStrFree(). Optional.
+ * @param ppszFlags Where to return the value flags.
+ * Needs to be free'd using RTStrFree(). Optional.
+ * @param puTimestamp Where to return the timestamp. This is only set
+ * on success. Optional.
+ */
+VBGLR3DECL(int) VbglR3GuestPropReadEx(uint32_t idClient,
+ const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp)
+{
+ AssertPtrReturn(pszPropName, VERR_INVALID_POINTER);
+
+ uint32_t cbBuf = _1K;
+ void *pvBuf = NULL;
+ int rc = VINF_SUCCESS; /* MSC can't figure out the loop */
+
+ if (ppszValue)
+ *ppszValue = NULL;
+
+ for (unsigned cTries = 0; cTries < 10; cTries++)
+ {
+ /*
+ * (Re-)Allocate the buffer and try read the property.
+ */
+ RTMemFree(pvBuf);
+ pvBuf = RTMemAlloc(cbBuf);
+ if (!pvBuf)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ char *pszValue;
+ char *pszFlags;
+ uint64_t uTimestamp;
+ rc = VbglR3GuestPropRead(idClient, pszPropName, pvBuf, cbBuf, &pszValue, &uTimestamp, &pszFlags, NULL);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ /* try again with a bigger buffer. */
+ cbBuf *= 2;
+ continue;
+ }
+ break;
+ }
+
+ if (ppszValue)
+ {
+ *ppszValue = RTStrDup(pszValue);
+ if (!*ppszValue)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+
+ if (puTimestamp)
+ *puTimestamp = uTimestamp;
+ if (ppszFlags)
+ *ppszFlags = RTStrDup(pszFlags);
+ break; /* done */
+ }
+
+ if (pvBuf)
+ RTMemFree(pvBuf);
+ return rc;
+}
+
+#ifndef VBOX_VBGLR3_XSERVER
+/**
+ * Retrieve a property value, allocating space for it.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success, *ppszValue containing valid data.
+ * @retval VERR_NOT_FOUND if the key wasn't found.
+ * @retval VERR_TOO_MUCH_DATA if we were unable to determine the right size
+ * to allocate for the buffer. This can happen as the result of a
+ * race between our allocating space and the host changing the
+ * property value.
+ *
+ * @param idClient The client id returned by VbglR3GuestPropConnect().
+ * @param pszName The value to read. Must be valid UTF-8.
+ * @param ppszValue Where to store the pointer to the value returned.
+ * This is always set to NULL or to the result, even
+ * on failure.
+ */
+VBGLR3DECL(int) VbglR3GuestPropReadValueAlloc(HGCMCLIENTID idClient, const char *pszName, char **ppszValue)
+{
+ /*
+ * Quick input validation.
+ */
+ AssertPtr(ppszValue);
+ *ppszValue = NULL;
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ /*
+ * There is a race here between our reading the property size and the
+ * host changing the value before we read it. Try up to ten times and
+ * report the problem if that fails.
+ */
+ char *pszValue = NULL;
+ void *pvBuf = NULL;
+ uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN;
+ int rc = VERR_BUFFER_OVERFLOW;
+ for (unsigned i = 0; i < 10 && rc == VERR_BUFFER_OVERFLOW; ++i)
+ {
+ /* We leave a bit of space here in case the maximum value is raised. */
+ cbBuf += 1024;
+ void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+ if (pvTmpBuf)
+ {
+ pvBuf = pvTmpBuf;
+ rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cbBuf, &pszValue, NULL, NULL, &cbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pszValue == (char *)pvBuf);
+ *ppszValue = pszValue;
+ }
+ else
+ {
+ RTMemFree(pvBuf);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ /* VERR_BUFFER_OVERFLOW has a different meaning here as a
+ * return code, but we need to report the race. */
+ rc = VERR_TOO_MUCH_DATA;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Free the memory used by VbglR3GuestPropReadValueAlloc for returning a
+ * value.
+ *
+ * @param pszValue the memory to be freed. NULL pointers will be ignored.
+ */
+VBGLR3DECL(void) VbglR3GuestPropReadValueFree(char *pszValue)
+{
+ RTMemFree(pszValue);
+}
+#endif /* VBOX_VBGLR3_XSERVER */
+
+/**
+ * Retrieve a property value, using a user-provided buffer to store it.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success, pszValue containing valid data.
+ * @retval VERR_BUFFER_OVERFLOW and the size needed in pcchValueActual if the
+ * buffer provided was too small
+ * @retval VERR_NOT_FOUND if the key wasn't found.
+ *
+ * @note There is a race here between obtaining the size of the buffer
+ * needed to hold the value and the value being updated.
+ *
+ * @param idClient The client id returned by VbglR3GuestPropConnect().
+ * @param pszName The value to read. Utf8
+ * @param pszValue Where to store the value retrieved.
+ * @param cchValue The size of the buffer pointed to by @a pszValue
+ * @param pcchValueActual Where to store the size of the buffer needed if
+ * the buffer supplied is too small. Optional.
+ */
+VBGLR3DECL(int) VbglR3GuestPropReadValue(HGCMCLIENTID idClient, const char *pszName,
+ char *pszValue, uint32_t cchValue,
+ uint32_t *pcchValueActual)
+{
+ void *pvBuf = pszValue;
+ uint32_t cchValueActual;
+ int rc = VbglR3GuestPropRead(idClient, pszName, pvBuf, cchValue, &pszValue, NULL, NULL, &cchValueActual);
+ if (pcchValueActual != NULL)
+ *pcchValueActual = cchValueActual;
+ return rc;
+}
+
+
+#ifndef VBOX_VBGLR3_XSERVER
+/**
+ * Raw API for enumerating guest properties which match a given pattern.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success and pcBuf points to a packed array
+ * of the form \<name\>, \<value\>, \<timestamp string\>, \<flags\>,
+ * terminated by four empty strings. pcbBufActual will contain the
+ * total size of the array.
+ * @retval VERR_BUFFER_OVERFLOW if the buffer provided was too small. In
+ * this case pcbBufActual will contain the size of the buffer needed.
+ * @returns IPRT error code in other cases, and pchBufActual is undefined.
+ *
+ * @param idClient The client ID returned by VbglR3GuestPropConnect
+ * @param pszzPatterns A packed array of zero terminated strings, terminated
+ * by an empty string.
+ * @param pcBuf The buffer to store the results to.
+ * @param cbBuf The size of the buffer
+ * @param pcbBufActual Where to store the size of the returned data on
+ * success or the buffer size needed if @a pcBuf is too
+ * small.
+ */
+VBGLR3DECL(int) VbglR3GuestPropEnumRaw(HGCMCLIENTID idClient,
+ const char *pszzPatterns,
+ char *pcBuf,
+ uint32_t cbBuf,
+ uint32_t *pcbBufActual)
+{
+ GuestPropMsgEnumProperties Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_ENUM_PROPS, 3);
+
+ /* Get the length of the patterns array... */
+ size_t cchPatterns = 0;
+ for (size_t cchCurrent = strlen(pszzPatterns); cchCurrent != 0;
+ cchCurrent = strlen(pszzPatterns + cchPatterns))
+ cchPatterns += cchCurrent + 1;
+ /* ...including the terminator. */
+ ++cchPatterns;
+ VbglHGCMParmPtrSet(&Msg.patterns, (char *)pszzPatterns, (uint32_t)cchPatterns);
+ VbglHGCMParmPtrSet(&Msg.strings, pcBuf, cbBuf);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+ if ( pcbBufActual
+ && ( RT_SUCCESS(rc)
+ || rc == VERR_BUFFER_OVERFLOW))
+ {
+ int rc2 = VbglHGCMParmUInt32Get(&Msg.size, pcbBufActual);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ return rc;
+}
+
+
+/**
+ * Start enumerating guest properties which match a given pattern.
+ *
+ * This function creates a handle which can be used to continue enumerating.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success, *ppHandle points to a handle for continuing
+ * the enumeration and *ppszName, *ppszValue, *pu64Timestamp and
+ * *ppszFlags are set.
+ * @retval VERR_TOO_MUCH_DATA if it was not possible to determine the amount
+ * of local space needed to store all the enumeration data. This is
+ * due to a race between allocating space and the host adding new
+ * data, so retrying may help here. Other parameters are left
+ * uninitialised
+ *
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ * @param papszPatterns The patterns against which the properties are
+ * matched. Pass NULL if everything should be matched.
+ * @param cPatterns The number of patterns in @a papszPatterns. 0 means
+ * match everything.
+ * @param ppHandle where the handle for continued enumeration is stored
+ * on success. This must be freed with
+ * VbglR3GuestPropEnumFree when it is no longer needed.
+ * @param ppszName Where to store the next property name. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ * @param ppszValue Where to store the next property value. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ * @param pu64Timestamp Where to store the next property timestamp. This
+ * will be set to zero if there are no more properties
+ * to enumerate. Optional.
+ * @param ppszFlags Where to store the next property flags. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ *
+ * @remarks While all output parameters are optional, you need at least one to
+ * figure out when to stop.
+ */
+VBGLR3DECL(int) VbglR3GuestPropEnum(HGCMCLIENTID idClient,
+ char const * const *papszPatterns,
+ uint32_t cPatterns,
+ PVBGLR3GUESTPROPENUM *ppHandle,
+ char const **ppszName,
+ char const **ppszValue,
+ uint64_t *pu64Timestamp,
+ char const **ppszFlags)
+{
+ /* Create the handle. */
+ PVBGLR3GUESTPROPENUM pHandle = (PVBGLR3GUESTPROPENUM)RTMemAllocZ(sizeof(VBGLR3GUESTPROPENUM));
+ if (RT_LIKELY(pHandle))
+ {/* likely */}
+ else
+ return VERR_NO_MEMORY;
+
+ /* Get the length of the pattern string, including the final terminator. */
+ size_t cbPatterns = 1;
+ for (uint32_t i = 0; i < cPatterns; ++i)
+ cbPatterns += strlen(papszPatterns[i]) + 1;
+
+ /* Pack the pattern array. */
+ char *pszzPatterns = (char *)RTMemAlloc(cbPatterns);
+ size_t off = 0;
+ for (uint32_t i = 0; i < cPatterns; ++i)
+ {
+ size_t cb = strlen(papszPatterns[i]) + 1;
+ memcpy(&pszzPatterns[off], papszPatterns[i], cb);
+ off += cb;
+ }
+ pszzPatterns[off] = '\0';
+
+ /* In reading the guest property data we are racing against the host
+ * adding more of it, so loop a few times and retry on overflow. */
+ uint32_t cbBuf = 4096; /* picked out of thin air */
+ char *pchBuf = NULL;
+ int rc = VINF_SUCCESS;
+ for (int i = 0; i < 10; ++i)
+ {
+ void *pvNew = RTMemRealloc(pchBuf, cbBuf);
+ if (pvNew)
+ pchBuf = (char *)pvNew;
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ rc = VbglR3GuestPropEnumRaw(idClient, pszzPatterns, pchBuf, cbBuf, &cbBuf);
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ cbBuf += 4096; /* Just to increase our chances */
+ }
+ RTMemFree(pszzPatterns);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Complete the handle and call VbglR3GuestPropEnumNext to retrieve the first entry.
+ */
+ pHandle->pchNext = pchBuf;
+ pHandle->pchBuf = pchBuf;
+ pHandle->pchBufEnd = pchBuf + cbBuf;
+
+ const char *pszNameTmp;
+ if (!ppszName)
+ ppszName = &pszNameTmp;
+ rc = VbglR3GuestPropEnumNext(pHandle, ppszName, ppszValue, pu64Timestamp, ppszFlags);
+ if (RT_SUCCESS(rc))
+ {
+ *ppHandle = pHandle;
+ return rc;
+ }
+ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ rc = VERR_TOO_MUCH_DATA;
+ RTMemFree(pchBuf);
+ RTMemFree(pHandle);
+ return rc;
+}
+
+
+/**
+ * Get the next guest property.
+ *
+ * See @a VbglR3GuestPropEnum.
+ *
+ * @returns VBox status code.
+ *
+ * @param pHandle Handle obtained from @a VbglR3GuestPropEnum.
+ * @param ppszName Where to store the next property name. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ * @param ppszValue Where to store the next property value. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ * @param pu64Timestamp Where to store the next property timestamp. This
+ * will be set to zero if there are no more properties
+ * to enumerate. Optional.
+ * @param ppszFlags Where to store the next property flags. This will be
+ * set to NULL if there are no more properties to
+ * enumerate. This pointer should not be freed. Optional.
+ *
+ * @remarks While all output parameters are optional, you need at least one to
+ * figure out when to stop.
+ */
+VBGLR3DECL(int) VbglR3GuestPropEnumNext(PVBGLR3GUESTPROPENUM pHandle,
+ char const **ppszName,
+ char const **ppszValue,
+ uint64_t *pu64Timestamp,
+ char const **ppszFlags)
+{
+ /*
+ * The VBGLR3GUESTPROPENUM structure contains a buffer containing the raw
+ * properties data and a pointer into the buffer which tracks how far we
+ * have parsed so far. The buffer contains packed strings in groups of
+ * four - name, value, timestamp (as a decimal string) and flags. It is
+ * terminated by four empty strings. We can rely on this layout unless
+ * the caller has been poking about in the structure internals, in which
+ * case they must take responsibility for the results.
+ *
+ * Layout:
+ * Name\0Value\0Timestamp\0Flags\0
+ */
+ char *pchNext = pHandle->pchNext; /* The cursor. */
+ char *pchEnd = pHandle->pchBufEnd; /* End of buffer, for size calculations. */
+
+ char *pszName = pchNext;
+ char *pszValue = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
+ AssertPtrReturn(pchNext, VERR_PARSE_ERROR); /* 0x1 is also an invalid pointer :) */
+
+ char *pszTimestamp = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
+ AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
+
+ char *pszFlags = pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
+ AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
+
+ /*
+ * Don't move the index pointer if we found the terminating "\0\0\0\0" entry.
+ * Don't try convert the timestamp either.
+ */
+ uint64_t u64Timestamp;
+ if (*pszName != '\0')
+ {
+ pchNext = RTStrEnd(pchNext, pchEnd - pchNext) + 1;
+ AssertPtrReturn(pchNext, VERR_PARSE_ERROR);
+
+ /* Convert the timestamp string into a number. */
+ int rc = RTStrToUInt64Full(pszTimestamp, 0, &u64Timestamp);
+ AssertRCSuccessReturn(rc, VERR_PARSE_ERROR);
+
+ pHandle->pchNext = pchNext;
+ AssertPtr(pchNext);
+ }
+ else
+ {
+ u64Timestamp = 0;
+ AssertMsgReturn(!*pszValue && !*pszTimestamp && !*pszFlags,
+ ("'%s' '%s' '%s'\n", pszValue, pszTimestamp, pszFlags),
+ VERR_PARSE_ERROR);
+ }
+
+ /*
+ * Everything is fine, set the return values.
+ */
+ if (ppszName)
+ *ppszName = *pszName != '\0' ? pszName : NULL;
+ if (ppszValue)
+ *ppszValue = *pszValue != '\0' ? pszValue : NULL;
+ if (pu64Timestamp)
+ *pu64Timestamp = u64Timestamp;
+ if (ppszFlags)
+ *ppszFlags = *pszFlags != '\0' ? pszFlags : NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Free an enumeration handle returned by @a VbglR3GuestPropEnum.
+ * @param pHandle the handle to free
+ */
+VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle)
+{
+ if (!pHandle)
+ return;
+ RTMemFree(pHandle->pchBuf);
+ RTMemFree(pHandle);
+}
+
+
+/**
+ * Deletes a guest property.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3InvsSvcConnect().
+ * @param pszName The property to delete. Utf8
+ */
+VBGLR3DECL(int) VbglR3GuestPropDelete(HGCMCLIENTID idClient, const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+
+ GuestPropMsgDelProperty Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, GUEST_PROP_FN_DEL_PROP, 1);
+ VbglHGCMParmPtrSetString(&Msg.name, pszName);
+ return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+}
+
+
+/**
+ * Deletes a set of keys.
+ *
+ * The set is specified in the same way as for VbglR3GuestPropEnum.
+ *
+ * @returns VBox status code. Stops on first failure.
+ * See also VbglR3GuestPropEnum.
+ *
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ * @param papszPatterns The patterns against which the properties are
+ * matched. Pass NULL if everything should be matched.
+ * @param cPatterns The number of patterns in @a papszPatterns. 0 means
+ * match everything.
+ */
+VBGLR3DECL(int) VbglR3GuestPropDelSet(HGCMCLIENTID idClient,
+ const char * const *papszPatterns,
+ uint32_t cPatterns)
+{
+ PVBGLR3GUESTPROPENUM pHandle;
+ char const *pszName, *pszValue, *pszFlags;
+ uint64_t pu64Timestamp;
+ int rc = VbglR3GuestPropEnum(idClient,
+ (char **)papszPatterns, /** @todo fix this cast. */
+ cPatterns,
+ &pHandle,
+ &pszName,
+ &pszValue,
+ &pu64Timestamp,
+ &pszFlags);
+
+ while (RT_SUCCESS(rc) && pszName)
+ {
+ rc = VbglR3GuestPropWriteValue(idClient, pszName, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = VbglR3GuestPropEnumNext(pHandle,
+ &pszName,
+ &pszValue,
+ &pu64Timestamp,
+ &pszFlags);
+ }
+
+ VbglR3GuestPropEnumFree(pHandle);
+ return rc;
+}
+
+
+/**
+ * Wait for notification of changes to a guest property. If this is called in
+ * a loop, the timestamp of the last notification seen can be passed as a
+ * parameter to be sure that no notifications are missed.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success, @a ppszName, @a ppszValue,
+ * @a pu64Timestamp and @a ppszFlags containing valid data.
+ * @retval VINF_NOT_FOUND if no previous notification could be found with the
+ * timestamp supplied. This will normally mean that a large number
+ * of notifications occurred in between.
+ * @retval VERR_BUFFER_OVERFLOW if the scratch buffer @a pvBuf is not large
+ * enough. In this case the size needed will be placed in
+ * @a pcbBufActual if it is not NULL.
+ * @retval VERR_TIMEOUT if a timeout occurred before a notification was seen.
+ *
+ * @param idClient The client id returned by VbglR3GuestPropConnect().
+ * @param pszPatterns The patterns that the property names must matchfor
+ * the change to be reported.
+ * @param pvBuf A scratch buffer to store the data retrieved into.
+ * The returned data is only valid for it's lifetime.
+ * @a ppszValue will point to the start of this buffer.
+ * @param cbBuf The size of @a pvBuf
+ * @param u64Timestamp The timestamp of the last event seen. Pass zero
+ * to wait for the next event.
+ * @param cMillies Timeout in milliseconds. Use RT_INDEFINITE_WAIT
+ * to wait indefinitely.
+ * @param ppszName Where to store the pointer to the name retrieved.
+ * Optional.
+ * @param ppszValue Where to store the pointer to the value retrieved.
+ * Optional.
+ * @param pu64Timestamp Where to store the timestamp. Optional.
+ * @param ppszFlags Where to store the pointer to the flags. Optional.
+ * @param pcbBufActual If @a pcBuf is not large enough, the size needed.
+ * Optional.
+ * @param pfWasDeleted A flag which indicates that property was deleted.
+ * Optional.
+ */
+VBGLR3DECL(int) VbglR3GuestPropWait(HGCMCLIENTID idClient,
+ const char *pszPatterns,
+ void *pvBuf, uint32_t cbBuf,
+ uint64_t u64Timestamp, uint32_t cMillies,
+ char ** ppszName, char **ppszValue,
+ uint64_t *pu64Timestamp, char **ppszFlags,
+ uint32_t *pcbBufActual, bool *pfWasDeleted)
+{
+ /*
+ * Create the GET_NOTIFICATION message and call the host.
+ */
+ GuestPropMsgGetNotification Msg;
+ VBGL_HGCM_HDR_INIT_TIMED(&Msg.hdr, idClient, GUEST_PROP_FN_GET_NOTIFICATION, 4, cMillies);
+
+ VbglHGCMParmPtrSetString(&Msg.patterns, pszPatterns);
+ RT_BZERO(pvBuf, cbBuf);
+ VbglHGCMParmPtrSet(&Msg.buffer, pvBuf, cbBuf);
+ VbglHGCMParmUInt64Set(&Msg.timestamp, u64Timestamp);
+ VbglHGCMParmUInt32Set(&Msg.size, 0);
+
+ int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
+
+ /*
+ * The cbBufActual parameter is also returned on overflow so the caller can
+ * adjust their buffer.
+ */
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && pcbBufActual != NULL)
+ {
+ int rc2 = Msg.size.GetUInt32(pcbBufActual);
+ AssertRCReturn(rc2, RT_FAILURE(rc) ? rc : rc2);
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Buffer layout: Name\0Value\0Flags\0fWasDeleted\0.
+ *
+ * If the caller cares about any of these strings, make sure things are
+ * properly terminated (paranoia).
+ */
+ if ( RT_SUCCESS(rc)
+ && (ppszName != NULL || ppszValue != NULL || ppszFlags != NULL || pfWasDeleted != NULL))
+ {
+ /* Validate / skip 'Name'. */
+ char *pszValue = RTStrEnd((char *)pvBuf, cbBuf) + 1;
+ AssertPtrReturn(pszValue, VERR_TOO_MUCH_DATA);
+ if (ppszName)
+ *ppszName = (char *)pvBuf;
+
+ /* Validate / skip 'Value'. */
+ char *pszFlags = RTStrEnd(pszValue, cbBuf - (pszValue - (char *)pvBuf)) + 1;
+ AssertPtrReturn(pszFlags, VERR_TOO_MUCH_DATA);
+ if (ppszValue)
+ *ppszValue = pszValue;
+
+ if (ppszFlags)
+ *ppszFlags = pszFlags;
+
+ /* Skip 'Flags' and deal with 'fWasDeleted' if it's present. */
+ char *pszWasDeleted = RTStrEnd(pszFlags, cbBuf - (pszFlags - (char *)pvBuf)) + 1;
+ AssertPtrReturn(pszWasDeleted, VERR_TOO_MUCH_DATA);
+ char chWasDeleted = 0;
+ if ( (size_t)pszWasDeleted - (size_t)pvBuf < cbBuf
+ && (chWasDeleted = *pszWasDeleted) != '\0')
+ AssertMsgReturn((chWasDeleted == '0' || chWasDeleted == '1') && pszWasDeleted[1] == '\0',
+ ("'%s'\n", pszWasDeleted), VERR_PARSE_ERROR);
+ if (pfWasDeleted)
+ *pfWasDeleted = chWasDeleted == '1';
+ }
+
+ /* And the timestamp, if requested. */
+ if (pu64Timestamp != NULL)
+ {
+ rc = Msg.timestamp.GetUInt64(pu64Timestamp);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+#endif /* VBOX_VBGLR3_XSERVER */
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp
new file mode 100644
index 00000000..afa280fa
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestUser.cpp
@@ -0,0 +1,119 @@
+/* $Id: VBoxGuestR3LibGuestUser.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * guest user reporting / utility functions.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <VBox/log.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Reports a state change of a specific guest user.
+ *
+ * @returns IPRT status value
+ * @param pszUser Guest user name to report state for.
+ * @param pszDomain Domain the guest user's account is bound to.
+ * @param enmState Guest user state to report.
+ * @param puDetails Pointer to state details. Optional.
+ * @param cbDetails Size (in bytes) of state details. Pass 0
+ * if puDetails is NULL.
+ */
+VBGLR3DECL(int) VbglR3GuestUserReportState(const char *pszUser, const char *pszDomain, VBoxGuestUserState enmState,
+ uint8_t *puDetails, uint32_t cbDetails)
+{
+ AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
+ /* pszDomain is optional. */
+ /* puDetails is optional. */
+ AssertReturn(cbDetails == 0 || puDetails != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(cbDetails < 16U*_1M, VERR_OUT_OF_RANGE);
+
+ uint32_t cbBase = sizeof(VMMDevReportGuestUserState);
+ uint32_t cbUser = (uint32_t)strlen(pszUser) + 1; /* Include terminating zero */
+ uint32_t cbDomain = pszDomain ? (uint32_t)strlen(pszDomain) + 1 /* Ditto */ : 0;
+
+ /* Allocate enough space for all fields. */
+ uint32_t cbSize = cbBase
+ + cbUser
+ + cbDomain
+ + cbDetails;
+ VMMDevReportGuestUserState *pReport = (VMMDevReportGuestUserState *)RTMemAllocZ(cbSize);
+ if (!pReport)
+ return VERR_NO_MEMORY;
+
+ int rc = vmmdevInitRequest(&pReport->header, VMMDevReq_ReportGuestUserState);
+ if (RT_SUCCESS(rc))
+ {
+ pReport->header.size = cbSize;
+
+ pReport->status.state = enmState;
+ pReport->status.cbUser = cbUser;
+ pReport->status.cbDomain = cbDomain;
+ pReport->status.cbDetails = cbDetails;
+
+ /*
+ * Note: cbOffDynamic contains the first dynamic array entry within
+ * VBoxGuestUserStatus.
+ * Therefore it's vital to *not* change the order of the struct members
+ * without altering this code. Don't try this at home.
+ */
+ uint32_t cbOffDynamic = RT_UOFFSETOF(VBoxGuestUserStatus, szUser);
+
+ /* pDynamic marks the beginning for the dynamically allocated areas. */
+ uint8_t *pDynamic = (uint8_t *)&pReport->status;
+ pDynamic += cbOffDynamic;
+ AssertPtr(pDynamic);
+
+ memcpy(pDynamic, pszUser, cbUser);
+ if (cbDomain)
+ memcpy(pDynamic + cbUser, pszDomain, cbDomain);
+ if (cbDetails)
+ memcpy(pDynamic + cbUser + cbDomain, puDetails, cbDetails);
+
+ rc = vbglR3GRPerform(&pReport->header);
+ }
+
+ RTMemFree(pReport);
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp
new file mode 100644
index 00000000..8053f0be
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHGCM.cpp
@@ -0,0 +1,108 @@
+/* $Id: VBoxGuestR3LibHGCM.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * generic HGCM.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+#include <VBox/VBoxGuestLib.h>
+#include <iprt/string.h>
+
+
+/**
+ * Connects to an HGCM service.
+ *
+ * @returns VBox status code
+ * @param pszServiceName Name of the host service.
+ * @param pidClient Where to put the client ID on success. The client ID
+ * must be passed to all the other calls to the service.
+ */
+VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient)
+{
+ AssertPtrReturn(pszServiceName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pidClient, VERR_INVALID_POINTER);
+
+ VBGLIOCHGCMCONNECT Info;
+ RT_ZERO(Info);
+ VBGLREQHDR_INIT(&Info.Hdr, HGCM_CONNECT);
+ Info.u.In.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
+ int rc = RTStrCopy(Info.u.In.Loc.u.host.achName, sizeof(Info.u.In.Loc.u.host.achName), pszServiceName);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CONNECT, &Info.Hdr, sizeof(Info));
+ if (RT_SUCCESS(rc))
+ *pidClient = Info.u.Out.idClient;
+ return rc;
+}
+
+
+/**
+ * Disconnect from an HGCM service.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3HGCMConnect().
+ */
+VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient)
+{
+ VBGLIOCHGCMDISCONNECT Info;
+ VBGLREQHDR_INIT(&Info.Hdr, HGCM_DISCONNECT);
+ Info.u.In.idClient = idClient;
+
+ return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_DISCONNECT, &Info.Hdr, sizeof(Info));
+}
+
+
+/**
+ * Makes a fully prepared HGCM call.
+ *
+ * @returns VBox status code.
+ * @param pInfo Fully prepared HGCM call info.
+ * @param cbInfo Size of the info. This may sometimes be larger than
+ * what the parameter count indicates because of
+ * parameter changes between versions and such.
+ */
+VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo)
+{
+ /* Expect caller to have filled in pInfo. */
+ AssertMsg(pInfo->Hdr.cbIn == cbInfo, ("cbIn=%#x cbInfo=%#zx\n", pInfo->Hdr.cbIn, cbInfo));
+ AssertMsg(pInfo->Hdr.cbOut == cbInfo, ("cbOut=%#x cbInfo=%#zx\n", pInfo->Hdr.cbOut, cbInfo));
+ Assert(sizeof(*pInfo) + pInfo->cParms * sizeof(HGCMFunctionParameter) <= cbInfo);
+ Assert(pInfo->u32ClientID != 0);
+
+ return vbglR3DoIOCtl(VBGL_IOCTL_HGCM_CALL(cbInfo), &pInfo->Hdr, cbInfo);
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp
new file mode 100644
index 00000000..9c9e377e
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostChannel.cpp
@@ -0,0 +1,235 @@
+/* $Id: VBoxGuestR3LibHostChannel.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Host Channel.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include <iprt/mem.h>
+
+#include <VBox/HostServices/VBoxHostChannel.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+VBGLR3DECL(int) VbglR3HostChannelInit(uint32_t *pidClient)
+{
+ return VbglR3HGCMConnect("VBoxHostChannel", pidClient);
+}
+
+VBGLR3DECL(void) VbglR3HostChannelTerm(uint32_t idClient)
+{
+ VbglR3HGCMDisconnect(idClient);
+}
+
+VBGLR3DECL(int) VbglR3HostChannelAttach(uint32_t *pu32ChannelHandle,
+ uint32_t u32HGCMClientId,
+ const char *pszName,
+ uint32_t u32Flags)
+{
+ /* Make a heap copy of the name, because HGCM can not use some of other memory types. */
+ size_t cbName = strlen(pszName) + 1;
+ char *pszCopy = (char *)RTMemAlloc(cbName);
+ if (pszCopy == NULL)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ memcpy(pszCopy, pszName, cbName);
+
+ VBoxHostChannelAttach parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_ATTACH, 3);
+ VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName);
+ VbglHGCMParmUInt32Set(&parms.flags, u32Flags);
+ VbglHGCMParmUInt32Set(&parms.handle, 0);
+
+ int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+
+ if (RT_SUCCESS(rc))
+ *pu32ChannelHandle = parms.handle.u.value32;
+
+ RTMemFree(pszCopy);
+
+ return rc;
+}
+
+VBGLR3DECL(void) VbglR3HostChannelDetach(uint32_t u32ChannelHandle,
+ uint32_t u32HGCMClientId)
+{
+ VBoxHostChannelDetach parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_DETACH, 1);
+ VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle);
+
+ VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+}
+
+VBGLR3DECL(int) VbglR3HostChannelSend(uint32_t u32ChannelHandle,
+ uint32_t u32HGCMClientId,
+ void *pvData,
+ uint32_t cbData)
+{
+ VBoxHostChannelSend parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_SEND, 2);
+ VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle);
+ VbglHGCMParmPtrSet(&parms.data, pvData, cbData);
+
+ return VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+}
+
+VBGLR3DECL(int) VbglR3HostChannelRecv(uint32_t u32ChannelHandle,
+ uint32_t u32HGCMClientId,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeReceived,
+ uint32_t *pu32SizeRemaining)
+{
+ VBoxHostChannelRecv parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_RECV, 4);
+ VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle);
+ VbglHGCMParmPtrSet(&parms.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&parms.sizeReceived, 0);
+ VbglHGCMParmUInt32Set(&parms.sizeRemaining, 0);
+
+ int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+
+ if (RT_SUCCESS(rc))
+ {
+ *pu32SizeReceived = parms.sizeReceived.u.value32;
+ *pu32SizeRemaining = parms.sizeRemaining.u.value32;
+ }
+
+ return rc;
+}
+
+VBGLR3DECL(int) VbglR3HostChannelControl(uint32_t u32ChannelHandle,
+ uint32_t u32HGCMClientId,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ VBoxHostChannelControl parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_CONTROL, 5);
+ VbglHGCMParmUInt32Set(&parms.handle, u32ChannelHandle);
+ VbglHGCMParmUInt32Set(&parms.code, u32Code);
+ VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm);
+ VbglHGCMParmPtrSet(&parms.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0);
+
+ int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+
+ if (RT_SUCCESS(rc))
+ {
+ *pu32SizeDataReturned = parms.sizeDataReturned.u.value32;
+ }
+
+ return rc;
+}
+
+VBGLR3DECL(int) VbglR3HostChannelEventWait(uint32_t *pu32ChannelHandle,
+ uint32_t u32HGCMClientId,
+ uint32_t *pu32EventId,
+ void *pvParm,
+ uint32_t cbParm,
+ uint32_t *pu32SizeReturned)
+{
+ VBoxHostChannelEventWait parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_WAIT, 4);
+ VbglHGCMParmUInt32Set(&parms.handle, 0);
+ VbglHGCMParmUInt32Set(&parms.id, 0);
+ VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm);
+ VbglHGCMParmUInt32Set(&parms.sizeReturned, 0);
+
+ int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+
+ if (RT_SUCCESS(rc))
+ {
+ *pu32ChannelHandle = parms.handle.u.value32;
+ *pu32EventId = parms.id.u.value32;
+ *pu32SizeReturned = parms.sizeReturned.u.value32;
+ }
+
+ return rc;
+}
+
+VBGLR3DECL(int) VbglR3HostChannelEventCancel(uint32_t u32ChannelHandle,
+ uint32_t u32HGCMClientId)
+{
+ RT_NOREF1(u32ChannelHandle);
+
+ VBoxHostChannelEventCancel parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_EVENT_CANCEL, 0);
+
+ return VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+}
+
+VBGLR3DECL(int) VbglR3HostChannelQuery(const char *pszName,
+ uint32_t u32HGCMClientId,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ /* Make a heap copy of the name, because HGCM can not use some of other memory types. */
+ size_t cbName = strlen(pszName) + 1;
+ char *pszCopy = (char *)RTMemAlloc(cbName);
+ if (pszCopy == NULL)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ memcpy(pszCopy, pszName, cbName);
+
+ VBoxHostChannelQuery parms;
+ VBGL_HGCM_HDR_INIT(&parms.hdr, u32HGCMClientId, VBOX_HOST_CHANNEL_FN_QUERY, 5);
+ VbglHGCMParmPtrSet(&parms.name, pszCopy, (uint32_t)cbName);
+ VbglHGCMParmUInt32Set(&parms.code, u32Code);
+ VbglHGCMParmPtrSet(&parms.parm, pvParm, cbParm);
+ VbglHGCMParmPtrSet(&parms.data, pvData, cbData);
+ VbglHGCMParmUInt32Set(&parms.sizeDataReturned, 0);
+
+ int rc = VbglR3HGCMCall(&parms.hdr, sizeof(parms));
+
+ if (RT_SUCCESS(rc))
+ {
+ *pu32SizeDataReturned = parms.sizeDataReturned.u.value32;
+ }
+
+ RTMemFree(pszCopy);
+
+ return rc;
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp
new file mode 100644
index 00000000..f3d7740f
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibHostVersion.cpp
@@ -0,0 +1,226 @@
+/* $Id: */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, host version check.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include <VBox/log.h>
+
+#ifdef RT_OS_WINDOWS
+ #define WIN32_LEAN_AND_MEAN
+ #include <iprt/win/windows.h>
+#endif
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Checks for a Guest Additions update by comparing the installed version on the
+ * guest and the reported host version.
+ *
+ * @returns VBox status code
+ *
+ * @param idClient The client id returned by
+ * VbglR3InfoSvcConnect().
+ * @param pfUpdate Receives pointer to boolean flag indicating
+ * whether an update was found or not.
+ * @param ppszHostVersion Receives pointer of allocated version string.
+ * The returned pointer must be freed using
+ * VbglR3GuestPropReadValueFree(). Always set to
+ * NULL.
+ * @param ppszGuestVersion Receives pointer of allocated revision string.
+ * The returned pointer must be freed using
+ * VbglR3GuestPropReadValueFree(). Always set to
+ * NULL.
+ */
+VBGLR3DECL(int) VbglR3HostVersionCheckForUpdate(HGCMCLIENTID idClient, bool *pfUpdate, char **ppszHostVersion, char **ppszGuestVersion)
+{
+#ifdef VBOX_WITH_GUEST_PROPS
+ Assert(idClient > 0);
+ AssertPtr(pfUpdate);
+ AssertPtr(ppszHostVersion);
+ AssertPtr(ppszGuestVersion);
+
+ *ppszHostVersion = NULL;
+ *ppszGuestVersion = NULL;
+
+ /* We assume we have an update initially.
+ Every block down below is allowed to veto */
+ *pfUpdate = true;
+
+ /* Do we need to do all this stuff? */
+ char *pszCheckHostVersion;
+ int rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/CheckHostVersion", &pszCheckHostVersion);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS; /* If we don't find the value above we do the check by default */
+ else
+ LogFlow(("Could not read check host version flag! rc = %Rrc\n", rc));
+ }
+ else
+ {
+ /* Only don't do the check if we have a valid "0" in it */
+ if (!strcmp(pszCheckHostVersion, "0"))
+ {
+ LogRel(("No host version update check performed (disabled).\n"));
+ *pfUpdate = false;
+ }
+ VbglR3GuestPropReadValueFree(pszCheckHostVersion);
+ }
+
+ /* Collect all needed information */
+ /* Make sure we only notify the user once by comparing the host version with
+ * the last checked host version (if any) */
+ if (RT_SUCCESS(rc) && *pfUpdate)
+ {
+ /* Look up host version */
+ rc = VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/HostInfo/VBoxVer", ppszHostVersion);
+ if (RT_FAILURE(rc))
+ {
+ LogFlow(("Could not read VBox host version! rc = %Rrc\n", rc));
+ }
+ else
+ {
+ LogFlow(("Host version: %s\n", *ppszHostVersion));
+
+ /* Get last checked host version */
+ char *pszLastCheckedHostVersion;
+ rc = VbglR3HostVersionLastCheckedLoad(idClient, &pszLastCheckedHostVersion);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("Last checked host version: %s\n", pszLastCheckedHostVersion));
+ if (strcmp(*ppszHostVersion, pszLastCheckedHostVersion) == 0)
+ *pfUpdate = false; /* We already notified this version, skip */
+ VbglR3GuestPropReadValueFree(pszLastCheckedHostVersion);
+ }
+ else if (rc == VERR_NOT_FOUND) /* Never wrote a last checked host version before */
+ {
+ LogFlow(("Never checked a host version before.\n"));
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /* Look up guest version */
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3GetAdditionsVersion(ppszGuestVersion, NULL /* Extended version not needed here */,
+ NULL /* Revision not needed here */);
+ if (RT_FAILURE(rc))
+ LogFlow(("Could not read VBox guest version! rc = %Rrc\n", rc));
+ }
+ }
+
+ /* Do the actual version comparison (if needed, see block(s) above) */
+ if (RT_SUCCESS(rc) && *pfUpdate)
+ {
+ if (RTStrVersionCompare(*ppszHostVersion, *ppszGuestVersion) > 0) /* Is host version greater than guest add version? */
+ {
+ /* Yay, we have an update! */
+ LogRel(("Guest Additions update found! Please upgrade this machine to the latest Guest Additions.\n"));
+ }
+ else
+ {
+ /* How sad ... */
+ *pfUpdate = false;
+ }
+ }
+
+ /* Cleanup on failure */
+ if (RT_FAILURE(rc))
+ {
+ if (*ppszHostVersion)
+ {
+ VbglR3GuestPropReadValueFree(*ppszHostVersion);
+ *ppszHostVersion = NULL;
+ }
+ if (*ppszGuestVersion)
+ {
+ VbglR3GuestPropReadValueFree(*ppszGuestVersion);
+ *ppszGuestVersion = NULL;
+ }
+ }
+ return rc;
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(idClient, pfUpdate, ppszHostVersion, ppszGuestVersion);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+/** Retrieves the last checked host version.
+ *
+ * @returns VBox status code.
+ *
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ * @param ppszVer Receives pointer of allocated version string.
+ * The returned pointer must be freed using RTStrFree() on VINF_SUCCESS.
+ */
+VBGLR3DECL(int) VbglR3HostVersionLastCheckedLoad(HGCMCLIENTID idClient, char **ppszVer)
+{
+#ifdef VBOX_WITH_GUEST_PROPS
+ Assert(idClient > 0);
+ AssertPtr(ppszVer);
+ return VbglR3GuestPropReadValueAlloc(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", ppszVer);
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(idClient, ppszVer);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+/** Stores the last checked host version for later lookup.
+ * Requires strings in form of "majorVer.minorVer.build".
+ *
+ * @returns VBox status code.
+ *
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ * @param pszVer Pointer to version string to store.
+ */
+VBGLR3DECL(int) VbglR3HostVersionLastCheckedStore(HGCMCLIENTID idClient, const char *pszVer)
+{
+#ifdef VBOX_WITH_GUEST_PROPS
+ Assert(idClient > 0);
+ AssertPtr(pszVer);
+ return VbglR3GuestPropWriteValue(idClient, "/VirtualBox/GuestAdd/HostVerLastChecked", pszVer);
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(idClient, pszVer);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h
new file mode 100644
index 00000000..fb97ace3
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibInternal.h
@@ -0,0 +1,131 @@
+/* $Id: VBoxGuestR3LibInternal.h $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 support library for the guest additions, Internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h
+#define GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VMMDev.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/VBoxGuestLib.h>
+
+#ifdef VBOX_VBGLR3_XFREE86
+/* Rather than try to resolve all the header file conflicts, I will just
+ prototype what we need here. */
+typedef unsigned long xf86size_t;
+extern "C" xf86size_t xf86strlen(const char*);
+# undef strlen
+# define strlen xf86strlen
+#endif /* VBOX_VBGLR3_XFREE86 */
+
+RT_C_DECLS_BEGIN
+
+int vbglR3DoIOCtl(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq);
+int vbglR3DoIOCtlRaw(uintptr_t uFunction, PVBGLREQHDR pReq, size_t cbReq);
+int vbglR3GRAlloc(VMMDevRequestHeader **ppReq, size_t cb, VMMDevRequestType enmReqType);
+int vbglR3GRPerform(VMMDevRequestHeader *pReq);
+void vbglR3GRFree(VMMDevRequestHeader *pReq);
+
+
+
+DECLINLINE(void) VbglHGCMParmUInt32Set(HGCMFunctionParameter *pParm, uint32_t u32)
+{
+ pParm->type = VMMDevHGCMParmType_32bit;
+ pParm->u.value64 = 0; /* init unused bits to 0 */
+ pParm->u.value32 = u32;
+}
+
+
+DECLINLINE(int) VbglHGCMParmUInt32Get(HGCMFunctionParameter *pParm, uint32_t *pu32)
+{
+ if (pParm->type == VMMDevHGCMParmType_32bit)
+ {
+ *pu32 = pParm->u.value32;
+ return VINF_SUCCESS;
+ }
+ *pu32 = UINT32_MAX; /* shut up gcc */
+ return VERR_WRONG_PARAMETER_TYPE;
+}
+
+
+DECLINLINE(void) VbglHGCMParmUInt64Set(HGCMFunctionParameter *pParm, uint64_t u64)
+{
+ pParm->type = VMMDevHGCMParmType_64bit;
+ pParm->u.value64 = u64;
+}
+
+
+DECLINLINE(int) VbglHGCMParmUInt64Get(HGCMFunctionParameter *pParm, uint64_t *pu64)
+{
+ if (pParm->type == VMMDevHGCMParmType_64bit)
+ {
+ *pu64 = pParm->u.value64;
+ return VINF_SUCCESS;
+ }
+ *pu64 = UINT64_MAX; /* shut up gcc */
+ return VERR_WRONG_PARAMETER_TYPE;
+}
+
+
+DECLINLINE(void) VbglHGCMParmPtrSet(HGCMFunctionParameter *pParm, void *pv, uint32_t cb)
+{
+ pParm->type = VMMDevHGCMParmType_LinAddr;
+ pParm->u.Pointer.size = cb;
+ pParm->u.Pointer.u.linearAddr = (uintptr_t)pv;
+}
+
+
+#ifdef IPRT_INCLUDED_string_h
+
+DECLINLINE(void) VbglHGCMParmPtrSetString(HGCMFunctionParameter *pParm, const char *psz)
+{
+ pParm->type = VMMDevHGCMParmType_LinAddr_In;
+ pParm->u.Pointer.size = (uint32_t)strlen(psz) + 1;
+ pParm->u.Pointer.u.linearAddr = (uintptr_t)psz;
+}
+
+#endif /* IPRT_INCLUDED_string_h */
+
+#ifdef VBOX_VBGLR3_XFREE86
+# undef strlen
+#endif /* VBOX_VBGLR3_XFREE86 */
+
+RT_C_DECLS_END
+
+#endif /* !GA_INCLUDED_SRC_common_VBoxGuest_lib_VBoxGuestR3LibInternal_h */
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp
new file mode 100644
index 00000000..04a167e0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibLog.cpp
@@ -0,0 +1,94 @@
+/* $Id: VBoxGuestR3LibLog.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Logging.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Write to the backdoor logger from ring 3 guest code.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pch The string to log. Does not need to be terminated.
+ * @param cch The number of chars (bytes) to log.
+ *
+ * @remarks This currently does not accept more than 255 bytes of data at
+ * one time. It should probably be rewritten to use pass a pointer
+ * in the IOCtl.
+ */
+VBGLR3DECL(int) VbglR3WriteLog(const char *pch, size_t cch)
+{
+ /*
+ * Quietly skip empty strings.
+ * (Happens in the RTLogBackdoorPrintf case.)
+ */
+ int rc;
+ if (cch > 0)
+ {
+ if (RT_VALID_PTR(pch))
+ {
+ /*
+ * We need to repackage the string for ring-0.
+ */
+ size_t cbMsg = VBGL_IOCTL_LOG_SIZE(cch);
+ PVBGLIOCLOG pMsg = (PVBGLIOCLOG)RTMemTmpAlloc(cbMsg);
+ if (pMsg)
+ {
+ VBGLREQHDR_INIT_EX(&pMsg->Hdr, VBGL_IOCTL_LOG_SIZE_IN(cch), VBGL_IOCTL_LOG_SIZE_OUT);
+ memcpy(pMsg->u.In.szMsg, pch, cch);
+ pMsg->u.In.szMsg[cch] = '\0';
+ rc = vbglR3DoIOCtl(VBGL_IOCTL_LOG(cch), &pMsg->Hdr, cbMsg);
+
+ RTMemTmpFree(pMsg);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp
new file mode 100644
index 00000000..81e7b272
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp
@@ -0,0 +1,135 @@
+/* $Id: VBoxGuestR3LibMisc.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Misc.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/log.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Change the IRQ filter mask.
+ *
+ * @returns IPRT status code.
+ * @param fOr The OR mask.
+ * @param fNot The NOT mask.
+ */
+VBGLR3DECL(int) VbglR3CtlFilterMask(uint32_t fOr, uint32_t fNot)
+{
+ VBGLIOCCHANGEFILTERMASK Info;
+ VBGLREQHDR_INIT(&Info.Hdr, CHANGE_FILTER_MASK);
+ Info.u.In.fOrMask = fOr;
+ Info.u.In.fNotMask = fNot;
+ return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_FILTER_MASK, &Info.Hdr, sizeof(Info));
+}
+
+
+/**
+ * Report a change in the capabilities that we support to the host.
+ *
+ * @returns IPRT status code.
+ * @param fOr Capabilities which have been added.
+ * @param fNot Capabilities which have been removed.
+ *
+ * @todo Move to a different file.
+ */
+VBGLR3DECL(int) VbglR3SetGuestCaps(uint32_t fOr, uint32_t fNot)
+{
+ VBGLIOCSETGUESTCAPS Info;
+ VBGLREQHDR_INIT(&Info.Hdr, CHANGE_GUEST_CAPABILITIES);
+ Info.u.In.fOrMask = fOr;
+ Info.u.In.fNotMask = fNot;
+ return vbglR3DoIOCtl(VBGL_IOCTL_CHANGE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info));
+}
+
+
+/**
+ * Acquire capabilities to report to the host.
+ *
+ * The capabilities which can be acquired are the same as those reported by
+ * VbglR3SetGuestCaps, and once a capability has been acquired once is is
+ * switched to "acquire mode" and can no longer be set using VbglR3SetGuestCaps.
+ * Capabilities can also be switched to acquire mode without actually being
+ * acquired. A client can not acquire a capability which has been acquired and
+ * not released by another client. Capabilities acquired are automatically
+ * released on session termination.
+ *
+ * @returns IPRT status code
+ * @returns VERR_RESOURCE_BUSY and acquires nothing if another client has
+ * acquired and not released at least one of the @a fOr capabilities
+ * @param fOr Capabilities to acquire or to switch to acquire mode
+ * @param fNot Capabilities to release
+ * @param fConfig if set, capabilities in @a fOr are switched to acquire mode
+ * but not acquired, and @a fNot is ignored. See
+ * VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE for details.
+ */
+VBGLR3DECL(int) VbglR3AcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fConfig)
+{
+ VBGLIOCACQUIREGUESTCAPS Info;
+ VBGLREQHDR_INIT(&Info.Hdr, ACQUIRE_GUEST_CAPABILITIES);
+ Info.u.In.fFlags = fConfig ? VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE : VBGL_IOC_AGC_FLAGS_DEFAULT;
+ Info.u.In.fOrMask = fOr;
+ Info.u.In.fNotMask = fNot;
+ return vbglR3DoIOCtl(VBGL_IOCTL_ACQUIRE_GUEST_CAPABILITIES, &Info.Hdr, sizeof(Info));
+}
+
+
+/**
+ * Query the session ID of this VM.
+ *
+ * The session id is an unique identifier that gets changed for each VM start,
+ * reset or restore. Useful for detection a VM restore.
+ *
+ * @returns IPRT status code.
+ * @param pu64IdSession Session id (out). This is NOT changed on
+ * failure, so the caller can depend on this to
+ * deal with backward compatibility (see
+ * VBoxServiceVMInfoWorker() for an example.)
+ */
+VBGLR3DECL(int) VbglR3GetSessionId(uint64_t *pu64IdSession)
+{
+ VMMDevReqSessionId Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetSessionId);
+ Req.idSession = 0;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ *pu64IdSession = Req.idSession;
+
+ return rc;
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp
new file mode 100644
index 00000000..371b42d7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibModule.cpp
@@ -0,0 +1,180 @@
+/* $Id: VBoxGuestR3LibModule.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared modules.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+/**
+ * Registers a new shared module for the VM
+ *
+ * @returns IPRT status code.
+ * @param pszModuleName Module name
+ * @param pszVersion Module version
+ * @param GCBaseAddr Module base address
+ * @param cbModule Module size
+ * @param cRegions Number of shared region descriptors
+ * @param pRegions Shared region(s)
+ */
+VBGLR3DECL(int) VbglR3RegisterSharedModule(char *pszModuleName, char *pszVersion,
+ RTGCPTR64 GCBaseAddr, uint32_t cbModule,
+ unsigned cRegions, VMMDEVSHAREDREGIONDESC *pRegions)
+{
+ VMMDevSharedModuleRegistrationRequest *pReq;
+ int rc;
+
+ /* Sanity check. */
+ AssertReturn(cRegions < VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER);
+
+ pReq = (VMMDevSharedModuleRegistrationRequest *)RTMemAllocZ(RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest,
+ aRegions[cRegions]));
+ AssertReturn(pReq, VERR_NO_MEMORY);
+
+ vmmdevInitRequest(&pReq->header, VMMDevReq_RegisterSharedModule);
+ pReq->header.size = RT_UOFFSETOF_DYN(VMMDevSharedModuleRegistrationRequest, aRegions[cRegions]);
+ pReq->GCBaseAddr = GCBaseAddr;
+ pReq->cbModule = cbModule;
+ pReq->cRegions = cRegions;
+#ifdef RT_OS_WINDOWS
+# if ARCH_BITS == 32
+ pReq->enmGuestOS = VBOXOSFAMILY_Windows32;
+# else
+ pReq->enmGuestOS = VBOXOSFAMILY_Windows64;
+# endif
+#else
+ /** @todo */
+ pReq->enmGuestOS = VBOXOSFAMILY_Unknown;
+#endif
+ for (unsigned i = 0; i < cRegions; i++)
+ pReq->aRegions[i] = pRegions[i];
+
+ if ( RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName) != VINF_SUCCESS
+ || RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion) != VINF_SUCCESS)
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ goto end;
+ }
+
+ rc = vbglR3GRPerform(&pReq->header);
+
+end:
+ RTMemFree(pReq);
+ return rc;
+
+}
+
+/**
+ * Unregisters a shared module for the VM
+ *
+ * @returns IPRT status code.
+ * @param pszModuleName Module name
+ * @param pszVersion Module version
+ * @param GCBaseAddr Module base address
+ * @param cbModule Module size
+ */
+VBGLR3DECL(int) VbglR3UnregisterSharedModule(char *pszModuleName, char *pszVersion, RTGCPTR64 GCBaseAddr, uint32_t cbModule)
+{
+ VMMDevSharedModuleUnregistrationRequest Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_UnregisterSharedModule);
+ Req.GCBaseAddr = GCBaseAddr;
+ Req.cbModule = cbModule;
+
+ if ( RTStrCopy(Req.szName, sizeof(Req.szName), pszModuleName) != VINF_SUCCESS
+ || RTStrCopy(Req.szVersion, sizeof(Req.szVersion), pszVersion) != VINF_SUCCESS)
+ {
+ return VERR_BUFFER_OVERFLOW;
+ }
+ return vbglR3GRPerform(&Req.header);
+}
+
+/**
+ * Checks registered modules for shared pages
+ *
+ * @returns IPRT status code.
+ */
+VBGLR3DECL(int) VbglR3CheckSharedModules()
+{
+ VMMDevSharedModuleCheckRequest Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_CheckSharedModules);
+ return vbglR3GRPerform(&Req.header);
+}
+
+/**
+ * Checks if page sharing is enabled.
+ *
+ * @returns true/false enabled/disabled
+ */
+VBGLR3DECL(bool) VbglR3PageSharingIsEnabled()
+{
+ VMMDevPageSharingStatusRequest Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetPageSharingStatus);
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ return Req.fEnabled;
+ return false;
+}
+
+/**
+ * Checks if page sharing is enabled.
+ *
+ * @returns true/false enabled/disabled
+ */
+VBGLR3DECL(int) VbglR3PageIsShared(RTGCPTR pPage, bool *pfShared, uint64_t *puPageFlags)
+{
+#ifdef DEBUG
+ VMMDevPageIsSharedRequest Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_DebugIsPageShared);
+ Req.GCPtrPage = pPage;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ *pfShared = Req.fShared;
+ *puPageFlags = Req.uPageFlags;
+ }
+ return rc;
+#else
+ RT_NOREF3(pPage, pfShared, puPageFlags);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp
new file mode 100644
index 00000000..b93872d4
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMouse.cpp
@@ -0,0 +1,90 @@
+/* $Id: VBoxGuestR3LibMouse.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Mouse.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Retrieve mouse coordinates and features from the host.
+ *
+ * @returns VBox status code.
+ *
+ * @param pfFeatures Where to store the mouse features.
+ * @param px Where to store the X co-ordinate.
+ * @param py Where to store the Y co-ordinate.
+ */
+VBGLR3DECL(int) VbglR3GetMouseStatus(uint32_t *pfFeatures, uint32_t *px, uint32_t *py)
+{
+ VMMDevReqMouseStatus Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetMouseStatus);
+ Req.mouseFeatures = 0;
+ Req.pointerXPos = 0;
+ Req.pointerYPos = 0;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfFeatures)
+ *pfFeatures = Req.mouseFeatures;
+ if (px)
+ *px = Req.pointerXPos;
+ if (py)
+ *py = Req.pointerYPos;
+ }
+ return rc;
+}
+
+
+/**
+ * Send mouse features to the host.
+ *
+ * @returns VBox status code.
+ *
+ * @param fFeatures Supported mouse pointer features. The main guest driver
+ * will mediate different callers and show the host any
+ * feature enabled by any guest caller.
+ */
+VBGLR3DECL(int) VbglR3SetMouseStatus(uint32_t fFeatures)
+{
+ VBGLIOCSETMOUSESTATUS Req;
+ VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
+ Req.u.In.fStatus = fFeatures;
+ return vbglR3DoIOCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &Req.Hdr, sizeof(Req));
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp
new file mode 100644
index 00000000..ff7c312d
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibPidFile.cpp
@@ -0,0 +1,151 @@
+/** $Id: VBoxGuestR3LibPidFile.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * Create a PID file.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/err.h>
+#include "VBoxGuestR3LibInternal.h"
+
+/* A time to wait before starting the next attempt to check a pidfile. */
+#define VBGL_PIDFILE_WAIT_RELAX_TIME_MS (250)
+
+/**
+ * Creates a PID File and returns the open file descriptor.
+ *
+ * On DOS based system, file sharing (deny write) is used for locking the PID
+ * file.
+ *
+ * On Unix-y systems, an exclusive advisory lock is used for locking the PID
+ * file since the file sharing support is usually missing there.
+ *
+ * This API will overwrite any existing PID Files without a lock on them, on the
+ * assumption that they are stale files which an old process did not properly
+ * clean up.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The path and filename to create the PID File under
+ * @param phFile Where to store the file descriptor of the open (and locked
+ * on Unix-y systems) PID File. On failure, or if another
+ * process owns the PID File, this will be set to NIL_RTFILE.
+ */
+VBGLR3DECL(int) VbglR3PidFile(const char *pszPath, PRTFILE phFile)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phFile, VERR_INVALID_PARAMETER);
+ *phFile = NIL_RTFILE;
+
+ RTFILE hPidFile;
+ int rc = RTFileOpen(&hPidFile, pszPath,
+ RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE
+ | (0644 << RTFILE_O_CREATE_MODE_SHIFT));
+ if (RT_SUCCESS(rc))
+ {
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ /** @todo using size 0 for locking means lock all on Posix.
+ * We should adopt this as our convention too, or something
+ * similar. */
+ rc = RTFileLock(hPidFile, RTFILE_LOCK_WRITE, 0, 0);
+ if (RT_FAILURE(rc))
+ RTFileClose(hPidFile);
+ else
+#endif
+ {
+ char szBuf[256];
+ size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n",
+ RTProcSelf());
+ RTFileWrite(hPidFile, szBuf, cbPid, NULL);
+ *phFile = hPidFile;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Close and remove an open PID File.
+ *
+ * @param pszPath The path to the PID File,
+ * @param hFile The handle for the file. NIL_RTFILE is ignored as usual.
+ */
+VBGLR3DECL(void) VbglR3ClosePidFile(const char *pszPath, RTFILE hFile)
+{
+ AssertPtrReturnVoid(pszPath);
+ if (hFile != NIL_RTFILE)
+ {
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ RTFileWriteAt(hFile, 0, "-1", 2, NULL);
+#else
+ RTFileDelete(pszPath);
+#endif
+ RTFileClose(hFile);
+ }
+}
+
+
+/**
+ * Wait for other process to release pidfile.
+ *
+ * This function is a wrapper to VbglR3PidFile().
+ *
+ * @returns IPRT status code.
+ * @param szPidfile Path to pidfile.
+ * @param phPidfile Handle to pidfile.
+ * @param u64TimeoutMs A timeout value in milliseconds to wait for
+ * other process to release pidfile.
+ */
+VBGLR3DECL(int) VbglR3PidfileWait(const char *szPidfile, RTFILE *phPidfile, uint64_t u64TimeoutMs)
+{
+ int rc = VERR_FILE_LOCK_VIOLATION;
+ uint64_t u64Start = RTTimeSystemMilliTS();
+
+ AssertPtrReturn(szPidfile, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phPidfile, VERR_INVALID_PARAMETER);
+
+ while ( !RT_SUCCESS((rc = VbglR3PidFile(szPidfile, phPidfile)))
+ && (RTTimeSystemMilliTS() - u64Start < u64TimeoutMs))
+ {
+ RTThreadSleep(VBGL_PIDFILE_WAIT_RELAX_TIME_MS);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp
new file mode 100644
index 00000000..8a9d15c0
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibRuntimeXF86.cpp
@@ -0,0 +1,108 @@
+/* $Id: VBoxGuestR3LibRuntimeXF86.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions,
+ * implements the minimum of runtime functions needed for
+ * XFree86 driver code.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#if defined(VBOX_VBGLR3_XFREE86)
+extern "C" {
+# define XFree86LOADER
+# include <xf86_ansic.h>
+# undef size_t
+}
+#else
+# include <stdarg.h>
+# include <stdlib.h>
+# define xalloc malloc
+# define xfree free
+extern "C" void ErrorF(const char *f, ...);
+#endif
+
+RTDECL(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ ErrorF("Assertion failed! Expression: %s at %s in\n", pszExpr,
+ pszFunction);
+ ErrorF("%s:%u\n", pszFile, uLine);
+}
+
+RTDECL(void) RTAssertMsg2Weak(const char *pszFormat, ...)
+{
+ NOREF(pszFormat);
+}
+
+RTDECL(bool) RTAssertShouldPanic(void)
+{
+ return false;
+}
+
+RTDECL(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup)
+{
+ NOREF(fFlagsAndGroup);
+ return NULL;
+}
+
+RTDECL(PRTLOGGER) RTLogRelGetDefaultInstance(void)
+{
+ return NULL;
+}
+
+RTDECL(PRTLOGGER) RTLogRelGetDefaultInstanceEx(uint32_t fFlagsAndGroup)
+{
+ NOREF(fFlagsAndGroup);
+ return NULL;
+}
+
+RTDECL(void) RTLogLoggerEx(PRTLOGGER, unsigned, unsigned, const char *pszFormat, ...)
+{
+ NOREF(pszFormat);
+}
+
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag)
+{
+ NOREF(pszTag);
+ return xalloc(cb);
+}
+
+RTDECL(void) RTMemTmpFree(void *pv)
+{
+ xfree(pv);
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp
new file mode 100644
index 00000000..a13a77b9
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSeamless.cpp
@@ -0,0 +1,209 @@
+/* $Id: VBoxGuestR3LibSeamless.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Seamless mode.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include <VBox/log.h>
+
+#include "VBoxGuestR3LibInternal.h"
+
+#ifdef VBOX_VBGLR3_XFREE86
+/* Rather than try to resolve all the header file conflicts, I will just
+ prototype what we need here. */
+extern "C" void* xf86memcpy(void*,const void*,xf86size_t);
+# undef memcpy
+# define memcpy xf86memcpy
+#endif /* VBOX_VBGLR3_XFREE86 */
+
+/**
+ * Tell the host that we support (or no longer support) seamless mode.
+ *
+ * @returns IPRT status value
+ * @param fState whether or not we support seamless mode
+ */
+VBGLR3DECL(int) VbglR3SeamlessSetCap(bool fState)
+{
+ if (fState)
+ return VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS, 0);
+ return VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS);
+}
+
+/**
+ * Wait for a seamless mode change event.
+ *
+ * @returns IPRT status value.
+ * @param[out] pMode On success, the seamless mode to switch into (i.e.
+ * disabled, visible region or host window).
+ */
+VBGLR3DECL(int) VbglR3SeamlessWaitEvent(VMMDevSeamlessMode *pMode)
+{
+ AssertPtrReturn(pMode, VERR_INVALID_POINTER);
+
+ /** @todo r=andy The (similar / duplicate) Windows code does similar waiting. Merge / fix this. */
+ uint32_t fEvent = 0;
+ int rc = VbglR3WaitEvent(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 1000 /* ms */, &fEvent);
+ if (RT_SUCCESS(rc))
+ {
+ /* did we get the right event? */
+ if (fEvent & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
+ {
+ VMMDevSeamlessChangeRequest seamlessChangeRequest;
+
+ /* get the seamless change request */
+ vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest);
+ seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1;
+ seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
+ rc = vbglR3GRPerform(&seamlessChangeRequest.header);
+ if (RT_SUCCESS(rc))
+ {
+ *pMode = seamlessChangeRequest.mode;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ rc = VERR_TRY_AGAIN;
+ }
+ else if ( rc == VERR_INTERRUPTED
+ || rc == VERR_TIMEOUT /* just in case */)
+ rc = VERR_TRY_AGAIN;
+ return rc;
+}
+
+/**
+ * Request the last seamless mode switch from the host again.
+ *
+ * @returns IPRT status value.
+ * @param[out] pMode On success, the seamless mode that was switched
+ * into (i.e. disabled, visible region or host window).
+ */
+VBGLR3DECL(int) VbglR3SeamlessGetLastEvent(VMMDevSeamlessMode *pMode)
+{
+ VMMDevSeamlessChangeRequest seamlessChangeRequest;
+ int rc;
+
+ AssertPtrReturn(pMode, VERR_INVALID_PARAMETER);
+
+ /* get the seamless change request */
+ vmmdevInitRequest(&seamlessChangeRequest.header, VMMDevReq_GetSeamlessChangeRequest);
+ seamlessChangeRequest.mode = (VMMDevSeamlessMode)-1;
+ seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
+ rc = vbglR3GRPerform(&seamlessChangeRequest.header);
+ if (RT_SUCCESS(rc))
+ {
+ *pMode = seamlessChangeRequest.mode;
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+/**
+ * Inform the host about the visible region
+ *
+ * @returns IPRT status code
+ * @param cRects number of rectangles in the list of visible rectangles
+ * @param pRects list of visible rectangles on the guest display
+ *
+ * @todo A scatter-gather version of vbglR3GRPerform would be nice, so that we don't have
+ * to copy our rectangle and header data into a single structure and perform an
+ * additional allocation.
+ * @todo Would that really gain us much, given that the rectangles may not
+ * be grouped at all, or in the format we need? Keeping the memory
+ * for our "single structure" around (re-alloc-ing it if necessary)
+ * sounds like a simpler optimisation if we need it.
+ */
+VBGLR3DECL(int) VbglR3SeamlessSendRects(uint32_t cRects, PRTRECT pRects)
+{
+ VMMDevVideoSetVisibleRegion *pReq;
+ int rc;
+
+ AssertReturn(pRects || cRects == 0, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(cRects <= _1M, ("%u\n", cRects), VERR_OUT_OF_RANGE);
+
+ rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq,
+ sizeof(VMMDevVideoSetVisibleRegion)
+ + cRects * sizeof(RTRECT)
+ - sizeof(RTRECT),
+ VMMDevReq_VideoSetVisibleRegion);
+ if (RT_SUCCESS(rc))
+ {
+ pReq->cRect = cRects;
+ if (cRects)
+ memcpy(&pReq->Rect, pRects, cRects * sizeof(RTRECT));
+ /* This will fail harmlessly for cRect == 0 and older host code */
+ rc = vbglR3GRPerform(&pReq->header);
+ LogFunc(("Visible region request returned %Rrc, internal %Rrc.\n",
+ rc, pReq->header.rc));
+ if (RT_SUCCESS(rc))
+ rc = pReq->header.rc;
+ vbglR3GRFree(&pReq->header);
+ }
+ LogFunc(("Sending %u rectangles to the host: %Rrc\n", cRects, rc));
+ return rc;
+}
+
+VBGLR3DECL(int) VbglR3SeamlessSendMonitorPositions(uint32_t cPositions, PRTPOINT pPositions)
+{
+ if (!pPositions || cPositions <= 0)
+ return VERR_INVALID_PARAMETER;
+
+ VMMDevVideoUpdateMonitorPositions *pReq;
+ int rc;
+
+ rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq,
+ sizeof(VMMDevVideoUpdateMonitorPositions)
+ + (cPositions - 1) * sizeof(RTPOINT),
+ VMMDevReq_VideoUpdateMonitorPositions);
+ if (RT_SUCCESS(rc))
+ {
+ pReq->cPositions = cPositions;
+ if (cPositions)
+ memcpy(&pReq->aPositions, pPositions, cPositions * sizeof(RTPOINT));
+ rc = vbglR3GRPerform(&pReq->header);
+ LogFunc(("Monitor position update request returned %Rrc, internal %Rrc.\n",
+ rc, pReq->header.rc));
+ if (RT_SUCCESS(rc))
+ rc = pReq->header.rc;
+ vbglR3GRFree(&pReq->header);
+ }
+ LogFunc(("Sending monitor positions (%u of them) to the host: %Rrc\n", cPositions, rc));
+ return rc;
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp
new file mode 100644
index 00000000..47137fd2
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibSharedFolders.cpp
@@ -0,0 +1,432 @@
+/* $Id: VBoxGuestR3LibSharedFolders.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, shared folders.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/cpp/autores.h>
+#include <iprt/stdarg.h>
+#include <VBox/log.h>
+#include <VBox/shflsvc.h> /** @todo File should be moved to VBox/HostServices/SharedFolderSvc.h */
+
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Connects to the shared folder service.
+ *
+ * @returns VBox status code
+ * @param pidClient Where to put the client id on success. The client id
+ * must be passed to all the other calls to the service.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderConnect(HGCMCLIENTID *pidClient)
+{
+ return VbglR3HGCMConnect("VBoxSharedFolders", pidClient);
+}
+
+
+/**
+ * Disconnect from the shared folder service.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ */
+VBGLR3DECL(int) VbglR3SharedFolderDisconnect(HGCMCLIENTID idClient)
+{
+ return VbglR3HGCMDisconnect(idClient);
+}
+
+
+/**
+ * Checks whether a shared folder share exists or not.
+ *
+ * @returns True if shared folder exists, false if not.
+ * @param idClient The client id returned by VbglR3InfoSvcConnect().
+ * @param pszShareName Shared folder name to check.
+ */
+VBGLR3DECL(bool) VbglR3SharedFolderExists(HGCMCLIENTID idClient, const char *pszShareName)
+{
+ AssertPtr(pszShareName);
+
+ uint32_t cMappings;
+ VBGLR3SHAREDFOLDERMAPPING *paMappings;
+
+ /** @todo Use some caching here? */
+ bool fFound = false;
+ int rc = VbglR3SharedFolderGetMappings(idClient, true /* Only process auto-mounted folders */, &paMappings, &cMappings);
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < cMappings && !fFound; i++)
+ {
+ char *pszName = NULL;
+ rc = VbglR3SharedFolderGetName(idClient, paMappings[i].u32Root, &pszName);
+ if ( RT_SUCCESS(rc)
+ && *pszName)
+ {
+ if (RTStrICmp(pszName, pszShareName) == 0)
+ fFound = true;
+ RTStrFree(pszName);
+ }
+ }
+ VbglR3SharedFolderFreeMappings(paMappings);
+ }
+ return fFound;
+}
+
+
+/**
+ * Get the list of available shared folders.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3SharedFolderConnect().
+ * @param fAutoMountOnly Flag whether only auto-mounted shared folders
+ * should be reported.
+ * @param ppaMappings Allocated array which will retrieve the mapping info. Needs
+ * to be freed with VbglR3SharedFolderFreeMappings() later.
+ * @param pcMappings The number of mappings returned in @a ppaMappings.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderGetMappings(HGCMCLIENTID idClient, bool fAutoMountOnly,
+ PVBGLR3SHAREDFOLDERMAPPING *ppaMappings, uint32_t *pcMappings)
+{
+ AssertPtrReturn(pcMappings, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppaMappings, VERR_INVALID_PARAMETER);
+
+ *pcMappings = 0;
+ *ppaMappings = NULL;
+
+ VBoxSFQueryMappings Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAPPINGS, 3);
+
+ /* Set the mapping flags. */
+ uint32_t u32Flags = 0; /** @todo SHFL_MF_UTF8 is not implemented yet. */
+ if (fAutoMountOnly) /* We only want the mappings which get auto-mounted. */
+ u32Flags |= SHFL_MF_AUTOMOUNT;
+ VbglHGCMParmUInt32Set(&Msg.flags, u32Flags);
+
+ /*
+ * Prepare and get the actual mappings from the host service.
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t cMappings = 8; /* Should be a good default value. */
+ uint32_t cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING);
+ VBGLR3SHAREDFOLDERMAPPING *ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)RTMemAllocZ(cbSize);
+ if (!ppaMappingsTemp)
+ return VERR_NO_MEMORY;
+
+ do
+ {
+ VbglHGCMParmUInt32Set(&Msg.numberOfMappings, cMappings);
+ VbglHGCMParmPtrSet(&Msg.mappings, ppaMappingsTemp, cbSize);
+
+ rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ VbglHGCMParmUInt32Get(&Msg.numberOfMappings, pcMappings);
+
+ /* Do we have more mappings than we have allocated space for? */
+ if (rc == VINF_BUFFER_OVERFLOW)
+ {
+ cMappings = *pcMappings;
+ cbSize = cMappings * sizeof(VBGLR3SHAREDFOLDERMAPPING);
+ void *pvNew = RTMemRealloc(ppaMappingsTemp, cbSize);
+ AssertPtrBreakStmt(pvNew, rc = VERR_NO_MEMORY);
+ ppaMappingsTemp = (PVBGLR3SHAREDFOLDERMAPPING)pvNew;
+ }
+ }
+ } while (rc == VINF_BUFFER_OVERFLOW); /** @todo r=bird: This won't happen because the weird host code never returns it. */
+
+ if ( RT_FAILURE(rc)
+ || !*pcMappings)
+ {
+ RTMemFree(ppaMappingsTemp);
+ ppaMappingsTemp = NULL;
+ }
+
+ /* In this case, just return success with 0 mappings */
+ if ( rc == VERR_INVALID_PARAMETER
+ && fAutoMountOnly)
+ rc = VINF_SUCCESS;
+
+ *ppaMappings = ppaMappingsTemp;
+
+ return rc;
+}
+
+
+/**
+ * Frees the shared folder mappings allocated by
+ * VbglR3SharedFolderGetMappings() before.
+ *
+ * @param paMappings What
+ */
+VBGLR3DECL(void) VbglR3SharedFolderFreeMappings(PVBGLR3SHAREDFOLDERMAPPING paMappings)
+{
+ if (paMappings)
+ RTMemFree(paMappings);
+}
+
+
+/**
+ * Get the real name of a shared folder.
+ *
+ * @returns VBox status code.
+ * @param idClient The client id returned by VbglR3InvsSvcConnect().
+ * @param u32Root Root ID of shared folder to get the name for.
+ * @param ppszName Where to return the name string. This shall be
+ * freed by calling RTStrFree.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderGetName(HGCMCLIENTID idClient, uint32_t u32Root, char **ppszName)
+{
+ AssertPtr(ppszName);
+
+ VBoxSFQueryMapName Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_NAME, 2);
+
+ int rc;
+ uint32_t cbString = SHFLSTRING_HEADER_SIZE + SHFL_MAX_LEN * sizeof(RTUTF16);
+ PSHFLSTRING pString = (PSHFLSTRING)RTMemAlloc(cbString);
+ if (pString)
+ {
+ if (!ShflStringInitBuffer(pString, cbString))
+ {
+ RTMemFree(pString);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ VbglHGCMParmUInt32Set(&Msg.root, u32Root);
+ VbglHGCMParmPtrSet(&Msg.name, pString, cbString);
+
+ rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ *ppszName = NULL;
+ rc = RTUtf16ToUtf8(&pString->String.ucs2[0], ppszName);
+ }
+ RTMemFree(pString);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Queries information about a shared folder.
+ *
+ * @returns VBox status code.
+ *
+ * @param idClient The client ID.
+ * @param idRoot The root ID of the folder to query information for.
+ * @param fQueryFlags SHFL_MIQF_XXX.
+ * @param ppszName Where to return the pointer to the name.
+ * Free using RTStrFree. Optional.
+ * @param ppszMountPoint Where to return the pointer to the auto mount point.
+ * Free using RTStrFree. Optional.
+ * @param pfFlags Where to return the flags (SHFL_MIF_XXX). Optional.
+ * @param puRootIdVersion where to return the root ID version. Optional.
+ * This helps detecting root-id reuse.
+ *
+ * @remarks ASSUMES UTF-16 connection to host.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderQueryFolderInfo(HGCMCLIENTID idClient, uint32_t idRoot, uint64_t fQueryFlags,
+ char **ppszName, char **ppszMountPoint,
+ uint64_t *pfFlags, uint32_t *puRootIdVersion)
+{
+ AssertReturn(!(fQueryFlags & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH)), VERR_INVALID_FLAGS);
+
+ /*
+ * Allocate string buffers first.
+ */
+ int rc;
+ PSHFLSTRING pNameBuf = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16));
+ PSHFLSTRING pMountPoint = (PSHFLSTRING)RTMemAlloc(SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16));
+ if (pNameBuf && pMountPoint)
+ {
+ ShflStringInitBuffer(pNameBuf, SHFLSTRING_HEADER_SIZE + (SHFL_MAX_LEN + 1) * sizeof(RTUTF16));
+ ShflStringInitBuffer(pMountPoint, SHFLSTRING_HEADER_SIZE + (260 + 1) * sizeof(RTUTF16));
+
+ /*
+ * Make the call.
+ */
+ VBoxSFQueryMapInfo Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_QUERY_MAP_INFO, 5);
+ VbglHGCMParmUInt32Set(&Msg.root, idRoot);
+ VbglHGCMParmPtrSet(&Msg.name, pNameBuf, SHFLSTRING_HEADER_SIZE + pNameBuf->u16Size);
+ VbglHGCMParmPtrSet(&Msg.mountPoint, pMountPoint, SHFLSTRING_HEADER_SIZE + pMountPoint->u16Size);
+ VbglHGCMParmUInt64Set(&Msg.flags, fQueryFlags);
+ VbglHGCMParmUInt32Set(&Msg.rootIdVersion, 0);
+
+ rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy out the results.
+ */
+ if (puRootIdVersion)
+ *puRootIdVersion = Msg.rootIdVersion.u.value64;
+
+ if (pfFlags)
+ *pfFlags = Msg.flags.u.value64;
+
+ if (ppszName)
+ {
+ *ppszName = NULL;
+ rc = RTUtf16ToUtf8Ex(pNameBuf->String.utf16, pNameBuf->u16Length / sizeof(RTUTF16), ppszName, 0, NULL);
+ }
+
+ if (ppszMountPoint && RT_SUCCESS(rc))
+ {
+ *ppszMountPoint = NULL;
+ rc = RTUtf16ToUtf8Ex(pMountPoint->String.utf16, pMountPoint->u16Length / sizeof(RTUTF16), ppszMountPoint, 0, NULL);
+ if (RT_FAILURE(rc) && ppszName)
+ {
+ RTStrFree(*ppszName);
+ *ppszName = NULL;
+ }
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pMountPoint);
+ RTMemFree(pNameBuf);
+ return rc;
+}
+
+
+/**
+ * Waits for changes to the mappings (add, remove, restore).
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on change
+ * @retval VINF_TRY_AGAIN on restore.
+ * @retval VERR_OUT_OF_RESOURCES if there are too many guys waiting.
+ *
+ * @param idClient The client ID.
+ * @param uPrevVersion The mappings config version number returned the last
+ * time around. Use UINT32_MAX for the first call.
+ * @param puCurVersion Where to return the current mappings config version.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderWaitForMappingsChanges(HGCMCLIENTID idClient, uint32_t uPrevVersion, uint32_t *puCurVersion)
+{
+ VBoxSFWaitForMappingsChanges Msg;
+ VBGL_HGCM_HDR_INIT(&Msg.callInfo, idClient, SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES, 1);
+ VbglHGCMParmUInt32Set(&Msg.version, uPrevVersion);
+
+ int rc = VbglR3HGCMCall(&Msg.callInfo, sizeof(Msg));
+
+ *puCurVersion = Msg.version.u.value32;
+ return rc;
+}
+
+
+/**
+ * Cancels all threads currently waiting for changes for this client.
+ *
+ * @returns VBox status code.
+ * @param idClient The client ID.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderCancelMappingsChangesWaits(HGCMCLIENTID idClient)
+{
+ VBGLIOCHGCMCALL CallInfo;
+ VBGL_HGCM_HDR_INIT(&CallInfo, idClient, SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS, 0);
+
+ return VbglR3HGCMCall(&CallInfo, sizeof(CallInfo));
+}
+
+
+/**
+ * Retrieves the prefix for a shared folder mount point. If no prefix
+ * is set in the guest properties "sf_" is returned.
+ *
+ * @returns VBox status code.
+ * @param ppszPrefix Where to return the prefix string. This shall be
+ * freed by calling RTStrFree.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderGetMountPrefix(char **ppszPrefix)
+{
+ AssertPtrReturn(ppszPrefix, VERR_INVALID_POINTER);
+ int rc;
+#ifdef VBOX_WITH_GUEST_PROPS
+ HGCMCLIENTID idClientGuestProp;
+ rc = VbglR3GuestPropConnect(&idClientGuestProp);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountPrefix", ppszPrefix);
+ if (rc == VERR_NOT_FOUND) /* No prefix set? Then set the default. */
+ {
+#endif
+/** @todo r=bird: Inconsistent! VbglR3SharedFolderGetMountDir does not return a default. */
+ rc = RTStrDupEx(ppszPrefix, "sf_");
+#ifdef VBOX_WITH_GUEST_PROPS
+ }
+ VbglR3GuestPropDisconnect(idClientGuestProp);
+ }
+#endif
+ return rc;
+}
+
+
+/**
+ * Retrieves the mount root directory for auto-mounted shared
+ * folders. mount point. If no string is set (VERR_NOT_FOUND)
+ * it's up on the caller (guest) to decide where to mount.
+ *
+ * @returns VBox status code.
+ * @param ppszDir Where to return the directory
+ * string. This shall be freed by
+ * calling RTStrFree.
+ */
+VBGLR3DECL(int) VbglR3SharedFolderGetMountDir(char **ppszDir)
+{
+ AssertPtrReturn(ppszDir, VERR_INVALID_POINTER);
+ int rc = VERR_NOT_FOUND;
+#ifdef VBOX_WITH_GUEST_PROPS
+ HGCMCLIENTID idClientGuestProp;
+ rc = VbglR3GuestPropConnect(&idClientGuestProp);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR3GuestPropReadValueAlloc(idClientGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountDir", ppszDir);
+ VbglR3GuestPropDisconnect(idClientGuestProp);
+ }
+#endif
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp
new file mode 100644
index 00000000..30bc4631
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibStat.cpp
@@ -0,0 +1,79 @@
+/* $Id: VBoxGuestR3LibStat.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Statistics.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+
+
+/**
+ * Query the current statistics update interval.
+ *
+ * @returns IPRT status code.
+ * @param pcMsInterval Update interval in ms (out).
+ */
+VBGLR3DECL(int) VbglR3StatQueryInterval(PRTMSINTERVAL pcMsInterval)
+{
+ VMMDevGetStatisticsChangeRequest Req;
+
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetStatisticsChangeRequest);
+ Req.eventAck = VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST;
+ Req.u32StatInterval = 1;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ *pcMsInterval = Req.u32StatInterval * 1000;
+ if (*pcMsInterval / 1000 != Req.u32StatInterval)
+ *pcMsInterval = ~(RTMSINTERVAL)0;
+ }
+ return rc;
+}
+
+
+/**
+ * Report guest statistics.
+ *
+ * @returns IPRT status code.
+ * @param pReq Request packet with statistics.
+ */
+VBGLR3DECL(int) VbglR3StatReport(VMMDevReportGuestStats *pReq)
+{
+ vmmdevInitRequest(&pReq->header, VMMDevReq_ReportGuestStats);
+ return vbglR3GRPerform(&pReq->header);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp
new file mode 100644
index 00000000..603e83f7
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibTime.cpp
@@ -0,0 +1,55 @@
+/* $Id: VBoxGuestR3LibTime.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Time.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+VBGLR3DECL(int) VbglR3GetHostTime(PRTTIMESPEC pTime)
+{
+ VMMDevReqHostTime Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetHostTime);
+ Req.time = UINT64_MAX;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ RTTimeSpecSetMilli(pTime, (int64_t)Req.time);
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp
new file mode 100644
index 00000000..38a79c58
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVideo.cpp
@@ -0,0 +1,618 @@
+/* $Id: VBoxGuestR3LibVideo.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Video.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR3LibInternal.h"
+
+#include <VBox/log.h>
+#include <VBox/HostServices/GuestPropertySvc.h> /* For Save and RetrieveVideoMode */
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#ifdef VBOX_VBGLR3_XFREE86
+/* Rather than try to resolve all the header file conflicts, I will just
+ prototype what we need here. */
+extern "C" void* xf86memcpy(void*,const void*,xf86size_t);
+# undef memcpy
+# define memcpy xf86memcpy
+extern "C" void* xf86memset(const void*,int,xf86size_t);
+# undef memset
+# define memset xf86memset
+#endif /* VBOX_VBGLR3_XFREE86 */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VIDEO_PROP_PREFIX "/VirtualBox/GuestAdd/Vbgl/Video/"
+
+
+/**
+ * Enable or disable video acceleration.
+ *
+ * @returns VBox status code.
+ *
+ * @param fEnable Pass zero to disable, any other value to enable.
+ */
+VBGLR3DECL(int) VbglR3VideoAccelEnable(bool fEnable)
+{
+ VMMDevVideoAccelEnable Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelEnable);
+ Req.u32Enable = fEnable;
+ Req.cbRingBuffer = VMMDEV_VBVA_RING_BUFFER_SIZE;
+ Req.fu32Status = 0;
+ return vbglR3GRPerform(&Req.header);
+}
+
+
+/**
+ * Flush the video buffer.
+ *
+ * @returns VBox status code.
+ */
+VBGLR3DECL(int) VbglR3VideoAccelFlush(void)
+{
+ VMMDevVideoAccelFlush Req;
+ vmmdevInitRequest(&Req.header, VMMDevReq_VideoAccelFlush);
+ return vbglR3GRPerform(&Req.header);
+}
+
+
+/**
+ * Send mouse pointer shape information to the host.
+ *
+ * @returns VBox status code.
+ *
+ * @param fFlags Mouse pointer flags.
+ * @param xHot X coordinate of hot spot.
+ * @param yHot Y coordinate of hot spot.
+ * @param cx Pointer width.
+ * @param cy Pointer height.
+ * @param pvImg Pointer to the image data (can be NULL).
+ * @param cbImg Size of the image data pointed to by pvImg.
+ */
+VBGLR3DECL(int) VbglR3SetPointerShape(uint32_t fFlags, uint32_t xHot, uint32_t yHot, uint32_t cx, uint32_t cy,
+ const void *pvImg, size_t cbImg)
+{
+ VMMDevReqMousePointer *pReq;
+ size_t cbReq = vmmdevGetMousePointerReqSize(cx, cy);
+ AssertReturn( !pvImg
+ || cbReq == RT_UOFFSETOF(VMMDevReqMousePointer, pointerData) + cbImg,
+ VERR_INVALID_PARAMETER);
+ int rc = vbglR3GRAlloc((VMMDevRequestHeader **)&pReq, cbReq, VMMDevReq_SetPointerShape);
+ if (RT_SUCCESS(rc))
+ {
+ pReq->fFlags = fFlags;
+ pReq->xHot = xHot;
+ pReq->yHot = yHot;
+ pReq->width = cx;
+ pReq->height = cy;
+ if (pvImg)
+ memcpy(pReq->pointerData, pvImg, cbImg);
+
+ rc = vbglR3GRPerform(&pReq->header);
+ if (RT_SUCCESS(rc))
+ rc = pReq->header.rc;
+ vbglR3GRFree(&pReq->header);
+ }
+ return rc;
+}
+
+
+/**
+ * Send mouse pointer shape information to the host.
+ * This version of the function accepts a request for clients that
+ * already allocate and manipulate the request structure directly.
+ *
+ * @returns VBox status code.
+ *
+ * @param pReq Pointer to the VMMDevReqMousePointer structure.
+ */
+VBGLR3DECL(int) VbglR3SetPointerShapeReq(VMMDevReqMousePointer *pReq)
+{
+ int rc = vbglR3GRPerform(&pReq->header);
+ if (RT_SUCCESS(rc))
+ rc = pReq->header.rc;
+ return rc;
+}
+
+
+/**
+ * Query the last display change request sent from the host to the guest.
+ *
+ * @returns iprt status value
+ * @param pcx Where to store the horizontal pixel resolution
+ * @param pcy Where to store the vertical pixel resolution
+ * requested (a value of zero means do not change).
+ * @param pcBits Where to store the bits per pixel requested (a value
+ * of zero means do not change).
+ * @param piDisplay Where to store the display number the request was for
+ * - 0 for the primary display, 1 for the first
+ * secondary display, etc.
+ * @param fAck whether or not to acknowledge the newest request sent by
+ * the host. If this is set, the function will return the
+ * most recent host request, otherwise it will return the
+ * last request to be acknowledged.
+ *
+ */
+static int getDisplayChangeRequest2(uint32_t *pcx, uint32_t *pcy,
+ uint32_t *pcBits, uint32_t *piDisplay,
+ bool fAck)
+{
+ VMMDevDisplayChangeRequest2 Req;
+
+ AssertPtrReturn(pcx, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcy, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER);
+ RT_ZERO(Req);
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequest2);
+ if (fAck)
+ Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ rc = Req.header.rc;
+ if (RT_SUCCESS(rc))
+ {
+ *pcx = Req.xres;
+ *pcy = Req.yres;
+ *pcBits = Req.bpp;
+ *piDisplay = Req.display;
+ }
+ return rc;
+}
+
+
+/**
+ * Query the last display change request sent from the host to the guest.
+ *
+ * @returns iprt status value
+ * @param pcx Where to store the horizontal pixel resolution
+ * requested (a value of zero means do not change).
+ * @param pcy Where to store the vertical pixel resolution
+ * requested (a value of zero means do not change).
+ * @param pcBits Where to store the bits per pixel requested (a value
+ * of zero means do not change).
+ * @param piDisplay Where to store the display number the request was for
+ * - 0 for the primary display, 1 for the first
+ * secondary display, etc.
+ * @param fAck whether or not to acknowledge the newest request sent by
+ * the host. If this is set, the function will return the
+ * most recent host request, otherwise it will return the
+ * last request to be acknowledged.
+ *
+ * @param pdx New horizontal position of the secondary monitor.
+ * Optional.
+ * @param pdy New vertical position of the secondary monitor.
+ * Optional.
+ * @param pfEnabled Secondary monitor is enabled or not. Optional.
+ * @param pfChangeOrigin Whether the mode hint retrieved included
+ * information about origin/display offset inside the
+ * frame-buffer. Optional.
+ *
+ */
+VBGLR3DECL(int) VbglR3GetDisplayChangeRequest(uint32_t *pcx, uint32_t *pcy,
+ uint32_t *pcBits,
+ uint32_t *piDisplay,
+ uint32_t *pdx, uint32_t *pdy,
+ bool *pfEnabled,
+ bool *pfChangeOrigin,
+ bool fAck)
+{
+ VMMDevDisplayChangeRequestEx Req;
+ int rc = VINF_SUCCESS;
+
+ AssertPtrReturn(pcx, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcy, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pdx, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pdy, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfEnabled, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfChangeOrigin, VERR_INVALID_PARAMETER);
+
+ RT_ZERO(Req);
+ rc = vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequestEx);
+ AssertRCReturn(rc, rc);
+ if (fAck)
+ Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
+ rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ rc = Req.header.rc;
+ if (RT_SUCCESS(rc))
+ {
+ *pcx = Req.xres;
+ *pcy = Req.yres;
+ *pcBits = Req.bpp;
+ *piDisplay = Req.display;
+ if (pdx)
+ *pdx = Req.cxOrigin;
+ if (pdy)
+ *pdy = Req.cyOrigin;
+ if (pfEnabled)
+ *pfEnabled = Req.fEnabled;
+ if (pfChangeOrigin)
+ *pfChangeOrigin = Req.fChangeOrigin;
+ return VINF_SUCCESS;
+ }
+
+ /* NEEDS TESTING: test below with current Additions on VBox 4.1 or older. */
+ /** @todo Can we find some standard grep-able string for "NEEDS TESTING"? */
+ if (rc == VERR_NOT_IMPLEMENTED) /* Fall back to the old API. */
+ {
+ if (pfEnabled)
+ *pfEnabled = true;
+ if (pfChangeOrigin)
+ *pfChangeOrigin = false;
+ return getDisplayChangeRequest2(pcx, pcy, pcBits, piDisplay, fAck);
+ }
+ return rc;
+}
+
+
+/**
+ * Query the last display change request sent from the host to the guest.
+ *
+ * @returns iprt status value
+ * @param cDisplaysIn How many elements in the paDisplays array.
+ * @param pcDisplaysOut How many elements were returned.
+ * @param paDisplays Display information.
+ * @param fAck Whether or not to acknowledge the newest request sent by
+ * the host. If this is set, the function will return the
+ * most recent host request, otherwise it will return the
+ * last request to be acknowledged.
+ */
+VBGLR3DECL(int) VbglR3GetDisplayChangeRequestMulti(uint32_t cDisplaysIn,
+ uint32_t *pcDisplaysOut,
+ VMMDevDisplayDef *paDisplays,
+ bool fAck)
+{
+ VMMDevDisplayChangeRequestMulti *pReq;
+ size_t cbDisplays;
+ size_t cbAlloc;
+ int rc = VINF_SUCCESS;
+
+ AssertReturn(cDisplaysIn > 0 && cDisplaysIn <= 64 /* VBOX_VIDEO_MAX_SCREENS */, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcDisplaysOut, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(paDisplays, VERR_INVALID_PARAMETER);
+
+ cbDisplays = cDisplaysIn * sizeof(VMMDevDisplayDef);
+ cbAlloc = RT_UOFFSETOF(VMMDevDisplayChangeRequestMulti, aDisplays) + cbDisplays;
+ pReq = (VMMDevDisplayChangeRequestMulti *)RTMemTmpAlloc(cbAlloc);
+ AssertPtrReturn(pReq, VERR_NO_MEMORY);
+
+ memset(pReq, 0, cbAlloc);
+ rc = vmmdevInitRequest(&pReq->header, VMMDevReq_GetDisplayChangeRequestMulti);
+ AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc);
+
+ pReq->header.size += (uint32_t)cbDisplays;
+ pReq->cDisplays = cDisplaysIn;
+ if (fAck)
+ pReq->eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
+
+ rc = vbglR3GRPerform(&pReq->header);
+ AssertRCReturnStmt(rc, RTMemTmpFree(pReq), rc);
+
+ rc = pReq->header.rc;
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(paDisplays, pReq->aDisplays, pReq->cDisplays * sizeof(VMMDevDisplayDef));
+ *pcDisplaysOut = pReq->cDisplays;
+ }
+
+ RTMemTmpFree(pReq);
+ return rc;
+}
+
+
+/**
+ * Query the host as to whether it likes a specific video mode.
+ *
+ * @returns the result of the query
+ * @param cx the width of the mode being queried
+ * @param cy the height of the mode being queried
+ * @param cBits the bpp of the mode being queried
+ */
+VBGLR3DECL(bool) VbglR3HostLikesVideoMode(uint32_t cx, uint32_t cy, uint32_t cBits)
+{
+ bool fRc = true; /* If for some reason we can't contact the host then
+ * we like everything. */
+ int rc;
+ VMMDevVideoModeSupportedRequest req;
+
+ vmmdevInitRequest(&req.header, VMMDevReq_VideoModeSupported);
+ req.width = cx;
+ req.height = cy;
+ req.bpp = cBits;
+ req.fSupported = true;
+ rc = vbglR3GRPerform(&req.header);
+ if (RT_SUCCESS(rc) && RT_SUCCESS(req.header.rc))
+ fRc = req.fSupported;
+ return fRc;
+}
+
+/**
+ * Get the highest screen number for which there is a saved video mode or "0"
+ * if there are no saved modes.
+ *
+ * @returns iprt status value
+ * @returns VERR_NOT_SUPPORTED if the guest property service is not available.
+ * @param pcScreen where to store the virtual screen number
+ */
+VBGLR3DECL(int) VbglR3VideoModeGetHighestSavedScreen(unsigned *pcScreen)
+{
+#if defined(VBOX_WITH_GUEST_PROPS)
+ int rc;
+ HGCMCLIENTID idClient = 0;
+ PVBGLR3GUESTPROPENUM pHandle = NULL;
+ const char *pszName = NULL;
+ unsigned cHighestScreen = 0;
+
+ /* Validate input. */
+ AssertPtrReturn(pcScreen, VERR_INVALID_POINTER);
+
+ /* Query the data. */
+ rc = VbglR3GuestPropConnect(&idClient);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszPattern = VIDEO_PROP_PREFIX"*";
+ rc = VbglR3GuestPropEnum(idClient, &pszPattern, 1, &pHandle, &pszName, NULL, NULL, NULL);
+ int rc2 = VbglR3GuestPropDisconnect(idClient);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ /* Process the data. */
+ while (RT_SUCCESS(rc) && pszName != NULL)
+ {
+ uint32_t cScreen;
+
+ rc = RTStrToUInt32Full(pszName + sizeof(VIDEO_PROP_PREFIX) - 1, 10, &cScreen);
+ if (RT_SUCCESS(rc)) /* There may be similar properties with text. */
+ cHighestScreen = RT_MAX(cHighestScreen, cScreen);
+ rc = VbglR3GuestPropEnumNext(pHandle, &pszName, NULL, NULL, NULL);
+ }
+
+ VbglR3GuestPropEnumFree(pHandle);
+
+ /* Return result. */
+ if (RT_SUCCESS(rc))
+ *pcScreen = cHighestScreen;
+ return rc;
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(pcScreen);
+ return VERR_NOT_SUPPORTED;
+#endif /* !VBOX_WITH_GUEST_PROPS */
+}
+
+/**
+ * Save video mode parameters to the guest property store.
+ *
+ * @returns iprt status value
+ * @param idScreen The virtual screen number.
+ * @param cx mode width
+ * @param cy mode height
+ * @param cBits bits per pixel for the mode
+ * @param x virtual screen X offset
+ * @param y virtual screen Y offset
+ * @param fEnabled is this virtual screen enabled?
+ */
+VBGLR3DECL(int) VbglR3SaveVideoMode(unsigned idScreen, unsigned cx, unsigned cy, unsigned cBits,
+ unsigned x, unsigned y, bool fEnabled)
+{
+#ifdef VBOX_WITH_GUEST_PROPS
+ unsigned cHighestScreen = 0;
+ int rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen);
+ if (RT_SUCCESS(rc))
+ {
+ HGCMCLIENTID idClient = 0;
+ rc = VbglR3GuestPropConnect(&idClient);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ char szModeName[GUEST_PROP_MAX_NAME_LEN];
+ char szModeParms[GUEST_PROP_MAX_VALUE_LEN];
+ RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen);
+ RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u,%ux%u,%u", cx, cy, cBits, x, y, (unsigned) fEnabled);
+
+ rc = VbglR3GuestPropWriteValue(idClient, szModeName, szModeParms);
+ /* Write out the mode using the legacy name too, in case the user
+ * re-installs older Additions. */
+ if (idScreen == 0)
+ {
+ RTStrPrintf(szModeParms, sizeof(szModeParms), "%ux%ux%u", cx, cy, cBits);
+ VbglR3GuestPropWriteValue(idClient, VIDEO_PROP_PREFIX "SavedMode", szModeParms);
+ }
+
+ rc2 = VbglR3GuestPropDisconnect(idClient);
+ if (rc != VINF_PERMISSION_DENIED)
+ {
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ if (RT_SUCCESS(rc))
+ {
+ /* Sanity check 1. We do not try to make allowance for someone else
+ * changing saved settings at the same time as us. */
+ bool fEnabled2 = false;
+ unsigned cx2 = 0;
+ unsigned cy2 = 0;
+ unsigned cBits2 = 0;
+ unsigned x2 = 0;
+ unsigned y2 = 0;
+ rc = VbglR3RetrieveVideoMode(idScreen, &cx2, &cy2, &cBits2, &x2, &y2, &fEnabled2);
+ if ( RT_SUCCESS(rc)
+ && (cx != cx2 || cy != cy2 || cBits != cBits2 || x != x2 || y != y2 || fEnabled != fEnabled2))
+ rc = VERR_WRITE_ERROR;
+ /* Sanity check 2. Same comment. */
+ else if (RT_SUCCESS(rc))
+ {
+ unsigned cHighestScreen2 = 0;
+ rc = VbglR3VideoModeGetHighestSavedScreen(&cHighestScreen2);
+ if (RT_SUCCESS(rc))
+ if (cHighestScreen2 != RT_MAX(cHighestScreen, idScreen))
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ }
+ }
+ }
+ return rc;
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(idScreen, cx, cy, cBits, x, y, fEnabled);
+ return VERR_NOT_SUPPORTED;
+#endif /* !VBOX_WITH_GUEST_PROPS */
+}
+
+
+/**
+ * Retrieve video mode parameters from the guest property store.
+ *
+ * @returns iprt status value
+ * @param idScreen The virtual screen number.
+ * @param pcx where to store the mode width
+ * @param pcy where to store the mode height
+ * @param pcBits where to store the bits per pixel for the mode
+ * @param px where to store the virtual screen X offset
+ * @param py where to store the virtual screen Y offset
+ * @param pfEnabled where to store whether this virtual screen is enabled
+ */
+VBGLR3DECL(int) VbglR3RetrieveVideoMode(unsigned idScreen,
+ unsigned *pcx, unsigned *pcy,
+ unsigned *pcBits,
+ unsigned *px, unsigned *py,
+ bool *pfEnabled)
+{
+#ifdef VBOX_WITH_GUEST_PROPS
+ /*
+ * First we retrieve the video mode which is saved as a string in the
+ * guest property store.
+ */
+ HGCMCLIENTID idClient = 0;
+ int rc = VbglR3GuestPropConnect(&idClient);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2;
+ /* The buffer for VbglR3GuestPropReadValue. If this is too small then
+ * something is wrong with the data stored in the property. */
+ char szModeParms[1024];
+ char szModeName[GUEST_PROP_MAX_NAME_LEN]; /** @todo add a VbglR3GuestPropReadValueF/FV that does the RTStrPrintf for you. */
+ RTStrPrintf(szModeName, sizeof(szModeName), VIDEO_PROP_PREFIX "%u", idScreen);
+ rc = VbglR3GuestPropReadValue(idClient, szModeName, szModeParms, sizeof(szModeParms), NULL);
+ /* Try legacy single screen name. */
+ if (rc == VERR_NOT_FOUND && idScreen == 0)
+ rc = VbglR3GuestPropReadValue(idClient,
+ VIDEO_PROP_PREFIX"SavedMode",
+ szModeParms, sizeof(szModeParms),
+ NULL);
+ rc2 = VbglR3GuestPropDisconnect(idClient);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ /*
+ * Now we convert the string returned to numeric values.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ /* Mandatory chunk: 640x480x32 */
+ char *pszNext;
+ uint32_t cx = 0;
+ rc = VERR_PARSE_ERROR;
+ rc2 = RTStrToUInt32Ex(szModeParms, &pszNext, 10, &cx);
+ if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x')
+ {
+ uint32_t cy = 0;
+ rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &cy);
+ if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x')
+ {
+ uint8_t cBits = 0;
+ rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &cBits);
+ if (rc2 == VINF_SUCCESS || rc2 == VWRN_TRAILING_CHARS)
+ {
+ /* Optional chunk: ,32x64,1 (we fail if this is partially there) */
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint8_t fEnabled = 1;
+ if (rc2 == VINF_SUCCESS)
+ rc = VINF_SUCCESS;
+ else if (*pszNext == ',')
+ {
+ rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &x);
+ if (rc2 == VWRN_TRAILING_CHARS && *pszNext == 'x')
+ {
+ rc2 = RTStrToUInt32Ex(pszNext + 1, &pszNext, 10, &y);
+ if (rc2 == VWRN_TRAILING_CHARS && *pszNext == ',')
+ {
+ rc2 = RTStrToUInt8Ex(pszNext + 1, &pszNext, 10, &fEnabled);
+ if (rc2 == VINF_SUCCESS)
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+
+ /*
+ * Set result if successful.
+ */
+ if (rc == VINF_SUCCESS)
+ {
+ if (pcx)
+ *pcx = cx;
+ if (pcy)
+ *pcy = cy;
+ if (pcBits)
+ *pcBits = cBits;
+ if (px)
+ *px = x;
+ if (py)
+ *py = y;
+ if (pfEnabled)
+ *pfEnabled = RT_BOOL(fEnabled);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return rc;
+#else /* !VBOX_WITH_GUEST_PROPS */
+ RT_NOREF(idScreen, pcx, pcy, pcBits, px, py, pfEnabled);
+ return VERR_NOT_SUPPORTED;
+#endif /* !VBOX_WITH_GUEST_PROPS */
+}
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp
new file mode 100644
index 00000000..89a19d09
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibVrdp.cpp
@@ -0,0 +1,64 @@
+/* $Id: VBoxGuestR3LibVrdp.cpp $ */
+/** @file
+ * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, VRDP.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include <iprt/string.h>
+#include "VBoxGuestR3LibInternal.h"
+
+
+VBGLR3DECL(int) VbglR3VrdpGetChangeRequest(bool *pfActive, uint32_t *puExperienceLevel)
+{
+ VMMDevVRDPChangeRequest Req;
+ RT_ZERO(Req); /* implicit padding */
+ vmmdevInitRequest(&Req.header, VMMDevReq_GetVRDPChangeRequest); //VMMDEV_REQ_HDR_INIT(&Req.header, sizeof(Req), VMMDevReq_GetVRDPChangeRequest);
+ int rc = vbglR3GRPerform(&Req.header);
+ if (RT_SUCCESS(rc))
+ {
+ *pfActive = Req.u8VRDPActive != 0;
+ *puExperienceLevel = Req.u32VRDPExperienceLevel;
+ }
+ else
+ {
+ *pfActive = false;
+ *puExperienceLevel = 0;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp
new file mode 100644
index 00000000..38646784
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/VbglR0CanUsePhysPageList.cpp
@@ -0,0 +1,52 @@
+/* $Id: VbglR0CanUsePhysPageList.cpp $ */
+/** @file
+ * VBoxGuestLibR0 - Physical memory heap.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxGuestR0LibInternal.h"
+
+
+/**
+ * Checks whether the host supports physical page lists or not.
+ *
+ * @returns true if it does, false if it doesn't.
+ */
+DECLR0VBGL(bool) VbglR0CanUsePhysPageList(void)
+{
+ /* a_fLocked is false, because the actual capability of the host is requested.
+ * See VBGLR0_CAN_USE_PHYS_PAGE_LIST definition.
+ */
+ int rc = vbglR0Enter();
+ return RT_SUCCESS(rc)
+ && VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false);
+}
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk
new file mode 100644
index 00000000..6d99a40f
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/Makefile.kmk
@@ -0,0 +1,52 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the common guest addition code library testcases.
+#
+
+#
+# Copyright (C) 2022-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD)
+
+ #
+ # Testcase for the physical heap.
+ #
+ PROGRAMS += tstVbglR0PhysHeap-1
+ tstVbglR0PhysHeap-1_TEMPLATE = VBoxR3TstExe
+ tstVbglR0PhysHeap-1_SOURCES = \
+ tstVbglR0PhysHeap-1.cpp
+
+
+endif # defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_BUILD)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp
new file mode 100644
index 00000000..3646fd95
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxGuest/lib/testcase/tstVbglR0PhysHeap-1.cpp
@@ -0,0 +1,415 @@
+/* $Id: tstVbglR0PhysHeap-1.cpp $ */
+/** @file
+ * IPRT Testcase - Offset Based Heap.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include <iprt/test.h>
+#include <iprt/time.h>
+
+#define IN_TESTCASE
+#define IN_RING0 /* pretend we're in ring-0 so we get access to the functions */
+#include "../VBoxGuestR0LibInternal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct
+{
+ uint32_t cb;
+ void *pv;
+} TSTHISTORYENTRY;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+VBGLDATA g_vbgldata;
+
+int g_cChunks = 0;
+size_t g_cbChunks = 0;
+
+/** Drop-in replacement for RTMemContAlloc */
+static void *tstMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ RTTESTI_CHECK(cb > 0);
+
+#define TST_MAX_CHUNKS 24
+ if (g_cChunks < TST_MAX_CHUNKS)
+ {
+ void *pvRet = RTMemAlloc(cb);
+ if (pvRet)
+ {
+ g_cChunks++;
+ g_cbChunks += cb;
+ *pPhys = (uint32_t)(uintptr_t)pvRet ^ (UINT32_C(0xf0f0f0f0) & ~(uint32_t)PAGE_OFFSET_MASK);
+
+ /* Avoid problematic values that won't happen in real life: */
+ if (!*pPhys)
+ *pPhys = 4U << PAGE_SHIFT;
+ if (UINT32_MAX - *pPhys < cb)
+ *pPhys -= RT_ALIGN_32(cb, PAGE_SIZE);
+
+ return pvRet;
+ }
+ }
+
+ *pPhys = NIL_RTCCPHYS;
+ return NULL;
+}
+
+
+/** Drop-in replacement for RTMemContFree */
+static void tstMemContFree(void *pv, size_t cb)
+{
+ RTTESTI_CHECK(RT_VALID_PTR(pv));
+ RTTESTI_CHECK(cb > 0);
+ RTTESTI_CHECK(g_cChunks > 0);
+ RTMemFree(pv);
+ g_cChunks--;
+ g_cbChunks -= cb;
+}
+
+
+#define RTMemContAlloc tstMemContAlloc
+#define RTMemContFree tstMemContFree
+#include "../VBoxGuestR0LibPhysHeap.cpp"
+
+
+static void PrintStats(TSTHISTORYENTRY const *paHistory, size_t cHistory, const char *pszDesc)
+{
+ size_t cbAllocated = 0;
+ unsigned cLargeBlocks = 0;
+ unsigned cAllocated = 0;
+ for (size_t i = 0; i < cHistory; i++)
+ if (paHistory[i].pv)
+ {
+ cAllocated += 1;
+ cbAllocated += paHistory[i].cb;
+ cLargeBlocks += paHistory[i].cb > _1K;
+ }
+
+ size_t const cbOverhead = g_cChunks * sizeof(VBGLPHYSHEAPCHUNK) + cAllocated * sizeof(VBGLPHYSHEAPBLOCK);
+ size_t const cbFragmentation = g_cbChunks - cbOverhead - cbAllocated;
+ RTTestIPrintf(RTTESTLVL_ALWAYS,
+ "%s: %'9zu bytes in %2d chunks; %'9zu bytes in %4u blocks (%2u large)\n"
+ " => int-frag %'9zu (%2zu.%1zu%%) overhead %'9zu (%1zu.%02zu%%)\n",
+ pszDesc,
+ g_cbChunks, g_cChunks,
+ cbAllocated, cAllocated, cLargeBlocks,
+ cbFragmentation, cbFragmentation * 100 / g_cbChunks, (cbFragmentation * 1000 / g_cbChunks) % 10,
+ cbOverhead, cbOverhead * 100 / g_cbChunks, (cbOverhead * 10000 / g_cbChunks) % 100);
+}
+
+
+int main(int argc, char **argv)
+{
+ RT_NOREF_PV(argc); RT_NOREF_PV(argv);
+
+ /*
+ * Init runtime.
+ */
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstVbglR0PhysHeap-1", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ /*
+ * Arguments are taken to be random seeding.
+ */
+ uint64_t uRandSeed = RTTimeNanoTS();
+ for (int i = 1; i < argc; i++)
+ {
+ rc = RTStrToUInt64Full(argv[i], 0, &uRandSeed);
+ if (rc != VINF_SUCCESS)
+ {
+ RTTestIFailed("Invalid parameter: %Rrc: %s\n", rc, argv[i]);
+ return RTTestSummaryAndDestroy(hTest);
+ }
+ }
+
+ /*
+ * Create a heap.
+ */
+ RTTestSub(hTest, "Basics");
+ RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ return RTTestSummaryAndDestroy(hTest);
+ RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL));
+
+#define CHECK_PHYS_ADDR(a_pv) do { \
+ uint32_t const uPhys = VbglR0PhysHeapGetPhysAddr(a_pv); \
+ if (uPhys == 0 || uPhys == UINT32_MAX || (uPhys & PAGE_OFFSET_MASK) != ((uintptr_t)(a_pv) & PAGE_OFFSET_MASK)) \
+ RTTestIFailed("line %u: %s=%p: uPhys=%#x\n", __LINE__, #a_pv, (a_pv), uPhys); \
+ } while (0)
+
+ /*
+ * Try allocate.
+ */
+ static struct TstPhysHeapOps
+ {
+ uint32_t cb;
+ unsigned iFreeOrder;
+ void *pvAlloc;
+ } s_aOps[] =
+ {
+ { 16, 0, NULL }, // 0
+ { 16, 1, NULL },
+ { 16, 2, NULL },
+ { 16, 5, NULL },
+ { 16, 4, NULL },
+ { 32, 3, NULL }, // 5
+ { 31, 6, NULL },
+ { 1024, 8, NULL },
+ { 1024, 10, NULL },
+ { 1024, 12, NULL },
+ { PAGE_SIZE, 13, NULL }, // 10
+ { 1024, 9, NULL },
+ { PAGE_SIZE, 11, NULL },
+ { PAGE_SIZE, 14, NULL },
+ { 16, 15, NULL },
+ { 9, 7, NULL }, // 15
+ { 16, 7, NULL },
+ { 36, 7, NULL },
+ { 16, 7, NULL },
+ { 12344, 7, NULL },
+ { 50, 7, NULL }, // 20
+ { 16, 7, NULL },
+ };
+ uint32_t i;
+ //RTHeapOffsetDump(Heap, (PFNRTHEAPOFFSETPRINTF)(uintptr_t)RTPrintf); /** @todo Add some detail info output with a signature identical to RTPrintf. */
+ //size_t cbBefore = VbglR0PhysHeapGetFreeSize();
+ static char const s_szFill[] = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /* allocate */
+ for (i = 0; i < RT_ELEMENTS(s_aOps); i++)
+ {
+ s_aOps[i].pvAlloc = VbglR0PhysHeapAlloc(s_aOps[i].cb);
+ RTTESTI_CHECK_MSG(s_aOps[i].pvAlloc, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i));
+ if (!s_aOps[i].pvAlloc)
+ return RTTestSummaryAndDestroy(hTest);
+
+ memset(s_aOps[i].pvAlloc, s_szFill[i], s_aOps[i].cb);
+ RTTESTI_CHECK_MSG(RT_ALIGN_P(s_aOps[i].pvAlloc, sizeof(void *)) == s_aOps[i].pvAlloc,
+ ("VbglR0PhysHeapAlloc(%#x) -> %p\n", s_aOps[i].cb, i));
+
+ CHECK_PHYS_ADDR(s_aOps[i].pvAlloc);
+
+ /* Check heap integrity: */
+ RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL));
+ }
+
+ /* free and allocate the same node again. */
+ for (i = 0; i < RT_ELEMENTS(s_aOps); i++)
+ {
+ if (!s_aOps[i].pvAlloc)
+ continue;
+ //RTPrintf("debug: i=%d pv=%#x cb=%#zx align=%#zx cbReal=%#zx\n", i, s_aOps[i].pvAlloc,
+ // s_aOps[i].cb, s_aOps[i].uAlignment, RTHeapOffsetSize(Heap, s_aOps[i].pvAlloc));
+ size_t cbBeforeSub = VbglR0PhysHeapGetFreeSize();
+ VbglR0PhysHeapFree(s_aOps[i].pvAlloc);
+ size_t cbAfterSubFree = VbglR0PhysHeapGetFreeSize();
+ RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL));
+
+ void *pv;
+ pv = VbglR0PhysHeapAlloc(s_aOps[i].cb);
+ RTTESTI_CHECK_MSG(pv, ("VbglR0PhysHeapAlloc(%#x) -> NULL i=%d\n", s_aOps[i].cb, i));
+ if (!pv)
+ return RTTestSummaryAndDestroy(hTest);
+ CHECK_PHYS_ADDR(pv);
+ RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL));
+
+ //RTPrintf("debug: i=%d pv=%p cbReal=%#zx cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx \n", i, pv, RTHeapOffsetSize(Heap, pv),
+ // cbBeforeSub, cbAfterSubFree, VbglR0PhysHeapGetFreeSize());
+
+ if (pv != s_aOps[i].pvAlloc)
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: Free+Alloc returned different address. new=%p old=%p i=%d\n", pv, s_aOps[i].pvAlloc, i);
+ s_aOps[i].pvAlloc = pv;
+ size_t cbAfterSubAlloc = VbglR0PhysHeapGetFreeSize();
+ if (cbBeforeSub != cbAfterSubAlloc)
+ {
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Warning: cbBeforeSub=%#zx cbAfterSubFree=%#zx cbAfterSubAlloc=%#zx. i=%d\n",
+ cbBeforeSub, cbAfterSubFree, cbAfterSubAlloc, i);
+ //return 1; - won't work correctly until we start creating free block instead of donating memory on alignment.
+ }
+ }
+
+ VbglR0PhysHeapTerminate();
+ RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks));
+
+
+ /*
+ * Use random allocation pattern
+ */
+ RTTestSub(hTest, "Random Test");
+ RTTESTI_CHECK_RC(rc = VbglR0PhysHeapInit(), VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ return RTTestSummaryAndDestroy(hTest);
+
+ RTRAND hRand;
+ RTTESTI_CHECK_RC(rc = RTRandAdvCreateParkMiller(&hRand), VINF_SUCCESS);
+ if (RT_FAILURE(rc))
+ return RTTestSummaryAndDestroy(hTest);
+ RTRandAdvSeed(hRand, uRandSeed);
+ RTTestValue(hTest, "RandSeed", uRandSeed, RTTESTUNIT_NONE);
+
+ static TSTHISTORYENTRY s_aHistory[3072];
+ RT_ZERO(s_aHistory);
+
+ for (unsigned iTest = 0; iTest < 131072; iTest++)
+ {
+ i = RTRandAdvU32Ex(hRand, 0, RT_ELEMENTS(s_aHistory) - 1);
+ if (!s_aHistory[i].pv)
+ {
+ s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 8, 1024);
+ s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb);
+ if (!s_aHistory[i].pv)
+ {
+ s_aHistory[i].cb = 9;
+ s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb);
+ }
+ if (s_aHistory[i].pv)
+ {
+ memset(s_aHistory[i].pv, 0xbb, s_aHistory[i].cb);
+ CHECK_PHYS_ADDR(s_aHistory[i].pv);
+ }
+ }
+ else
+ {
+ VbglR0PhysHeapFree(s_aHistory[i].pv);
+ s_aHistory[i].pv = NULL;
+ }
+
+#if 1
+ /* Check heap integrity: */
+ RTTESTI_CHECK_RC_OK(VbglR0PhysHeapCheck(NULL));
+ int cChunks = 0;
+ for (VBGLPHYSHEAPCHUNK *pCurChunk = g_vbgldata.pChunkHead; pCurChunk; pCurChunk = pCurChunk->pNext)
+ cChunks++;
+ RTTESTI_CHECK_MSG(cChunks == g_cChunks, ("g_cChunks=%u, but only %u chunks in the list!\n", g_cChunks, cChunks));
+#endif
+
+ if ((iTest % 7777) == 7776)
+ {
+ /* exhaust the heap */
+ PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-pre ");
+
+ for (i = 0; i < RT_ELEMENTS(s_aHistory) && (VbglR0PhysHeapGetFreeSize() >= 256 || g_cChunks < TST_MAX_CHUNKS); i++)
+ if (!s_aHistory[i].pv)
+ {
+ s_aHistory[i].cb = RTRandAdvU32Ex(hRand, VBGL_PH_CHUNKSIZE / 8, VBGL_PH_CHUNKSIZE / 2 + VBGL_PH_CHUNKSIZE / 4);
+ s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb);
+ if (s_aHistory[i].pv)
+ {
+ memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb);
+ CHECK_PHYS_ADDR(s_aHistory[i].pv);
+ }
+ }
+
+ size_t cbFree = VbglR0PhysHeapGetFreeSize();
+ if (cbFree)
+ for (i = 0; i < RT_ELEMENTS(s_aHistory); i++)
+ if (!s_aHistory[i].pv)
+ {
+ s_aHistory[i].cb = RTRandAdvU32Ex(hRand, 1, (uint32_t)cbFree);
+ s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb);
+ while (s_aHistory[i].pv == NULL && s_aHistory[i].cb > 2)
+ {
+ s_aHistory[i].cb >>= 1;
+ s_aHistory[i].pv = VbglR0PhysHeapAlloc(s_aHistory[i].cb);
+ }
+ if (s_aHistory[i].pv)
+ {
+ memset(s_aHistory[i].pv, 0x55, s_aHistory[i].cb);
+ CHECK_PHYS_ADDR(s_aHistory[i].pv);
+ }
+
+ cbFree = VbglR0PhysHeapGetFreeSize();
+ if (!cbFree)
+ break;
+ }
+
+ RTTESTI_CHECK_MSG(VbglR0PhysHeapGetFreeSize() == 0, ("%zu\n", VbglR0PhysHeapGetFreeSize()));
+ PrintStats(s_aHistory, RT_ELEMENTS(s_aHistory), "Exhaust-post");
+ }
+ else if ((iTest % 7777) == 1111)
+ {
+ /* free all */
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-pre: cFreeBlocks=%u cAllocedBlocks=%u in %u chunk(s)\n",
+ g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks - g_vbgldata.cFreeBlocks, g_cChunks);
+ for (i = 0; i < RT_ELEMENTS(s_aHistory); i++)
+ {
+ VbglR0PhysHeapFree(s_aHistory[i].pv);
+ s_aHistory[i].pv = NULL;
+ }
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Free-all-post: cFreeBlocks=%u in %u chunk(s)\n", g_vbgldata.cFreeBlocks, g_cChunks);
+ RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks));
+ RTTESTI_CHECK_MSG(g_vbgldata.cFreeBlocks == g_vbgldata.cBlocks,
+ ("g_vbgldata.cFreeBlocks=%d cBlocks=%d\n", g_vbgldata.cFreeBlocks, g_vbgldata.cBlocks));
+
+ //size_t cbAfterRand = VbglR0PhysHeapGetFreeSize();
+ //RTTESTI_CHECK_MSG(cbAfterRand == cbAfter, ("cbAfterRand=%zu cbAfter=%zu\n", cbAfterRand, cbAfter));
+ }
+ }
+
+ /* free the rest. */
+ for (i = 0; i < RT_ELEMENTS(s_aHistory); i++)
+ {
+ VbglR0PhysHeapFree(s_aHistory[i].pv);
+ s_aHistory[i].pv = NULL;
+ }
+
+ RTTESTI_CHECK_MSG(g_cChunks == 1, ("g_cChunks=%d\n", g_cChunks));
+
+ VbglR0PhysHeapTerminate();
+ RTTESTI_CHECK_MSG(g_cChunks == 0, ("g_cChunks=%d\n", g_cChunks));
+
+ RTTESTI_CHECK_RC(rc = RTRandAdvDestroy(hRand), VINF_SUCCESS);
+ return RTTestSummaryAndDestroy(hTest);
+}
+