summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/darwin
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/darwin
parentInitial commit. (diff)
downloadvirtualbox-upstream/7.0.14-dfsg.tar.xz
virtualbox-upstream/7.0.14-dfsg.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/darwin')
-rw-r--r--src/VBox/Additions/darwin/Installer/.scm-settings37
-rwxr-xr-xsrc/VBox/Additions/darwin/Installer/DiskImage/Uninstall.tool124
-rw-r--r--src/VBox/Additions/darwin/Installer/Makefile.kmk414
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist15
-rwxr-xr-xsrc/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/postflight104
-rwxr-xr-xsrc/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper63
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist17
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist15
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Conclusion.rtf9
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Localizable.strings14
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Welcome.rtf13
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-amd64.dist70
-rw-r--r--src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-x86.dist70
-rw-r--r--src/VBox/Additions/darwin/Makefile.kmk35
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/Makefile.kmk57
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp298
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp341
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp390
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp315
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h112
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/.scm-settings29
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/Info.plist44
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/Makefile.kmk80
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp608
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp843
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp639
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp261
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h117
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h54
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp97
30 files changed, 5285 insertions, 0 deletions
diff --git a/src/VBox/Additions/darwin/Installer/.scm-settings b/src/VBox/Additions/darwin/Installer/.scm-settings
new file mode 100644
index 00000000..d46569be
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/.scm-settings
@@ -0,0 +1,37 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for OS X guest additions installer.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+
+--filter-out-files /VBoxGuestAdditions_mpkg/Conclusion.rtf
+--filter-out-files /VBoxGuestAdditions_mpkg/Welcome.rtf
+--filter-out-files /VBoxGuestAdditions_mpkg/Localizable.strings
+
+/DiskImage/Uninstall.tool: --treat-as .sh
+/VBoxGuestAdditionsKEXTs/postflight: --treat-as .sh
+/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper: --treat-as .sh
+/VBoxGuestAdditions_mpkg/distribution.dist: --treat-as .xml
+
diff --git a/src/VBox/Additions/darwin/Installer/DiskImage/Uninstall.tool b/src/VBox/Additions/darwin/Installer/DiskImage/Uninstall.tool
new file mode 100755
index 00000000..e057c250
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/DiskImage/Uninstall.tool
@@ -0,0 +1,124 @@
+#!/bin/sh
+# $Id: Uninstall.tool $
+## #file
+# VirtualBox Guest Additions uninstall script.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+# Override any funny stuff from the user.
+export PATH="/bin:/usr/bin:/sbin:/usr/sbin:$PATH"
+
+#
+# Display a simple welcome message first.
+#
+echo ""
+echo "Welcome to the VirtualBox Guest Additions uninstall script."
+echo ""
+
+# Check if user interraction is required to start uninstall process.
+fUnattended=0
+if test "$#" != "0"; then
+ if test "$#" != "1" -o "$1" != "--unattended"; then
+ echo "Error: Unknown argument(s): $*"
+ echo ""
+ echo "Usage: $0 [--unattended]"
+ echo ""
+ echo "If the '--unattended' option is not given, you will be prompted"
+ echo "for a Yes/No before doing the actual uninstallation."
+ echo ""
+ exit 4;
+ fi
+ fUnattended="Yes"
+fi
+
+if test "$fUnattended" != "Yes"; then
+ echo "Do you wish to continue none the less (Yes/No)?"
+ read fUnattended
+ if test "$fUnattended" != "Yes" -a "$fUnattended" != "YES" -a "$fUnattended" != "yes"; then
+ echo "Aborting uninstall. (answer: '$fUnattended')".
+ exit 2;
+ fi
+ echo ""
+fi
+
+# Stop services
+echo "Checking running services..."
+unload()
+{
+ ITEM_ID=$1
+ ITEM_PATH=$2
+ FORCED_USER=$3
+
+ echo "Unloading $ITEM_ID"
+
+
+ loaded="NO"
+ test -n "$(sudo -u "$FORCED_USER" launchctl list | grep $ITEM_ID)" && loaded="YES"
+ if [ "$loaded" = "YES" ] ; then
+ sudo -p "Please enter $FORCED_USER's password (unloading $ITEM_ID):" sudo -u "$FORCED_USER" launchctl unload -F "$ITEM_PATH/$ITEM_ID.plist"
+ fi
+
+}
+
+unload "org.virtualbox.additions.vboxservice" "/Library/LaunchDaemons" "root"
+unload "org.virtualbox.additions.vboxclient" "/Library/LaunchAgents" `whoami`
+
+# Unload kernel extensions
+echo "Checking running kernel extensions..."
+items="VBoxGuest"
+for item in $items; do
+ kext_item="org.virtualbox.kext.$item"
+ loaded=`kextstat | grep $kext_item`
+ if [ ! -z "$loaded" ] ; then
+ echo "Unloading $item kernel extension"
+ sudo -p "Please enter %u's password (unloading $item):" kextunload -b $kext_item
+ fi
+done
+
+# Remove files and directories
+echo "Checking files and directories..."
+sudo -p "Please enter %u's password (removing files and directories):" rm -rf "/Library/Application Support/VirtualBox Guest Additions"
+sudo -p "Please enter %u's password (removing files and directories):" rm -rf "/Library/Extensions/VBoxGuest.kext"
+sudo -p "Please enter %u's password (removing files and directories):" rm -rf "/Library/LaunchAgents/org.virtualbox.additions.vboxclient.plist"
+sudo -p "Please enter %u's password (removing files and directories):" rm -rf "/Library/LaunchDaemons/org.virtualbox.additions.vboxservice.plist"
+
+# Cleaning up pkgutil database
+echo "Checking package database ..."
+items="kexts tools-and-services"
+for item in $items; do
+ pkg_item="org.virtualbox.pkg.additions.$item"
+ installed=`pkgutil --pkgs="$pkg_item"`
+ if [ ! -z "$installed" ] ; then
+ sudo -p "Please enter %u's password (removing $pkg_item):" pkgutil --forget "$pkg_item"
+ fi
+done
+
+# Remove our kexts from the cache.
+echo "Updating kernel cache."
+sudo -p "Please enter %u's password (refreshing kext cache):" touch "/System/Library/Extensions/"
+sudo -p "Please enter %u's password (refreshing kext cache):" kextcache -update-volume /
+
+echo "Done."
+exit 0;
+
diff --git a/src/VBox/Additions/darwin/Installer/Makefile.kmk b/src/VBox/Additions/darwin/Installer/Makefile.kmk
new file mode 100644
index 00000000..0dae3d97
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/Makefile.kmk
@@ -0,0 +1,414 @@
+# $Id: Makefile.kmk $
+## @file
+# Install misc stuff and create dist packages for Mac OS X Guest Additions.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Globals
+#
+VBOX_ADD_PATH_DI_SRC := $(PATH_SUB_CURRENT)
+VBOX_ADD_DI_OUT_DIR := $(PATH_TARGET)/additions/Installer
+BLDDIRS += $(VBOX_ADD_DI_OUT_DIR)
+
+ifdef VBOX_WITH_COMBINED_PACKAGE
+ ifeq ($(KBUILD_TARGET_ARCH),x86)
+ VBOX_PATH_DIST_32 = $(VBOX_PATH_DIST)/additions
+ VBOX_PATH_DIST_64 = $(PATH_OUT_BASE)/darwin.amd64/$(KBUILD_TYPE)/dist/additions
+ else
+ VBOX_PATH_DIST_64 = $(VBOX_PATH_DIST)/additions
+ VBOX_PATH_DIST_32 = $(PATH_OUT_BASE)/darwin.x86/$(KBUILD_TYPE)/dist
+ endif
+ VBOX_DI_FN_DEP_BOTH = $(VBOX_PATH_DIST_32)/$1 $(VBOX_PATH_DIST_64)/$2
+ VBOX_DI_FN_DEP_32 = $(VBOX_PATH_DIST_32)/$1
+ VBOX_DI_FN_DEP_64 = $(VBOX_PATH_DIST_64)/$1
+ VBOX_DI_LIPO = lipo
+else
+ VBOX_DI_FN_DEP_BOTH = $(VBOX_PATH_DIST)/additions/$1
+ ifeq ($(KBUILD_TARGET_ARCH),x86)
+ VBOX_DI_FN_DEP_32 = $(VBOX_PATH_DIST)/additions/$1
+ VBOX_DI_FN_DEP_64 =
+ else
+ VBOX_DI_FN_DEP_64 = $(VBOX_PATH_DIST)/additions/$1
+ VBOX_DI_FN_DEP_32 =
+ endif
+endif
+
+# Unset this to speed up things during makefile hacking.
+VBOX_DARWIN_INST_DEP_ON_MAKEFILE := $(MAKEFILE_CURRENT)
+
+# The location of the pkgbuild program.
+ifndef VBOX_PKGBUILD
+ VBOX_PKGBUILD := pkgbuild
+endif
+
+# The location of the productbuild program.
+ifndef VBOX_PRODUCTBUILD
+ VBOX_PRODUCTBUILD := productbuild
+endif
+
+# Where we do the packing (override this in LocalConfig.kmk if building on smbfs).
+ifndef VBOX_PATH_PACK_TMP
+ VBOX_PATH_PACK_TMP := $(VBOX_ADD_DI_OUT_DIR)
+endif
+
+
+#
+# The packing.
+#
+PACKING += \
+ $(PATH_STAGE)/$(INST_ADDITIONS)VBoxGuestAdditions.pkg \
+ $(PATH_STAGE)/$(INST_ADDITIONS)VBoxDarwinAdditionsUninstall.tool
+
+#OTHER_CLEAN = TODO
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
+
+#
+# We're running commands as root here, take some care and assertion
+# a sane environment.
+#
+ifeq ($(strip $(VBOX_PATH_DIST)),)
+ $(error VBOX_PATH_DIST=$(VBOX_PATH_DIST))
+endif
+ifeq ($(strip $(VBOX_PATH_DIST)),/)
+ $(error VBOX_PATH_DIST=$(VBOX_PATH_DIST))
+endif
+ifeq ($(strip $(VBOX_PATH_PACK_TMP)),)
+ $(error VBOX_PATH_PACK_TMP=$(VBOX_PATH_PACK_TMP))
+endif
+ifeq ($(strip $(VBOX_PATH_PACK_TMP)),/)
+ $(error VBOX_PATH_PACK_TMP=$(VBOX_PATH_PACK_TMP))
+endif
+
+#
+# The packing targets.
+#
+$(PATH_STAGE)/$(INST_ADDITIONS)VBoxGuestAdditions.pkg: $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditions.pkg
+ $(MKDIR) -p "$(@D)"
+ $(INSTALL) -m 0644 -- "$<" "$@"
+
+$(PATH_STAGE)/$(INST_ADDITIONS)VBoxDarwinAdditionsUninstall.tool: $(VBOX_ADD_PATH_DI_SRC)/DiskImage/Uninstall.tool
+ $(MKDIR) -p "$(@D)"
+ $(INSTALL) -m 0755 -- "$<" "$@"
+
+
+#
+# The meta-package.
+#
+$(VBOX_PATH_PACK_TMP)/VBoxGuestAdditions.pkg: \
+ $(VBOX_PATH_PACK_TMP)/Packages/VBoxGuestAdditionsKEXTs.pkg \
+ $(VBOX_PATH_PACK_TMP)/Packages/VBoxGuestAdditionsToolsAndServices.pkg \
+ $$(wildcard $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/* \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/*.lproj/*) \
+ $(foreach f,$(VBOX_INSTALLER_ADD_LANGUAGES), $(VBOX_BRAND_$(f)_VIRTUALBOX_WELCOME_RTF)) \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/Welcome.rtf \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/Conclusion.rtf \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/distribution-$(KBUILD_TARGET_ARCH).dist \
+ $(VBOX_DARWIN_INST_DEP_ON_MAKEFILE)
+ $(call MSG_TOOL,productbuild,,,$@)
+ @# Cleanup any previously failed attempts.
+ sudo rm -Rf \
+ $@ \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.root \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res
+ @# Correct directory permissions are important.
+ $(MKDIR) -p \
+ $(@D) \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/English.lproj
+
+ @# Do keyword replacement in the package info and description files.
+ $(SED) \
+ -e 's+@VBOX_VERSION_STRING@+$(VBOX_VERSION_STRING)+g' \
+ -e 's+@VBOX_VERSION_MAJOR@+$(VBOX_VERSION_MAJOR)+g' \
+ -e 's+@VBOX_VERSION_MINOR@+$(VBOX_VERSION_MINOR)+g' \
+ -e 's+@VBOX_VERSION_BUILD@+$(VBOX_VERSION_BUILD)+g' \
+ -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \
+ -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \
+ -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \
+ --output $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/English.lproj/Welcome.rtf \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/Welcome.rtf
+ @# Copy the resources.
+ $(INSTALL) -m 0644 $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/Conclusion.rtf $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/English.lproj/Conclusion.rtf
+
+ $(SED) \
+ -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \
+ -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \
+ -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \
+ --output $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/English.lproj/Localizable.strings \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/Localizable.strings
+ $(INSTALL) -m 0644 $(VBOX_BRAND_DARWIN_INSTALLER_BG) $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/background.tif
+
+ $(foreach f,$(VBOX_INSTALLER_ADD_LANGUAGES), \
+ $(MKDIR) -p \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/$(VBOX_INSTALLER_$(f)_DARWIN_TARGET).lproj$(NLTAB) \
+ $(SED) \
+ -e 's+@VBOX_VERSION_STRING@+$(VBOX_VERSION_STRING)+g' \
+ -e 's+@VBOX_VERSION_MAJOR@+$(VBOX_VERSION_MAJOR)+g' \
+ -e 's+@VBOX_VERSION_MINOR@+$(VBOX_VERSION_MINOR)+g' \
+ -e 's+@VBOX_VERSION_BUILD@+$(VBOX_VERSION_BUILD)+g' \
+ --output $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/$(VBOX_INSTALLER_$(f)_DARWIN_TARGET).lproj/Welcome.rtf \
+ $(VBOX_BRAND_$(f)_VIRTUALBOX_WELCOME_RTF)$(NLTAB) \
+ $(INSTALL) -m 0644 $(VBOX_BRAND_$(f)_VIRTUALBOX_CONCLUSION_RTF) $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/$(VBOX_INSTALLER_$(f)_DARWIN_TARGET).lproj/Conclusion.rtf$(NLTAB) \
+ $(SED) \
+ -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \
+ -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \
+ -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \
+ --output $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res/$(VBOX_INSTALLER_$(f)_DARWIN_TARGET).lproj/Localizable.strings \
+ $(VBOX_BRAND_$(f)_VIRTUALBOX_LOCALIZABLE_STRINGS)$(NLTAB) \
+ )
+
+ @# Build the package.
+ $(VBOX_PRODUCTBUILD) \
+ --distribution $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditions_mpkg/distribution-$(KBUILD_TARGET_ARCH).dist \
+ --package-path $(VBOX_PATH_PACK_TMP)/Packages \
+ --resources $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res \
+ --identifier org.VirtualBox.mpkg.GuestAdditions \
+ --version $(VBOX_VERSION_MAJOR).$(VBOX_VERSION_MINOR).$(VBOX_VERSION_BUILD) \
+ $(if-expr defined(VBOX_MACOSX_INSTALLER_SIGN) && $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING)) == "",--sign "$(VBOX_MACOSX_INSTALLER_SIGN)",) \
+ $@
+ifdef VBOX_SIGNING_MODE
+ if $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING))
+ @# Sign the created pkg.
+ $(call VBOX_SIGN_PKG_FN,$@,org.VirtualBox.mpkg.GuestAdditions)
+ if $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING))
+ @# Notarize the signed pkg (includes stapling).
+ $(call VBOX_NOTARIZE_FILE_FN,$@,org.virtualbox.VBoxGuestAdditions.$(VBOX_VERSION_MAJOR).$(VBOX_VERSION_MINOR).$(VBOX_VERSION_BUILD).$(VBOX_SVN_REV))
+ endif
+ endif
+endif
+
+ @# Cleanup.
+ sudo rm -Rf \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.root \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.dist.res
+
+VBoxDarwinAdditions.pkg:: $(VBOX_PATH_PACK_TMP)/VBoxDarwinAdditions.pkg
+
+#
+# The VirtualBox Kernel extensions.
+#
+VBOX_ADD_DI_KEXTS_UNIVERSAL = VBoxGuest
+VBOX_ADD_DI_KEXTS = $(VBOX_ADD_DI_KEXTS_UNIVERSAL)
+
+$(VBOX_PATH_PACK_TMP)/Packages/VBoxGuestAdditionsKEXTs.pkg: \
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS_UNIVERSAL), $(call VBOX_DI_FN_DEP_BOTH,$(kext).kext/Contents/MacOS/$(kext))) \
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS), $(VBOX_PATH_DIST)/additions/$(kext).kext/Contents/Info.plist) \
+ $$(wildcard $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/* \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/*.lproj/*) \
+ $(foreach f,$(VBOX_INSTALLER_ADD_LANGUAGES), \
+ $(VBOX_BRAND_$(f)_VBOXKEXTS_DESCRIPTION_PLIST) \
+ $(VBOX_BRAND_$(f)_VBOXKEXTS_README_HTML) \
+ $(VBOX_BRAND_$(f)_VBOXKEXTS_INSTALLATIONCHECK_STRINGS)) \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/postflight \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist \
+ $(VBOX_DARWIN_INST_DEP_ON_MAKEFILE)
+ $(call MSG_TOOL,pkgbuild,,,$@)
+ @# Cleanup any previously failed attempts.
+ sudo rm -Rf \
+ $@ \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res
+ @# Correct directory permissions are important.
+ $(MKDIR) -p \
+ $(@D) \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res/English.lproj
+ $(MKDIR) -p -m 1775 $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library
+ $(MKDIR) -p -m 0755 \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions \
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS), \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents/MacOS )
+ @# Copy the common files (Info.plist).
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS), \
+ $(NLTAB)$(INSTALL) -m 0644 $(VBOX_PATH_DIST)/additions/$(kext).kext/Contents/Info.plist $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents/)
+ @# Copy the binaries and invoking lipo.
+ifdef VBOX_WITH_COMBINED_PACKAGE
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS_UNIVERSAL), \
+ $(NLTAB)$(VBOX_DI_LIPO) -create \
+ $(VBOX_PATH_DIST_32)/additions/$(kext).kext/Contents/MacOS/$(kext) \
+ $(VBOX_PATH_DIST_64)/additions/$(kext).kext/Contents/MacOS/$(kext) \
+ -output $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents/MacOS/$(kext))
+else
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS), \
+ $(NLTAB)$(INSTALL) -m 0755 $(VBOX_PATH_DIST)/additions/$(kext).kext/Contents/MacOS/$(kext) $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents/MacOS/)
+endif
+ @# Sign the kext bundles.
+ifdef VBOX_SIGNING_MODE
+ $(foreach kext,$(VBOX_ADD_DI_KEXTS), \
+ $(NLTAB)$(call VBOX_SIGN_BUNDLE_FN,$(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext,) \
+ $(NLTAB)chmod a+r $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/$(kext).kext/Contents/_CodeSignature/* )
+endif
+ @# Set the correct owners.
+ sudo chown root:admin $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library
+ sudo chown -R root:wheel $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions
+
+ # Copy package internal files
+ $(INSTALL) $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.desc/PkgBuildComponent.plist
+
+ # Copy installer scripts
+ $(INSTALL) -m 0755 $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsKEXTs/postflight $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res
+
+ @# Build the package.
+ $(VBOX_PKGBUILD) \
+ --root $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root/Library/Extensions/ \
+ --component-plist $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.desc/PkgBuildComponent.plist \
+ --script $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res \
+ --identifier org.virtualbox.pkg.additions.kexts \
+ --version $(VBOX_VERSION_MAJOR).$(VBOX_VERSION_MINOR).$(VBOX_VERSION_BUILD) \
+ --install-location /Library/Extensions/ \
+ --ownership preserve \
+ $(if-expr defined(VBOX_MACOSX_INSTALLER_SIGN) && $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING)) == "",--sign "$(VBOX_MACOSX_INSTALLER_SIGN)",) \
+ $@
+ifdef VBOX_SIGNING_MODE
+ if $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING))
+ @# Sign the created pkg.
+ $(call VBOX_SIGN_PKG_FN,$@,org.virtualbox.pkg.vboxguestadditionskexts)
+ endif
+endif
+ @# Cleanup
+ sudo rm -Rf \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.root \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.desc \
+ $(VBOX_PATH_PACK_TMP)/VBoxGuestAdditionsKEXTs.pkg.res
+
+#
+# The VirtualBox Guest Additions Tools & Services.
+#
+
+VBOX_GA_PKG = VBoxGuestAdditionsToolsAndServices.pkg
+VBOX_DI_VB_GA_BINARIES = VBoxClient VBoxControl VBoxService
+$(VBOX_PATH_PACK_TMP)/Packages/$(VBOX_GA_PKG): \
+ $(foreach f, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(call VBOX_DI_FN_DEP_BOTH,$(f)) ) \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist \
+ $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper \
+ $(VBOX_ADD_PATH_DI_SRC)/DiskImage/Uninstall.tool \
+ $(VBOX_DARWIN_INST_DEP_ON_MAKEFILE)
+ $(call MSG_TOOL,pkgbuild,,,$@)
+ @# Cleanup any previously failed attempts.
+ sudo rm -Rf \
+ $@ \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).desc \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).res
+ @# Correct directory permissions are important.
+ $(MKDIR) -p \
+ $(@D) \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).desc \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).res \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).res/English.lproj
+
+ @# Create directory structure within a package w/ proper permittions
+ $(MKDIR) -p -m 0775 \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions" \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/LaunchAgents" \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/LaunchDaemons"
+
+ @# Install binaries
+ifdef VBOX_WITH_COMBINED_PACKAGE
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(VBOX_DI_LIPO) -create \
+ $(VBOX_PATH_DIST_32)/additions/$(binary) \
+ $(VBOX_PATH_DIST_64)/additions/$(binary) \
+ -output "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)"$(NLTAB))
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES), \
+ $(NLTAB)$(INSTALL) -m 0755 $(VBOX_PATH_DIST_32)/additions/$(binary) "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-x86" \
+ $(NLTAB)$(INSTALL) -m 0755 $(VBOX_PATH_DIST_64)/additions/$(binary) "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-amd64" )
+else
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(INSTALL) -m 0755 $(VBOX_PATH_DIST)/additions/$(binary) "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)"$(NLTAB))
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(INSTALL) -m 0755 $(VBOX_PATH_DIST)/additions/$(binary) "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-$(KBUILD_TARGET_ARCH)"$(NLTAB))
+endif
+ # Add Uninstall.tool
+ $(INSTALL) -m 0755 $(VBOX_ADD_PATH_DI_SRC)/DiskImage/Uninstall.tool "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/"
+ifdef VBOX_SIGNING_MODE
+ $(call VBOX_SIGN_FILE_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/Uninstall.tool,org.virtualbox.app.guestadditions.uninstaller)
+endif
+
+ @# Install launchd stuff
+ $(INSTALL) -m 0755 $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/"
+ $(INSTALL) -m 644 $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/LaunchAgents/"
+ $(INSTALL) -m 644 $(VBOX_ADD_PATH_DI_SRC)/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist \
+ "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/LaunchDaemons/"
+
+ @# Sign the binaries.
+ifdef VBOX_SIGNING_MODE
+ ifdef VBOX_WITH_COMBINED_PACKAGE
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(NLTAB)$(call VBOX_SIGN_MACHO_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary),org.virtualbox.app.guestadditions.$(notdir $(binary))) )
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(NLTAB)$(call VBOX_SIGN_MACHO_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-x86,org.virtualbox.app.guestadditions.$(notdir $(binary))-x86) )
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(NLTAB)$(call VBOX_SIGN_MACHO_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-amd64,org.virtualbox.app.guestadditions.$(notdir $(binary))-amd64) )
+ else
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(NLTAB)$(call VBOX_SIGN_MACHO_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary),org.virtualbox.app.guestadditions.$(notdir $(binary))) )
+ $(foreach binary, $(VBOX_DI_VB_GA_BINARIES) \
+ ,$(NLTAB)$(call VBOX_SIGN_MACHO_FN,$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/Application Support/VirtualBox Guest Additions/$(binary)-$(KBUILD_TARGET_ARCH),org.virtualbox.app.guestadditions.$(notdir $(binary))-$(KBUILD_TARGET_ARCH)) )
+ endif
+endif
+
+ @# Correct ownership
+ sudo chown -R root:wheel "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/"
+
+ @# Build the package.
+ $(VBOX_PKGBUILD) \
+ --root "$(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root/Library/" \
+ --script $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).res \
+ --identifier org.virtualbox.pkg.additions.tools-and-services \
+ --version $(VBOX_VERSION_MAJOR).$(VBOX_VERSION_MINOR).$(VBOX_VERSION_BUILD) \
+ --install-location "/Library/" \
+ --ownership preserve \
+ $(if-expr defined(VBOX_MACOSX_INSTALLER_SIGN) && $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING)) == "",--sign "$(VBOX_MACOSX_INSTALLER_SIGN)",) \
+ $@
+ifdef VBOX_SIGNING_MODE
+ if $(intersects darwin all 1,$(VBOX_WITH_CORP_CODE_SIGNING))
+ @# Sign the created pkg.
+ $(call VBOX_SIGN_PKG_FN,$@,org.virtualbox.pkg.vboxguestadditions)
+ endif
+endif
+ @# Cleanup
+ sudo rm -Rf \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).root \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).desc \
+ $(VBOX_PATH_PACK_TMP)/$(VBOX_GA_PKG).res
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist
new file mode 100644
index 00000000..56d8701b
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/PkgBuildComponent.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array>
+ <dict>
+ <key>RootRelativeBundlePath</key> <string>VBoxGuest.kext</string>
+ <key>BundleIsRelocatable</key> <false/>
+ <key>BundleIsVersionChecked</key> <false/>
+ <key>BundleHasStrictIdentifier</key> <false/>
+ <key>BundleOverwriteAction</key> <string>upgrade</string>
+ <key>BundlePostInstallScriptPath</key> <string>postflight</string>
+ </dict>
+</array>
+</plist>
+
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/postflight b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/postflight
new file mode 100755
index 00000000..5b0a0b8c
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsKEXTs/postflight
@@ -0,0 +1,104 @@
+#!/bin/sh
+# $Id: postflight $
+## @file
+# Post flight installer script for the VirtualBox OS X kernel extensions.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+set -e
+
+# Setup environment.
+export PATH="/bin:/usr/bin:/sbin:/usr/sbin:$PATH"
+
+unload_service()
+{
+ ITEM_ID=$1
+ ITEM_PATH=$2
+ FORCED_USER=$3
+
+ loaded="NO"
+ test -n "$(sudo -u "$FORCED_USER" launchctl list | grep $ITEM_ID)" && loaded="YES"
+ if [ "$loaded" = "YES" ] ; then
+ echo "Unloading previously installed service: $ITEM_ID"
+ sudo -u "$FORCED_USER" launchctl unload -F "$ITEM_PATH/$ITEM_ID.plist"
+ fi
+}
+
+load_service()
+{
+ ITEM_ID=$1
+ ITEM_PATH=$2
+ FORCED_USER=$3
+
+ echo "Loading newly installed service: $ITEM_ID"
+ sudo -u "$FORCED_USER" launchctl load -F "$ITEM_PATH/$ITEM_ID.plist"
+}
+
+unload_service "org.virtualbox.additions.vboxservice" "/Library/LaunchDaemons" "root"
+
+# Remove the old service for all users
+for user in $(dscl . -list /Users | grep -v -e'^_' -e'root'); do
+ system_user="YES"
+ test -n "$(dscl . -read /Users/$user NFSHomeDirectory | grep '/Users')" && system_user="NO"
+ if [ "$system_user" = "NO" ]; then
+ unload_service "org.virtualbox.additions.vboxclient" "/Library/LaunchAgents" "$user"
+ fi
+done
+
+items="VBoxGuest"
+for item in $items; do
+ kext_item="org.virtualbox.kext.$item"
+
+ loaded="NO"
+ test -n "$(kextstat | grep $kext_item)" && loaded="YES"
+ if [ "$loaded" = "YES" ] ; then
+ echo "Unloading $item kernel extension..."
+ kextunload -b $kext_item
+ fi
+done
+
+MACOS_VERS=$(sw_vers -productVersion)
+
+echo "Updating kernel cache (should trigger loading of new modules)."
+# /System/Library/Extensions is readonly in Catalina and later,
+# so touch returns always false on these systems
+if [[ ${MACOS_VERS} != 11.* ]] && [[ ${MACOS_VERS} != 10.15.* ]]; then
+ touch "/System/Library/Extensions/"
+fi
+kextcache -update-volume / || true
+
+load_service "org.virtualbox.additions.vboxservice" "/Library/LaunchDaemons" "root"
+# Add VBoxClient for all currently defined users
+for user in $(dscl . -list /Users | grep -v -e'^_' -e'root'); do
+ system_user="YES"
+ test -n "$(dscl . -read /Users/$user NFSHomeDirectory | grep '/Users')" && system_user="NO"
+ if [ "$system_user" = "NO" ]; then
+ load_service "org.virtualbox.additions.vboxclient" "/Library/LaunchAgents" "$user"
+ fi
+done
+
+echo "Warning: If VBoxService adjusts the time backwards (because of --biossystemtimeoffset), the installer may hang."
+echo "Done."
+
+exit 0;
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper
new file mode 100755
index 00000000..864fc76b
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/VBoxServiceWrapper
@@ -0,0 +1,63 @@
+#!/bin/sh
+## @file
+# VBoxService wrapper script.
+#
+# Load required kernel extensions before start service (if necessary).
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+export PATH="/bin:/usr/bin:/sbin:/usr/sbin:$PATH"
+
+echo "Check if kernel extensions loaded..."
+items="VBoxGuest"
+for item in $items; do
+ kext_item="org.virtualbox.kext.$item"
+ loaded=`kextstat | grep $kext_item`
+ if [ -z "$loaded" ] ; then
+ echo "Loading $item kernel extension..."
+ XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1`
+ if [ "$XNU_VERSION" -ge "10" ]; then
+ kextutil /Library/Extensions/$item.kext
+ else
+ kextload /Library/Extensions/$item.kext
+ fi
+ fi
+done
+
+echo "Check if VBoxClient is added for all non-system users"
+for user in $(dscl . -list /Users | grep -v -e'^_' -e'root'); do
+ system_user="YES"
+ test -n "$(dscl . -read /Users/$user NFSHomeDirectory | grep '/Users')" && system_user="NO"
+ if [ "$system_user" = "NO" ]; then
+ loaded="NO"
+ test -n "$(sudo -u "$user" launchctl list | grep 'org.virtualbox.additions.vboxclient')" && loaded="YES"
+ if [ "$loaded" = "NO" ] ; then
+ echo "Loading org.virtualbox.additions.vboxclient for $user"
+ sudo -u "$user" launchctl load -F "/Library/LaunchAgents/org.virtualbox.additions.vboxclient.plist"
+ fi
+ fi
+done
+
+exec "/Library/Application Support/VirtualBox Guest Additions/VBoxService" -f
+
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist
new file mode 100644
index 00000000..6b8b6b05
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxclient.plist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Disabled</key> <false/>
+ <key>RunAtLoad</key> <true/>
+ <key>KeepAlive</key> <true/>
+ <key>Label</key> <string>org.virtualbox.additions.vboxclient</string>
+
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Library/Application Support/VirtualBox Guest Additions/VBoxClient</string>
+ <string>-f</string>
+ </array>
+</dict>
+</plist>
+
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist
new file mode 100644
index 00000000..d462b50d
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditionsToolsAndServices/org.virtualbox.additions.vboxservice.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Disabled</key> <false/>
+ <key>RunAtLoad</key> <true/>
+ <key>KeepAlive</key> <true/>
+ <key>Label</key> <string>org.virtualbox.additions.vboxservice</string>
+
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Library/Application Support/VirtualBox Guest Additions/VBoxServiceWrapper</string>
+ </array>
+</dict>
+</plist>
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Conclusion.rtf b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Conclusion.rtf
new file mode 100644
index 00000000..66a61b5e
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Conclusion.rtf
@@ -0,0 +1,9 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\vieww10800\viewh8400\viewkind0
+\deftab720
+\pard\pardeftab720\sa240
+
+\f0\fs24 \cf0 VirtualBox Guest Addition successfully installed and ready for use.\
+} \ No newline at end of file
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Localizable.strings b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Localizable.strings
new file mode 100644
index 00000000..3781727e
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Localizable.strings
@@ -0,0 +1,14 @@
+'VirtualBox_title' = '@VBOX_PRODUCT@';
+
+'choiceVBoxKEXTs_title' = 'Kernel Extensions';
+'choiceVBoxKEXTs_msg' = 'Installs the @VBOX_PRODUCT@ Guest Additions Kernel Extensions into /Library/Extensions.';
+
+'choiceVBoxToolsAndServices_title' = 'Tools and Services';
+'choiceVBoxToolsAndServices_msg' = 'Installs the @VBOX_PRODUCT@ Guest Additions Tools and Services to /Library/Application Support/VirtualBox Guest Additions.';
+
+'UNSUPPORTED_AMD64_OS_TLE' = "Incompatible OS version detected!";
+'UNSUPPORTED_AMD64_OS_MSG' = "VirtualBox Guest Additions require Mac OS X 10.7 or later running in 64-bit mode.";
+
+'UNSUPPORTED_X86_OS_TLE' = "Incompatible OS version detected!";
+'UNSUPPORTED_X86_OS_MSG' = "The 32-bit VirtualBox Guest Additions are only for Mac OS X 10.7 and earlier running in 32-bit mode.";
+
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Welcome.rtf b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Welcome.rtf
new file mode 100644
index 00000000..344017e3
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/Welcome.rtf
@@ -0,0 +1,13 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\vieww10800\viewh8400\viewkind0
+\deftab720
+\pard\pardeftab720\sa280
+
+\f0\b\fs36 \cf0 @VBOX_PRODUCT@ Guest Additions for Mac OS X
+\b0 \
+\pard\pardeftab720\sa240
+
+\fs24 \cf0 Welcome to @VBOX_PRODUCT@ Guest Additions @VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@ for Mac OS X! This installer will guide you through the installation process.\
+} \ No newline at end of file
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-amd64.dist b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-amd64.dist
new file mode 100644
index 00000000..9e66538a
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-amd64.dist
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2008-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ SPDX-License-Identifier: GPL-3.0-only
+-->
+<installer-gui-script minSpecVersion="1.0">
+ <title>VirtualBox_title</title>
+ <options customize="allow" allow-external-scripts="false" rootVolumeOnly="true" hostArchitectures="i386"/>
+ <domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true"/>
+ <!-- This allowes a better error message compared to allowed-os-versions: -->
+ <installation-check script="checkPrerequisites()"></installation-check>
+ <script>
+ /* js:pkmk:start */
+ function checkPrerequisites()
+ {
+ sArch = system.sysctl('hw.machine');
+ system.log("Detected hw arch: " + sArch);
+ sVer = system.version['ProductVersion'];
+ system.log("Detected OS version: " + sVer);
+ if (sArch == 'x86_64')
+ {
+ if (system.compareVersions(sVer, '10.7') &gt;= 0)
+ {
+ system.log("checkPrerequisites returns true");
+ return true;
+ }
+ }
+ system.log("checkPrerequisites returns false!");
+ my.result.type = 'Fatal';
+ my.result.title = system.localizedString('UNSUPPORTED_AMD64_OS_TLE');
+ my.result.message = system.localizedString('UNSUPPORTED_AMD64_OS_MSG');
+ return false;
+ }
+ /* js:pkmk:end */
+ </script>
+ <background file="background.tif" alignment="topleft" scaling="none"/>
+ <welcome file="Welcome.rtf" mime-type="text/rtf" uti="public.rtf"/>
+ <choices-outline>
+ <line choice="choiceVBoxToolsAndServices"></line>
+ <line choice="choiceVBoxKEXTs"></line>
+ </choices-outline>
+
+ <choice id="choiceVBoxToolsAndServices" title="choiceVBoxToolsAndServices_title" description="choiceVBoxToolsAndServices_msg" start_selected="true" start_enabled="false" start_visible="true">
+ <pkg-ref id="org.virtualbox.pkg.additions.tools-and-services"></pkg-ref>
+ </choice>
+ <choice id="choiceVBoxKEXTs" title="choiceVBoxKEXTs_title" description="choiceVBoxKEXTs_msg" start_selected="true" start_enabled="false" start_visible="true">
+ <pkg-ref id="org.virtualbox.pkg.additions.kexts"></pkg-ref>
+ </choice>
+
+ <pkg-ref id="org.virtualbox.pkg.additions.tools-and-services" auth="Root">file:./Contents/Packages/VBoxGuestAdditionsToolsAndServices.pkg</pkg-ref>
+ <pkg-ref id="org.virtualbox.pkg.additions.kexts" auth="Root">file:./Contents/Packages/VBoxGuestAdditionsKEXTs.pkg</pkg-ref>
+
+</installer-gui-script>
diff --git a/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-x86.dist b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-x86.dist
new file mode 100644
index 00000000..640081b3
--- /dev/null
+++ b/src/VBox/Additions/darwin/Installer/VBoxGuestAdditions_mpkg/distribution-x86.dist
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2008-2023 Oracle and/or its affiliates.
+
+ This file is part of VirtualBox base platform packages, as
+ available from https://www.virtualbox.org.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation, in version 3 of the
+ License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses>.
+
+ SPDX-License-Identifier: GPL-3.0-only
+-->
+<installer-gui-script minSpecVersion="1.0">
+ <title>VirtualBox_title</title>
+ <options customize="allow" allow-external-scripts="false" rootVolumeOnly="true" hostArchitectures="i386"/>
+ <domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true"/>
+ <!-- This allowes a better error message compared to allowed-os-versions: -->
+ <installation-check script="checkPrerequisites()"></installation-check>
+ <script>
+ /* js:pkmk:start */
+ function checkPrerequisites()
+ {
+ sArch = system.sysctl('hw.machine');
+ system.log("Detected hw arch: " + sArch);
+ sVer = system.version['ProductVersion'];
+ system.log("Detected OS version: " + sVer);
+ if (sArch == 'i386')
+ {
+ if (system.compareVersions(sVer, '10.8') &lt; 0)
+ {
+ system.log("checkPrerequisites returns true");
+ return true;
+ }
+ }
+ system.log("checkPrerequisites returns false!");
+ my.result.type = 'Fatal';
+ my.result.title = system.localizedString('UNSUPPORTED_X86_OS_TLE');
+ my.result.message = system.localizedString('UNSUPPORTED_X86_OS_MSG');
+ return false;
+ }
+ /* js:pkmk:end */
+ </script>
+ <background file="background.tif" alignment="topleft" scaling="none"/>
+ <welcome file="Welcome.rtf" mime-type="text/rtf" uti="public.rtf"/>
+ <choices-outline>
+ <line choice="choiceVBoxToolsAndServices"></line>
+ <line choice="choiceVBoxKEXTs"></line>
+ </choices-outline>
+
+ <choice id="choiceVBoxToolsAndServices" title="choiceVBoxToolsAndServices_title" description="choiceVBoxToolsAndServices_msg" start_selected="true" start_enabled="false" start_visible="true">
+ <pkg-ref id="org.virtualbox.pkg.additions.tools-and-services"></pkg-ref>
+ </choice>
+ <choice id="choiceVBoxKEXTs" title="choiceVBoxKEXTs_title" description="choiceVBoxKEXTs_msg" start_selected="true" start_enabled="false" start_visible="true">
+ <pkg-ref id="org.virtualbox.pkg.additions.kexts"></pkg-ref>
+ </choice>
+
+ <pkg-ref id="org.virtualbox.pkg.additions.tools-and-services" auth="Root">file:./Contents/Packages/VBoxGuestAdditionsToolsAndServices.pkg</pkg-ref>
+ <pkg-ref id="org.virtualbox.pkg.additions.kexts" auth="Root">file:./Contents/Packages/VBoxGuestAdditionsKEXTs.pkg</pkg-ref>
+
+</installer-gui-script>
diff --git a/src/VBox/Additions/darwin/Makefile.kmk b/src/VBox/Additions/darwin/Makefile.kmk
new file mode 100644
index 00000000..43f19541
--- /dev/null
+++ b/src/VBox/Additions/darwin/Makefile.kmk
@@ -0,0 +1,35 @@
+# $Id: Makefile.kmk $
+## @file
+# Makefile for the Mac OS guest additions base directory.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+include $(PATH_SUB_CURRENT)/VBoxClient/Makefile.kmk
+include $(PATH_SUB_CURRENT)/Installer/Makefile.kmk
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk b/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk
new file mode 100644
index 00000000..ba520a28
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk
@@ -0,0 +1,57 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VirtualBox Guest Addition Darwin Client.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# VBoxClient - shared clipboard support.
+#
+
+PROGRAMS += VBoxClient
+
+VBoxClient_TEMPLATE = VBoxGuestR3Exe
+VBoxClient_DEFS += VBOX_WITH_HGCM
+
+VBoxClient_SOURCES = \
+ VBoxClient.cpp
+
+ifdef VBOX_WITH_SHARED_CLIPBOARD
+ VBoxClient_DEFS += \
+ VBOX_WITH_SHARED_CLIPBOARD
+ VBoxClient_SOURCES += \
+ VBoxClientClipboard.cpp \
+ VBoxClientClipboardHostToGuest.cpp \
+ VBoxClientClipboardGuestToHost.cpp \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp
+endif
+
+VBoxClient_LDFLAGS = -framework IOKit -framework ApplicationServices
+VBoxClient_INST = $(INST_ADDITIONS)
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp
new file mode 100644
index 00000000..c80b1c9b
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp
@@ -0,0 +1,298 @@
+/** $Id: VBoxClient.cpp $ */
+/** @file
+ * VBoxClient - User specific services, Darwin.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+#include <iprt/stream.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+#include "VBoxClientInternal.h"
+
+
+/*********************************************************************************************************************************
+* Glogal Variables *
+*********************************************************************************************************************************/
+
+static int g_cVerbosity = 0;
+static PRTLOGGER g_pLogger = NULL;
+
+static VBOXCLIENTSERVICE g_aServices[] =
+{
+#ifdef VBOX_WITH_SHARED_CLIPBOARD
+ g_ClipboardService
+#endif
+};
+
+
+/**
+ * Create default logger in order to print output to the specified file.
+ *
+ * @return IPRT status code.
+ */
+static int vbclInitLogger(char *pszLogFileName)
+{
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ int rc = RTLogCreateEx(&g_pLogger, "VBOXCLIENT_RELEASE_LOG", RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG, "all",
+ RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
+ 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT,
+ NULL /*pfnPhase*/,
+ pszLogFileName ? 10 : 0 /*cHistory*/,
+ pszLogFileName ? 100 * _1M : 0 /*cbHistoryFileMax*/,
+ pszLogFileName ? RT_SEC_1DAY : 0 /*cSecsHistoryTimeSlot*/,
+ NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
+ NULL /*pErrInfo*/, "%s", pszLogFileName ? pszLogFileName : "");
+ AssertRCReturn(rc, rc);
+
+ /* Register this logger as the release logger */
+ RTLogRelSetDefaultInstance(g_pLogger);
+
+ /* Explicitly flush the log in case of VBOXCLIENT_RELEASE_LOG=buffered. */
+ RTLogFlush(g_pLogger);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroy logger.
+ */
+static void vbclTermLogger(char *szLogFileName)
+{
+ // Why SIGBUS here?
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+
+ if (szLogFileName)
+ RTStrFree(szLogFileName);
+}
+
+/**
+ * Displays a verbose message.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+void VBoxClientVerbose(int iLevel, const char *pszFormat, ...)
+{
+ if (iLevel > g_cVerbosity)
+ return;
+
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ LogRel(("%s", psz));
+
+ RTStrFree(psz);
+}
+
+/**
+ * Wait for signals in order to safely terminate process.
+ */
+static void vbclWait(void)
+{
+ sigset_t signalMask;
+ int iSignal;
+
+ /* Register signals that we are waiting for */
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, SIGHUP);
+ sigaddset(&signalMask, SIGINT);
+ sigaddset(&signalMask, SIGQUIT);
+ sigaddset(&signalMask, SIGABRT);
+ sigaddset(&signalMask, SIGTERM);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ /* Ignoring return status */
+ sigwait(&signalMask, &iSignal);
+}
+
+/**
+ * Start registered services.
+ *
+ * @return IPRT status code.
+ */
+static int vbclStartServices(void)
+{
+ int rc;
+ unsigned int iServiceId = 0;
+
+ VBoxClientVerbose(1, "Starting services...\n");
+ for (iServiceId = 0; iServiceId < RT_ELEMENTS(g_aServices); iServiceId++)
+ {
+ VBoxClientVerbose(1, "Starting service: %s\n", g_aServices[iServiceId].pszName);
+ rc = (g_aServices[iServiceId].pfnStart)();
+ if (RT_FAILURE(rc))
+ {
+ VBoxClientVerbose(1, "unable to start service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rc);
+ VBoxClientVerbose(1, "Rolling back..\n");
+
+ /* Stop running services */
+ do
+ {
+ VBoxClientVerbose(1, "Stopping service: %s\n", g_aServices[iServiceId].pszName);
+ int rcStop = (g_aServices[iServiceId].pfnStop)();
+ if (RT_FAILURE(rcStop))
+ VBoxClientVerbose(1, "unable to stop service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rcStop);
+ } while (--iServiceId != 0);
+
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ VBoxClientVerbose(1, "Services start completed.\n");
+
+ return rc;
+}
+
+/**
+ * Stop registered services.
+ *
+ * @return IPRT status code.
+ */
+static void vbclStopServices(void)
+{
+ unsigned int iServiceId = 0;
+
+ VBoxClientVerbose(1, "Stopping services...\n");
+ for (iServiceId = 0; iServiceId < RT_ELEMENTS(g_aServices); iServiceId++)
+ {
+ VBoxClientVerbose(1, "Stopping service: %s\n", g_aServices[iServiceId].pszName);
+ int rc = (g_aServices[iServiceId].pfnStop)();
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(1, "unable to stop service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rc);
+ }
+ VBoxClientVerbose(1, "Services stop completed\n");
+}
+
+
+static void usage(char *sProgName)
+{
+ RTPrintf("usage: %s [-fvl]\n", sProgName);
+ RTPrintf(" -f\tRun in foreground (default: no)\n");
+ RTPrintf(" -v\tIncrease verbosity level (default: no verbosity)\n");
+ RTPrintf(" -l\tSpecify log file name (default: no log file)\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int c;
+
+ bool fDemonize = true;
+ static char *szLogFileName = NULL;
+
+ rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("RTR3InitExe() failed: (%Rrc)\n", rc);
+ return RTMsgInitFailure(rc);
+ }
+
+ /* Parse command line */
+ while((c = getopt(argc, argv, "fvl:")) != -1)
+ {
+ switch(c)
+ {
+ case 'f':
+ fDemonize = false;
+ break;
+ case 'v':
+ g_cVerbosity++;
+ break;
+ case 'l':
+ szLogFileName = RTStrDup(optarg);
+ break;
+
+ default : usage(argv[0]);
+ }
+ }
+
+ /* No more arguments allowed */
+ if ((argc - optind) != 0)
+ usage(argv[0]);
+
+ if (fDemonize)
+ {
+ rc = RTProcDaemonizeUsingFork(true /* fNoChDir */, false /* fNoClose */, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("failed to run into background\n");
+ return 1;
+ }
+ }
+
+ rc = VbglR3Init();
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclInitLogger(szLogFileName);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclStartServices();
+ if (RT_SUCCESS(rc))
+ {
+ vbclWait();
+ vbclStopServices();
+ }
+ else
+ {
+ RTPrintf("failed to start services: (%Rrc)\n", rc);
+ }
+
+ vbclTermLogger(szLogFileName);
+ }
+ else
+ {
+ RTPrintf("failed to start logger: (%Rrc)\n", rc);
+ }
+
+ VbglR3Term();
+ }
+ else
+ {
+ RTPrintf("failed to initialize guest library: (%Rrc)\n", rc);
+ }
+
+ return 0;
+}
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp
new file mode 100644
index 00000000..cab193f5
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp
@@ -0,0 +1,341 @@
+/** $Id: VBoxClientClipboard.cpp $ */
+/** @file
+ * VBoxClient - Shared Slipboard Dispatcher, Darwin.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <Carbon/Carbon.h>
+
+#include <iprt/asm.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/critsect.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Host clipboard connection client ID */
+static uint32_t g_u32ClientId;
+/* Guest clipboard reference */
+static PasteboardRef g_PasteboardRef;
+/* Dispatcher tharead handle */
+RTTHREAD g_DispatcherThread;
+/* Pasteboard polling tharead handle */
+RTTHREAD g_GuestPasteboardThread;
+/* Flag that indicates whether or not dispatcher and Pasteboard polling threada should stop */
+static bool volatile g_fShouldStop;
+/* Barrier for Pasteboard */
+static RTCRITSECT g_critsect;
+
+
+/*********************************************************************************************************************************
+* Local Macros *
+*********************************************************************************************************************************/
+
+#define VBOXCLIENT_SERVICE_NAME "clipboard"
+
+
+/*********************************************************************************************************************************
+* Local Function Prototypes *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) vbclClipboardStop(void);
+
+
+/**
+ * Clipboard dispatcher function.
+ *
+ * Forwards cliproard content between host and guest.
+ *
+ * @param ThreadSelf Unused parameter.
+ * @param pvUser Unused parameter.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardDispatcher(RTTHREAD ThreadSelf, void *pvUser)
+{
+ bool fQuit = false;
+ NOREF(ThreadSelf);
+ NOREF(pvUser);
+
+ VBoxClientVerbose(2, "starting host clipboard polling thread\n");
+
+ /*
+ * Block all signals for this thread. Only the main thread will handle signals.
+ */
+ sigset_t signalMask;
+ sigfillset(&signalMask);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ while (!fQuit && !ASMAtomicReadBool(&g_fShouldStop))
+ {
+ int rc;
+ uint32_t Msg;
+ uint32_t fFormats;
+
+ VBoxClientVerbose(2, "waiting for new host request\n");
+
+ rc = VbglR3ClipboardGetHostMsgOld(g_u32ClientId, &Msg, &fFormats);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectEnter(&g_critsect);
+ switch (Msg)
+ {
+ /* The host is terminating */
+ case VBOX_SHCL_HOST_MSG_QUIT:
+ VBoxClientVerbose(2, "host requested quit\n");
+ fQuit = true;
+ break;
+
+ /* The host needs data in the specified format */
+ case VBOX_SHCL_HOST_MSG_READ_DATA:
+ VBoxClientVerbose(2, "host requested guest's clipboard read\n");
+ rc = vbclClipboardForwardToHost(g_u32ClientId, g_PasteboardRef, fFormats);
+ AssertMsg(RT_SUCCESS(rc), ("paste to host failed\n"));
+ break;
+
+ /* The host has announced available clipboard formats */
+ case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
+ VBoxClientVerbose(2, "host requested guest's clipboard write\n");
+ rc = vbclClipboardForwardToGuest(g_u32ClientId, g_PasteboardRef, fFormats);
+ AssertMsg(RT_SUCCESS(rc), ("paste to guest failed\n"));
+ break;
+
+ default:
+ VBoxClientVerbose(2, "received unknow command from host service\n");
+ RTThreadSleep(1000);
+ }
+
+ RTCritSectLeave(&g_critsect);
+ }
+ else
+ {
+ RTThreadSleep(1000);
+ }
+ }
+
+ VBoxClientVerbose(2, "host clipboard polling thread stopped\n");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Clipboard dispatcher function.
+ *
+ * Forwards cliproard content between host and guest.
+ *
+ * @param hThreadSelf Unused parameter.
+ * @param pvUser Unused parameter.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclGuestPasteboardPoll(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf, pvUser);
+
+ /*
+ * Block all signals for this thread. Only the main thread will handle signals.
+ */
+ sigset_t signalMask;
+ sigfillset(&signalMask);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ VBoxClientVerbose(2, "starting guest clipboard polling thread\n");
+
+ while (!ASMAtomicReadBool(&g_fShouldStop))
+ {
+ PasteboardSyncFlags fSyncFlags;
+ uint32_t fFormats;
+ int rc;
+
+ RTCritSectEnter(&g_critsect);
+
+ fSyncFlags = PasteboardSynchronize(g_PasteboardRef);
+ if (fSyncFlags & kPasteboardModified)
+ {
+ fFormats = vbclClipboardGetAvailableFormats(g_PasteboardRef);
+ rc = VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
+ if (RT_FAILURE(rc))
+ {
+ VBoxClientVerbose(2, "failed to report pasteboard update (%Rrc)\n", rc);
+ }
+ else
+ {
+ VBoxClientVerbose(2, "guest clipboard update reported: %d\n", (int)fFormats);
+ }
+ }
+
+ RTCritSectLeave(&g_critsect);
+
+ /* Check pasteboard every 200 ms */
+ RTThreadSleep(200);
+ }
+
+ VBoxClientVerbose(2, "guest clipboard polling thread stopped\n");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize host and guest clipboards, start clipboard dispatcher loop.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardStart(void)
+{
+ int rc;
+
+ VBoxClientVerbose(2, "starting clipboard\n");
+
+ rc = RTCritSectInit(&g_critsect);
+ if (RT_FAILURE(rc))
+ return VERR_GENERAL_FAILURE;
+
+ rc = VbglR3ClipboardConnect(&g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ {
+ rc = PasteboardCreate(kPasteboardClipboard, &g_PasteboardRef);
+ if (rc == noErr)
+ {
+ /* Start dispatcher loop */
+ ASMAtomicWriteBool(&g_fShouldStop, false);
+ rc = RTThreadCreate(&g_DispatcherThread,
+ vbclClipboardDispatcher,
+ (void *)NULL,
+ 0,
+ RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE,
+ VBOXCLIENT_SERVICE_NAME);
+ if (RT_SUCCESS(rc))
+ {
+ /* Start dispatcher loop */
+ ASMAtomicWriteBool(&g_fShouldStop, false);
+ rc = RTThreadCreate(&g_GuestPasteboardThread,
+ vbclGuestPasteboardPoll,
+ (void *)NULL,
+ 0,
+ RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE,
+ VBOXCLIENT_SERVICE_NAME);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ /* Stop dispatcher thread */
+ ASMAtomicWriteBool(&g_fShouldStop, true);
+ RTThreadWait(g_DispatcherThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+
+ }
+ VBoxClientVerbose(2, "unable create dispatcher thread\n");
+ CFRelease(g_PasteboardRef);
+ g_PasteboardRef = NULL;
+
+ }
+ else
+ {
+ rc = VERR_GENERAL_FAILURE;
+ VBoxClientVerbose(2, "unable access guest clipboard\n");
+ }
+
+ vbclClipboardStop();
+
+ }
+ else
+ {
+ VBoxClientVerbose(2, "unable to establish connection to clipboard service: %Rrc\n", rc);
+ }
+
+ RTCritSectDelete(&g_critsect);
+
+ return rc;
+}
+
+
+/**
+ * Release host and guest clipboards, stop clipboard dispatcher loop.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardStop(void)
+{
+ int rc;
+
+ VBoxClientVerbose(2, "stopping clipboard\n");
+
+ AssertReturn(g_u32ClientId != 0, VERR_GENERAL_FAILURE);
+
+ VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+
+ rc = VbglR3ClipboardDisconnect(g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ g_u32ClientId = 0;
+ else
+ VBoxClientVerbose(2, "unable to close clipboard service connection: %Rrc\n", rc);
+
+ if (g_PasteboardRef)
+ {
+ CFRelease(g_PasteboardRef);
+ g_PasteboardRef = NULL;
+ }
+
+ /* Stop dispatcher thread */
+ ASMAtomicWriteBool(&g_fShouldStop, true);
+ rc = RTThreadWait(g_DispatcherThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(2, "failed to stop dispatcher thread");
+
+ /* Stop Pasteboard polling thread */
+ rc = RTThreadWait(g_GuestPasteboardThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(2, "failed to stop pasteboard polling thread");
+
+ RTCritSectDelete(&g_critsect);
+
+ return rc;
+}
+
+
+/* Clipboard service struct */
+VBOXCLIENTSERVICE g_ClipboardService =
+{
+ /* pszName */
+ VBOXCLIENT_SERVICE_NAME,
+
+ /* pfnStart */
+ vbclClipboardStart,
+
+ /* pfnStop */
+ vbclClipboardStop,
+};
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp
new file mode 100644
index 00000000..2cef7257
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp
@@ -0,0 +1,390 @@
+/** $Id: VBoxClientClipboardGuestToHost.cpp $ */
+/** @file
+ * VBoxClient - Shared Clipboard Guest -> Host copying, Darwin.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <Carbon/Carbon.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <iprt/thread.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/utf16.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+/**
+ * Walk through pasteboard items and report currently available item types.
+ *
+ * @param pPasteboard Reference to guest Pasteboard.
+ * @returns Available formats bit field.
+ */
+uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard)
+{
+ uint32_t fFormats = 0;
+ ItemCount cItems = 0;
+ ItemCount iItem;
+ OSStatus rc;
+
+#define VBOXCL_ADD_FORMAT_IF_PRESENT(a_kDarwinFmt, a_fVBoxFmt) \
+ if (PasteboardCopyItemFlavorData(pPasteboard, iItemID, a_kDarwinFmt, &flavorData) == noErr) \
+ { \
+ fFormats |= (uint32_t)a_fVBoxFmt; \
+ CFRelease(flavorData); \
+ }
+
+ rc = PasteboardGetItemCount(pPasteboard, &cItems);
+ AssertReturn((rc == noErr) && (cItems > 0), fFormats);
+
+ for (iItem = 1; iItem <= cItems; iItem++)
+ {
+ PasteboardItemID iItemID;
+ CFDataRef flavorData;
+
+ rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
+ if (rc == noErr)
+ {
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF16PlainText, VBOX_SHCL_FMT_UNICODETEXT);
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF8PlainText, VBOX_SHCL_FMT_UNICODETEXT);
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeBMP, VBOX_SHCL_FMT_BITMAP );
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeHTML, VBOX_SHCL_FMT_HTML );
+
+#ifdef CLIPBOARD_DUMP_CONTENT_FORMATS
+ CFArrayRef flavorTypeArray;
+ CFIndex flavorCount;
+ CFStringRef flavorType;
+
+ rc = PasteboardCopyItemFlavors(pPasteboard, iItemID, &flavorTypeArray);
+ if (rc == noErr)
+ {
+ VBoxClientVerbose(3, "SCAN..\n");
+ flavorCount = CFArrayGetCount(flavorTypeArray);
+ VBoxClientVerbose(3, "SCAN (%d)..\n", (int)flavorCount);
+ for(CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
+ {
+ VBoxClientVerbose(3, "SCAN #%d..\n", (int)flavorIndex);
+ flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+
+ CFDataRef flavorData1;
+ rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, flavorType, &flavorData1);
+ if (rc == noErr)
+ {
+ VBoxClientVerbose(3, "Found: %s, size: %d\n", (char *)CFStringGetCStringPtr(flavorType, kCFStringEncodingMacRoman), (int)CFDataGetLength(flavorData1));
+ CFRelease(flavorData1);
+ }
+ }
+ VBoxClientVerbose(3, "SCAN COMPLETE\n");
+ CFRelease(flavorTypeArray);
+ }
+#endif /* CLIPBOARD_DUMP_CONTENT_FORMATS */
+ }
+ }
+
+#undef VBOXCL_ADD_FORMAT_IF_PRESENT
+
+ return fFormats;
+}
+
+
+/**
+ * Search for content of specified type in guest clipboard buffer and put
+ * it into newly allocated buffer.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormat Data formats we are looking for.
+ * @param ppvData Where to return pointer to the received data. M
+ * @param pcbData Where to return the size of the data.
+ * @param pcbAlloc Where to return the size of the memory block
+ * *ppvData pointes to. (Usually greater than *cbData
+ * because the allocation is page aligned.)
+ * @returns IPRT status code.
+ */
+static int vbclClipboardReadGuestData(PasteboardRef pPasteboard, CFStringRef sFormat, void **ppvData, uint32_t *pcbData,
+ uint32_t *pcbAlloc)
+{
+ ItemCount cItems, iItem;
+ OSStatus rc;
+
+ void *pvData = NULL;
+ uint32_t cbData = 0;
+ uint32_t cbAlloc = 0;
+
+ AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbAlloc, VERR_INVALID_POINTER);
+
+ rc = PasteboardGetItemCount(pPasteboard, &cItems);
+ AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
+ AssertReturn(cItems > 0, VERR_INVALID_PARAMETER);
+
+ /* Walk through all the items in PasteBoard in order to find
+ that one that correcponds to requested data format. */
+ for (iItem = 1; iItem <= cItems; iItem++)
+ {
+ PasteboardItemID iItemID;
+ CFDataRef flavorData;
+
+ /* Now, get the item's flavors that corresponds to requested type. */
+ rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
+ AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
+ rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, sFormat, &flavorData);
+ if (rc == noErr)
+ {
+ void *flavorDataPtr = (void *)CFDataGetBytePtr(flavorData);
+ cbData = CFDataGetLength(flavorData);
+ if (flavorDataPtr && cbData > 0)
+ {
+ cbAlloc = RT_ALIGN_32(cbData, PAGE_SIZE);
+ pvData = RTMemPageAllocZ(cbAlloc);
+ if (pvData)
+ memcpy(pvData, flavorDataPtr, cbData);
+ }
+
+ CFRelease(flavorData);
+
+ /* Found first matching item, no more search. */
+ break;
+ }
+
+ }
+
+ /* Found match */
+ if (pvData)
+ {
+ *ppvData = pvData;
+ *pcbData = cbData;
+ *pcbAlloc = cbAlloc;
+
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Release resources occupied by vbclClipboardReadGuestData().
+ */
+static void vbclClipboardReleaseGuestData(void **ppvData, uint32_t cbAlloc)
+{
+ AssertReturnVoid(ppvData);
+ RTMemPageFree(*ppvData, cbAlloc);
+ *ppvData = NULL;
+}
+
+/**
+ * Pass data to host.
+ */
+static int vbclClipboardHostPasteData(uint32_t u32ClientId, uint32_t u32Format, const void *pvData, uint32_t cbData)
+{
+ /* Allow empty buffers */
+ if (cbData == 0)
+ return VbglR3ClipboardWriteData(u32ClientId, u32Format, NULL, 0);
+
+ AssertReturn(pvData, VERR_INVALID_PARAMETER);
+ return VbglR3ClipboardWriteData(u32ClientId, u32Format, (void *)pvData, cbData); /** @todo r=bird: Why on earth does a write function like VbglR3ClipboardWriteData take a non-const parameter? */
+}
+
+/**
+ * Paste text data into host clipboard.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pwszData UTF-16 encoded string.
+ * @param cbData The length of the string, in bytes, probably
+ * including a terminating zero.
+ */
+static int vbclClipboardHostPasteText(uint32_t u32ClientId, PRTUTF16 pwszData, uint32_t cbData)
+{
+ AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pwszData, VERR_INVALID_POINTER);
+
+ size_t cwcTmp; /* (includes a schwarzenegger character) */
+ int rc = ShClUtf16LFLenUtf8(pwszData, cbData / sizeof(RTUTF16), &cwcTmp);
+ AssertRCReturn(rc, rc);
+
+ cwcTmp++; /* Add space for terminator. */
+
+ PRTUTF16 pwszTmp = (PRTUTF16)RTMemAlloc(cwcTmp * sizeof(RTUTF16));
+ AssertReturn(pwszTmp, VERR_NO_MEMORY);
+
+ rc = ShClConvUtf16LFToCRLF(pwszData, cbData / sizeof(RTUTF16), pwszTmp, cwcTmp);
+ if (RT_SUCCESS(rc))
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT,
+ pwszTmp, cwcTmp * sizeof(RTUTF16));
+
+ RTMemFree(pwszTmp);
+
+ return rc;
+}
+
+
+/**
+ * Paste a bitmap onto the host clipboard.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pvData The bitmap data.
+ * @param cbData The size of the bitmap.
+ */
+static int vbclClipboardHostPasteBitmap(uint32_t u32ClientId, void *pvData, uint32_t cbData)
+{
+ const void *pvDib;
+ size_t cbDib;
+ int rc = ShClBmpGetDib(pvData, cbData, &pvDib, &cbDib);
+ AssertRCReturn(rc, rc);
+
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, pvDib, cbDib);
+
+ return rc;
+}
+
+
+/**
+ * Read guest's clipboard buffer and forward its content to host.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
+{
+ int rc = VINF_SUCCESS;
+
+ void *pvData = NULL;
+ uint32_t cbData = 0;
+ uint32_t cbAlloc = 0;
+
+ VBoxClientVerbose(3, "vbclClipboardForwardToHost: %d\n", fFormats);
+
+ /* Walk across all item(s) formats */
+ uint32_t fFormatsLeft = fFormats;
+ while (fFormatsLeft)
+ {
+ if (fFormatsLeft & VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_UNICODETEXT: %d\n", fFormats);
+
+ RTUTF16 *pUtf16Str = NULL;
+
+ /* First, try to get UTF16 encoded buffer */
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF16PlainText, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTUtf16DupEx(&pUtf16Str, (PRTUTF16)pvData, 0);
+ if (RT_FAILURE(rc))
+ pUtf16Str = NULL;
+ }
+ else /* Failed to get UTF16 buffer */
+ {
+ /* Then, try to get UTF8 encoded buffer */
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF8PlainText, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrToUtf16((const char *)pvData, &pUtf16Str);
+ if (RT_FAILURE(rc))
+ pUtf16Str = NULL;
+ }
+ }
+
+ /* Finally, we got UTF16 encoded buffer */
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteText(u32ClientId, (PRTUTF16)pvData, cbData);
+
+ if (pUtf16Str)
+ {
+ RTUtf16Free(pUtf16Str);
+ pUtf16Str = NULL;
+ }
+
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_UNICODETEXT;
+ }
+
+ else if (fFormatsLeft & VBOX_SHCL_FMT_BITMAP)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_BITMAP: %d\n", fFormats);
+
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeBMP, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteBitmap(u32ClientId, pvData, cbData);
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_BITMAP;
+ }
+
+ else if (fFormatsLeft & VBOX_SHCL_FMT_HTML)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_HTML: %d\n", fFormats);
+
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeHTML, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, pvData, cbData);
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_HTML;
+ }
+
+ else
+ {
+ VBoxClientVerbose(3, "requested data in unsupported format: %#x\n", fFormatsLeft);
+ break;
+ }
+ }
+
+ return rc; /** @todo r=bird: If there are multiple formats available, which rc is returned here? Does it matter? */
+}
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp
new file mode 100644
index 00000000..1b2d729f
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp
@@ -0,0 +1,315 @@
+/** $Id: VBoxClientClipboardHostToGuest.cpp $ */
+/** @file
+ * VBoxClient - Shared Clipboard Host -> Guest copying, Darwin.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <Carbon/Carbon.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+/**
+ * Allocate memory for host buffer and receive it.
+ *
+ * @param u32ClientId Host connection.
+ * @param fFormat Buffer data format.
+ * @param pData Where to store received data.
+ * @param cbDataSize The size of the received data.
+ * @param cbMemSize The actual size of memory occupied by *pData.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardReadHostData(uint32_t u32ClientId, uint32_t fFormat, void **pData, uint32_t *cbDataSize, uint32_t *cbMemSize)
+{
+ int rc;
+
+ AssertReturn(pData && cbDataSize && cbMemSize, VERR_INVALID_PARAMETER);
+
+ uint32_t cbDataSizeInternal = _4K;
+ uint32_t cbMemSizeInternal = cbDataSizeInternal;
+ void *pDataInternal = RTMemPageAllocZ(cbDataSizeInternal);
+
+ if (!pDataInternal)
+ return VERR_NO_MEMORY;
+
+ rc = VbglR3ClipboardReadData(u32ClientId, fFormat, pDataInternal, cbMemSizeInternal, &cbDataSizeInternal);
+ if (rc == VINF_BUFFER_OVERFLOW)
+ {
+ /* Reallocate bigger buffer and receive all the data */
+ RTMemPageFree(pDataInternal, cbMemSizeInternal);
+ cbDataSizeInternal = cbMemSizeInternal = RT_ALIGN_32(cbDataSizeInternal, PAGE_SIZE);
+ pDataInternal = RTMemPageAllocZ(cbMemSizeInternal);
+ if (!pDataInternal)
+ return VERR_NO_MEMORY;
+
+ rc = VbglR3ClipboardReadData(u32ClientId, fFormat, pDataInternal, cbMemSizeInternal, &cbDataSizeInternal);
+ }
+
+ /* Error occurred of zero-sized buffer */
+ if (RT_FAILURE(rc))
+ {
+ RTMemPageFree(pDataInternal, cbMemSizeInternal);
+ return VERR_NO_MEMORY;
+ }
+
+ *pData = pDataInternal;
+ *cbDataSize = cbDataSizeInternal;
+ *cbMemSize = cbMemSizeInternal;
+
+ return rc;
+}
+
+/**
+ * Release memory occupied by host buffer.
+ *
+ * @param pData Pointer to memory occupied by host buffer.
+ * @param cbMemSize The actual size of memory occupied by *pData.
+ */
+static void vbclClipboardReleaseHostData(void **pData, uint32_t cbMemSize)
+{
+ AssertReturnVoid(pData && cbMemSize > 0);
+ RTMemPageFree(*pData, cbMemSize);
+}
+
+/**
+ * Paste buffer into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize The size of *pData.
+ * @param fFormat Buffer data format.
+ * @param fClear Whether or not clear guest clipboard before insert data.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardGuestPasteData(PasteboardRef pPasteboard, UInt8 *pData, CFIndex cbDataSize, CFStringRef sFormat, bool fClear)
+{
+ PasteboardItemID itemId = (PasteboardItemID)1;
+ CFDataRef textData = NULL;
+ OSStatus rc;
+
+ /* Ignoring sunchronization flags here */
+ PasteboardSynchronize(pPasteboard);
+
+ if (fClear)
+ {
+ rc = PasteboardClear(pPasteboard);
+ AssertReturn(rc == noErr, VERR_NOT_SUPPORTED);
+ }
+
+ /* Create a CData object which we could pass to the pasteboard */
+ if ((textData = CFDataCreate(kCFAllocatorDefault, pData, cbDataSize)))
+ {
+ /* Put the Utf-8 version to the pasteboard */
+ rc = PasteboardPutItemFlavor(pPasteboard, itemId, sFormat, textData, 0);
+ CFRelease(textData);
+ if (rc != noErr)
+ {
+ VBoxClientVerbose(3, "unable to put data into guest's clipboard: %d\n", rc);
+ return VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ return VERR_NO_MEMORY;
+
+ /* Synchronize updated content */
+ PasteboardSynchronize(pPasteboard);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Paste text data into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize Size of *pData.
+ */
+static int vbclClipboardGuestPasteText(PasteboardRef pPasteboard, void *pData, uint32_t cbDataSize)
+{
+ AssertReturn(pData, VERR_INVALID_PARAMETER);
+
+ /* Skip zero-sized buffer */
+ AssertReturn(cbDataSize > 0, VINF_SUCCESS);
+
+ /* If buffer content is Unicode text, then deliver
+ it in both formats UTF16 (original) and UTF8. */
+
+ /* Convert END-OF-LINE */
+ size_t cwcDst;
+ int rc = ShClUtf16CRLFLenUtf8((RTUTF16 *)pData, cbDataSize / sizeof(RTUTF16), &cwcDst);
+ AssertRCReturn(rc, rc);
+
+ cwcDst++; /* Add space for terminator. */
+
+ PRTUTF16 pwszDst = (RTUTF16 *)RTMemAlloc(cwcDst * sizeof(RTUTF16));
+ AssertPtrReturn(pwszDst, VERR_NO_MEMORY);
+
+ rc = ShClConvUtf16CRLFToLF((RTUTF16 *)pData, cbDataSize / sizeof(RTUTF16), pwszDst, cwcDst);
+ if (RT_SUCCESS(rc))
+ {
+ /* Paste UTF16 */
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pwszDst, cwcDst * sizeof(RTUTF16), kUTTypeUTF16PlainText, true);
+ if (RT_SUCCESS(rc))
+ {
+ /* Paste UTF8 */
+ char *pszDst;
+ rc = RTUtf16ToUtf8((PRTUTF16)pwszDst, &pszDst);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pszDst, strlen(pszDst), kUTTypeUTF8PlainText, false);
+ RTStrFree(pszDst);
+ }
+ }
+
+ }
+
+ RTMemFree(pwszDst);
+
+ return rc;
+}
+
+/**
+ * Paste picture data into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize The size of *pData.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardGuestPastePicture(PasteboardRef pPasteboard, void *pData, uint32_t cbDataSize)
+{
+ int rc;
+ void *pBmp;
+ size_t cbBmpSize;
+
+ AssertReturn(pData, VERR_INVALID_PARAMETER);
+ /* Skip zero-sized buffer */
+ AssertReturn(cbDataSize > 0, VINF_SUCCESS);
+
+ rc = ShClDibToBmp(pData, cbDataSize, &pBmp, &cbBmpSize);
+ AssertReturn(RT_SUCCESS(rc), rc);
+
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pBmp, cbBmpSize, kUTTypeBMP, true);
+ RTMemFree(pBmp);
+
+ return rc;
+}
+
+/**
+ * Read host's clipboard buffer and put its content to guest clipboard.
+ *
+ * @param u32ClientId Host connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+int vbclClipboardForwardToGuest(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ void *pData;
+ uint32_t cbDataSize, cbMemSize;
+ uint32_t fFormatsInternal = fFormats;
+
+ /* Walk across all item(s) formats */
+ while (fFormatsInternal)
+ {
+ if (fFormatsInternal & VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_UNICODETEXT: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPasteText(pPasteboard, pData, cbDataSize);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_UNICODETEXT);
+ }
+
+ else if (fFormatsInternal & VBOX_SHCL_FMT_BITMAP)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_BITMAP: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_BITMAP, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPastePicture(pPasteboard, pData, cbDataSize);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_BITMAP);
+ }
+
+ else if (fFormatsInternal & VBOX_SHCL_FMT_HTML)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_HTML: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_HTML, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pData, cbDataSize, kUTTypeHTML, true);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_HTML);
+ }
+
+ else
+ {
+ VBoxClientVerbose(3, "received data in unsupported format: %d\n", fFormats);
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h b/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h
new file mode 100644
index 00000000..73a93b2a
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h
@@ -0,0 +1,112 @@
+/** $Id: VBoxClientInternal.h $ */
+/** @file
+ * VBoxClient - common definitions, Darwin.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h
+#define GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VBoxGuestLib.h>
+#include <Carbon/Carbon.h>
+
+/* Service description */
+typedef struct
+{
+ /** The service name. */
+ const char *pszName;
+
+ /**
+ * Start service.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnStart,(void));
+
+ /**
+ * Stop service.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnStop,(void));
+
+} VBOXCLIENTSERVICE;
+
+
+/*
+ * Services
+ */
+
+RT_C_DECLS_BEGIN
+
+extern VBOXCLIENTSERVICE g_ClipboardService;
+
+RT_C_DECLS_END
+
+
+/*
+ * Functions
+ */
+
+/**
+ * Displays a verbose message.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+extern void VBoxClientVerbose(int iLevel, const char *pszFormat, ...);
+
+/**
+ * Walk through pasteboard items and report currently available item types.
+ *
+ * @param pPasteboard Reference to guest Pasteboard.
+ # @returns Available formats bit field.
+ */
+extern uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard);
+
+/**
+ * Read host's clipboard buffer and put its content to guest clipboard.
+ *
+ * @param u32ClientId Host connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+extern int vbclClipboardForwardToGuest(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats);
+
+/**
+ * Read guest's clipboard buffer and forward its content to host.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+extern int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats);
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h */
diff --git a/src/VBox/Additions/darwin/VBoxSF/.scm-settings b/src/VBox/Additions/darwin/VBoxSF/.scm-settings
new file mode 100644
index 00000000..51fd9c2c
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/.scm-settings
@@ -0,0 +1,29 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for the OS X shared folders driver.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+--filter-out-files /vboxfs.git.tar.bz2
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/Info.plist b/src/VBox/Additions/darwin/VBoxSF/Info.plist
new file mode 100644
index 00000000..b4be6f70
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/Info.plist
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleExecutable</key> <string>VBoxSF</string>
+ <key>CFBundleIconFile</key> <string></string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxSF</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>CFBundleName</key> <string>VBoxSF</string>
+ <key>CFBundlePackageType</key> <string>KEXT</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>OSBundleCompatibleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>IOKitPersonalities</key>
+
+ <dict>
+ <key>VBoxSF</key>
+ <dict>
+ <key>IOMatchCategory</key> <string>org_virtualbox_VBoxSF</string>
+ <key>IOClientClass</key> <string>VBoxSFClient</string>
+ <key>IOClass</key> <string>org_virtualbox_VBoxSF</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxSF</string>
+ <key>IOKitDebug</key> <integer>65535</integer>
+ <key>IOProviderClass</key> <string>IOPCIDevice</string>
+ <key>IONameMatch</key> <string>pci80ee,cafe</string>
+ </dict>
+ </dict>
+
+ <key>NSHumanReadableCopyright</key> <string>Copyright © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+
+ <key>OSBundleLibraries</key>
+ <dict>
+ <key>com.apple.iokit.IOPCIFamily</key> <string>2.5</string>
+ <key>com.apple.kpi.bsd</key> <string>10.6</string>
+ <key>com.apple.kpi.iokit</key> <string>10.6</string>
+ <key>com.apple.kpi.libkern</key> <string>10.6</string>
+ <key>com.apple.kpi.mach</key> <string>10.6</string>
+ <key>com.apple.kpi.unsupported</key> <string>10.6</string>
+ <key>org.virtualbox.kext.VBoxGuest</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+</dict>
+</plist>
diff --git a/src/VBox/Additions/darwin/VBoxSF/Makefile.kmk b/src/VBox/Additions/darwin/VBoxSF/Makefile.kmk
new file mode 100644
index 00000000..63ce78e6
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/Makefile.kmk
@@ -0,0 +1,80 @@
+# $Id: Makefile.kmk $
+## @file
+# sub-makefile for Darwin Shared Folders.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# VBoxSF - The shared folders kernel extension.
+#
+SYSMODS += VBoxSF
+VBoxSF_TEMPLATE = VBoxGuestR0Drv
+VBoxSF_INST = $(INST_ADDITIONS)VBoxSF.kext/Contents/MacOS/
+VBoxSF_DEFS = VBOX_WITH_HGCM
+VBoxSF_LIBS = $(VBOX_LIB_VBGL_R0)
+VBoxSF_SOURCES = \
+ VBoxSF.cpp \
+ VBoxSF-VfsOps.cpp \
+ VBoxSF-VNodeOps.cpp \
+ VBoxSF-Utils.cpp
+
+
+#
+# Files necessary to make a darwin kernel extension bundle.
+#
+INSTALLS += VBoxSF.kext
+VBoxSF.kext_INST = $(INST_ADDITIONS)VBoxSF.kext/Contents/
+VBoxSF.kext_SOURCES = $(VBoxSF.kext_0_OUTDIR)/Info.plist
+VBoxSF.kext_CLEAN = $(VBoxSF.kext_0_OUTDIR)/Info.plist
+
+$$(VBoxSF.kext_0_OUTDIR)/Info.plist: \
+ $(PATH_SUB_CURRENT)/Info.plist \
+ $(VBOX_VERSION_MK) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxSF,$@,$<)
+ $(QUIET)$(RM) -f $@
+ $(QUIET)$(SED) \
+ -e 's+@VBOX_VERSION_STRING@+$(VBOX_VERSION_STRING)+g' \
+ -e 's+@VBOX_VERSION_MAJOR@+$(VBOX_VERSION_MAJOR)+g' \
+ -e 's+@VBOX_VERSION_MINOR@+$(VBOX_VERSION_MINOR)+g' \
+ -e 's+@VBOX_VERSION_BUILD@+$(VBOX_VERSION_BUILD)+g' \
+ -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \
+ -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \
+ -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \
+ --output $@ \
+ $<
+
+#
+# mount.vboxsf - The Shared Folders mounting tool.
+#
+PROGRAMS += mount.vboxsf
+mount.vboxsf_TEMPLATE = VBoxGuestR3Exe
+mount.vboxsf_SOURCES = mount.vboxsf.cpp
+mount.vboxsf_INST = $(INST_ADDITIONS)VBoxSF.kext/Contents/MacOS/
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp
new file mode 100644
index 00000000..feac592a
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp
@@ -0,0 +1,608 @@
+/* $Id: VBoxSF-Utils.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <VBox/log.h>
+
+#if 0
+/**
+ * Helper function to create XNU VFS vnode object.
+ *
+ * @param mp Mount data structure
+ * @param type vnode type (directory, regular file, etc)
+ * @param pParent Parent vnode object (NULL for VBoxVFS root vnode)
+ * @param fIsRoot Flag that indicates if created vnode object is
+ * VBoxVFS root vnode (TRUE for VBoxVFS root vnode, FALSE
+ * for all aother vnodes)
+ * @param Path within Shared Folder
+ * @param ret Returned newly created vnode
+ *
+ * @return 0 on success, error code otherwise
+ */
+int
+vboxvfs_create_vnode_internal(struct mount *mp, enum vtype type, vnode_t pParent, int fIsRoot, PSHFLSTRING Path, vnode_t *ret)
+{
+ int rc;
+ vnode_t vnode;
+
+ vboxvfs_vnode_t *pVnodeData;
+ vboxvfs_mount_t *pMount;
+
+ AssertReturn(mp, EINVAL);
+
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp);
+ AssertReturn(pMount, EINVAL);
+ AssertReturn(pMount->pLockGroup, EINVAL);
+
+ AssertReturn(Path, EINVAL);
+
+ pVnodeData = (vboxvfs_vnode_t *)RTMemAllocZ(sizeof(vboxvfs_vnode_t));
+ AssertReturn(pVnodeData, ENOMEM);
+
+ /* Initialize private data */
+ pVnodeData->pHandle = SHFL_HANDLE_NIL;
+ pVnodeData->pPath = Path;
+
+ pVnodeData->pLockAttr = lck_attr_alloc_init();
+ if (pVnodeData->pLockAttr)
+ {
+ pVnodeData->pLock = lck_rw_alloc_init(pMount->pLockGroup, pVnodeData->pLockAttr);
+ if (pVnodeData->pLock)
+ {
+ struct vnode_fsparam vnode_params;
+
+ vnode_params.vnfs_mp = mp;
+ vnode_params.vnfs_vtype = type;
+ vnode_params.vnfs_str = NULL;
+ vnode_params.vnfs_dvp = pParent;
+ vnode_params.vnfs_fsnode = pVnodeData; /** Private data attached per xnu's vnode object */
+ vnode_params.vnfs_vops = g_papfnVBoxVFSVnodeDirOpsVector;
+
+ vnode_params.vnfs_markroot = fIsRoot;
+ vnode_params.vnfs_marksystem = FALSE;
+ vnode_params.vnfs_rdev = 0;
+ vnode_params.vnfs_filesize = 0;
+ vnode_params.vnfs_cnp = NULL;
+
+ vnode_params.vnfs_flags = VNFS_ADDFSREF | VNFS_NOCACHE;
+
+ rc = vnode_create(VNCREATE_FLAVOR, sizeof(vnode_params), &vnode_params, &vnode);
+ if (rc == 0)
+ *ret = vnode;
+
+ return 0;
+ }
+ else
+ {
+ PDEBUG("Unable to allocate lock");
+ rc = ENOMEM;
+ }
+
+ lck_attr_free(pVnodeData->pLockAttr);
+ }
+ else
+ {
+ PDEBUG("Unable to allocate lock attr");
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/**
+ * Convert guest absolute VFS path (starting from VFS root) to a host path
+ * within mounted shared folder (returning it as a char *).
+ *
+ * @param mp Mount data structure
+ * @param pszGuestPath Guest absolute VFS path (starting from VFS root)
+ * @param cbGuestPath Size of pszGuestPath
+ * @param pszHostPath Returned char * wich contains host path
+ * @param cbHostPath Returned pszHostPath size
+ *
+ * @return 0 on success, error code otherwise
+ */
+int
+vboxvfs_guest_path_to_char_path_internal(mount_t mp, char *pszGuestPath, int cbGuestPath, char **pszHostPath, int *cbHostPath)
+{
+ vboxvfs_mount_t *pMount;
+
+ /* Guest side: mount point path buffer and its size */
+ char *pszMntPointPath;
+ int cbMntPointPath = MAXPATHLEN;
+
+ /* Host side: path within mounted shared folder and its size */
+ char *pszHostPathInternal;
+ size_t cbHostPathInternal;
+
+ int rc;
+
+ AssertReturn(mp, EINVAL);
+ AssertReturn(pszGuestPath, EINVAL); AssertReturn(cbGuestPath >= 0, EINVAL);
+ AssertReturn(pszHostPath, EINVAL); AssertReturn(cbHostPath, EINVAL);
+
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL); AssertReturn(pMount->pRootVnode, EINVAL);
+
+ /* Get mount point path */
+ pszMntPointPath = (char *)RTMemAllocZ(cbMntPointPath);
+ if (pszMntPointPath)
+ {
+ rc = vn_getpath(pMount->pRootVnode, pszMntPointPath, &cbMntPointPath);
+ if (rc == 0 && cbGuestPath >= cbMntPointPath)
+ {
+ cbHostPathInternal = cbGuestPath - cbMntPointPath + 1;
+ pszHostPathInternal = (char *)RTMemAllocZ(cbHostPathInternal);
+ if (pszHostPathInternal)
+ {
+ memcpy(pszHostPathInternal, pszGuestPath + cbMntPointPath, cbGuestPath - cbMntPointPath);
+ PDEBUG("guest<->host path converion result: '%s' mounted to '%s'", pszHostPathInternal, pszMntPointPath);
+
+ RTMemFree(pszMntPointPath);
+
+ *pszHostPath = pszHostPathInternal;
+ *cbHostPath = cbGuestPath - cbMntPointPath;
+
+ return 0;
+
+ }
+ else
+ {
+ PDEBUG("No memory to allocate buffer for guest<->host path conversion (cbHostPathInternal)");
+ rc = ENOMEM;
+ }
+
+ }
+ else
+ {
+ PDEBUG("Unable to get guest vnode path: %d", rc);
+ }
+
+ RTMemFree(pszMntPointPath);
+ }
+ else
+ {
+ PDEBUG("No memory to allocate buffer for guest<->host path conversion (pszMntPointPath)");
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/**
+ * Convert guest absolute VFS path (starting from VFS root) to a host path
+ * within mounted shared folder.
+ *
+ * @param mp Mount data structure
+ * @param pszGuestPath Guest absolute VFS path (starting from VFS root)
+ * @param cbGuestPath Size of pszGuestPath
+ * @param ppResult Returned PSHFLSTRING object wich contains host path
+ *
+ * @return 0 on success, error code otherwise
+ */
+int
+vboxvfs_guest_path_to_shflstring_path_internal(mount_t mp, char *pszGuestPath, int cbGuestPath, PSHFLSTRING *ppResult)
+{
+ vboxvfs_mount_t *pMount;
+
+ /* Guest side: mount point path buffer and its size */
+ char *pszMntPointPath;
+ int cbMntPointPath = MAXPATHLEN;
+
+ /* Host side: path within mounted shared folder and its size */
+ PSHFLSTRING pSFPath;
+ size_t cbSFPath;
+
+ int rc;
+
+ AssertReturn(mp, EINVAL);
+ AssertReturn(pszGuestPath, EINVAL);
+ AssertReturn(cbGuestPath >= 0, EINVAL);
+
+ char *pszHostPath;
+ int cbHostPath;
+
+ rc = vboxvfs_guest_path_to_char_path_internal(mp, pszGuestPath, cbGuestPath, &pszHostPath, &cbHostPath);
+ if (rc == 0)
+ {
+ cbSFPath = offsetof(SHFLSTRING, String.utf8) + (size_t)cbHostPath + 1;
+ pSFPath = (PSHFLSTRING)RTMemAllocZ(cbSFPath);
+ if (pSFPath)
+ {
+ pSFPath->u16Length = cbHostPath;
+ pSFPath->u16Size = cbHostPath + 1;
+ memcpy(pSFPath->String.utf8, pszHostPath, cbHostPath);
+ vboxvfs_put_path_internal((void **)&pszHostPath);
+
+ *ppResult = pSFPath;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Wrapper function for vboxvfs_guest_path_to_char_path_internal() which
+ * converts guest path to host path using vnode object information.
+ *
+ * @param vnode Guest's VFS object
+ * @param ppHostPath Allocated char * which contain a path
+ * @param pcbPath Size of ppPath
+ *
+ * @return 0 on success, error code otherwise.
+ */
+int
+vboxvfs_guest_vnode_to_char_path_internal(vnode_t vnode, char **ppHostPath, int *pcbHostPath)
+{
+ mount_t mp;
+ int rc;
+
+ char *pszPath;
+ int cbPath = MAXPATHLEN;
+
+ AssertReturn(ppHostPath, EINVAL);
+ AssertReturn(pcbHostPath, EINVAL);
+ AssertReturn(vnode, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+
+ pszPath = (char *)RTMemAllocZ(cbPath);
+ if (pszPath)
+ {
+ rc = vn_getpath(vnode, pszPath, &cbPath);
+ if (rc == 0)
+ {
+ return vboxvfs_guest_path_to_char_path_internal(mp, pszPath, cbPath, ppHostPath, pcbHostPath);
+ }
+ }
+ else
+ {
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/**
+ * Wrapper function for vboxvfs_guest_path_to_shflstring_path_internal() which
+ * converts guest path to host path using vnode object information.
+ *
+ * @param vnode Guest's VFS object
+ * @param ppResult Allocated PSHFLSTRING object which contain a path
+ *
+ * @return 0 on success, error code otherwise.
+ */
+int
+vboxvfs_guest_vnode_to_shflstring_path_internal(vnode_t vnode, PSHFLSTRING *ppResult)
+{
+ mount_t mp;
+ int rc;
+
+ char *pszPath;
+ int cbPath = MAXPATHLEN;
+
+ AssertReturn(ppResult, EINVAL);
+ AssertReturn(vnode, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+
+ pszPath = (char *)RTMemAllocZ(cbPath);
+ if (pszPath)
+ {
+ rc = vn_getpath(vnode, pszPath, &cbPath);
+ if (rc == 0)
+ {
+ return vboxvfs_guest_path_to_shflstring_path_internal(mp, pszPath, cbPath, ppResult);
+ }
+ }
+ else
+ {
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Free resources allocated by vboxvfs_path_internal() and vboxvfs_guest_vnode_to_shflstring_path_internal().
+ *
+ * @param ppHandle Reference to object to be freed.
+ */
+void
+vboxvfs_put_path_internal(void **ppHandle)
+{
+ AssertReturnVoid(ppHandle);
+ AssertReturnVoid(*ppHandle);
+ RTMemFree(*ppHandle);
+}
+
+static void
+vboxvfs_g2h_mode_dump_inernal(uint32_t fHostMode)
+{
+ PDEBUG("Host VFS object flags (0x%X) dump:", (int)fHostMode);
+
+ if (fHostMode & SHFL_CF_ACCESS_READ) PDEBUG("SHFL_CF_ACCESS_READ");
+ if (fHostMode & SHFL_CF_ACCESS_WRITE) PDEBUG("SHFL_CF_ACCESS_WRITE");
+ if (fHostMode & SHFL_CF_ACCESS_APPEND) PDEBUG("SHFL_CF_ACCESS_APPEND");
+
+ if ((fHostMode & (SHFL_CF_ACT_FAIL_IF_EXISTS |
+ SHFL_CF_ACT_REPLACE_IF_EXISTS |
+ SHFL_CF_ACT_OVERWRITE_IF_EXISTS)) == 0)
+ PDEBUG("SHFL_CF_ACT_OPEN_IF_EXISTS");
+
+ if (fHostMode & SHFL_CF_ACT_CREATE_IF_NEW) PDEBUG("SHFL_CF_ACT_CREATE_IF_NEW");
+ if (fHostMode & SHFL_CF_ACT_FAIL_IF_NEW) PDEBUG("SHFL_CF_ACT_FAIL_IF_NEW");
+ if (fHostMode & SHFL_CF_ACT_OVERWRITE_IF_EXISTS) PDEBUG("SHFL_CF_ACT_OVERWRITE_IF_EXISTS");
+ if (fHostMode & SHFL_CF_DIRECTORY) PDEBUG("SHFL_CF_DIRECTORY");
+
+ PDEBUG("Done");
+}
+
+
+/**
+ * Open existing VBoxVFS object and return its handle.
+ *
+ * @param pMount Mount session data.
+ * @param pPath VFS path to the object relative to mount point.
+ * @param fFlags For directory object it should be
+ * SHFL_CF_DIRECTORY and 0 for any other object.
+ * @param pHandle Returned handle.
+ *
+ * @return 0 on success, error code otherwise.
+ */
+int
+vboxvfs_open_internal(vboxvfs_mount_t *pMount, PSHFLSTRING pPath, uint32_t fFlags, SHFLHANDLE *pHandle)
+{
+ SHFLCREATEPARMS parms;
+
+ int rc;
+
+ AssertReturn(pMount, EINVAL);
+ AssertReturn(pPath, EINVAL);
+ AssertReturn(pHandle, EINVAL);
+
+ bzero(&parms, sizeof(parms));
+
+ vboxvfs_g2h_mode_dump_inernal(fFlags);
+
+ parms.Handle = SHFL_HANDLE_NIL;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = fFlags;
+
+ rc = VbglR0SfCreate(&g_SfClientDarwin, &pMount->pMap, pPath, &parms);
+ if (RT_SUCCESS(rc))
+ {
+ *pHandle = parms.Handle;
+ }
+ else
+ {
+ PDEBUG("vboxvfs_open_internal() failed: %d", rc);
+ }
+
+ return rc;
+}
+
+/**
+ * Release VBoxVFS object handle openned by vboxvfs_open_internal().
+ *
+ * @param pMount Mount session data.
+ * @param pHandle Handle to close.
+ *
+ * @return 0 on success, IPRT error code otherwise.
+ */
+int
+vboxvfs_close_internal(vboxvfs_mount_t *pMount, SHFLHANDLE pHandle)
+{
+ AssertReturn(pMount, EINVAL);
+ return VbglR0SfClose(&g_SfClientDarwin, &pMount->pMap, pHandle);
+}
+
+/**
+ * Get information about host VFS object.
+ *
+ * @param mp Mount point data
+ * @param pSHFLDPath Path to VFS object within mounted shared folder
+ * @param Info Returned info
+ *
+ * @return 0 on success, error code otherwise.
+ */
+int
+vboxvfs_get_info_internal(mount_t mp, PSHFLSTRING pSHFLDPath, PSHFLFSOBJINFO Info)
+{
+ vboxvfs_mount_t *pMount;
+ SHFLCREATEPARMS parms;
+
+ int rc;
+
+ AssertReturn(mp, EINVAL);
+ AssertReturn(pSHFLDPath, EINVAL);
+ AssertReturn(Info, EINVAL);
+
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ parms.Handle = 0;
+ parms.Info.cbObject = 0;
+ parms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+
+ rc = VbglR0SfCreate(&g_SfClientDarwin, &pMount->pMap, pSHFLDPath, &parms);
+ if (rc == 0)
+ *Info = parms.Info;
+
+ return rc;
+}
+
+/**
+ * Check if VFS object exists on a host side.
+ *
+ * @param vnode Guest VFS vnode that corresponds to host VFS object
+ *
+ * @return 1 if exists, 0 otherwise.
+ */
+int
+vboxvfs_exist_internal(vnode_t vnode)
+{
+ int rc;
+
+ PSHFLSTRING pSFPath = NULL;
+ SHFLHANDLE handle;
+ uint32_t fFlags;
+
+ vboxvfs_mount_t *pMount;
+ mount_t mp;
+
+ /* Return FALSE if invalid parameter given */
+ AssertReturn(vnode, 0);
+
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ fFlags = (vnode_isdir(vnode)) ? SHFL_CF_DIRECTORY : 0;
+ fFlags |= SHFL_CF_ACCESS_READ | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW;
+
+ rc = vboxvfs_guest_vnode_to_shflstring_path_internal(vnode, &pSFPath); AssertReturn(rc == 0, rc);
+ if (rc == 0)
+ {
+ rc = vboxvfs_open_internal(pMount, pSFPath, fFlags, &handle);
+ if (rc == 0)
+ {
+ rc = vboxvfs_close_internal(pMount, handle);
+ if (rc != 0)
+ {
+ PDEBUG("Unable to close() VBoxVFS object handle while checking if object exist on host: %d", rc);
+ }
+ }
+ }
+
+ vboxvfs_put_path_internal((void **)&pSFPath);
+
+ return (rc == 0);
+}
+
+/**
+ * Convert host VFS object mode flags into guest ones.
+ *
+ * @param fHostMode Host flags
+ *
+ * @return Guest flags
+ */
+mode_t
+vboxvfs_h2g_mode_inernal(RTFMODE fHostMode)
+{
+ mode_t fGuestMode = 0;
+
+ fGuestMode = /* Owner */
+ ((fHostMode & RTFS_UNIX_IRUSR) ? S_IRUSR : 0 ) |
+ ((fHostMode & RTFS_UNIX_IWUSR) ? S_IWUSR : 0 ) |
+ ((fHostMode & RTFS_UNIX_IXUSR) ? S_IXUSR : 0 ) |
+ /* Group */
+ ((fHostMode & RTFS_UNIX_IRGRP) ? S_IRGRP : 0 ) |
+ ((fHostMode & RTFS_UNIX_IWGRP) ? S_IWGRP : 0 ) |
+ ((fHostMode & RTFS_UNIX_IXGRP) ? S_IXGRP : 0 ) |
+ /* Other */
+ ((fHostMode & RTFS_UNIX_IROTH) ? S_IROTH : 0 ) |
+ ((fHostMode & RTFS_UNIX_IWOTH) ? S_IWOTH : 0 ) |
+ ((fHostMode & RTFS_UNIX_IXOTH) ? S_IXOTH : 0 ) |
+ /* SUID, SGID, SVTXT */
+ ((fHostMode & RTFS_UNIX_ISUID) ? S_ISUID : 0 ) |
+ ((fHostMode & RTFS_UNIX_ISGID) ? S_ISGID : 0 ) |
+ ((fHostMode & RTFS_UNIX_ISTXT) ? S_ISVTX : 0 ) |
+ /* VFS object types */
+ ((RTFS_IS_FIFO(fHostMode)) ? S_IFIFO : 0 ) |
+ ((RTFS_IS_DEV_CHAR(fHostMode)) ? S_IFCHR : 0 ) |
+ ((RTFS_IS_DIRECTORY(fHostMode)) ? S_IFDIR : 0 ) |
+ ((RTFS_IS_DEV_BLOCK(fHostMode)) ? S_IFBLK : 0 ) |
+ ((RTFS_IS_FILE(fHostMode)) ? S_IFREG : 0 ) |
+ ((RTFS_IS_SYMLINK(fHostMode)) ? S_IFLNK : 0 ) |
+ ((RTFS_IS_SOCKET(fHostMode)) ? S_IFSOCK : 0 );
+
+ return fGuestMode;
+}
+
+/**
+ * Convert guest VFS object mode flags into host ones.
+ *
+ * @param fGuestMode Host flags
+ *
+ * @return Host flags
+ */
+uint32_t
+vboxvfs_g2h_mode_inernal(mode_t fGuestMode)
+{
+ uint32_t fHostMode = 0;
+
+ fHostMode = ((fGuestMode & FREAD) ? SHFL_CF_ACCESS_READ : 0 ) |
+ ((fGuestMode & FWRITE) ? SHFL_CF_ACCESS_WRITE : 0 ) |
+ /* skipped: O_NONBLOCK */
+ ((fGuestMode & O_APPEND) ? SHFL_CF_ACCESS_APPEND : 0 ) |
+ /* skipped: O_SYNC */
+ /* skipped: O_SHLOCK */
+ /* skipped: O_EXLOCK */
+ /* skipped: O_ASYNC */
+ /* skipped: O_FSYNC */
+ /* skipped: O_NOFOLLOW */
+ ((fGuestMode & O_CREAT) ? SHFL_CF_ACT_CREATE_IF_NEW | (!(fGuestMode & O_TRUNC) ? SHFL_CF_ACT_OPEN_IF_EXISTS : 0) : SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW ) |
+ ((fGuestMode & O_TRUNC) ? SHFL_CF_ACT_OVERWRITE_IF_EXISTS | SHFL_CF_ACCESS_WRITE : 0 );
+ /* skipped: O_EXCL */
+
+ return fHostMode;
+}
+
+/**
+ * Mount helper: Contruct SHFLSTRING which contains VBox share name or path.
+ *
+ * @returns Initialize string buffer on success, NULL if out of memory.
+ * @param pachName The string to pack in a buffer. Does not need to be
+ * zero terminated.
+ * @param cchName The length of pachName to use. RTSTR_MAX for strlen.
+ */
+SHFLSTRING *
+vboxvfs_construct_shflstring(const char *pachName, size_t cchName)
+{
+ AssertReturn(pachName, NULL);
+
+ if (cchName == RTSTR_MAX)
+ cchName = strlen(pachName);
+
+ SHFLSTRING *pSHFLString = (SHFLSTRING *)RTMemAlloc(SHFLSTRING_HEADER_SIZE + cchName + 1);
+ if (pSHFLString)
+ {
+ pSHFLString->u16Length = cchName;
+ pSHFLString->u16Size = cchName + 1;
+ memcpy(pSHFLString->String.utf8, pachName, cchName);
+ pSHFLString->String.utf8[cchName] = '\0';
+
+ return pSHFLString;
+ }
+ return NULL;
+}
+
+#endif
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp
new file mode 100644
index 00000000..d90e6dbc
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp
@@ -0,0 +1,843 @@
+/* $Id: VBoxSF-VNodeOps.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, VNode Operations.
+ */
+
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+struct default_error_args_hack
+{
+ struct default_error_vdesc_hack
+ {
+ int vdesc_offset;
+ const char *vdesc_name;
+ } const *a_desc;
+};
+
+
+
+/**
+ * Default implementation that returns ENOTSUP.
+ */
+static int vboxSfDwnVnDefaultError(struct default_error_args_hack *pArgs)
+{
+ Log(("vboxSfDwnVnDefaultError: %s\n", RT_VALID_PTR(pArgs) && RT_VALID_PTR(pArgs->a_desc) ? pArgs->a_desc->vdesc_name : "??"));
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+}
+
+
+static int vboxFsDwnVnGetAttr(struct vnop_getattr_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+
+ vboxvfs_mount_t *pMount;
+ struct vnode_attr *vnode_args;
+ vboxvfs_vnode_t *pVnodeData;
+
+ struct timespec timespec;
+
+ SHFLFSOBJINFO Info;
+ mount_t mp;
+ vnode_t vnode;
+ int rc;
+
+ PDEBUG("Getting vnode attribute...");
+
+ AssertReturn(pArgs, EINVAL);
+
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL);
+ vnode_args = pArgs->a_vap; AssertReturn(vnode_args, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+
+ lck_rw_lock_shared(pVnodeData->pLock);
+
+ rc = vboxvfs_get_info_internal(mp, pVnodeData->pPath, &Info);
+ if (rc == 0)
+ {
+ /* Set timestamps */
+ RTTimeSpecGetTimespec(&Info.BirthTime, &timespec); VATTR_RETURN(vnode_args, va_create_time, timespec);
+ RTTimeSpecGetTimespec(&Info.AccessTime, &timespec); VATTR_RETURN(vnode_args, va_access_time, timespec);
+ RTTimeSpecGetTimespec(&Info.ModificationTime, &timespec); VATTR_RETURN(vnode_args, va_modify_time, timespec);
+ RTTimeSpecGetTimespec(&Info.ChangeTime, &timespec); VATTR_RETURN(vnode_args, va_change_time, timespec);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_backup_time);
+
+ /* Set owner info. */
+ VATTR_RETURN(vnode_args, va_uid, pMount->owner);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_gid);
+
+ /* Access mode and flags */
+ VATTR_RETURN(vnode_args, va_mode, vboxvfs_h2g_mode_inernal(Info.Attr.fMode));
+ VATTR_RETURN(vnode_args, va_flags, Info.Attr.u.Unix.fFlags);
+
+ /* The current generation number (0 if this information is not available) */
+ VATTR_RETURN(vnode_args, va_gen, Info.Attr.u.Unix.GenerationId);
+
+ VATTR_RETURN(vnode_args, va_rdev, 0);
+ VATTR_RETURN(vnode_args, va_nlink, 2);
+
+ VATTR_RETURN(vnode_args, va_data_size, sizeof(struct dirent)); /* Size of data returned per each readdir() request */
+
+ /* Hope, when it overflows nothing catastrophical will heppen! If we will not assign
+ * a uniq va_fileid to each vnode, `ls`, 'find' (and simmilar tools that uses fts_read() calls) will think that
+ * each sub-directory is self-cycled. */
+ VATTR_RETURN(vnode_args, va_fileid, (pMount->cFileIdCounter++));
+
+ /* Not supported */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_linkid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_parentid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_fsid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_filerev);
+
+ /* Not present on 10.6 */
+ //VATTR_CLEAR_ACTIVE(vnode_args, va_addedtime);
+
+ /** @todo take care about va_encoding (file name encoding) */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_encoding);
+ /** @todo take care about: va_acl */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_acl);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_name);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_uuuid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_guuid);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_total_size);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_total_alloc);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_data_alloc);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_iosize);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_nchildren);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_dirlinkcount);
+ }
+ else
+ {
+ PDEBUG("getattr: unable to get VBoxVFS object info");
+ }
+
+ lck_rw_unlock_shared(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+#if 0
+/**
+ * Helper function for vboxvfs_vnode_lookup(): create new vnode.
+ */
+static int
+vboxvfs_vnode_lookup_instantinate_vnode(vnode_t parent_vnode, char *entry_name, vnode_t *result_vnode)
+{
+ /* We need to construct full path to vnode in order to get
+ * vboxvfs_get_info_internal() to understand us! */
+
+ char *pszCurDirPath;
+ int cbCurDirPath = MAXPATHLEN;
+
+ mount_t mp = vnode_mount(parent_vnode); AssertReturn(mp, EINVAL);
+ vnode_t vnode;
+
+ int rc;
+
+ pszCurDirPath = (char *)RTMemAllocZ(cbCurDirPath);
+ if (pszCurDirPath)
+ {
+ rc = vn_getpath(parent_vnode, pszCurDirPath, &cbCurDirPath);
+ if (rc == 0 && cbCurDirPath < MAXPATHLEN)
+ {
+ SHFLFSOBJINFO Info;
+ PSHFLSTRING pSHFLPath;
+
+ /* Add '/' between path parts and truncate name if it is too long */
+ strncat(pszCurDirPath, "/", 1); strncat(pszCurDirPath, entry_name, MAXPATHLEN - cbCurDirPath - 1);
+
+ rc = vboxvfs_guest_path_to_shflstring_path_internal(mp, pszCurDirPath, strlen(pszCurDirPath) + 1, &pSHFLPath);
+ if (rc == 0)
+ {
+ rc = vboxvfs_get_info_internal(mp, pSHFLPath, (PSHFLFSOBJINFO)&Info);
+ if (rc == 0)
+ {
+ enum vtype type;
+
+ if (RTFS_IS_DIRECTORY(Info.Attr.fMode)) type = VDIR;
+ else if (RTFS_IS_FILE (Info.Attr.fMode)) type = VREG;
+ else
+ {
+ PDEBUG("Not supported VFS object (%s) type: mode 0x%X",
+ entry_name,
+ Info.Attr.fMode);
+
+ RTMemFree(pszCurDirPath);
+ vboxvfs_put_path_internal((void **)&pSHFLPath);
+ return ENOENT;
+ }
+ /* Create new vnode */
+ rc = vboxvfs_create_vnode_internal(mp, type, parent_vnode, FALSE, pSHFLPath, &vnode);
+ if (rc == 0)
+ {
+ PDEBUG("new vnode object '%s' has been created", entry_name);
+
+ *result_vnode = vnode;
+ RTMemFree(pszCurDirPath);
+
+ return 0;
+ }
+ else
+ PDEBUG("Unable to create vnode: %d", rc);
+ }
+ else
+ PDEBUG("Unable to get host object info: %d", rc);
+
+ vboxvfs_put_path_internal((void **)&pSHFLPath);
+ }
+ else
+ PDEBUG("Unable to convert guest<->host path");
+ }
+ else
+ PDEBUG("Unable to construct vnode path: %d", rc);
+
+ RTMemFree(pszCurDirPath);
+ }
+ else
+ {
+ PDEBUG("Unable to allocate memory for path buffer");
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/**
+ * Helper function for vboxvfs_vnode_lookup(): take care
+ * about '.' and '..' directory entries.
+ */
+static int
+vboxvfs_vnode_lookup_dot_handler(struct vnop_lookup_args *pArgs, vnode_t *result_vnode)
+{
+ vnode_t vnode = NULL;
+
+ if (pArgs->a_cnp->cn_flags & ISDOTDOT)
+ {
+ vnode = vnode_getparent(pArgs->a_dvp);
+ if (vnode)
+ {
+ PDEBUG("return parent directory");
+ *result_vnode = vnode;
+ return 0;
+ }
+ else
+ {
+ PDEBUG("return parent directory not found, return current directory");
+ *result_vnode = pArgs->a_dvp;
+ return 0;
+ }
+ }
+ else if ((strncmp(pArgs->a_cnp->cn_nameptr, ".", 1) == 0) &&
+ pArgs->a_cnp->cn_namelen == 1)
+ {
+ PDEBUG("return current directory");
+ *result_vnode = pArgs->a_dvp;
+ return 0;
+ }
+
+ return ENOENT;
+}
+#endif
+
+static int vboxSfDwnVnLookup(struct vnop_lookup_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ int rc;
+
+ vnode_t vnode;
+ vboxvfs_vnode_t *pVnodeData;
+
+ PDEBUG("Looking up for vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+ AssertReturn(pArgs->a_dvp, EINVAL);
+ AssertReturn(vnode_isdir(pArgs->a_dvp), EINVAL);
+ AssertReturn(pArgs->a_cnp, EINVAL);
+ AssertReturn(pArgs->a_cnp->cn_nameptr, EINVAL);
+ AssertReturn(pArgs->a_vpp, EINVAL);
+
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(pArgs->a_dvp);
+ AssertReturn(pVnodeData, EINVAL);
+ AssertReturn(pVnodeData->pLock, EINVAL);
+
+ /*
+ todo: take care about pArgs->a_cnp->cn_nameiop
+ */
+
+ if (pArgs->a_cnp->cn_nameiop == LOOKUP) PDEBUG("LOOKUP");
+ else if (pArgs->a_cnp->cn_nameiop == CREATE) PDEBUG("CREATE");
+ else if (pArgs->a_cnp->cn_nameiop == RENAME) PDEBUG("RENAME");
+ else if (pArgs->a_cnp->cn_nameiop == DELETE) PDEBUG("DELETE");
+ else PDEBUG("Unknown cn_nameiop: 0x%X", (int)pArgs->a_cnp->cn_nameiop);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ /* Take care about '.' and '..' entries */
+ if (vboxvfs_vnode_lookup_dot_handler(pArgs, &vnode) == 0)
+ {
+ vnode_get(vnode);
+ *pArgs->a_vpp = vnode;
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return 0;
+ }
+
+ /* Look into VFS cache and attempt to find previously allocated vnode there. */
+ rc = cache_lookup(pArgs->a_dvp, &vnode, pArgs->a_cnp);
+ if (rc == -1) /* Record found */
+ {
+ PDEBUG("Found record in VFS cache");
+
+ /* Check if VFS object still exist on a host side */
+ if (vboxvfs_exist_internal(vnode))
+ {
+ /* Prepare & return cached vnode */
+ vnode_get(vnode);
+ *pArgs->a_vpp = vnode;
+
+ rc = 0;
+ }
+ else
+ {
+ /* If vnode exist in guets VFS cache, but not exist on a host -- just forget it. */
+ cache_purge(vnode);
+ /** @todo free vnode data here */
+ rc = ENOENT;
+ }
+ }
+ else
+ {
+ PDEBUG("cache_lookup() returned %d, create new VFS vnode", rc);
+
+ rc = vboxvfs_vnode_lookup_instantinate_vnode(pArgs->a_dvp, pArgs->a_cnp->cn_nameptr, &vnode);
+ if (rc == 0)
+ {
+ cache_enter(pArgs->a_dvp, vnode, pArgs->a_cnp);
+ *pArgs->a_vpp = vnode;
+ }
+ else
+ {
+ rc = ENOENT;
+ }
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+static int vboxSfDwnVnOpen(struct vnop_open_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ vnode_t vnode;
+ vboxvfs_vnode_t *pVnodeData;
+ uint32_t fHostFlags;
+ mount_t mp;
+ vboxvfs_mount_t *pMount;
+
+ int rc;
+
+ PDEBUG("Opening vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ if (vnode_isinuse(vnode, 0))
+ {
+ PDEBUG("vnode '%s' (handle 0x%X) already has VBoxVFS object handle assigned, just return ok",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return 0;
+ }
+
+ /* At this point we must make sure that nobody is using VBoxVFS object handle */
+ //if (pVnodeData->Handle != SHFL_HANDLE_NIL)
+ //{
+ // PDEBUG("vnode has active VBoxVFS object handle set, aborting");
+ // lck_rw_unlock_exclusive(pVnodeData->pLock);
+ // return EINVAL;
+ //}
+
+ fHostFlags = vboxvfs_g2h_mode_inernal(pArgs->a_mode);
+ fHostFlags |= (vnode_isdir(vnode) ? SHFL_CF_DIRECTORY : 0);
+
+ SHFLHANDLE Handle;
+ rc = vboxvfs_open_internal(pMount, pVnodeData->pPath, fHostFlags, &Handle);
+ if (rc == 0)
+ {
+ PDEBUG("Open success: '%s' (handle 0x%X)",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)Handle);
+
+ pVnodeData->pHandle = Handle;
+ }
+ else
+ {
+ PDEBUG("Unable to open: '%s': %d",
+ (char *)pVnodeData->pPath->String.utf8,
+ rc);
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+static int vboxSfDwnVnClose(struct vnop_close_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+
+ vnode_t vnode;
+ mount_t mp;
+ vboxvfs_vnode_t *pVnodeData;
+ vboxvfs_mount_t *pMount;
+
+ int rc;
+
+ PDEBUG("Closing vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ if (vnode_isinuse(vnode, 0))
+ {
+ PDEBUG("vnode '%s' (handle 0x%X) is still in use, just return ok",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return 0;
+ }
+
+ /* At this point we must make sure that vnode has VBoxVFS object handle assigned */
+ if (pVnodeData->pHandle == SHFL_HANDLE_NIL)
+ {
+ PDEBUG("vnode has no active VBoxVFS object handle set, aborting");
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return EINVAL;
+ }
+
+ rc = vboxvfs_close_internal(pMount, pVnodeData->pHandle);
+ if (rc == 0)
+ {
+ PDEBUG("Close success: '%s' (handle 0x%X)",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ /* Forget about previously assigned VBoxVFS object handle */
+ pVnodeData->pHandle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ PDEBUG("Unable to close: '%s' (handle 0x%X): %d",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle, rc);
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+#if 0
+/**
+ * Convert SHFLDIRINFO to struct dirent and copy it back to user.
+ */
+static int
+vboxvfs_vnode_readdir_copy_data(ino_t index, SHFLDIRINFO *Info, struct uio *uio, int *numdirent)
+{
+ struct dirent entry;
+
+ int rc;
+
+ entry.d_ino = index;
+ entry.d_reclen = (__uint16_t)sizeof(entry);
+
+ /* Detect dir entry type */
+ if (RTFS_IS_DIRECTORY(Info->Info.Attr.fMode))
+ entry.d_type = DT_DIR;
+ else if (RTFS_IS_FILE(Info->Info.Attr.fMode))
+ entry.d_type = DT_REG;
+ else
+ {
+ PDEBUG("Unknown type of host file: mode 0x%X", (int)Info->Info.Attr.fMode);
+ return ENOTSUP;
+ }
+
+ entry.d_namlen = (__uint8_t)min(sizeof(entry.d_name), Info->name.u16Size);
+ memcpy(entry.d_name, Info->name.String.utf8, entry.d_namlen);
+
+ rc = uiomove((char *)&entry, sizeof(entry), uio);
+ if (rc == 0)
+ {
+ uio_setoffset(uio, index * sizeof(struct dirent));
+ *numdirent = (int)index;
+
+ PDEBUG("discovered entry: '%s' (%d bytes), item #%d", entry.d_name, (int)entry.d_namlen, (int)index);
+ }
+ else
+ {
+ PDEBUG("Failed to return dirent data item #%d (%d)", (int)index, rc);
+ }
+
+ return rc;
+}
+#endif
+
+static int vboxSfDwnVnReadDir(struct vnop_readdir_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ vboxvfs_mount_t *pMount;
+ vboxvfs_vnode_t *pVnodeData;
+ SHFLDIRINFO *Info;
+ uint32_t cbInfo;
+ mount_t mp;
+ vnode_t vnode;
+ struct uio *uio;
+
+ int rc = 0, rc2;
+
+ PDEBUG("Reading directory...");
+
+ AssertReturn(pArgs, EINVAL);
+ AssertReturn(pArgs->a_eofflag, EINVAL);
+ AssertReturn(pArgs->a_numdirent, EINVAL);
+
+ uio = pArgs->a_uio; AssertReturn(uio, EINVAL);
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL); AssertReturn(vnode_isdir(vnode), EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_shared(pVnodeData->pLock);
+
+ cbInfo = sizeof(Info) + MAXPATHLEN;
+ Info = (SHFLDIRINFO *)RTMemAllocZ(cbInfo);
+ if (!Info)
+ {
+ PDEBUG("No memory to allocate internal data");
+ lck_rw_unlock_shared(pVnodeData->pLock);
+ return ENOMEM;
+ }
+
+ uint32_t index = (uint32_t)uio_offset(uio) / (uint32_t)sizeof(struct dirent);
+ uint32_t cFiles = 0;
+
+ PDEBUG("Exploring VBoxVFS directory (%s), handle (0x%.8X), offset (0x%X), count (%d)", (char *)pVnodeData->pPath->String.utf8, (int)pVnodeData->pHandle, index, uio_iovcnt(uio));
+
+ /* Currently, there is a problem when VbglR0SfDirInfo() is not able to
+ * continue retrieve directory content if the same VBoxVFS handle is used.
+ * This macro forces to use a new handle in readdir() callback. If enabled,
+ * the original handle (obtained in open() callback is ignored). */
+
+ SHFLHANDLE Handle;
+ rc = vboxvfs_open_internal(pMount,
+ pVnodeData->pPath,
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW,
+ &Handle);
+ if (rc != 0)
+ {
+ PDEBUG("Unable to open dir: %d", rc);
+ RTMemFree(Info);
+ lck_rw_unlock_shared(pVnodeData->pLock);
+ return rc;
+ }
+
+#if 0
+ rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0, 0, index, &cbInfo, (PSHFLDIRINFO)Info, &cFiles);
+#else
+ SHFLSTRING *pMask = vboxvfs_construct_shflstring("*", strlen("*"));
+ if (pMask)
+ {
+ for (uint32_t cSkip = 0; (cSkip < index + 1) && (rc == VINF_SUCCESS); cSkip++)
+ {
+ //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0 /* pMask */, 0 /* SHFL_LIST_RETURN_ONE */, 0, &cbInfo, (PSHFLDIRINFO)Info, &cFiles);
+
+ uint32_t cbReturned = cbInfo;
+ //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, pMask, SHFL_LIST_RETURN_ONE, 0, &cbReturned, (PSHFLDIRINFO)Info, &cFiles);
+ rc = VbglR0SfDirInfo(&g_SfClientDarwin, &pMount->pMap, Handle, 0, SHFL_LIST_RETURN_ONE, 0,
+ &cbReturned, (PSHFLDIRINFO)Info, &cFiles);
+
+ }
+
+ PDEBUG("read %d files", cFiles);
+ RTMemFree(pMask);
+ }
+ else
+ {
+ PDEBUG("Can't alloc mask");
+ rc = ENOMEM;
+ }
+#endif
+ rc2 = vboxvfs_close_internal(pMount, Handle);
+ if (rc2 != 0)
+ {
+ PDEBUG("Unable to close directory: %s: %d",
+ pVnodeData->pPath->String.utf8,
+ rc2);
+ }
+
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ {
+ rc = vboxvfs_vnode_readdir_copy_data((ino_t)(index + 1), Info, uio, pArgs->a_numdirent);
+ break;
+ }
+
+ case VERR_NO_MORE_FILES:
+ {
+ PDEBUG("No more entries in directory");
+ *(pArgs->a_eofflag) = 1;
+ break;
+ }
+
+ default:
+ {
+ PDEBUG("VbglR0SfDirInfo() for item #%d has failed: %d", (int)index, (int)rc);
+ rc = EINVAL;
+ break;
+ }
+ }
+
+ RTMemFree(Info);
+ lck_rw_unlock_shared(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+
+static int vboxSfDwnVnPathConf(struct vnop_pathconf_args *pArgs)
+{
+ Log(("vboxSfDwnVnPathConf:\n"));
+ RT_NOREF(pArgs);
+ return 0;
+}
+
+
+/**
+ * vnop_reclaim implementation.
+ *
+ * VBoxVFS reclaim callback.
+ * Called when vnode is going to be deallocated. Should release
+ * all the VBoxVFS resources that correspond to current vnode object.
+ *
+ * @param pArgs Operation arguments passed from VFS layer.
+ *
+ * @return 0 on success, BSD error code otherwise.
+ */
+static int vboxSfDwnVnReclaim(struct vnop_reclaim_args *pArgs)
+{
+ AssertReturn(pArgs && pArgs->a_vp, EINVAL);
+
+ /* Check that it's not a root node that's in use. */
+ PVBOXSFMNTDATA pMntData = (PVBOXSFMNTDATA)vfs_fsprivate(vnode_mount(pArgs->a_vp));
+ AssertReturn(!pMntData || pMntData->pVnRoot != pArgs->a_vp, EBUSY);
+
+ /* Get the private data and free it. */
+ PVBOXSFDWNVNDATA pVnData = (PVBOXSFDWNVNDATA)vnode_fsnode(pArgs->a_vp);
+ AssertPtrReturn(pVnData, 0);
+
+ if (pVnData->hHandle != SHFL_HANDLE_NIL)
+ {
+ /** @todo can this happen? */
+ pVnData->hHandle = SHFL_HANDLE_NIL;
+ }
+
+ RTMemFree(pVnData);
+ return 0;
+}
+
+
+/**
+ * Allocates a vnode.
+ *
+ * @returns Pointer to the new VNode, NULL if out of memory.
+ * @param pMount The file system mount structure.
+ * @param enmType The vnode type.
+ * @param pParent The parent vnode, NULL if root.
+ * @param cbFile The file size
+ */
+vnode_t vboxSfDwnVnAlloc(mount_t pMount, enum vtype enmType, vnode_t pParent, uint64_t cbFile)
+{
+ /*
+ * Create our private data.
+ */
+ PVBOXSFDWNVNDATA pVnData = (PVBOXSFDWNVNDATA)RTMemAllocZ(sizeof(*pVnData));
+ if (pVnData)
+ {
+ pVnData->hHandle = SHFL_HANDLE_NIL;
+
+ struct vnode_fsparam VnParms;
+ RT_ZERO(VnParms);
+ VnParms.vnfs_mp = pMount;
+ VnParms.vnfs_vtype = enmType;
+ VnParms.vnfs_str = "vboxsf";
+ VnParms.vnfs_dvp = pParent;
+ VnParms.vnfs_fsnode = pVnData;
+ VnParms.vnfs_vops = g_papfnVBoxSfDwnVnDirOpsVector;
+ VnParms.vnfs_markroot = pParent == NULL;
+ VnParms.vnfs_marksystem = 0;
+ VnParms.vnfs_rdev = 0;
+ VnParms.vnfs_filesize = cbFile;
+ VnParms.vnfs_cnp = 0;
+ VnParms.vnfs_flags = VNFS_NOCACHE;
+
+ vnode_t pVnRet;
+ int rc = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &VnParms, &pVnRet);
+ if (rc == 0)
+ return pVnRet;
+ RTMemFree(pVnData);
+ }
+ printf("vboxSfDwnVnAlloc: out of memory!\n");
+ return NULL;
+}
+
+
+/**
+ * Vnode operations.
+ */
+static struct vnodeopv_entry_desc g_VBoxSfDirOpsDescList[] =
+{
+#define VNODEOPFUNC int(*)(void *)
+ { &vnop_default_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_access_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - probably not needed.
+ //{ &vnop_advlock_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - later.
+ //{ &vnop_allocate_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - maybe, need shfl function
+ { &vnop_blktooff_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_blockmap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_bwrite_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_close_desc, (VNODEOPFUNC)vboxSfDwnVnClose },
+ //{ &vnop_copyfile_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_create_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_exchange_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_fsync_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_getattr_desc, (VNODEOPFUNC)vboxFsDwnVnGetAttr },
+ //{ &vnop_getnamedstream_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_getxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_inactive_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_ioctl_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_link_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_listxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_lookup_desc, (VNODEOPFUNC)vboxSfDwnVnLookup },
+ { &vnop_mkdir_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mknod_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mmap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mnomap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_offtoblk_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_open_desc, (VNODEOPFUNC)vboxSfDwnVnOpen },
+ { &vnop_pagein_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_pageout_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_pathconf_desc, (VNODEOPFUNC)vboxSfDwnVnPathConf },
+ /* { &vnop_print_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, undefined in ML */
+ { &vnop_read_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_readdir_desc, (VNODEOPFUNC)vboxSfDwnVnReadDir },
+ //{ &vnop_readdirattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - hfs specific.
+ { &vnop_readlink_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_reclaim_desc, (VNODEOPFUNC)vboxSfDwnVnReclaim },
+ { &vnop_remove_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_removexattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_rename_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_revoke_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_rmdir_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_searchfs_desc, (VNODEOPFUNC)err_searchfs },
+ //{ &vnop_select_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_setattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_setxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_strategy_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_symlink_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ /* { &vnop_truncate_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, undefined in ML */
+ //{ &vnop_whiteout_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed/supported
+ { &vnop_write_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { NULL, (VNODEOPFUNC)NULL },
+#undef VNODEOPFUNC
+};
+
+/** ??? */
+int (**g_papfnVBoxSfDwnVnDirOpsVector)(void *);
+
+/**
+ * VNode operation descriptors.
+ */
+struct vnodeopv_desc g_VBoxSfVnodeOpvDesc =
+{
+ &g_papfnVBoxSfDwnVnDirOpsVector,
+ g_VBoxSfDirOpsDescList
+};
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp
new file mode 100644
index 00000000..e1c9c486
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp
@@ -0,0 +1,639 @@
+/* $Id: VBoxSF-VfsOps.cpp $ */
+/** @file
+ * VBoxFS - Darwin Shared Folders, Virtual File System Operations.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <VBox/log.h>
+
+
+
+/**
+ * vfsops::vfs_getattr implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param pFsAttr Input & output structure.
+ * @param pContext Unused kAuth parameter.
+ */
+static int vboxSfDwnVfsGetAttr(mount_t pMount, struct vfs_attr *pFsAttr, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, EBADMSG);
+ LogFlow(("vboxSfDwnVfsGetAttr: %s\n", pThis->MntInfo.szFolder));
+ RT_NOREF(pContext);
+
+ /*
+ * Get the file system stats from the host.
+ */
+ int rc;
+ struct MyEmbReq
+ {
+ VBGLIOCIDCHGCMFASTCALL Hdr;
+ VMMDevHGCMCall Call;
+ VBoxSFParmInformation Parms;
+ SHFLVOLINFO VolInfo;
+ } *pReq = (struct MyEmbReq *)VbglR0PhysHeapAlloc(sizeof(*pReq));
+ if (pReq)
+ {
+ RT_ZERO(pReq->VolInfo);
+
+ VBGLIOCIDCHGCMFASTCALL_INIT(&pReq->Hdr, VbglR0PhysHeapGetPhysAddr(pReq), &pReq->Call, g_SfClientDarwin.idClient,
+ SHFL_FN_INFORMATION, SHFL_CPARMS_INFORMATION, sizeof(*pReq));
+ pReq->Parms.id32Root.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.id32Root.u.value32 = pThis->hHostFolder.root;
+ pReq->Parms.u64Handle.type = VMMDevHGCMParmType_64bit;
+ pReq->Parms.u64Handle.u.value64 = 0;
+ pReq->Parms.f32Flags.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.f32Flags.u.value32 = SHFL_INFO_VOLUME | SHFL_INFO_GET;
+ pReq->Parms.cb32.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.cb32.u.value32 = sizeof(pReq->VolInfo);
+ pReq->Parms.pInfo.type = VMMDevHGCMParmType_Embedded;
+ pReq->Parms.pInfo.u.Embedded.cbData = sizeof(pReq->VolInfo);
+ pReq->Parms.pInfo.u.Embedded.offData = RT_UOFFSETOF(struct MyEmbReq, VolInfo) - sizeof(VBGLIOCIDCHGCMFASTCALL);
+ pReq->Parms.pInfo.u.Embedded.fFlags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+
+ int vrc = VbglR0HGCMFastCall(g_SfClientDarwin.handle, &pReq->Hdr, sizeof(*pReq));
+ if (RT_SUCCESS(vrc))
+ vrc = pReq->Call.header.result;
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Fill in stuff.
+ */
+ /* Copy over the results we got from the host. */
+ uint32_t cbUnit = pReq->VolInfo.ulBytesPerSector * pReq->VolInfo.ulBytesPerAllocationUnit;
+ VFSATTR_RETURN(pFsAttr, f_bsize, cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_iosize, _64K); /** @todo what's a good block size... */
+ VFSATTR_RETURN(pFsAttr, f_blocks, (uint64_t)pReq->VolInfo.ullTotalAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bavail, (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bfree, (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bused,
+ ((uint64_t)pReq->VolInfo.ullTotalAllocationBytes - (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes) / cbUnit);
+ fsid_t const fsid = { { vfs_statfs(pMount)->f_fsid.val[0], vfs_typenum(pMount) } };
+ VFSATTR_RETURN(pFsAttr, f_fsid, fsid);
+
+ /* f_owner is handled by caller. */
+ /* f_signature is handled by caller. */
+
+ struct timespec TmpTv = { 1084190406, 0 };
+ VFSATTR_RETURN(pFsAttr, f_create_time, TmpTv);
+
+ /*
+ * Unsupported bits.
+ */
+ /* Dummies for some values we don't support. */
+ VFSATTR_RETURN(pFsAttr, f_objcount, 0);
+ VFSATTR_RETURN(pFsAttr, f_filecount, 0);
+ VFSATTR_RETURN(pFsAttr, f_dircount, 0);
+ VFSATTR_RETURN(pFsAttr, f_maxobjcount, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_files, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_ffree, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_fssubtype, 0);
+ VFSATTR_RETURN(pFsAttr, f_carbon_fsid, 0);
+
+ /* Totally not supported: */
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_modify_time);
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_access_time);
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_backup_time);
+
+ /*
+ * Annoying capability stuff.
+ * The 'valid' bits are only supposed to be set when we know for sure.
+ */
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_capabilities))
+ {
+ vol_capabilities_attr_t *pCaps = &pFsAttr->f_capabilities;
+
+ pCaps->valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS
+ | VOL_CAP_FMT_SYMBOLICLINKS
+ | VOL_CAP_FMT_HARDLINKS
+ | VOL_CAP_FMT_JOURNAL
+ | VOL_CAP_FMT_JOURNAL_ACTIVE
+ | VOL_CAP_FMT_NO_ROOT_TIMES
+ | VOL_CAP_FMT_SPARSE_FILES
+ | VOL_CAP_FMT_ZERO_RUNS
+ | VOL_CAP_FMT_CASE_SENSITIVE
+ | VOL_CAP_FMT_CASE_PRESERVING
+ | VOL_CAP_FMT_FAST_STATFS
+ | VOL_CAP_FMT_2TB_FILESIZE
+ | VOL_CAP_FMT_OPENDENYMODES
+ | VOL_CAP_FMT_HIDDEN_FILES
+ | VOL_CAP_FMT_PATH_FROM_ID
+ | VOL_CAP_FMT_NO_VOLUME_SIZES
+ | VOL_CAP_FMT_DECMPFS_COMPRESSION
+ | VOL_CAP_FMT_64BIT_OBJECT_IDS;
+ pCaps->capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_2TB_FILESIZE
+ /// @todo | VOL_CAP_FMT_SYMBOLICLINKS - later
+ /// @todo | VOL_CAP_FMT_SPARSE_FILES - probably, needs testing.
+ /*| VOL_CAP_FMT_CASE_SENSITIVE - case-insensitive */
+ | VOL_CAP_FMT_CASE_PRESERVING
+ /// @todo | VOL_CAP_FMT_HIDDEN_FILES - if windows host.
+ /// @todo | VOL_CAP_FMT_OPENDENYMODES - if windows host.
+ ;
+ pCaps->valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_SEARCHFS
+ | VOL_CAP_INT_ATTRLIST
+ | VOL_CAP_INT_NFSEXPORT
+ | VOL_CAP_INT_READDIRATTR
+ | VOL_CAP_INT_EXCHANGEDATA
+ | VOL_CAP_INT_COPYFILE
+ | VOL_CAP_INT_ALLOCATE
+ | VOL_CAP_INT_VOL_RENAME
+ | VOL_CAP_INT_ADVLOCK
+ | VOL_CAP_INT_FLOCK
+ | VOL_CAP_INT_EXTENDED_SECURITY
+ | VOL_CAP_INT_USERACCESS
+ | VOL_CAP_INT_MANLOCK
+ | VOL_CAP_INT_NAMEDSTREAMS
+ | VOL_CAP_INT_EXTENDED_ATTR;
+ pCaps->capabilities[VOL_CAPABILITIES_INTERFACES] = 0
+ /// @todo | VOL_CAP_INT_SEARCHFS
+ /// @todo | VOL_CAP_INT_COPYFILE
+ /// @todo | VOL_CAP_INT_READDIRATTR
+ ;
+
+ pCaps->valid[VOL_CAPABILITIES_RESERVED1] = 0;
+ pCaps->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
+
+ pCaps->valid[VOL_CAPABILITIES_RESERVED2] = 0;
+ pCaps->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
+
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_capabilities);
+ }
+
+
+ /*
+ * Annoying attribute stuff.
+ * The 'valid' bits are only supposed to be set when we know for sure.
+ */
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_attributes))
+ {
+ vol_attributes_attr_t *pAt = &pFsAttr->f_attributes;
+
+ pAt->validattr.commonattr = ATTR_CMN_NAME
+ | ATTR_CMN_DEVID
+ | ATTR_CMN_FSID
+ | ATTR_CMN_OBJTYPE
+ | ATTR_CMN_OBJTAG
+ | ATTR_CMN_OBJID
+ | ATTR_CMN_OBJPERMANENTID
+ | ATTR_CMN_PAROBJID
+ | ATTR_CMN_SCRIPT
+ | ATTR_CMN_CRTIME
+ | ATTR_CMN_MODTIME
+ | ATTR_CMN_CHGTIME
+ | ATTR_CMN_ACCTIME
+ | ATTR_CMN_BKUPTIME
+ | ATTR_CMN_FNDRINFO
+ | ATTR_CMN_OWNERID
+ | ATTR_CMN_GRPID
+ | ATTR_CMN_ACCESSMASK
+ | ATTR_CMN_FLAGS
+ | ATTR_CMN_USERACCESS
+ | ATTR_CMN_EXTENDED_SECURITY
+ | ATTR_CMN_UUID
+ | ATTR_CMN_GRPUUID
+ | ATTR_CMN_FILEID
+ | ATTR_CMN_PARENTID
+ | ATTR_CMN_FULLPATH
+ | ATTR_CMN_ADDEDTIME;
+ pAt->nativeattr.commonattr = ATTR_CMN_NAME
+ | ATTR_CMN_DEVID
+ | ATTR_CMN_FSID
+ | ATTR_CMN_OBJTYPE
+ | ATTR_CMN_OBJTAG
+ | ATTR_CMN_OBJID
+ //| ATTR_CMN_OBJPERMANENTID
+ | ATTR_CMN_PAROBJID
+ //| ATTR_CMN_SCRIPT
+ | ATTR_CMN_CRTIME
+ | ATTR_CMN_MODTIME
+ | ATTR_CMN_CHGTIME
+ | ATTR_CMN_ACCTIME
+ //| ATTR_CMN_BKUPTIME
+ //| ATTR_CMN_FNDRINFO
+ //| ATTR_CMN_OWNERID
+ //| ATTR_CMN_GRPID
+ | ATTR_CMN_ACCESSMASK
+ //| ATTR_CMN_FLAGS
+ //| ATTR_CMN_USERACCESS
+ //| ATTR_CMN_EXTENDED_SECURITY
+ //| ATTR_CMN_UUID
+ //| ATTR_CMN_GRPUUID
+ | ATTR_CMN_FILEID
+ | ATTR_CMN_PARENTID
+ | ATTR_CMN_FULLPATH
+ //| ATTR_CMN_ADDEDTIME
+ ;
+ pAt->validattr.volattr = ATTR_VOL_FSTYPE
+ | ATTR_VOL_SIGNATURE
+ | ATTR_VOL_SIZE
+ | ATTR_VOL_SPACEFREE
+ | ATTR_VOL_SPACEAVAIL
+ | ATTR_VOL_MINALLOCATION
+ | ATTR_VOL_ALLOCATIONCLUMP
+ | ATTR_VOL_IOBLOCKSIZE
+ | ATTR_VOL_OBJCOUNT
+ | ATTR_VOL_FILECOUNT
+ | ATTR_VOL_DIRCOUNT
+ | ATTR_VOL_MAXOBJCOUNT
+ | ATTR_VOL_MOUNTPOINT
+ | ATTR_VOL_NAME
+ | ATTR_VOL_MOUNTFLAGS
+ | ATTR_VOL_MOUNTEDDEVICE
+ | ATTR_VOL_ENCODINGSUSED
+ | ATTR_VOL_CAPABILITIES
+ | ATTR_VOL_UUID
+ | ATTR_VOL_ATTRIBUTES
+ | ATTR_VOL_INFO;
+ pAt->nativeattr.volattr = ATTR_VOL_FSTYPE
+ //| ATTR_VOL_SIGNATURE
+ | ATTR_VOL_SIZE
+ | ATTR_VOL_SPACEFREE
+ | ATTR_VOL_SPACEAVAIL
+ | ATTR_VOL_MINALLOCATION
+ | ATTR_VOL_ALLOCATIONCLUMP
+ | ATTR_VOL_IOBLOCKSIZE
+ //| ATTR_VOL_OBJCOUNT
+ //| ATTR_VOL_FILECOUNT
+ //| ATTR_VOL_DIRCOUNT
+ //| ATTR_VOL_MAXOBJCOUNT
+ //| ATTR_VOL_MOUNTPOINT - ??
+ | ATTR_VOL_NAME
+ | ATTR_VOL_MOUNTFLAGS
+ | ATTR_VOL_MOUNTEDDEVICE
+ //| ATTR_VOL_ENCODINGSUSED
+ | ATTR_VOL_CAPABILITIES
+ //| ATTR_VOL_UUID
+ | ATTR_VOL_ATTRIBUTES
+ //| ATTR_VOL_INFO
+ ;
+ pAt->validattr.dirattr = ATTR_DIR_LINKCOUNT
+ | ATTR_DIR_ENTRYCOUNT
+ | ATTR_DIR_MOUNTSTATUS;
+ pAt->nativeattr.dirattr = 0 //ATTR_DIR_LINKCOUNT
+ | ATTR_DIR_ENTRYCOUNT
+ | ATTR_DIR_MOUNTSTATUS
+ ;
+ pAt->validattr.fileattr = ATTR_FILE_LINKCOUNT
+ | ATTR_FILE_TOTALSIZE
+ | ATTR_FILE_ALLOCSIZE
+ | ATTR_FILE_IOBLOCKSIZE
+ | ATTR_FILE_DEVTYPE
+ | ATTR_FILE_FORKCOUNT
+ | ATTR_FILE_FORKLIST
+ | ATTR_FILE_DATALENGTH
+ | ATTR_FILE_DATAALLOCSIZE
+ | ATTR_FILE_RSRCLENGTH
+ | ATTR_FILE_RSRCALLOCSIZE;
+ pAt->nativeattr.fileattr = 0
+ //|ATTR_FILE_LINKCOUNT
+ | ATTR_FILE_TOTALSIZE
+ | ATTR_FILE_ALLOCSIZE
+ //| ATTR_FILE_IOBLOCKSIZE
+ | ATTR_FILE_DEVTYPE
+ //| ATTR_FILE_FORKCOUNT
+ //| ATTR_FILE_FORKLIST
+ | ATTR_FILE_DATALENGTH
+ | ATTR_FILE_DATAALLOCSIZE
+ | ATTR_FILE_RSRCLENGTH
+ | ATTR_FILE_RSRCALLOCSIZE
+ ;
+ pAt->validattr.forkattr = ATTR_FORK_TOTALSIZE
+ | ATTR_FORK_ALLOCSIZE;
+ pAt->nativeattr.forkattr = 0
+ //| ATTR_FORK_TOTALSIZE
+ //| ATTR_FORK_ALLOCSIZE
+ ;
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_attributes);
+ }
+
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_vol_name))
+ {
+ RTStrCopy(pFsAttr->f_vol_name, MAXPATHLEN, pThis->MntInfo.szFolder);
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_vol_name);
+ }
+
+ rc = 0;
+ }
+ else
+ {
+ Log(("vboxSfOs2QueryFileInfo: VbglR0SfFsInfo failed: %Rrc\n", vrc));
+ rc = RTErrConvertToErrno(vrc);
+ }
+
+ VbglR0PhysHeapFree(pReq);
+ }
+ else
+ rc = ENOMEM;
+ return rc;
+}
+
+
+/**
+ * vfsops::vfs_root implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param ppVnode Where to return the referenced root node on success.
+ * @param pContext Unused kAuth parameter.
+ */
+static int vboxSfDwnVfsRoot(mount_t pMount, vnode_t *ppVnode, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, EBADMSG);
+ LogFlow(("vboxSfDwnVfsRoot: pThis=%p:{%s}\n", pThis, pThis->MntInfo.szFolder));
+ RT_NOREF(pContext);
+
+ /*
+ * We shouldn't be callable during unmount, should we?
+ */
+ AssertReturn(vfs_isunmount(pMount), EBUSY);
+
+ /*
+ * There should always be a root node around.
+ */
+ if (pThis->pVnRoot)
+ {
+ int rc = vnode_get(pThis->pVnRoot);
+ if (rc == 0)
+ {
+ *ppVnode = pThis->pVnRoot;
+ LogFlow(("vboxSfDwnVfsRoot: return %p\n", *ppVnode));
+ return 0;
+ }
+ Log(("vboxSfDwnVfsRoot: vnode_get failed! %d\n", rc));
+ return rc;
+ }
+
+ LogRel(("vboxSfDwnVfsRoot: pVnRoot is NULL!\n"));
+ return EILSEQ;
+}
+
+
+/**
+ * vfsops::vfs_umount implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data.
+ * @param fFlags Unmount flags.
+ * @param pContext kAuth context which we don't care much about.
+ *
+ */
+static int vboxSfDwnVfsUnmount(mount_t pMount, int fFlags, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, 0);
+ LogFlowFunc(("pThis=%p:{%s} fFlags=%#x\n", pThis, pThis->MntInfo.szFolder, fFlags));
+ RT_NOREF(pContext);
+
+ /*
+ * Flush vnodes.
+ */
+ int rc = vflush(pMount, pThis->pVnRoot, fFlags & MNT_FORCE ? FORCECLOSE : 0);
+ if (rc == 0)
+ {
+ /*
+ * Is the file system still busy?
+ *
+ * Until we find a way of killing any active host calls, we cannot properly
+ * respect the MNT_FORCE flag here. So, MNT_FORCE is ignored here.
+ */
+ if ( !pThis->pVnRoot
+ || !vnode_isinuse(pThis->pVnRoot, 1))
+ {
+ /*
+ * Release our root vnode reference and do another flush.
+ */
+ if (pThis->pVnRoot)
+ {
+ vnode_put(pThis->pVnRoot);
+ pThis->pVnRoot = NULL;
+ }
+ vflush(pMount, NULLVP, FORCECLOSE);
+
+ /*
+ * Unmap the shared folder and destroy our mount info structure.
+ */
+ vfs_setfsprivate(pMount, NULL);
+
+ rc = VbglR0SfUnmapFolder(&g_SfClientDarwin, &pThis->hHostFolder);
+ AssertRC(rc);
+
+ RT_ZERO(*pThis);
+ RTMemFree(pThis);
+
+ vfs_clearflags(pMount, MNT_LOCAL); /* ?? */
+ rc = 0;
+
+ g_cVBoxSfMounts--;
+ }
+ else
+ {
+ Log(("VBoxSF: umount failed: file system busy! (%s)\n", pThis->MntInfo.szFolder));
+ rc = EBUSY;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * vfsops::vfs_start implementation.
+ */
+static int vboxSfDwnVfsStart(mount_t pMount, int fFlags, vfs_context_t pContext)
+{
+ RT_NOREF(pMount, fFlags, pContext);
+ return 0;
+}
+
+
+/**
+ * vfsops::vfs_mount implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param pDevVp The device to mount. Not used by us.
+ * @param pUserData User space address of parameters supplied to mount().
+ * We expect a VBOXSFDRWNMOUNTINFO structure.
+ * @param pContext kAuth context needed in order to authentificate mount
+ * operation.
+ */
+static int vboxSfDwnVfsMount(mount_t pMount, vnode_t pDevVp, user_addr_t pUserData, vfs_context_t pContext)
+{
+ RT_NOREF(pDevVp, pContext);
+
+ /*
+ * We don't support mount updating.
+ */
+ if (vfs_isupdate(pMount))
+ {
+ LogRel(("VBoxSF: mount: MNT_UPDATE is not supported.\n"));
+ return ENOTSUP;
+ }
+ if (pUserData == USER_ADDR_NULL)
+ {
+ LogRel(("VBoxSF: mount: pUserData is NULL.\n"));
+ return EINVAL;
+ }
+ struct vfsstatfs *pFsStats = vfs_statfs(pMount);
+ AssertReturn(pFsStats, EINVAL);
+
+ /*
+ * Get the mount information from userland.
+ */
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return ENOMEM;
+ pThis->uidMounter = pFsStats->f_owner;
+
+ int rc = RTR0MemUserCopyFrom(&pThis->MntInfo, (RTR3PTR)pUserData, sizeof(pThis->MntInfo));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxSF: mount: Failed to copy in mount user data: %Rrc\n", rc));
+ rc = EFAULT;
+ }
+ else if (pThis->MntInfo.u32Magic != VBOXSFDRWNMOUNTINFO_MAGIC)
+ {
+ LogRel(("VBoxSF: mount: Invalid user data magic (%#x)\n", pThis->MntInfo.u32Magic));
+ rc = EINVAL;
+ }
+ else if ( (rc = RTStrValidateEncodingEx(pThis->MntInfo.szFolder, sizeof(pThis->MntInfo.szFolder),
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED)) != VINF_SUCCESS
+ || pThis->MntInfo.szFolder[0] == '\0')
+ {
+ LogRel(("VBoxSF: mount: Invalid or empty share name!\n"));
+ rc = EINVAL;
+ }
+ else
+ {
+ /*
+ * Try map the shared folder.
+ */
+ if (vboxSfDwnConnect())
+ {
+ PSHFLSTRING pName = ShflStringDupUtf8(pThis->MntInfo.szFolder);
+ if (pName)
+ {
+ rc = VbglR0SfMapFolder(&g_SfClientDarwin, pName, &pThis->hHostFolder);
+ RTMemFree(pName);
+ if (RT_SUCCESS(rc))
+ {
+
+ /*
+ * Create a root node, that avoid races later.
+ */
+ pThis->pVnRoot = vboxSfDwnVnAlloc(pMount, VDIR, NULL /*pParent*/, 0);
+ if (pThis->pVnRoot)
+ {
+ /*
+ * Fill file system stats with dummy data.
+ */
+ pFsStats->f_bsize = 512;
+ pFsStats->f_iosize = _64K;
+ pFsStats->f_blocks = _1M;
+ pFsStats->f_bavail = _1M / 4 * 3;
+ pFsStats->f_bused = _1M / 4;
+ pFsStats->f_files = 1024;
+ pFsStats->f_ffree = _64K;
+ vfs_getnewfsid(pMount); /* f_fsid */
+ /* pFsStats->f_fowner - don't touch */
+ /* pFsStats->f_fstypename - don't touch */
+ /* pFsStats->f_mntonname - don't touch */
+ RTStrCopy(pFsStats->f_mntfromname, sizeof(pFsStats->f_mntfromname), pThis->MntInfo.szFolder);
+ /* pFsStats->f_fssubtype - don't touch? */
+ /* pFsStats->f_reserved[0] - don't touch? */
+ /* pFsStats->f_reserved[1] - don't touch? */
+
+ /*
+ * We're good. Set private data and flags.
+ */
+ vfs_setfsprivate(pMount, pThis);
+ vfs_setflags(pMount, MNT_SYNCHRONOUS | MNT_NOSUID | MNT_NODEV);
+ /** @todo Consider flags like MNT_NOEXEC ? */
+
+ /// @todo vfs_setauthopaque(pMount)?
+ /// @todo vfs_clearauthopaqueaccess(pMount)?
+ /// @todo vfs_clearextendedsecurity(pMount)?
+
+ LogRel(("VBoxSF: mount: Successfully mounted '%s' (uidMounter=%u).\n",
+ pThis->MntInfo.szFolder, pThis->uidMounter));
+ return 0;
+ }
+
+ LogRel(("VBoxSF: mount: Failed to allocate root node!\n"));
+ rc = ENOMEM;
+ }
+ else
+ {
+ LogRel(("VBoxSF: mount: VbglR0SfMapFolder failed on '%s': %Rrc\n", pThis->MntInfo.szFolder, rc));
+ rc = ENOENT;
+ }
+ }
+ else
+ rc = ENOMEM;
+ }
+ else
+ {
+ LogRel(("VBoxSF: mount: Not connected to shared folders service!\n"));
+ rc = ENOTCONN;
+ }
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+/**
+ * VFS operations
+ */
+struct vfsops g_VBoxSfVfsOps =
+{
+ vboxSfDwnVfsMount,
+ vboxSfDwnVfsStart,
+ vboxSfDwnVfsUnmount,
+ vboxSfDwnVfsRoot,
+ NULL, /* Skipped: vfs_quotactl */
+ vboxSfDwnVfsGetAttr,
+ NULL, /* Skipped: vfs_sync */
+ NULL, /* Skipped: vfs_vget */
+ NULL, /* Skipped: vfs_fhtovp */
+ NULL, /* Skipped: vfs_vptofh */
+ NULL, /* Skipped: vfs_init */
+ NULL, /* Skipped: vfs_sysctl */
+ NULL, /* Skipped: vfs_setattr */
+ /* Reserved */
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, },
+};
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp
new file mode 100644
index 00000000..8d8388a2
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp
@@ -0,0 +1,261 @@
+/* $Id: VBoxSF.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, KEXT entry points.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static kern_return_t vboxSfDwnModuleLoad(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t vboxSfDwnModuleUnload(struct kmod_info *pKModInfo, void *pvData);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The VBoxGuest service if we've managed to connect to it already. */
+static IOService *g_pVBoxGuest = NULL;
+/** The shared folder service client structure. */
+VBGLSFCLIENT g_SfClientDarwin = { UINT32_MAX, NULL };
+/** Number of active mounts. Used for unload prevention. */
+uint32_t volatile g_cVBoxSfMounts = 0;
+
+/** VFS table entry for our file system (for vfs_fsremove). */
+static vfstable_t g_pVBoxSfVfsTableEntry;
+/** For vfs_fsentry. */
+static struct vnodeopv_desc *g_apVBoxSfVnodeOpDescList[] =
+{
+ &g_VBoxSfVnodeOpvDesc,
+};
+/** VFS registration structure. */
+static struct vfs_fsentry g_VBoxSfFsEntry =
+{
+ .vfe_vfsops = &g_VBoxSfVfsOps,
+ .vfe_vopcnt = RT_ELEMENTS(g_apVBoxSfVnodeOpDescList),
+ .vfe_opvdescs = g_apVBoxSfVnodeOpDescList,
+ .vfe_fstypenum = -1,
+ .vfe_fsname = VBOXSF_DARWIN_FS_NAME,
+ .vfe_flags = VFS_TBLTHREADSAFE /* Required. */
+ | VFS_TBLFSNODELOCK /* Required. */
+ | VFS_TBLNOTYPENUM /* No historic file system number. */
+ | VFS_TBL64BITREADY, /* Can handle 64-bit processes */
+ /** @todo add VFS_TBLREADDIR_EXTENDED */
+ .vfe_reserv = { NULL, NULL },
+};
+
+
+/**
+ * Declare the module stuff.
+ */
+RT_C_DECLS_BEGIN
+extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
+extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
+
+KMOD_EXPLICIT_DECL(VBoxSF, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = vboxSfDwnModuleLoad;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = vboxSfDwnModuleUnload;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+
+/**
+ * Connect to VBoxGuest and host shared folders service.
+ *
+ * @returns true if connected, false if not.
+ */
+bool vboxSfDwnConnect(void)
+{
+ /*
+ * Grab VBoxGuest - since it's a dependency of this module, it shouldn't be hard.
+ */
+ if (!g_pVBoxGuest)
+ {
+ OSDictionary *pServiceMatcher = IOService::serviceMatching("org_virtualbox_VBoxGuest");
+ if (pServiceMatcher)
+ {
+ IOService *pVBoxGuest = IOService::waitForMatchingService(pServiceMatcher, 10 * RT_NS_1SEC);
+ if (pVBoxGuest)
+ g_pVBoxGuest = pVBoxGuest;
+ else
+ LogRel(("vboxSfDwnConnect: IOService::waitForMatchingService failed!!\n"));
+ }
+ else
+ LogRel(("vboxSfDwnConnect: serviceMatching failed\n"));
+ }
+
+ if (g_pVBoxGuest)
+ {
+ /*
+ * Get hold of the shared folders service if we haven't already.
+ */
+ if (g_SfClientDarwin.handle != NULL)
+ return true;
+
+ int rc = VbglR0SfConnect(&g_SfClientDarwin);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfSetUtf8(&g_SfClientDarwin);
+ if (RT_SUCCESS(rc))
+ return true;
+
+ LogRel(("VBoxSF: VbglR0SfSetUtf8 failed: %Rrc\n", rc));
+
+ VbglR0SfDisconnect(&g_SfClientDarwin);
+ g_SfClientDarwin.handle = NULL;
+ }
+ else
+ LogRel(("VBoxSF: VbglR0SfConnect failed: %Rrc\n", rc));
+ }
+
+ return false;
+}
+
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t vboxSfDwnModuleLoad(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+#ifdef DEBUG
+ printf("vboxSfDwnModuleLoad\n");
+ RTLogBackdoorPrintf("vboxSfDwnModuleLoad\n");
+#endif
+
+ /*
+ * Initialize IPRT and the ring-0 guest library.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfInit();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register the file system.
+ */
+ rc = vfs_fsadd(&g_VBoxSfFsEntry, &g_pVBoxSfVfsTableEntry);
+ if (rc == 0)
+ {
+ /*
+ * Try find VBoxGuest and connect to the shared folders service on the host.
+ */
+ /** @todo should we just ignore the error here and retry at mount time?
+ * Technically, VBoxGuest should be available since it's one of our
+ * dependencies... */
+ vboxSfDwnConnect();
+
+ /*
+ * We're done for now. We'll deal with
+ */
+ LogRel(("VBoxSF: loaded\n"));
+ return KERN_SUCCESS;
+ }
+
+ printf("VBoxSF: vfs_fsadd failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: vfs_fsadd failed: %d\n", rc);
+ VbglR0SfTerm();
+ }
+ else
+ {
+ printf("VBoxSF: VbglR0SfInit failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: VbglR0SfInit failed: %Rrc\n", rc);
+ }
+ RTR0Term();
+ }
+ else
+ {
+ printf("VBoxSF: RTR0Init failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: RTR0Init failed: %Rrc\n", rc);
+ }
+ return KERN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t vboxSfDwnModuleUnload(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+#ifdef DEBUG
+ printf("vboxSfDwnModuleUnload\n");
+ RTLogBackdoorPrintf("vboxSfDwnModuleUnload\n");
+#endif
+
+
+ /*
+ * Are we busy? If so fail. Otherwise try deregister the file system.
+ */
+ if (g_cVBoxSfMounts > 0)
+ {
+ LogRel(("VBoxSF: Refusing to unload with %u active mounts\n", g_cVBoxSfMounts));
+ return KERN_NO_ACCESS;
+ }
+
+ if (g_pVBoxSfVfsTableEntry)
+ {
+ int rc = vfs_fsremove(g_pVBoxSfVfsTableEntry);
+ if (rc != 0)
+ {
+ LogRel(("VBoxSF: vfs_fsremove failed: %d\n", rc));
+ return KERN_NO_ACCESS;
+ }
+ }
+
+ /*
+ * Disconnect and terminate libraries we're using.
+ */
+ if (g_SfClientDarwin.handle != NULL)
+ {
+ VbglR0SfDisconnect(&g_SfClientDarwin);
+ g_SfClientDarwin.handle = NULL;
+ }
+
+ if (g_pVBoxGuest)
+ {
+ g_pVBoxGuest->release();
+ g_pVBoxGuest = NULL;
+ }
+
+ VbglR0SfTerm();
+ RTR0Term();
+ return KERN_SUCCESS;
+}
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h b/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h
new file mode 100644
index 00000000..6d297e82
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h
@@ -0,0 +1,117 @@
+/* $Id: VBoxSFInternal.h $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, internal header.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h
+#define GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxSFMount.h"
+
+#include <libkern/libkern.h>
+#include <iprt/types.h>
+#include <IOKit/IOLib.h>
+#include <IOKit/IOService.h>
+#include <mach/mach_port.h>
+#include <mach/kmod.h>
+#include <mach/mach_types.h>
+#include <sys/errno.h>
+#include <sys/dirent.h>
+#include <sys/lock.h>
+#include <sys/fcntl.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <vfs/vfs_support.h>
+#undef PVM
+
+#include <iprt/mem.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/VBoxGuestLibSharedFolders.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Private data we associate with a mount.
+ */
+typedef struct VBOXSFMNTDATA
+{
+ /** The shared folder mapping */
+ VBGLSFMAP hHostFolder;
+ /** The root VNode. */
+ vnode_t pVnRoot;
+ /** User that mounted shared folder (anyone but root?). */
+ uid_t uidMounter;
+ /** The mount info from the mount() call. */
+ VBOXSFDRWNMOUNTINFO MntInfo;
+} VBOXSFMNTDATA;
+/** Pointer to private mount data. */
+typedef VBOXSFMNTDATA *PVBOXSFMNTDATA;
+
+/**
+ * Private data we associate with a VNode.
+ */
+typedef struct VBOXSFDWNVNDATA
+{
+ /** The handle to the host object. */
+ SHFLHANDLE hHandle;
+ ///PSHFLSTRING pPath; /** Path within shared folder */
+ ///lck_attr_t *pLockAttr; /** BSD locking stuff */
+ ///lck_rw_t *pLock; /** BSD locking stuff */
+} VBOXSFDWNVNDATA;
+/** Pointer to private vnode data. */
+typedef VBOXSFDWNVNDATA *PVBOXSFDWNVNDATA;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern VBGLSFCLIENT g_SfClientDarwin;
+extern uint32_t volatile g_cVBoxSfMounts;
+extern struct vfsops g_VBoxSfVfsOps;
+extern struct vnodeopv_desc g_VBoxSfVnodeOpvDesc;
+extern int (**g_papfnVBoxSfDwnVnDirOpsVector)(void *);
+
+
+
+/*********************************************************************************************************************************
+* Functions *
+*********************************************************************************************************************************/
+bool vboxSfDwnConnect(void);
+vnode_t vboxSfDwnVnAlloc(mount_t pMount, enum vtype enmType, vnode_t pParent, uint64_t cbFile);
+
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h */
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h b/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h
new file mode 100644
index 00000000..318d6e42
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h
@@ -0,0 +1,54 @@
+/* $Id: VBoxSFMount.h $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, mount interface.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h
+#define GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/** The shared folders file system name. */
+#define VBOXSF_DARWIN_FS_NAME "vboxsf"
+
+/**
+ * Mount information that gets passed from userland on mount.
+ */
+typedef struct VBOXSFDRWNMOUNTINFO
+{
+ /** Magic value (VBOXSFDRWNMOUNTINFO_MAGIC). */
+ uint32_t u32Magic;
+ /** The shared folder name. */
+ char szFolder[260];
+} VBOXSFDRWNMOUNTINFO;
+typedef VBOXSFDRWNMOUNTINFO *PVBOXSFDRWNMOUNTINFO;
+/** Magic value for VBOXSFDRWNMOUNTINFO::u32Magic. */
+#define VBOXSFDRWNMOUNTINFO_MAGIC UINT32_C(0xc001cafe)
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h */
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp b/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp
new file mode 100644
index 00000000..57db36bc
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp
@@ -0,0 +1,97 @@
+/* $Id: mount.vboxsf.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, Mount Utility.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+#include "VBoxSFMount.h"
+#include <iprt/string.h>
+
+
+static RTEXITCODE usage(const char *pszArg0)
+{
+ fprintf(stderr, "usage: %s [OPTIONS] <shared folder name> <mount point>\n", pszArg0);
+ return RTEXITCODE_SYNTAX;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Skip past parameters.
+ */
+ int c;
+ while ((c = getopt(argc, argv, "o:")) != -1)
+ {
+ switch (c)
+ {
+ case 'o':
+ break;
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ /* Two arguments are rquired: <share name> and <mount point> */
+ if (argc - optind != 2)
+ return usage(argv[0]);
+ const char * const pszFolder = argv[optind++];
+ const char * const pszMountPoint = argv[optind];
+
+ /*
+ * Check that the folder is within bounds and doesn't include any shady characters.
+ */
+ size_t cchFolder = strlen(pszFolder);
+ if ( cchFolder < 1
+ || cchFolder >= RT_SIZEOFMEMB(VBOXSFDRWNMOUNTINFO, szFolder)
+ || strpbrk(pszFolder, "\\/") != NULL)
+ {
+ fprintf(stderr, "Invalid shared folder name '%s'!\n", pszFolder);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Do the mounting.
+ */
+ VBOXSFDRWNMOUNTINFO MntInfo;
+ RT_ZERO(MntInfo);
+ MntInfo.u32Magic = VBOXSFDRWNMOUNTINFO_MAGIC;
+ memcpy(MntInfo.szFolder, pszFolder, cchFolder);
+ int rc = mount(VBOXSF_DARWIN_FS_NAME, pszMountPoint, 0, &MntInfo);
+ if (rc == 0)
+ return 0;
+
+ fprintf(stderr, "error mounting '%s' at '%s': %s (%d)\n", pszFolder, pszMountPoint, strerror(errno), errno);
+ return RTEXITCODE_FAILURE;
+}