diff options
Diffstat (limited to 'src/VBox/Additions/darwin')
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') >= 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') < 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, ×pec); VATTR_RETURN(vnode_args, va_create_time, timespec); + RTTimeSpecGetTimespec(&Info.AccessTime, ×pec); VATTR_RETURN(vnode_args, va_access_time, timespec); + RTTimeSpecGetTimespec(&Info.ModificationTime, ×pec); VATTR_RETURN(vnode_args, va_modify_time, timespec); + RTTimeSpecGetTimespec(&Info.ChangeTime, ×pec); 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; +} |