summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/darwin/VBoxSF
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/darwin/VBoxSF')
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/.scm-settings29
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/Info.plist44
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/Makefile.kmk80
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-Utils.cpp608
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-VNodeOps.cpp843
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp639
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp261
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h117
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h54
-rw-r--r--src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp97
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, &timespec); VATTR_RETURN(vnode_args, va_create_time, timespec);
+ RTTimeSpecGetTimespec(&Info.AccessTime, &timespec); VATTR_RETURN(vnode_args, va_access_time, timespec);
+ RTTimeSpecGetTimespec(&Info.ModificationTime, &timespec); VATTR_RETURN(vnode_args, va_modify_time, timespec);
+ RTTimeSpecGetTimespec(&Info.ChangeTime, &timespec); VATTR_RETURN(vnode_args, va_change_time, timespec);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_backup_time);
+
+ /* Set owner info. */
+ VATTR_RETURN(vnode_args, va_uid, pMount->owner);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_gid);
+
+ /* Access mode and flags */
+ VATTR_RETURN(vnode_args, va_mode, vboxvfs_h2g_mode_inernal(Info.Attr.fMode));
+ VATTR_RETURN(vnode_args, va_flags, Info.Attr.u.Unix.fFlags);
+
+ /* The current generation number (0 if this information is not available) */
+ VATTR_RETURN(vnode_args, va_gen, Info.Attr.u.Unix.GenerationId);
+
+ VATTR_RETURN(vnode_args, va_rdev, 0);
+ VATTR_RETURN(vnode_args, va_nlink, 2);
+
+ VATTR_RETURN(vnode_args, va_data_size, sizeof(struct dirent)); /* Size of data returned per each readdir() request */
+
+ /* Hope, when it overflows nothing catastrophical will heppen! If we will not assign
+ * a uniq va_fileid to each vnode, `ls`, 'find' (and simmilar tools that uses fts_read() calls) will think that
+ * each sub-directory is self-cycled. */
+ VATTR_RETURN(vnode_args, va_fileid, (pMount->cFileIdCounter++));
+
+ /* Not supported */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_linkid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_parentid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_fsid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_filerev);
+
+ /* Not present on 10.6 */
+ //VATTR_CLEAR_ACTIVE(vnode_args, va_addedtime);
+
+ /** @todo take care about va_encoding (file name encoding) */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_encoding);
+ /** @todo take care about: va_acl */
+ VATTR_CLEAR_ACTIVE(vnode_args, va_acl);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_name);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_uuuid);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_guuid);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_total_size);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_total_alloc);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_data_alloc);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_iosize);
+
+ VATTR_CLEAR_ACTIVE(vnode_args, va_nchildren);
+ VATTR_CLEAR_ACTIVE(vnode_args, va_dirlinkcount);
+ }
+ else
+ {
+ PDEBUG("getattr: unable to get VBoxVFS object info");
+ }
+
+ lck_rw_unlock_shared(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+#if 0
+/**
+ * Helper function for vboxvfs_vnode_lookup(): create new vnode.
+ */
+static int
+vboxvfs_vnode_lookup_instantinate_vnode(vnode_t parent_vnode, char *entry_name, vnode_t *result_vnode)
+{
+ /* We need to construct full path to vnode in order to get
+ * vboxvfs_get_info_internal() to understand us! */
+
+ char *pszCurDirPath;
+ int cbCurDirPath = MAXPATHLEN;
+
+ mount_t mp = vnode_mount(parent_vnode); AssertReturn(mp, EINVAL);
+ vnode_t vnode;
+
+ int rc;
+
+ pszCurDirPath = (char *)RTMemAllocZ(cbCurDirPath);
+ if (pszCurDirPath)
+ {
+ rc = vn_getpath(parent_vnode, pszCurDirPath, &cbCurDirPath);
+ if (rc == 0 && cbCurDirPath < MAXPATHLEN)
+ {
+ SHFLFSOBJINFO Info;
+ PSHFLSTRING pSHFLPath;
+
+ /* Add '/' between path parts and truncate name if it is too long */
+ strncat(pszCurDirPath, "/", 1); strncat(pszCurDirPath, entry_name, MAXPATHLEN - cbCurDirPath - 1);
+
+ rc = vboxvfs_guest_path_to_shflstring_path_internal(mp, pszCurDirPath, strlen(pszCurDirPath) + 1, &pSHFLPath);
+ if (rc == 0)
+ {
+ rc = vboxvfs_get_info_internal(mp, pSHFLPath, (PSHFLFSOBJINFO)&Info);
+ if (rc == 0)
+ {
+ enum vtype type;
+
+ if (RTFS_IS_DIRECTORY(Info.Attr.fMode)) type = VDIR;
+ else if (RTFS_IS_FILE (Info.Attr.fMode)) type = VREG;
+ else
+ {
+ PDEBUG("Not supported VFS object (%s) type: mode 0x%X",
+ entry_name,
+ Info.Attr.fMode);
+
+ RTMemFree(pszCurDirPath);
+ vboxvfs_put_path_internal((void **)&pSHFLPath);
+ return ENOENT;
+ }
+ /* Create new vnode */
+ rc = vboxvfs_create_vnode_internal(mp, type, parent_vnode, FALSE, pSHFLPath, &vnode);
+ if (rc == 0)
+ {
+ PDEBUG("new vnode object '%s' has been created", entry_name);
+
+ *result_vnode = vnode;
+ RTMemFree(pszCurDirPath);
+
+ return 0;
+ }
+ else
+ PDEBUG("Unable to create vnode: %d", rc);
+ }
+ else
+ PDEBUG("Unable to get host object info: %d", rc);
+
+ vboxvfs_put_path_internal((void **)&pSHFLPath);
+ }
+ else
+ PDEBUG("Unable to convert guest<->host path");
+ }
+ else
+ PDEBUG("Unable to construct vnode path: %d", rc);
+
+ RTMemFree(pszCurDirPath);
+ }
+ else
+ {
+ PDEBUG("Unable to allocate memory for path buffer");
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/**
+ * Helper function for vboxvfs_vnode_lookup(): take care
+ * about '.' and '..' directory entries.
+ */
+static int
+vboxvfs_vnode_lookup_dot_handler(struct vnop_lookup_args *pArgs, vnode_t *result_vnode)
+{
+ vnode_t vnode = NULL;
+
+ if (pArgs->a_cnp->cn_flags & ISDOTDOT)
+ {
+ vnode = vnode_getparent(pArgs->a_dvp);
+ if (vnode)
+ {
+ PDEBUG("return parent directory");
+ *result_vnode = vnode;
+ return 0;
+ }
+ else
+ {
+ PDEBUG("return parent directory not found, return current directory");
+ *result_vnode = pArgs->a_dvp;
+ return 0;
+ }
+ }
+ else if ((strncmp(pArgs->a_cnp->cn_nameptr, ".", 1) == 0) &&
+ pArgs->a_cnp->cn_namelen == 1)
+ {
+ PDEBUG("return current directory");
+ *result_vnode = pArgs->a_dvp;
+ return 0;
+ }
+
+ return ENOENT;
+}
+#endif
+
+static int vboxSfDwnVnLookup(struct vnop_lookup_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ int rc;
+
+ vnode_t vnode;
+ vboxvfs_vnode_t *pVnodeData;
+
+ PDEBUG("Looking up for vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+ AssertReturn(pArgs->a_dvp, EINVAL);
+ AssertReturn(vnode_isdir(pArgs->a_dvp), EINVAL);
+ AssertReturn(pArgs->a_cnp, EINVAL);
+ AssertReturn(pArgs->a_cnp->cn_nameptr, EINVAL);
+ AssertReturn(pArgs->a_vpp, EINVAL);
+
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(pArgs->a_dvp);
+ AssertReturn(pVnodeData, EINVAL);
+ AssertReturn(pVnodeData->pLock, EINVAL);
+
+ /*
+ todo: take care about pArgs->a_cnp->cn_nameiop
+ */
+
+ if (pArgs->a_cnp->cn_nameiop == LOOKUP) PDEBUG("LOOKUP");
+ else if (pArgs->a_cnp->cn_nameiop == CREATE) PDEBUG("CREATE");
+ else if (pArgs->a_cnp->cn_nameiop == RENAME) PDEBUG("RENAME");
+ else if (pArgs->a_cnp->cn_nameiop == DELETE) PDEBUG("DELETE");
+ else PDEBUG("Unknown cn_nameiop: 0x%X", (int)pArgs->a_cnp->cn_nameiop);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ /* Take care about '.' and '..' entries */
+ if (vboxvfs_vnode_lookup_dot_handler(pArgs, &vnode) == 0)
+ {
+ vnode_get(vnode);
+ *pArgs->a_vpp = vnode;
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return 0;
+ }
+
+ /* Look into VFS cache and attempt to find previously allocated vnode there. */
+ rc = cache_lookup(pArgs->a_dvp, &vnode, pArgs->a_cnp);
+ if (rc == -1) /* Record found */
+ {
+ PDEBUG("Found record in VFS cache");
+
+ /* Check if VFS object still exist on a host side */
+ if (vboxvfs_exist_internal(vnode))
+ {
+ /* Prepare & return cached vnode */
+ vnode_get(vnode);
+ *pArgs->a_vpp = vnode;
+
+ rc = 0;
+ }
+ else
+ {
+ /* If vnode exist in guets VFS cache, but not exist on a host -- just forget it. */
+ cache_purge(vnode);
+ /** @todo free vnode data here */
+ rc = ENOENT;
+ }
+ }
+ else
+ {
+ PDEBUG("cache_lookup() returned %d, create new VFS vnode", rc);
+
+ rc = vboxvfs_vnode_lookup_instantinate_vnode(pArgs->a_dvp, pArgs->a_cnp->cn_nameptr, &vnode);
+ if (rc == 0)
+ {
+ cache_enter(pArgs->a_dvp, vnode, pArgs->a_cnp);
+ *pArgs->a_vpp = vnode;
+ }
+ else
+ {
+ rc = ENOENT;
+ }
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+static int vboxSfDwnVnOpen(struct vnop_open_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ vnode_t vnode;
+ vboxvfs_vnode_t *pVnodeData;
+ uint32_t fHostFlags;
+ mount_t mp;
+ vboxvfs_mount_t *pMount;
+
+ int rc;
+
+ PDEBUG("Opening vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ if (vnode_isinuse(vnode, 0))
+ {
+ PDEBUG("vnode '%s' (handle 0x%X) already has VBoxVFS object handle assigned, just return ok",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return 0;
+ }
+
+ /* At this point we must make sure that nobody is using VBoxVFS object handle */
+ //if (pVnodeData->Handle != SHFL_HANDLE_NIL)
+ //{
+ // PDEBUG("vnode has active VBoxVFS object handle set, aborting");
+ // lck_rw_unlock_exclusive(pVnodeData->pLock);
+ // return EINVAL;
+ //}
+
+ fHostFlags = vboxvfs_g2h_mode_inernal(pArgs->a_mode);
+ fHostFlags |= (vnode_isdir(vnode) ? SHFL_CF_DIRECTORY : 0);
+
+ SHFLHANDLE Handle;
+ rc = vboxvfs_open_internal(pMount, pVnodeData->pPath, fHostFlags, &Handle);
+ if (rc == 0)
+ {
+ PDEBUG("Open success: '%s' (handle 0x%X)",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)Handle);
+
+ pVnodeData->pHandle = Handle;
+ }
+ else
+ {
+ PDEBUG("Unable to open: '%s': %d",
+ (char *)pVnodeData->pPath->String.utf8,
+ rc);
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+static int vboxSfDwnVnClose(struct vnop_close_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+
+ vnode_t vnode;
+ mount_t mp;
+ vboxvfs_vnode_t *pVnodeData;
+ vboxvfs_mount_t *pMount;
+
+ int rc;
+
+ PDEBUG("Closing vnode...");
+
+ AssertReturn(pArgs, EINVAL);
+
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_exclusive(pVnodeData->pLock);
+
+ if (vnode_isinuse(vnode, 0))
+ {
+ PDEBUG("vnode '%s' (handle 0x%X) is still in use, just return ok",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return 0;
+ }
+
+ /* At this point we must make sure that vnode has VBoxVFS object handle assigned */
+ if (pVnodeData->pHandle == SHFL_HANDLE_NIL)
+ {
+ PDEBUG("vnode has no active VBoxVFS object handle set, aborting");
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+ return EINVAL;
+ }
+
+ rc = vboxvfs_close_internal(pMount, pVnodeData->pHandle);
+ if (rc == 0)
+ {
+ PDEBUG("Close success: '%s' (handle 0x%X)",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle);
+
+ /* Forget about previously assigned VBoxVFS object handle */
+ pVnodeData->pHandle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ PDEBUG("Unable to close: '%s' (handle 0x%X): %d",
+ (char *)pVnodeData->pPath->String.utf8,
+ (int)pVnodeData->pHandle, rc);
+ }
+
+ lck_rw_unlock_exclusive(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+#if 0
+/**
+ * Convert SHFLDIRINFO to struct dirent and copy it back to user.
+ */
+static int
+vboxvfs_vnode_readdir_copy_data(ino_t index, SHFLDIRINFO *Info, struct uio *uio, int *numdirent)
+{
+ struct dirent entry;
+
+ int rc;
+
+ entry.d_ino = index;
+ entry.d_reclen = (__uint16_t)sizeof(entry);
+
+ /* Detect dir entry type */
+ if (RTFS_IS_DIRECTORY(Info->Info.Attr.fMode))
+ entry.d_type = DT_DIR;
+ else if (RTFS_IS_FILE(Info->Info.Attr.fMode))
+ entry.d_type = DT_REG;
+ else
+ {
+ PDEBUG("Unknown type of host file: mode 0x%X", (int)Info->Info.Attr.fMode);
+ return ENOTSUP;
+ }
+
+ entry.d_namlen = (__uint8_t)min(sizeof(entry.d_name), Info->name.u16Size);
+ memcpy(entry.d_name, Info->name.String.utf8, entry.d_namlen);
+
+ rc = uiomove((char *)&entry, sizeof(entry), uio);
+ if (rc == 0)
+ {
+ uio_setoffset(uio, index * sizeof(struct dirent));
+ *numdirent = (int)index;
+
+ PDEBUG("discovered entry: '%s' (%d bytes), item #%d", entry.d_name, (int)entry.d_namlen, (int)index);
+ }
+ else
+ {
+ PDEBUG("Failed to return dirent data item #%d (%d)", (int)index, rc);
+ }
+
+ return rc;
+}
+#endif
+
+static int vboxSfDwnVnReadDir(struct vnop_readdir_args *pArgs)
+{
+#if 1
+ RT_NOREF(pArgs);
+ return ENOTSUP;
+#else
+ vboxvfs_mount_t *pMount;
+ vboxvfs_vnode_t *pVnodeData;
+ SHFLDIRINFO *Info;
+ uint32_t cbInfo;
+ mount_t mp;
+ vnode_t vnode;
+ struct uio *uio;
+
+ int rc = 0, rc2;
+
+ PDEBUG("Reading directory...");
+
+ AssertReturn(pArgs, EINVAL);
+ AssertReturn(pArgs->a_eofflag, EINVAL);
+ AssertReturn(pArgs->a_numdirent, EINVAL);
+
+ uio = pArgs->a_uio; AssertReturn(uio, EINVAL);
+ vnode = pArgs->a_vp; AssertReturn(vnode, EINVAL); AssertReturn(vnode_isdir(vnode), EINVAL);
+ pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL);
+ mp = vnode_mount(vnode); AssertReturn(mp, EINVAL);
+ pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL);
+
+ lck_rw_lock_shared(pVnodeData->pLock);
+
+ cbInfo = sizeof(Info) + MAXPATHLEN;
+ Info = (SHFLDIRINFO *)RTMemAllocZ(cbInfo);
+ if (!Info)
+ {
+ PDEBUG("No memory to allocate internal data");
+ lck_rw_unlock_shared(pVnodeData->pLock);
+ return ENOMEM;
+ }
+
+ uint32_t index = (uint32_t)uio_offset(uio) / (uint32_t)sizeof(struct dirent);
+ uint32_t cFiles = 0;
+
+ PDEBUG("Exploring VBoxVFS directory (%s), handle (0x%.8X), offset (0x%X), count (%d)", (char *)pVnodeData->pPath->String.utf8, (int)pVnodeData->pHandle, index, uio_iovcnt(uio));
+
+ /* Currently, there is a problem when VbglR0SfDirInfo() is not able to
+ * continue retrieve directory content if the same VBoxVFS handle is used.
+ * This macro forces to use a new handle in readdir() callback. If enabled,
+ * the original handle (obtained in open() callback is ignored). */
+
+ SHFLHANDLE Handle;
+ rc = vboxvfs_open_internal(pMount,
+ pVnodeData->pPath,
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW,
+ &Handle);
+ if (rc != 0)
+ {
+ PDEBUG("Unable to open dir: %d", rc);
+ RTMemFree(Info);
+ lck_rw_unlock_shared(pVnodeData->pLock);
+ return rc;
+ }
+
+#if 0
+ rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0, 0, index, &cbInfo, (PSHFLDIRINFO)Info, &cFiles);
+#else
+ SHFLSTRING *pMask = vboxvfs_construct_shflstring("*", strlen("*"));
+ if (pMask)
+ {
+ for (uint32_t cSkip = 0; (cSkip < index + 1) && (rc == VINF_SUCCESS); cSkip++)
+ {
+ //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0 /* pMask */, 0 /* SHFL_LIST_RETURN_ONE */, 0, &cbInfo, (PSHFLDIRINFO)Info, &cFiles);
+
+ uint32_t cbReturned = cbInfo;
+ //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, pMask, SHFL_LIST_RETURN_ONE, 0, &cbReturned, (PSHFLDIRINFO)Info, &cFiles);
+ rc = VbglR0SfDirInfo(&g_SfClientDarwin, &pMount->pMap, Handle, 0, SHFL_LIST_RETURN_ONE, 0,
+ &cbReturned, (PSHFLDIRINFO)Info, &cFiles);
+
+ }
+
+ PDEBUG("read %d files", cFiles);
+ RTMemFree(pMask);
+ }
+ else
+ {
+ PDEBUG("Can't alloc mask");
+ rc = ENOMEM;
+ }
+#endif
+ rc2 = vboxvfs_close_internal(pMount, Handle);
+ if (rc2 != 0)
+ {
+ PDEBUG("Unable to close directory: %s: %d",
+ pVnodeData->pPath->String.utf8,
+ rc2);
+ }
+
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ {
+ rc = vboxvfs_vnode_readdir_copy_data((ino_t)(index + 1), Info, uio, pArgs->a_numdirent);
+ break;
+ }
+
+ case VERR_NO_MORE_FILES:
+ {
+ PDEBUG("No more entries in directory");
+ *(pArgs->a_eofflag) = 1;
+ break;
+ }
+
+ default:
+ {
+ PDEBUG("VbglR0SfDirInfo() for item #%d has failed: %d", (int)index, (int)rc);
+ rc = EINVAL;
+ break;
+ }
+ }
+
+ RTMemFree(Info);
+ lck_rw_unlock_shared(pVnodeData->pLock);
+
+ return rc;
+#endif
+}
+
+
+static int vboxSfDwnVnPathConf(struct vnop_pathconf_args *pArgs)
+{
+ Log(("vboxSfDwnVnPathConf:\n"));
+ RT_NOREF(pArgs);
+ return 0;
+}
+
+
+/**
+ * vnop_reclaim implementation.
+ *
+ * VBoxVFS reclaim callback.
+ * Called when vnode is going to be deallocated. Should release
+ * all the VBoxVFS resources that correspond to current vnode object.
+ *
+ * @param pArgs Operation arguments passed from VFS layer.
+ *
+ * @return 0 on success, BSD error code otherwise.
+ */
+static int vboxSfDwnVnReclaim(struct vnop_reclaim_args *pArgs)
+{
+ AssertReturn(pArgs && pArgs->a_vp, EINVAL);
+
+ /* Check that it's not a root node that's in use. */
+ PVBOXSFMNTDATA pMntData = (PVBOXSFMNTDATA)vfs_fsprivate(vnode_mount(pArgs->a_vp));
+ AssertReturn(!pMntData || pMntData->pVnRoot != pArgs->a_vp, EBUSY);
+
+ /* Get the private data and free it. */
+ PVBOXSFDWNVNDATA pVnData = (PVBOXSFDWNVNDATA)vnode_fsnode(pArgs->a_vp);
+ AssertPtrReturn(pVnData, 0);
+
+ if (pVnData->hHandle != SHFL_HANDLE_NIL)
+ {
+ /** @todo can this happen? */
+ pVnData->hHandle = SHFL_HANDLE_NIL;
+ }
+
+ RTMemFree(pVnData);
+ return 0;
+}
+
+
+/**
+ * Allocates a vnode.
+ *
+ * @returns Pointer to the new VNode, NULL if out of memory.
+ * @param pMount The file system mount structure.
+ * @param enmType The vnode type.
+ * @param pParent The parent vnode, NULL if root.
+ * @param cbFile The file size
+ */
+vnode_t vboxSfDwnVnAlloc(mount_t pMount, enum vtype enmType, vnode_t pParent, uint64_t cbFile)
+{
+ /*
+ * Create our private data.
+ */
+ PVBOXSFDWNVNDATA pVnData = (PVBOXSFDWNVNDATA)RTMemAllocZ(sizeof(*pVnData));
+ if (pVnData)
+ {
+ pVnData->hHandle = SHFL_HANDLE_NIL;
+
+ struct vnode_fsparam VnParms;
+ RT_ZERO(VnParms);
+ VnParms.vnfs_mp = pMount;
+ VnParms.vnfs_vtype = enmType;
+ VnParms.vnfs_str = "vboxsf";
+ VnParms.vnfs_dvp = pParent;
+ VnParms.vnfs_fsnode = pVnData;
+ VnParms.vnfs_vops = g_papfnVBoxSfDwnVnDirOpsVector;
+ VnParms.vnfs_markroot = pParent == NULL;
+ VnParms.vnfs_marksystem = 0;
+ VnParms.vnfs_rdev = 0;
+ VnParms.vnfs_filesize = cbFile;
+ VnParms.vnfs_cnp = 0;
+ VnParms.vnfs_flags = VNFS_NOCACHE;
+
+ vnode_t pVnRet;
+ int rc = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &VnParms, &pVnRet);
+ if (rc == 0)
+ return pVnRet;
+ RTMemFree(pVnData);
+ }
+ printf("vboxSfDwnVnAlloc: out of memory!\n");
+ return NULL;
+}
+
+
+/**
+ * Vnode operations.
+ */
+static struct vnodeopv_entry_desc g_VBoxSfDirOpsDescList[] =
+{
+#define VNODEOPFUNC int(*)(void *)
+ { &vnop_default_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_access_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - probably not needed.
+ //{ &vnop_advlock_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - later.
+ //{ &vnop_allocate_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - maybe, need shfl function
+ { &vnop_blktooff_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_blockmap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_bwrite_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_close_desc, (VNODEOPFUNC)vboxSfDwnVnClose },
+ //{ &vnop_copyfile_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_create_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_exchange_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_fsync_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_getattr_desc, (VNODEOPFUNC)vboxFsDwnVnGetAttr },
+ //{ &vnop_getnamedstream_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_getxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_inactive_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_ioctl_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_link_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_listxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_lookup_desc, (VNODEOPFUNC)vboxSfDwnVnLookup },
+ { &vnop_mkdir_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mknod_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mmap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_mnomap_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_offtoblk_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_open_desc, (VNODEOPFUNC)vboxSfDwnVnOpen },
+ { &vnop_pagein_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_pageout_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_pathconf_desc, (VNODEOPFUNC)vboxSfDwnVnPathConf },
+ /* { &vnop_print_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, undefined in ML */
+ { &vnop_read_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_readdir_desc, (VNODEOPFUNC)vboxSfDwnVnReadDir },
+ //{ &vnop_readdirattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - hfs specific.
+ { &vnop_readlink_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_reclaim_desc, (VNODEOPFUNC)vboxSfDwnVnReclaim },
+ { &vnop_remove_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_removexattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_rename_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_revoke_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_rmdir_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_searchfs_desc, (VNODEOPFUNC)err_searchfs },
+ //{ &vnop_select_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_setattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { &vnop_setxattr_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ //{ &vnop_strategy_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed
+ { &vnop_symlink_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ /* { &vnop_truncate_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, undefined in ML */
+ //{ &vnop_whiteout_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError }, - not needed/supported
+ { &vnop_write_desc, (VNODEOPFUNC)vboxSfDwnVnDefaultError },
+ { NULL, (VNODEOPFUNC)NULL },
+#undef VNODEOPFUNC
+};
+
+/** ??? */
+int (**g_papfnVBoxSfDwnVnDirOpsVector)(void *);
+
+/**
+ * VNode operation descriptors.
+ */
+struct vnodeopv_desc g_VBoxSfVnodeOpvDesc =
+{
+ &g_papfnVBoxSfDwnVnDirOpsVector,
+ g_VBoxSfDirOpsDescList
+};
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp
new file mode 100644
index 00000000..e1c9c486
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp
@@ -0,0 +1,639 @@
+/* $Id: VBoxSF-VfsOps.cpp $ */
+/** @file
+ * VBoxFS - Darwin Shared Folders, Virtual File System Operations.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <VBox/log.h>
+
+
+
+/**
+ * vfsops::vfs_getattr implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param pFsAttr Input & output structure.
+ * @param pContext Unused kAuth parameter.
+ */
+static int vboxSfDwnVfsGetAttr(mount_t pMount, struct vfs_attr *pFsAttr, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, EBADMSG);
+ LogFlow(("vboxSfDwnVfsGetAttr: %s\n", pThis->MntInfo.szFolder));
+ RT_NOREF(pContext);
+
+ /*
+ * Get the file system stats from the host.
+ */
+ int rc;
+ struct MyEmbReq
+ {
+ VBGLIOCIDCHGCMFASTCALL Hdr;
+ VMMDevHGCMCall Call;
+ VBoxSFParmInformation Parms;
+ SHFLVOLINFO VolInfo;
+ } *pReq = (struct MyEmbReq *)VbglR0PhysHeapAlloc(sizeof(*pReq));
+ if (pReq)
+ {
+ RT_ZERO(pReq->VolInfo);
+
+ VBGLIOCIDCHGCMFASTCALL_INIT(&pReq->Hdr, VbglR0PhysHeapGetPhysAddr(pReq), &pReq->Call, g_SfClientDarwin.idClient,
+ SHFL_FN_INFORMATION, SHFL_CPARMS_INFORMATION, sizeof(*pReq));
+ pReq->Parms.id32Root.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.id32Root.u.value32 = pThis->hHostFolder.root;
+ pReq->Parms.u64Handle.type = VMMDevHGCMParmType_64bit;
+ pReq->Parms.u64Handle.u.value64 = 0;
+ pReq->Parms.f32Flags.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.f32Flags.u.value32 = SHFL_INFO_VOLUME | SHFL_INFO_GET;
+ pReq->Parms.cb32.type = VMMDevHGCMParmType_32bit;
+ pReq->Parms.cb32.u.value32 = sizeof(pReq->VolInfo);
+ pReq->Parms.pInfo.type = VMMDevHGCMParmType_Embedded;
+ pReq->Parms.pInfo.u.Embedded.cbData = sizeof(pReq->VolInfo);
+ pReq->Parms.pInfo.u.Embedded.offData = RT_UOFFSETOF(struct MyEmbReq, VolInfo) - sizeof(VBGLIOCIDCHGCMFASTCALL);
+ pReq->Parms.pInfo.u.Embedded.fFlags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
+
+ int vrc = VbglR0HGCMFastCall(g_SfClientDarwin.handle, &pReq->Hdr, sizeof(*pReq));
+ if (RT_SUCCESS(vrc))
+ vrc = pReq->Call.header.result;
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Fill in stuff.
+ */
+ /* Copy over the results we got from the host. */
+ uint32_t cbUnit = pReq->VolInfo.ulBytesPerSector * pReq->VolInfo.ulBytesPerAllocationUnit;
+ VFSATTR_RETURN(pFsAttr, f_bsize, cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_iosize, _64K); /** @todo what's a good block size... */
+ VFSATTR_RETURN(pFsAttr, f_blocks, (uint64_t)pReq->VolInfo.ullTotalAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bavail, (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bfree, (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes / cbUnit);
+ VFSATTR_RETURN(pFsAttr, f_bused,
+ ((uint64_t)pReq->VolInfo.ullTotalAllocationBytes - (uint64_t)pReq->VolInfo.ullAvailableAllocationBytes) / cbUnit);
+ fsid_t const fsid = { { vfs_statfs(pMount)->f_fsid.val[0], vfs_typenum(pMount) } };
+ VFSATTR_RETURN(pFsAttr, f_fsid, fsid);
+
+ /* f_owner is handled by caller. */
+ /* f_signature is handled by caller. */
+
+ struct timespec TmpTv = { 1084190406, 0 };
+ VFSATTR_RETURN(pFsAttr, f_create_time, TmpTv);
+
+ /*
+ * Unsupported bits.
+ */
+ /* Dummies for some values we don't support. */
+ VFSATTR_RETURN(pFsAttr, f_objcount, 0);
+ VFSATTR_RETURN(pFsAttr, f_filecount, 0);
+ VFSATTR_RETURN(pFsAttr, f_dircount, 0);
+ VFSATTR_RETURN(pFsAttr, f_maxobjcount, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_files, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_ffree, UINT32_MAX);
+ VFSATTR_RETURN(pFsAttr, f_fssubtype, 0);
+ VFSATTR_RETURN(pFsAttr, f_carbon_fsid, 0);
+
+ /* Totally not supported: */
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_modify_time);
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_access_time);
+ VFSATTR_CLEAR_ACTIVE(pFsAttr, f_backup_time);
+
+ /*
+ * Annoying capability stuff.
+ * The 'valid' bits are only supposed to be set when we know for sure.
+ */
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_capabilities))
+ {
+ vol_capabilities_attr_t *pCaps = &pFsAttr->f_capabilities;
+
+ pCaps->valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS
+ | VOL_CAP_FMT_SYMBOLICLINKS
+ | VOL_CAP_FMT_HARDLINKS
+ | VOL_CAP_FMT_JOURNAL
+ | VOL_CAP_FMT_JOURNAL_ACTIVE
+ | VOL_CAP_FMT_NO_ROOT_TIMES
+ | VOL_CAP_FMT_SPARSE_FILES
+ | VOL_CAP_FMT_ZERO_RUNS
+ | VOL_CAP_FMT_CASE_SENSITIVE
+ | VOL_CAP_FMT_CASE_PRESERVING
+ | VOL_CAP_FMT_FAST_STATFS
+ | VOL_CAP_FMT_2TB_FILESIZE
+ | VOL_CAP_FMT_OPENDENYMODES
+ | VOL_CAP_FMT_HIDDEN_FILES
+ | VOL_CAP_FMT_PATH_FROM_ID
+ | VOL_CAP_FMT_NO_VOLUME_SIZES
+ | VOL_CAP_FMT_DECMPFS_COMPRESSION
+ | VOL_CAP_FMT_64BIT_OBJECT_IDS;
+ pCaps->capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_2TB_FILESIZE
+ /// @todo | VOL_CAP_FMT_SYMBOLICLINKS - later
+ /// @todo | VOL_CAP_FMT_SPARSE_FILES - probably, needs testing.
+ /*| VOL_CAP_FMT_CASE_SENSITIVE - case-insensitive */
+ | VOL_CAP_FMT_CASE_PRESERVING
+ /// @todo | VOL_CAP_FMT_HIDDEN_FILES - if windows host.
+ /// @todo | VOL_CAP_FMT_OPENDENYMODES - if windows host.
+ ;
+ pCaps->valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_SEARCHFS
+ | VOL_CAP_INT_ATTRLIST
+ | VOL_CAP_INT_NFSEXPORT
+ | VOL_CAP_INT_READDIRATTR
+ | VOL_CAP_INT_EXCHANGEDATA
+ | VOL_CAP_INT_COPYFILE
+ | VOL_CAP_INT_ALLOCATE
+ | VOL_CAP_INT_VOL_RENAME
+ | VOL_CAP_INT_ADVLOCK
+ | VOL_CAP_INT_FLOCK
+ | VOL_CAP_INT_EXTENDED_SECURITY
+ | VOL_CAP_INT_USERACCESS
+ | VOL_CAP_INT_MANLOCK
+ | VOL_CAP_INT_NAMEDSTREAMS
+ | VOL_CAP_INT_EXTENDED_ATTR;
+ pCaps->capabilities[VOL_CAPABILITIES_INTERFACES] = 0
+ /// @todo | VOL_CAP_INT_SEARCHFS
+ /// @todo | VOL_CAP_INT_COPYFILE
+ /// @todo | VOL_CAP_INT_READDIRATTR
+ ;
+
+ pCaps->valid[VOL_CAPABILITIES_RESERVED1] = 0;
+ pCaps->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
+
+ pCaps->valid[VOL_CAPABILITIES_RESERVED2] = 0;
+ pCaps->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
+
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_capabilities);
+ }
+
+
+ /*
+ * Annoying attribute stuff.
+ * The 'valid' bits are only supposed to be set when we know for sure.
+ */
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_attributes))
+ {
+ vol_attributes_attr_t *pAt = &pFsAttr->f_attributes;
+
+ pAt->validattr.commonattr = ATTR_CMN_NAME
+ | ATTR_CMN_DEVID
+ | ATTR_CMN_FSID
+ | ATTR_CMN_OBJTYPE
+ | ATTR_CMN_OBJTAG
+ | ATTR_CMN_OBJID
+ | ATTR_CMN_OBJPERMANENTID
+ | ATTR_CMN_PAROBJID
+ | ATTR_CMN_SCRIPT
+ | ATTR_CMN_CRTIME
+ | ATTR_CMN_MODTIME
+ | ATTR_CMN_CHGTIME
+ | ATTR_CMN_ACCTIME
+ | ATTR_CMN_BKUPTIME
+ | ATTR_CMN_FNDRINFO
+ | ATTR_CMN_OWNERID
+ | ATTR_CMN_GRPID
+ | ATTR_CMN_ACCESSMASK
+ | ATTR_CMN_FLAGS
+ | ATTR_CMN_USERACCESS
+ | ATTR_CMN_EXTENDED_SECURITY
+ | ATTR_CMN_UUID
+ | ATTR_CMN_GRPUUID
+ | ATTR_CMN_FILEID
+ | ATTR_CMN_PARENTID
+ | ATTR_CMN_FULLPATH
+ | ATTR_CMN_ADDEDTIME;
+ pAt->nativeattr.commonattr = ATTR_CMN_NAME
+ | ATTR_CMN_DEVID
+ | ATTR_CMN_FSID
+ | ATTR_CMN_OBJTYPE
+ | ATTR_CMN_OBJTAG
+ | ATTR_CMN_OBJID
+ //| ATTR_CMN_OBJPERMANENTID
+ | ATTR_CMN_PAROBJID
+ //| ATTR_CMN_SCRIPT
+ | ATTR_CMN_CRTIME
+ | ATTR_CMN_MODTIME
+ | ATTR_CMN_CHGTIME
+ | ATTR_CMN_ACCTIME
+ //| ATTR_CMN_BKUPTIME
+ //| ATTR_CMN_FNDRINFO
+ //| ATTR_CMN_OWNERID
+ //| ATTR_CMN_GRPID
+ | ATTR_CMN_ACCESSMASK
+ //| ATTR_CMN_FLAGS
+ //| ATTR_CMN_USERACCESS
+ //| ATTR_CMN_EXTENDED_SECURITY
+ //| ATTR_CMN_UUID
+ //| ATTR_CMN_GRPUUID
+ | ATTR_CMN_FILEID
+ | ATTR_CMN_PARENTID
+ | ATTR_CMN_FULLPATH
+ //| ATTR_CMN_ADDEDTIME
+ ;
+ pAt->validattr.volattr = ATTR_VOL_FSTYPE
+ | ATTR_VOL_SIGNATURE
+ | ATTR_VOL_SIZE
+ | ATTR_VOL_SPACEFREE
+ | ATTR_VOL_SPACEAVAIL
+ | ATTR_VOL_MINALLOCATION
+ | ATTR_VOL_ALLOCATIONCLUMP
+ | ATTR_VOL_IOBLOCKSIZE
+ | ATTR_VOL_OBJCOUNT
+ | ATTR_VOL_FILECOUNT
+ | ATTR_VOL_DIRCOUNT
+ | ATTR_VOL_MAXOBJCOUNT
+ | ATTR_VOL_MOUNTPOINT
+ | ATTR_VOL_NAME
+ | ATTR_VOL_MOUNTFLAGS
+ | ATTR_VOL_MOUNTEDDEVICE
+ | ATTR_VOL_ENCODINGSUSED
+ | ATTR_VOL_CAPABILITIES
+ | ATTR_VOL_UUID
+ | ATTR_VOL_ATTRIBUTES
+ | ATTR_VOL_INFO;
+ pAt->nativeattr.volattr = ATTR_VOL_FSTYPE
+ //| ATTR_VOL_SIGNATURE
+ | ATTR_VOL_SIZE
+ | ATTR_VOL_SPACEFREE
+ | ATTR_VOL_SPACEAVAIL
+ | ATTR_VOL_MINALLOCATION
+ | ATTR_VOL_ALLOCATIONCLUMP
+ | ATTR_VOL_IOBLOCKSIZE
+ //| ATTR_VOL_OBJCOUNT
+ //| ATTR_VOL_FILECOUNT
+ //| ATTR_VOL_DIRCOUNT
+ //| ATTR_VOL_MAXOBJCOUNT
+ //| ATTR_VOL_MOUNTPOINT - ??
+ | ATTR_VOL_NAME
+ | ATTR_VOL_MOUNTFLAGS
+ | ATTR_VOL_MOUNTEDDEVICE
+ //| ATTR_VOL_ENCODINGSUSED
+ | ATTR_VOL_CAPABILITIES
+ //| ATTR_VOL_UUID
+ | ATTR_VOL_ATTRIBUTES
+ //| ATTR_VOL_INFO
+ ;
+ pAt->validattr.dirattr = ATTR_DIR_LINKCOUNT
+ | ATTR_DIR_ENTRYCOUNT
+ | ATTR_DIR_MOUNTSTATUS;
+ pAt->nativeattr.dirattr = 0 //ATTR_DIR_LINKCOUNT
+ | ATTR_DIR_ENTRYCOUNT
+ | ATTR_DIR_MOUNTSTATUS
+ ;
+ pAt->validattr.fileattr = ATTR_FILE_LINKCOUNT
+ | ATTR_FILE_TOTALSIZE
+ | ATTR_FILE_ALLOCSIZE
+ | ATTR_FILE_IOBLOCKSIZE
+ | ATTR_FILE_DEVTYPE
+ | ATTR_FILE_FORKCOUNT
+ | ATTR_FILE_FORKLIST
+ | ATTR_FILE_DATALENGTH
+ | ATTR_FILE_DATAALLOCSIZE
+ | ATTR_FILE_RSRCLENGTH
+ | ATTR_FILE_RSRCALLOCSIZE;
+ pAt->nativeattr.fileattr = 0
+ //|ATTR_FILE_LINKCOUNT
+ | ATTR_FILE_TOTALSIZE
+ | ATTR_FILE_ALLOCSIZE
+ //| ATTR_FILE_IOBLOCKSIZE
+ | ATTR_FILE_DEVTYPE
+ //| ATTR_FILE_FORKCOUNT
+ //| ATTR_FILE_FORKLIST
+ | ATTR_FILE_DATALENGTH
+ | ATTR_FILE_DATAALLOCSIZE
+ | ATTR_FILE_RSRCLENGTH
+ | ATTR_FILE_RSRCALLOCSIZE
+ ;
+ pAt->validattr.forkattr = ATTR_FORK_TOTALSIZE
+ | ATTR_FORK_ALLOCSIZE;
+ pAt->nativeattr.forkattr = 0
+ //| ATTR_FORK_TOTALSIZE
+ //| ATTR_FORK_ALLOCSIZE
+ ;
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_attributes);
+ }
+
+ if (VFSATTR_IS_ACTIVE(pFsAttr, f_vol_name))
+ {
+ RTStrCopy(pFsAttr->f_vol_name, MAXPATHLEN, pThis->MntInfo.szFolder);
+ VFSATTR_SET_SUPPORTED(pFsAttr, f_vol_name);
+ }
+
+ rc = 0;
+ }
+ else
+ {
+ Log(("vboxSfOs2QueryFileInfo: VbglR0SfFsInfo failed: %Rrc\n", vrc));
+ rc = RTErrConvertToErrno(vrc);
+ }
+
+ VbglR0PhysHeapFree(pReq);
+ }
+ else
+ rc = ENOMEM;
+ return rc;
+}
+
+
+/**
+ * vfsops::vfs_root implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param ppVnode Where to return the referenced root node on success.
+ * @param pContext Unused kAuth parameter.
+ */
+static int vboxSfDwnVfsRoot(mount_t pMount, vnode_t *ppVnode, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, EBADMSG);
+ LogFlow(("vboxSfDwnVfsRoot: pThis=%p:{%s}\n", pThis, pThis->MntInfo.szFolder));
+ RT_NOREF(pContext);
+
+ /*
+ * We shouldn't be callable during unmount, should we?
+ */
+ AssertReturn(vfs_isunmount(pMount), EBUSY);
+
+ /*
+ * There should always be a root node around.
+ */
+ if (pThis->pVnRoot)
+ {
+ int rc = vnode_get(pThis->pVnRoot);
+ if (rc == 0)
+ {
+ *ppVnode = pThis->pVnRoot;
+ LogFlow(("vboxSfDwnVfsRoot: return %p\n", *ppVnode));
+ return 0;
+ }
+ Log(("vboxSfDwnVfsRoot: vnode_get failed! %d\n", rc));
+ return rc;
+ }
+
+ LogRel(("vboxSfDwnVfsRoot: pVnRoot is NULL!\n"));
+ return EILSEQ;
+}
+
+
+/**
+ * vfsops::vfs_umount implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data.
+ * @param fFlags Unmount flags.
+ * @param pContext kAuth context which we don't care much about.
+ *
+ */
+static int vboxSfDwnVfsUnmount(mount_t pMount, int fFlags, vfs_context_t pContext)
+{
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)vfs_fsprivate(pMount);
+ AssertReturn(pThis, 0);
+ LogFlowFunc(("pThis=%p:{%s} fFlags=%#x\n", pThis, pThis->MntInfo.szFolder, fFlags));
+ RT_NOREF(pContext);
+
+ /*
+ * Flush vnodes.
+ */
+ int rc = vflush(pMount, pThis->pVnRoot, fFlags & MNT_FORCE ? FORCECLOSE : 0);
+ if (rc == 0)
+ {
+ /*
+ * Is the file system still busy?
+ *
+ * Until we find a way of killing any active host calls, we cannot properly
+ * respect the MNT_FORCE flag here. So, MNT_FORCE is ignored here.
+ */
+ if ( !pThis->pVnRoot
+ || !vnode_isinuse(pThis->pVnRoot, 1))
+ {
+ /*
+ * Release our root vnode reference and do another flush.
+ */
+ if (pThis->pVnRoot)
+ {
+ vnode_put(pThis->pVnRoot);
+ pThis->pVnRoot = NULL;
+ }
+ vflush(pMount, NULLVP, FORCECLOSE);
+
+ /*
+ * Unmap the shared folder and destroy our mount info structure.
+ */
+ vfs_setfsprivate(pMount, NULL);
+
+ rc = VbglR0SfUnmapFolder(&g_SfClientDarwin, &pThis->hHostFolder);
+ AssertRC(rc);
+
+ RT_ZERO(*pThis);
+ RTMemFree(pThis);
+
+ vfs_clearflags(pMount, MNT_LOCAL); /* ?? */
+ rc = 0;
+
+ g_cVBoxSfMounts--;
+ }
+ else
+ {
+ Log(("VBoxSF: umount failed: file system busy! (%s)\n", pThis->MntInfo.szFolder));
+ rc = EBUSY;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * vfsops::vfs_start implementation.
+ */
+static int vboxSfDwnVfsStart(mount_t pMount, int fFlags, vfs_context_t pContext)
+{
+ RT_NOREF(pMount, fFlags, pContext);
+ return 0;
+}
+
+
+/**
+ * vfsops::vfs_mount implementation.
+ *
+ * @returns 0 on success or errno.h value on failure.
+ * @param pMount The mount data structure.
+ * @param pDevVp The device to mount. Not used by us.
+ * @param pUserData User space address of parameters supplied to mount().
+ * We expect a VBOXSFDRWNMOUNTINFO structure.
+ * @param pContext kAuth context needed in order to authentificate mount
+ * operation.
+ */
+static int vboxSfDwnVfsMount(mount_t pMount, vnode_t pDevVp, user_addr_t pUserData, vfs_context_t pContext)
+{
+ RT_NOREF(pDevVp, pContext);
+
+ /*
+ * We don't support mount updating.
+ */
+ if (vfs_isupdate(pMount))
+ {
+ LogRel(("VBoxSF: mount: MNT_UPDATE is not supported.\n"));
+ return ENOTSUP;
+ }
+ if (pUserData == USER_ADDR_NULL)
+ {
+ LogRel(("VBoxSF: mount: pUserData is NULL.\n"));
+ return EINVAL;
+ }
+ struct vfsstatfs *pFsStats = vfs_statfs(pMount);
+ AssertReturn(pFsStats, EINVAL);
+
+ /*
+ * Get the mount information from userland.
+ */
+ PVBOXSFMNTDATA pThis = (PVBOXSFMNTDATA)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return ENOMEM;
+ pThis->uidMounter = pFsStats->f_owner;
+
+ int rc = RTR0MemUserCopyFrom(&pThis->MntInfo, (RTR3PTR)pUserData, sizeof(pThis->MntInfo));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VBoxSF: mount: Failed to copy in mount user data: %Rrc\n", rc));
+ rc = EFAULT;
+ }
+ else if (pThis->MntInfo.u32Magic != VBOXSFDRWNMOUNTINFO_MAGIC)
+ {
+ LogRel(("VBoxSF: mount: Invalid user data magic (%#x)\n", pThis->MntInfo.u32Magic));
+ rc = EINVAL;
+ }
+ else if ( (rc = RTStrValidateEncodingEx(pThis->MntInfo.szFolder, sizeof(pThis->MntInfo.szFolder),
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED)) != VINF_SUCCESS
+ || pThis->MntInfo.szFolder[0] == '\0')
+ {
+ LogRel(("VBoxSF: mount: Invalid or empty share name!\n"));
+ rc = EINVAL;
+ }
+ else
+ {
+ /*
+ * Try map the shared folder.
+ */
+ if (vboxSfDwnConnect())
+ {
+ PSHFLSTRING pName = ShflStringDupUtf8(pThis->MntInfo.szFolder);
+ if (pName)
+ {
+ rc = VbglR0SfMapFolder(&g_SfClientDarwin, pName, &pThis->hHostFolder);
+ RTMemFree(pName);
+ if (RT_SUCCESS(rc))
+ {
+
+ /*
+ * Create a root node, that avoid races later.
+ */
+ pThis->pVnRoot = vboxSfDwnVnAlloc(pMount, VDIR, NULL /*pParent*/, 0);
+ if (pThis->pVnRoot)
+ {
+ /*
+ * Fill file system stats with dummy data.
+ */
+ pFsStats->f_bsize = 512;
+ pFsStats->f_iosize = _64K;
+ pFsStats->f_blocks = _1M;
+ pFsStats->f_bavail = _1M / 4 * 3;
+ pFsStats->f_bused = _1M / 4;
+ pFsStats->f_files = 1024;
+ pFsStats->f_ffree = _64K;
+ vfs_getnewfsid(pMount); /* f_fsid */
+ /* pFsStats->f_fowner - don't touch */
+ /* pFsStats->f_fstypename - don't touch */
+ /* pFsStats->f_mntonname - don't touch */
+ RTStrCopy(pFsStats->f_mntfromname, sizeof(pFsStats->f_mntfromname), pThis->MntInfo.szFolder);
+ /* pFsStats->f_fssubtype - don't touch? */
+ /* pFsStats->f_reserved[0] - don't touch? */
+ /* pFsStats->f_reserved[1] - don't touch? */
+
+ /*
+ * We're good. Set private data and flags.
+ */
+ vfs_setfsprivate(pMount, pThis);
+ vfs_setflags(pMount, MNT_SYNCHRONOUS | MNT_NOSUID | MNT_NODEV);
+ /** @todo Consider flags like MNT_NOEXEC ? */
+
+ /// @todo vfs_setauthopaque(pMount)?
+ /// @todo vfs_clearauthopaqueaccess(pMount)?
+ /// @todo vfs_clearextendedsecurity(pMount)?
+
+ LogRel(("VBoxSF: mount: Successfully mounted '%s' (uidMounter=%u).\n",
+ pThis->MntInfo.szFolder, pThis->uidMounter));
+ return 0;
+ }
+
+ LogRel(("VBoxSF: mount: Failed to allocate root node!\n"));
+ rc = ENOMEM;
+ }
+ else
+ {
+ LogRel(("VBoxSF: mount: VbglR0SfMapFolder failed on '%s': %Rrc\n", pThis->MntInfo.szFolder, rc));
+ rc = ENOENT;
+ }
+ }
+ else
+ rc = ENOMEM;
+ }
+ else
+ {
+ LogRel(("VBoxSF: mount: Not connected to shared folders service!\n"));
+ rc = ENOTCONN;
+ }
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+/**
+ * VFS operations
+ */
+struct vfsops g_VBoxSfVfsOps =
+{
+ vboxSfDwnVfsMount,
+ vboxSfDwnVfsStart,
+ vboxSfDwnVfsUnmount,
+ vboxSfDwnVfsRoot,
+ NULL, /* Skipped: vfs_quotactl */
+ vboxSfDwnVfsGetAttr,
+ NULL, /* Skipped: vfs_sync */
+ NULL, /* Skipped: vfs_vget */
+ NULL, /* Skipped: vfs_fhtovp */
+ NULL, /* Skipped: vfs_vptofh */
+ NULL, /* Skipped: vfs_init */
+ NULL, /* Skipped: vfs_sysctl */
+ NULL, /* Skipped: vfs_setattr */
+ /* Reserved */
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, },
+};
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp b/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp
new file mode 100644
index 00000000..8d8388a2
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSF.cpp
@@ -0,0 +1,261 @@
+/* $Id: VBoxSF.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, KEXT entry points.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "VBoxSFInternal.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static kern_return_t vboxSfDwnModuleLoad(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t vboxSfDwnModuleUnload(struct kmod_info *pKModInfo, void *pvData);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The VBoxGuest service if we've managed to connect to it already. */
+static IOService *g_pVBoxGuest = NULL;
+/** The shared folder service client structure. */
+VBGLSFCLIENT g_SfClientDarwin = { UINT32_MAX, NULL };
+/** Number of active mounts. Used for unload prevention. */
+uint32_t volatile g_cVBoxSfMounts = 0;
+
+/** VFS table entry for our file system (for vfs_fsremove). */
+static vfstable_t g_pVBoxSfVfsTableEntry;
+/** For vfs_fsentry. */
+static struct vnodeopv_desc *g_apVBoxSfVnodeOpDescList[] =
+{
+ &g_VBoxSfVnodeOpvDesc,
+};
+/** VFS registration structure. */
+static struct vfs_fsentry g_VBoxSfFsEntry =
+{
+ .vfe_vfsops = &g_VBoxSfVfsOps,
+ .vfe_vopcnt = RT_ELEMENTS(g_apVBoxSfVnodeOpDescList),
+ .vfe_opvdescs = g_apVBoxSfVnodeOpDescList,
+ .vfe_fstypenum = -1,
+ .vfe_fsname = VBOXSF_DARWIN_FS_NAME,
+ .vfe_flags = VFS_TBLTHREADSAFE /* Required. */
+ | VFS_TBLFSNODELOCK /* Required. */
+ | VFS_TBLNOTYPENUM /* No historic file system number. */
+ | VFS_TBL64BITREADY, /* Can handle 64-bit processes */
+ /** @todo add VFS_TBLREADDIR_EXTENDED */
+ .vfe_reserv = { NULL, NULL },
+};
+
+
+/**
+ * Declare the module stuff.
+ */
+RT_C_DECLS_BEGIN
+extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
+extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
+
+KMOD_EXPLICIT_DECL(VBoxSF, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = vboxSfDwnModuleLoad;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = vboxSfDwnModuleUnload;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+
+/**
+ * Connect to VBoxGuest and host shared folders service.
+ *
+ * @returns true if connected, false if not.
+ */
+bool vboxSfDwnConnect(void)
+{
+ /*
+ * Grab VBoxGuest - since it's a dependency of this module, it shouldn't be hard.
+ */
+ if (!g_pVBoxGuest)
+ {
+ OSDictionary *pServiceMatcher = IOService::serviceMatching("org_virtualbox_VBoxGuest");
+ if (pServiceMatcher)
+ {
+ IOService *pVBoxGuest = IOService::waitForMatchingService(pServiceMatcher, 10 * RT_NS_1SEC);
+ if (pVBoxGuest)
+ g_pVBoxGuest = pVBoxGuest;
+ else
+ LogRel(("vboxSfDwnConnect: IOService::waitForMatchingService failed!!\n"));
+ }
+ else
+ LogRel(("vboxSfDwnConnect: serviceMatching failed\n"));
+ }
+
+ if (g_pVBoxGuest)
+ {
+ /*
+ * Get hold of the shared folders service if we haven't already.
+ */
+ if (g_SfClientDarwin.handle != NULL)
+ return true;
+
+ int rc = VbglR0SfConnect(&g_SfClientDarwin);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfSetUtf8(&g_SfClientDarwin);
+ if (RT_SUCCESS(rc))
+ return true;
+
+ LogRel(("VBoxSF: VbglR0SfSetUtf8 failed: %Rrc\n", rc));
+
+ VbglR0SfDisconnect(&g_SfClientDarwin);
+ g_SfClientDarwin.handle = NULL;
+ }
+ else
+ LogRel(("VBoxSF: VbglR0SfConnect failed: %Rrc\n", rc));
+ }
+
+ return false;
+}
+
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t vboxSfDwnModuleLoad(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+#ifdef DEBUG
+ printf("vboxSfDwnModuleLoad\n");
+ RTLogBackdoorPrintf("vboxSfDwnModuleLoad\n");
+#endif
+
+ /*
+ * Initialize IPRT and the ring-0 guest library.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VbglR0SfInit();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register the file system.
+ */
+ rc = vfs_fsadd(&g_VBoxSfFsEntry, &g_pVBoxSfVfsTableEntry);
+ if (rc == 0)
+ {
+ /*
+ * Try find VBoxGuest and connect to the shared folders service on the host.
+ */
+ /** @todo should we just ignore the error here and retry at mount time?
+ * Technically, VBoxGuest should be available since it's one of our
+ * dependencies... */
+ vboxSfDwnConnect();
+
+ /*
+ * We're done for now. We'll deal with
+ */
+ LogRel(("VBoxSF: loaded\n"));
+ return KERN_SUCCESS;
+ }
+
+ printf("VBoxSF: vfs_fsadd failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: vfs_fsadd failed: %d\n", rc);
+ VbglR0SfTerm();
+ }
+ else
+ {
+ printf("VBoxSF: VbglR0SfInit failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: VbglR0SfInit failed: %Rrc\n", rc);
+ }
+ RTR0Term();
+ }
+ else
+ {
+ printf("VBoxSF: RTR0Init failed: %d\n", rc);
+ RTLogBackdoorPrintf("VBoxSF: RTR0Init failed: %Rrc\n", rc);
+ }
+ return KERN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t vboxSfDwnModuleUnload(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+#ifdef DEBUG
+ printf("vboxSfDwnModuleUnload\n");
+ RTLogBackdoorPrintf("vboxSfDwnModuleUnload\n");
+#endif
+
+
+ /*
+ * Are we busy? If so fail. Otherwise try deregister the file system.
+ */
+ if (g_cVBoxSfMounts > 0)
+ {
+ LogRel(("VBoxSF: Refusing to unload with %u active mounts\n", g_cVBoxSfMounts));
+ return KERN_NO_ACCESS;
+ }
+
+ if (g_pVBoxSfVfsTableEntry)
+ {
+ int rc = vfs_fsremove(g_pVBoxSfVfsTableEntry);
+ if (rc != 0)
+ {
+ LogRel(("VBoxSF: vfs_fsremove failed: %d\n", rc));
+ return KERN_NO_ACCESS;
+ }
+ }
+
+ /*
+ * Disconnect and terminate libraries we're using.
+ */
+ if (g_SfClientDarwin.handle != NULL)
+ {
+ VbglR0SfDisconnect(&g_SfClientDarwin);
+ g_SfClientDarwin.handle = NULL;
+ }
+
+ if (g_pVBoxGuest)
+ {
+ g_pVBoxGuest->release();
+ g_pVBoxGuest = NULL;
+ }
+
+ VbglR0SfTerm();
+ RTR0Term();
+ return KERN_SUCCESS;
+}
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h b/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h
new file mode 100644
index 00000000..6d297e82
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSFInternal.h
@@ -0,0 +1,117 @@
+/* $Id: VBoxSFInternal.h $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, internal header.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h
+#define GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxSFMount.h"
+
+#include <libkern/libkern.h>
+#include <iprt/types.h>
+#include <IOKit/IOLib.h>
+#include <IOKit/IOService.h>
+#include <mach/mach_port.h>
+#include <mach/kmod.h>
+#include <mach/mach_types.h>
+#include <sys/errno.h>
+#include <sys/dirent.h>
+#include <sys/lock.h>
+#include <sys/fcntl.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <vfs/vfs_support.h>
+#undef PVM
+
+#include <iprt/mem.h>
+#include <VBox/VBoxGuest.h>
+#include <VBox/VBoxGuestLibSharedFolders.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Private data we associate with a mount.
+ */
+typedef struct VBOXSFMNTDATA
+{
+ /** The shared folder mapping */
+ VBGLSFMAP hHostFolder;
+ /** The root VNode. */
+ vnode_t pVnRoot;
+ /** User that mounted shared folder (anyone but root?). */
+ uid_t uidMounter;
+ /** The mount info from the mount() call. */
+ VBOXSFDRWNMOUNTINFO MntInfo;
+} VBOXSFMNTDATA;
+/** Pointer to private mount data. */
+typedef VBOXSFMNTDATA *PVBOXSFMNTDATA;
+
+/**
+ * Private data we associate with a VNode.
+ */
+typedef struct VBOXSFDWNVNDATA
+{
+ /** The handle to the host object. */
+ SHFLHANDLE hHandle;
+ ///PSHFLSTRING pPath; /** Path within shared folder */
+ ///lck_attr_t *pLockAttr; /** BSD locking stuff */
+ ///lck_rw_t *pLock; /** BSD locking stuff */
+} VBOXSFDWNVNDATA;
+/** Pointer to private vnode data. */
+typedef VBOXSFDWNVNDATA *PVBOXSFDWNVNDATA;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern VBGLSFCLIENT g_SfClientDarwin;
+extern uint32_t volatile g_cVBoxSfMounts;
+extern struct vfsops g_VBoxSfVfsOps;
+extern struct vnodeopv_desc g_VBoxSfVnodeOpvDesc;
+extern int (**g_papfnVBoxSfDwnVnDirOpsVector)(void *);
+
+
+
+/*********************************************************************************************************************************
+* Functions *
+*********************************************************************************************************************************/
+bool vboxSfDwnConnect(void);
+vnode_t vboxSfDwnVnAlloc(mount_t pMount, enum vtype enmType, vnode_t pParent, uint64_t cbFile);
+
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFInternal_h */
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h b/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h
new file mode 100644
index 00000000..318d6e42
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/VBoxSFMount.h
@@ -0,0 +1,54 @@
+/* $Id: VBoxSFMount.h $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, mount interface.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h
+#define GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/** The shared folders file system name. */
+#define VBOXSF_DARWIN_FS_NAME "vboxsf"
+
+/**
+ * Mount information that gets passed from userland on mount.
+ */
+typedef struct VBOXSFDRWNMOUNTINFO
+{
+ /** Magic value (VBOXSFDRWNMOUNTINFO_MAGIC). */
+ uint32_t u32Magic;
+ /** The shared folder name. */
+ char szFolder[260];
+} VBOXSFDRWNMOUNTINFO;
+typedef VBOXSFDRWNMOUNTINFO *PVBOXSFDRWNMOUNTINFO;
+/** Magic value for VBOXSFDRWNMOUNTINFO::u32Magic. */
+#define VBOXSFDRWNMOUNTINFO_MAGIC UINT32_C(0xc001cafe)
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxSF_VBoxSFMount_h */
+
diff --git a/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp b/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp
new file mode 100644
index 00000000..57db36bc
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxSF/mount.vboxsf.cpp
@@ -0,0 +1,97 @@
+/* $Id: mount.vboxsf.cpp $ */
+/** @file
+ * VBoxSF - Darwin Shared Folders, Mount Utility.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+#include "VBoxSFMount.h"
+#include <iprt/string.h>
+
+
+static RTEXITCODE usage(const char *pszArg0)
+{
+ fprintf(stderr, "usage: %s [OPTIONS] <shared folder name> <mount point>\n", pszArg0);
+ return RTEXITCODE_SYNTAX;
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * Skip past parameters.
+ */
+ int c;
+ while ((c = getopt(argc, argv, "o:")) != -1)
+ {
+ switch (c)
+ {
+ case 'o':
+ break;
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ /* Two arguments are rquired: <share name> and <mount point> */
+ if (argc - optind != 2)
+ return usage(argv[0]);
+ const char * const pszFolder = argv[optind++];
+ const char * const pszMountPoint = argv[optind];
+
+ /*
+ * Check that the folder is within bounds and doesn't include any shady characters.
+ */
+ size_t cchFolder = strlen(pszFolder);
+ if ( cchFolder < 1
+ || cchFolder >= RT_SIZEOFMEMB(VBOXSFDRWNMOUNTINFO, szFolder)
+ || strpbrk(pszFolder, "\\/") != NULL)
+ {
+ fprintf(stderr, "Invalid shared folder name '%s'!\n", pszFolder);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Do the mounting.
+ */
+ VBOXSFDRWNMOUNTINFO MntInfo;
+ RT_ZERO(MntInfo);
+ MntInfo.u32Magic = VBOXSFDRWNMOUNTINFO_MAGIC;
+ memcpy(MntInfo.szFolder, pszFolder, cchFolder);
+ int rc = mount(VBOXSF_DARWIN_FS_NAME, pszMountPoint, 0, &MntInfo);
+ if (rc == 0)
+ return 0;
+
+ fprintf(stderr, "error mounting '%s' at '%s': %s (%d)\n", pszFolder, pszMountPoint, strerror(errno), errno);
+ return RTEXITCODE_FAILURE;
+}