diff options
Diffstat (limited to 'src/VBox/Additions/darwin/VBoxSF')
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/.scm-settings | 29 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/Info.plist | 44 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/Makefile.kmk | 80 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp | 608 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp | 843 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp | 639 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp | 261 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h | 117 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h | 54 | ||||
-rw-r--r-- | src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp | 97 |
10 files changed, 2772 insertions, 0 deletions
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; +} |