summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/SharedFolders
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/HostServices/SharedFolders
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostServices/SharedFolders')
-rw-r--r--src/VBox/HostServices/SharedFolders/Makefile.kmk62
-rw-r--r--src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp1971
-rw-r--r--src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc61
-rw-r--r--src/VBox/HostServices/SharedFolders/mappings.cpp1024
-rw-r--r--src/VBox/HostServices/SharedFolders/mappings.h92
-rw-r--r--src/VBox/HostServices/SharedFolders/shfl.h89
-rw-r--r--src/VBox/HostServices/SharedFolders/shflhandle.cpp236
-rw-r--r--src/VBox/HostServices/SharedFolders/shflhandle.h91
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk104
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp1368
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h140
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp452
-rw-r--r--src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp144
-rw-r--r--src/VBox/HostServices/SharedFolders/teststubs.h104
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsf.cpp2690
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsf.h68
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpath.cpp709
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpath.h80
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp200
19 files changed, 9685 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedFolders/Makefile.kmk b/src/VBox/HostServices/SharedFolders/Makefile.kmk
new file mode 100644
index 00000000..fc0ff6c5
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/Makefile.kmk
@@ -0,0 +1,62 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Folders Host Service.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The shared folder service DLL.
+#
+DLLS += VBoxSharedFolders
+VBoxSharedFolders_TEMPLATE = VBoxR3Dll
+VBoxSharedFolders_NAME.os2 = VBoxSFld
+VBoxSharedFolders_DEFS = VBOX_WITH_HGCM RTSHFL
+VBoxSharedFolders_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxSharedFolders_LDFLAGS.darwin = \
+ -framework Carbon \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedFolders.dylib
+
+VBoxSharedFolders_SOURCES = \
+ VBoxSharedFoldersSvc.cpp \
+ shflhandle.cpp \
+ vbsf.cpp \
+ vbsfpath.cpp \
+ vbsfpathabs.cpp \
+ mappings.cpp
+VBoxSharedFolders_SOURCES.win = \
+ VBoxSharedFoldersSvc.rc
+
+VBoxSharedFolders_LIBS = \
+ $(LIB_RUNTIME)
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp
new file mode 100644
index 00000000..3486709d
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp
@@ -0,0 +1,1971 @@
+/* $Id: VBoxSharedFoldersSvc.cpp $ */
+/** @file
+ * Shared Folders - Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include <VBox/shflsvc.h>
+
+#include "shfl.h"
+#include "mappings.h"
+#include "shflhandle.h"
+#include "vbsf.h"
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <VBox/AssertGuest.h>
+#include <VBox/vmm/ssm.h>
+#include <VBox/vmm/pdmifs.h>
+#include <VBox/vmm/vmmr3vtable.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16 2
+#define SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT 3
+#define SHFL_SAVED_STATE_VERSION_PRE_ERROR_STYLE 4
+#define SHFL_SAVED_STATE_VERSION 5
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+PVBOXHGCMSVCHELPERS g_pHelpers;
+static PPDMLED g_pStatusLed = NULL;
+
+/** @name Shared folder statistics.
+ * @{ */
+static STAMPROFILE g_StatQueryMappings;
+static STAMPROFILE g_StatQueryMappingsFail;
+static STAMPROFILE g_StatQueryMapName;
+static STAMPROFILE g_StatCreate;
+static STAMPROFILE g_StatCreateFail;
+static STAMPROFILE g_StatLookup;
+static STAMPROFILE g_StatLookupFail;
+static STAMPROFILE g_StatClose;
+static STAMPROFILE g_StatCloseFail;
+static STAMPROFILE g_StatRead;
+static STAMPROFILE g_StatReadFail;
+static STAMPROFILE g_StatWrite;
+static STAMPROFILE g_StatWriteFail;
+static STAMPROFILE g_StatLock;
+static STAMPROFILE g_StatLockFail;
+static STAMPROFILE g_StatList;
+static STAMPROFILE g_StatListFail;
+static STAMPROFILE g_StatReadLink;
+static STAMPROFILE g_StatReadLinkFail;
+static STAMPROFILE g_StatMapFolderOld;
+static STAMPROFILE g_StatMapFolder;
+static STAMPROFILE g_StatMapFolderFail;
+static STAMPROFILE g_StatUnmapFolder;
+static STAMPROFILE g_StatUnmapFolderFail;
+static STAMPROFILE g_StatInformationFail;
+static STAMPROFILE g_StatInformationSetFile;
+static STAMPROFILE g_StatInformationSetFileFail;
+static STAMPROFILE g_StatInformationSetSize;
+static STAMPROFILE g_StatInformationSetSizeFail;
+static STAMPROFILE g_StatInformationGetFile;
+static STAMPROFILE g_StatInformationGetFileFail;
+static STAMPROFILE g_StatInformationGetVolume;
+static STAMPROFILE g_StatInformationGetVolumeFail;
+static STAMPROFILE g_StatRemove;
+static STAMPROFILE g_StatRemoveFail;
+static STAMPROFILE g_StatCloseAndRemove;
+static STAMPROFILE g_StatCloseAndRemoveFail;
+static STAMPROFILE g_StatRename;
+static STAMPROFILE g_StatRenameFail;
+static STAMPROFILE g_StatFlush;
+static STAMPROFILE g_StatFlushFail;
+static STAMPROFILE g_StatSetErrorStyle;
+static STAMPROFILE g_StatSetUtf8;
+static STAMPROFILE g_StatSetFileSize;
+static STAMPROFILE g_StatSetFileSizeFail;
+static STAMPROFILE g_StatSymlink;
+static STAMPROFILE g_StatSymlinkFail;
+static STAMPROFILE g_StatSetSymlinks;
+static STAMPROFILE g_StatQueryMapInfo;
+static STAMPROFILE g_StatQueryFeatures;
+static STAMPROFILE g_StatCopyFile;
+static STAMPROFILE g_StatCopyFileFail;
+static STAMPROFILE g_StatCopyFilePart;
+static STAMPROFILE g_StatCopyFilePartFail;
+static STAMPROFILE g_StatWaitForMappingsChanges;
+static STAMPROFILE g_StatWaitForMappingsChangesFail;
+static STAMPROFILE g_StatCancelMappingsChangesWait;
+static STAMPROFILE g_StatUnknown;
+static STAMPROFILE g_StatMsgStage1;
+/** @} */
+
+
+/** @page pg_shfl_svc Shared Folders Host Service
+ *
+ * Shared Folders map a host file system to guest logical filesystem.
+ * A mapping represents 'host name'<->'guest name' translation and a root
+ * identifier to be used to access this mapping.
+ * Examples: "C:\WINNT"<->"F:", "C:\WINNT\System32"<->"/mnt/host/system32".
+ *
+ * Therefore, host name and guest name are strings interpreted
+ * only by host service and guest client respectively. Host name is
+ * passed to guest only for informational purpose. Guest may for example
+ * display the string or construct volume label out of the string.
+ *
+ * Root identifiers are unique for whole guest life,
+ * that is until next guest reset/fresh start.
+ * 32 bit value incremented for each new mapping is used.
+ *
+ * Mapping strings are taken from VM XML configuration on VM startup.
+ * The service DLL takes mappings during initialization. There is
+ * also API for changing mappings at runtime.
+ *
+ * Current mappings and root identifiers are saved when VM is saved.
+ *
+ * Guest may use any of these mappings. Full path information
+ * about an object on a mapping consists of the root identifier and
+ * a full path of object.
+ *
+ * Guest IFS connects to the service and calls SHFL_FN_QUERY_MAP
+ * function which returns current mappings. For guest convenience,
+ * removed mappings also returned with REMOVED flag and new mappings
+ * are marked with NEW flag.
+ *
+ * To access host file system guest just forwards file system calls
+ * to the service, and specifies full paths or handles for objects.
+ *
+ *
+ */
+
+
+
+static DECLCALLBACK(int) svcUnload (void *)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("svcUnload\n"));
+ vbsfFreeHandleTable();
+
+ if (g_pHelpers)
+ HGCMSvcHlpStamDeregister(g_pHelpers, "/HGCM/VBoxSharedFolders/*");
+ return rc;
+}
+
+static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(u32ClientID, fRequestor, fRestoring);
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+ Log(("SharedFolders host service: connected, u32ClientID = %u\n", u32ClientID));
+
+ pClient->fHasMappingCounts = true;
+ pClient->enmErrorStyle = SHFLERRORSTYLE_NATIVE;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(u32ClientID);
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ /* When a client disconnects, make sure that outstanding change waits are being canceled.
+ *
+ * Usually this will be done actively by VBoxService on the guest side when shutting down,
+ * but the VM could be reset without having VBoxService the chance of cancelling those waits.
+ *
+ * This in turn will eat up the call completion handle restrictions on the HGCM host side, throwing assertions. */
+ int rc = vbsfMappingsCancelChangesWaits(pClient);
+
+ Log(("SharedFolders host service: disconnected, u32ClientID = %u, rc = %Rrc\n", u32ClientID, rc));
+
+ vbsfDisconnect(pClient);
+ return rc;
+}
+
+/** @note We only save as much state as required to access the shared folder again after restore.
+ * All I/O requests pending at the time of saving will never be completed or result in errors.
+ * (file handles no longer valid etc)
+ * This works as designed at the moment. A full state save would be difficult and not always possible
+ * as the contents of a shared folder might change in between save and restore.
+ */
+static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM)
+{
+#ifndef UNITTEST /* Read this as not yet tested */
+ RT_NOREF1(u32ClientID);
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ Log(("SharedFolders host service: saving state, u32ClientID = %u\n", u32ClientID));
+
+ int rc = pVMM->pfnSSMR3PutU32(pSSM, SHFL_SAVED_STATE_VERSION);
+ AssertRCReturn(rc, rc);
+
+ rc = pVMM->pfnSSMR3PutU32(pSSM, SHFL_MAX_MAPPINGS);
+ AssertRCReturn(rc, rc);
+
+ /* Save client structure length & contents */
+ rc = pVMM->pfnSSMR3PutU32(pSSM, sizeof(*pClient));
+ AssertRCReturn(rc, rc);
+
+ rc = pVMM->pfnSSMR3PutMem(pSSM, pClient, sizeof(*pClient));
+ AssertRCReturn(rc, rc);
+
+ /* Save all the active mappings. */
+ for (int i=0;i<SHFL_MAX_MAPPINGS;i++)
+ {
+ /* Mapping are saved in the order of increasing root handle values. */
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
+
+ rc = pVMM->pfnSSMR3PutU32(pSSM, pFolderMapping? pFolderMapping->cMappings: 0);
+ AssertRCReturn(rc, rc);
+
+ rc = pVMM->pfnSSMR3PutBool(pSSM, pFolderMapping? pFolderMapping->fValid: false);
+ AssertRCReturn(rc, rc);
+
+ if (pFolderMapping && pFolderMapping->fValid)
+ {
+ uint32_t len = (uint32_t)strlen(pFolderMapping->pszFolderName);
+ pVMM->pfnSSMR3PutU32(pSSM, len);
+ pVMM->pfnSSMR3PutStrZ(pSSM, pFolderMapping->pszFolderName);
+
+ len = ShflStringSizeOfBuffer(pFolderMapping->pMapName);
+ pVMM->pfnSSMR3PutU32(pSSM, len);
+ pVMM->pfnSSMR3PutMem(pSSM, pFolderMapping->pMapName, len);
+
+ pVMM->pfnSSMR3PutBool(pSSM, pFolderMapping->fHostCaseSensitive);
+
+ pVMM->pfnSSMR3PutBool(pSSM, pFolderMapping->fGuestCaseSensitive);
+
+ len = ShflStringSizeOfBuffer(pFolderMapping->pAutoMountPoint);
+ pVMM->pfnSSMR3PutU32(pSSM, len);
+ rc = pVMM->pfnSSMR3PutMem(pSSM, pFolderMapping->pAutoMountPoint, len);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+#else
+ RT_NOREF(u32ClientID, pvClient, pSSM, pVMM);
+#endif
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient,
+ PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM, uint32_t uVersion)
+{
+#ifndef UNITTEST /* Read this as not yet tested */
+ RT_NOREF(u32ClientID, uVersion);
+ uint32_t nrMappings;
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+ uint32_t len;
+
+ Log(("SharedFolders host service: loading state, u32ClientID = %u\n", u32ClientID));
+
+ uint32_t uShfVersion = 0;
+ int rc = pVMM->pfnSSMR3GetU32(pSSM, &uShfVersion);
+ AssertRCReturn(rc, rc);
+
+ if ( uShfVersion > SHFL_SAVED_STATE_VERSION
+ || uShfVersion < SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16)
+ return pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION, RT_SRC_POS,
+ "Unknown shared folders state version %u!", uShfVersion);
+
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &nrMappings);
+ AssertRCReturn(rc, rc);
+ if (nrMappings != SHFL_MAX_MAPPINGS)
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+
+ /* Restore the client data (flags + path delimiter + mapping counts (new) at the moment) */
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &len);
+ AssertRCReturn(rc, rc);
+
+ if (len == RT_UOFFSETOF(SHFLCLIENTDATA, acMappings))
+ pClient->fHasMappingCounts = false;
+ else if (len != sizeof(*pClient))
+ return pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Saved SHFLCLIENTDATA size %u differs from current %u!", len, sizeof(*pClient));
+
+ rc = pVMM->pfnSSMR3GetMem(pSSM, pClient, len);
+ AssertRCReturn(rc, rc);
+
+ /* For older saved state, use the default native error style, otherwise
+ check that the restored value makes sense to us. */
+ if (uShfVersion <= SHFL_SAVED_STATE_VERSION_PRE_ERROR_STYLE)
+ pClient->enmErrorStyle = SHFLERRORSTYLE_NATIVE;
+ else if ( pClient->enmErrorStyle <= kShflErrorStyle_Invalid
+ || pClient->enmErrorStyle >= kShflErrorStyle_End)
+ return pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Saved SHFLCLIENTDATA enmErrorStyle value %d is not known/valid!", pClient->enmErrorStyle);
+
+ /* Drop the root IDs of all configured mappings before restoring: */
+ vbsfMappingLoadingStart();
+
+ /* We don't actually (fully) restore the state; we simply check if the current state is as we it expect it to be. */
+ for (SHFLROOT i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ /* Load the saved mapping description and try to find it in the mappings. */
+ MAPPING mapping;
+ RT_ZERO(mapping);
+
+ /* restore the folder mapping counter. */
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &mapping.cMappings);
+ AssertRCReturn(rc, rc);
+
+ rc = pVMM->pfnSSMR3GetBool(pSSM, &mapping.fValid);
+ AssertRCReturn(rc, rc);
+
+ if (mapping.fValid)
+ {
+ /* Load the host path name. */
+ uint32_t cb;
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+
+ char *pszFolderName;
+ if (uShfVersion == SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16) /* (See version range check above.) */
+ {
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad folder name size: %#x", cb));
+ PSHFLSTRING pFolderName = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pFolderName != NULL, VERR_NO_MEMORY);
+
+ rc = pVMM->pfnSSMR3GetMem(pSSM, pFolderName, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pFolderName->u16Size < cb && pFolderName->u16Length < pFolderName->u16Size,
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad folder name string: %#x/%#x cb=%#x",
+ pFolderName->u16Size, pFolderName->u16Length, cb));
+
+ rc = RTUtf16ToUtf8(pFolderName->String.ucs2, &pszFolderName);
+ RTMemFree(pFolderName);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ pszFolderName = (char *)RTStrAlloc(cb + 1);
+ AssertReturn(pszFolderName, VERR_NO_MEMORY);
+
+ rc = pVMM->pfnSSMR3GetStrZ(pSSM, pszFolderName, cb + 1);
+ AssertRCReturn(rc, rc);
+ mapping.pszFolderName = pszFolderName;
+ }
+
+ /* Load the map name. */
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad map name size: %#x", cb));
+
+ PSHFLSTRING pMapName = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pMapName != NULL, VERR_NO_MEMORY);
+
+ rc = pVMM->pfnSSMR3GetMem(pSSM, pMapName, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pMapName->u16Size < cb && pMapName->u16Length < pMapName->u16Size,
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad map name string: %#x/%#x cb=%#x",
+ pMapName->u16Size, pMapName->u16Length, cb));
+
+ /* Load case sensitivity config. */
+ rc = pVMM->pfnSSMR3GetBool(pSSM, &mapping.fHostCaseSensitive);
+ AssertRCReturn(rc, rc);
+
+ rc = pVMM->pfnSSMR3GetBool(pSSM, &mapping.fGuestCaseSensitive);
+ AssertRCReturn(rc, rc);
+
+ /* Load the auto mount point. */
+ PSHFLSTRING pAutoMountPoint;
+ if (uShfVersion > SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT)
+ {
+ rc = pVMM->pfnSSMR3GetU32(pSSM, &cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1),
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad auto mount point size: %#x", cb));
+
+ pAutoMountPoint = (PSHFLSTRING)RTMemAlloc(cb);
+ AssertReturn(pAutoMountPoint != NULL, VERR_NO_MEMORY);
+
+ rc = pVMM->pfnSSMR3GetMem(pSSM, pAutoMountPoint, cb);
+ AssertRCReturn(rc, rc);
+ AssertReturn(pAutoMountPoint->u16Size < cb && pAutoMountPoint->u16Length < pAutoMountPoint->u16Size,
+ pVMM->pfnSSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS,
+ "Bad auto mount point string: %#x/%#x cb=%#x",
+ pAutoMountPoint->u16Size, pAutoMountPoint->u16Length, cb));
+
+ }
+ else
+ {
+ pAutoMountPoint = ShflStringDupUtf8("");
+ AssertReturn(pAutoMountPoint, VERR_NO_MEMORY);
+ }
+
+ mapping.pszFolderName = pszFolderName;
+ mapping.pMapName = pMapName;
+ mapping.pAutoMountPoint = pAutoMountPoint;
+
+ /* 'i' is the root handle of the saved mapping. */
+ rc = vbsfMappingLoaded(&mapping, i);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SharedFolders host service: %Rrc loading %d [%ls] -> [%s]\n",
+ rc, i, pMapName->String.ucs2, pszFolderName));
+ }
+
+ RTMemFree(pAutoMountPoint);
+ RTMemFree(pMapName);
+ RTStrFree(pszFolderName);
+
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ /* Make sure all mappings have root IDs (global folders changes, VM
+ config changes (paranoia)): */
+ vbsfMappingLoadingDone();
+
+ Log(("SharedFolders host service: successfully loaded state\n"));
+#else
+ RT_NOREF(u32ClientID, pvClient, pSSM, pVMM, uVersion);
+#endif
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient,
+ uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival)
+{
+ RT_NOREF(u32ClientID, tsArrival);
+#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
+ uint64_t tsStart;
+ STAM_GET_TS(tsStart);
+ STAM_REL_PROFILE_ADD_PERIOD(&g_StatMsgStage1, tsStart - tsArrival);
+#endif
+ Log(("SharedFolders host service: svcCall: u32ClientID = %u, fn = %u, cParms = %u, pparms = %p\n", u32ClientID, u32Function, cParms, paParms));
+
+ SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient;
+
+ bool fAsynchronousProcessing = false;
+
+#ifdef LOG_ENABLED
+ for (uint32_t i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %u, value %u\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ int rc = VINF_SUCCESS;
+ PSTAMPROFILE pStat, pStatFail;
+ switch (u32Function)
+ {
+ case SHFL_FN_QUERY_MAPPINGS:
+ {
+ pStat = &g_StatQueryMappings;
+ pStatFail = &g_StatQueryMappingsFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAPPINGS\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_QUERY_MAPPINGS)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* numberOfMappings */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* mappings */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ uint32_t fu32Flags = paParms[0].u.uint32;
+ uint32_t cMappings = paParms[1].u.uint32;
+ SHFLMAPPING *pMappings = (SHFLMAPPING *)paParms[2].u.pointer.addr;
+ uint32_t cbMappings = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( (fu32Flags & ~SHFL_MF_MASK) != 0
+ || cbMappings / sizeof (SHFLMAPPING) != cMappings
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (fu32Flags & SHFL_MF_UTF8)
+ pClient->fu32Flags |= SHFL_CF_UTF8;
+ /// @todo r=bird: Someone please explain this amusing code (r63916):
+ //if (fu32Flags & SHFL_MF_AUTOMOUNT)
+ // pClient->fu32Flags |= SHFL_MF_AUTOMOUNT;
+ //
+ //rc = vbsfMappingsQuery(pClient, pMappings, &cMappings);
+
+ rc = vbsfMappingsQuery(pClient, RT_BOOL(fu32Flags & SHFL_MF_AUTOMOUNT), pMappings, &cMappings);
+ if (RT_SUCCESS(rc))
+ {
+ /* Report that there are more mappings to get if
+ * handed in buffer is too small. */
+ if (paParms[1].u.uint32 < cMappings)
+ rc = VINF_BUFFER_OVERFLOW;
+
+ /* Update parameters. */
+ paParms[1].u.uint32 = cMappings;
+ }
+ }
+ }
+
+
+ } break;
+
+ case SHFL_FN_QUERY_MAP_NAME:
+ {
+ pStatFail = pStat = &g_StatQueryMapName;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAP_NAME\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_QUERY_MAP_NAME)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* Root. */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* Name. */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pString = (SHFLSTRING *)paParms[1].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidOut(pString, paParms[1].u.pointer.size))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsQueryName(pClient, root, pString);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* None. */
+ }
+ }
+ }
+
+ } break;
+
+ case SHFL_FN_CREATE:
+ {
+ pStat = &g_StatCreate;
+ pStatFail = &g_StatCreateFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_CREATE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_CREATE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parms */
+ )
+ {
+ Log(("SharedFolders host service: Invalid parameters types\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t cbPath = paParms[1].u.pointer.size;
+ SHFLCREATEPARMS *pParms = (SHFLCREATEPARMS *)paParms[2].u.pointer.addr;
+ uint32_t cbParms = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || (cbParms != sizeof (SHFLCREATEPARMS))
+ )
+ {
+ AssertMsgFailed (("Invalid parameters cbPath or cbParms (%x, %x - expected >=%x, %x)\n",
+ cbPath, cbParms, sizeof(SHFLSTRING), sizeof (SHFLCREATEPARMS)));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (pParms->CreateFlags & SHFL_CF_LOOKUP)
+ {
+ pStat = &g_StatLookup;
+ pStatFail = &g_StatLookupFail;
+ }
+
+ /* Execute the function. */
+ rc = vbsfCreate (pClient, root, pPath, cbPath, pParms);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_CLOSE:
+ {
+ pStat = &g_StatClose;
+ pStatFail = &g_StatCloseFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_CLOSE\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_CLOSE)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfClose (pClient, root, Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+
+ }
+
+ /* Read object content. */
+ case SHFL_FN_READ:
+ {
+ pStat = &g_StatRead;
+ pStatFail = &g_StatReadFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_READ\n"));
+ /* Verify parameter count and types. */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_READ, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* handle */
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* offset */
+ ASSERT_GUEST_STMT_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* count */
+ ASSERT_GUEST_STMT_BREAK( paParms[4].type == VBOX_HGCM_SVC_PARM_PTR
+ || paParms[4].type == VBOX_HGCM_SVC_PARM_PAGES, rc = VERR_WRONG_PARAMETER_TYPE); /* buffer */
+
+ /* Fetch parameters. */
+ SHFLROOT const idRoot = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE const hFile = paParms[1].u.uint64;
+ uint64_t const offFile = paParms[2].u.uint64;
+ uint32_t cbRead = paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ ASSERT_GUEST_STMT_BREAK(hFile != SHFL_HANDLE_ROOT, rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(hFile != SHFL_HANDLE_NIL, rc = VERR_INVALID_HANDLE);
+ if (paParms[4].type == VBOX_HGCM_SVC_PARM_PTR)
+ ASSERT_GUEST_STMT_BREAK(cbRead <= paParms[4].u.pointer.size, rc = VERR_INVALID_HANDLE);
+ else
+ ASSERT_GUEST_STMT_BREAK(cbRead <= paParms[4].u.Pages.cb, rc = VERR_OUT_OF_RANGE);
+
+ /* Execute the function. */
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1;
+ }
+
+ if (paParms[4].type == VBOX_HGCM_SVC_PARM_PTR)
+ rc = vbsfRead(pClient, idRoot, hFile, offFile, &cbRead, (uint8_t *)paParms[4].u.pointer.addr);
+ else
+ rc = vbsfReadPages(pClient, idRoot, hFile, offFile, &cbRead, &paParms[4].u.Pages);
+
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fReading = 0;
+
+ /* Update parameters.*/
+ paParms[3].u.uint32 = RT_SUCCESS(rc) ? cbRead : 0 /* nothing read */;
+ break;
+ }
+
+ /* Write new object content. */
+ case SHFL_FN_WRITE:
+ {
+ pStat = &g_StatWrite;
+ pStatFail = &g_StatWriteFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_WRITE\n"));
+
+ /* Verify parameter count and types. */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_WRITE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* handle */
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* offset */
+ ASSERT_GUEST_STMT_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* count */
+ ASSERT_GUEST_STMT_BREAK( paParms[4].type == VBOX_HGCM_SVC_PARM_PTR
+ || paParms[4].type == VBOX_HGCM_SVC_PARM_PAGES, rc = VERR_WRONG_PARAMETER_TYPE); /* buffer */
+ /* Fetch parameters. */
+ SHFLROOT const idRoot = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE const hFile = paParms[1].u.uint64;
+ uint64_t offFile = paParms[2].u.uint64;
+ uint32_t cbWrite = paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ ASSERT_GUEST_STMT_BREAK(hFile != SHFL_HANDLE_ROOT, rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(hFile != SHFL_HANDLE_NIL, rc = VERR_INVALID_HANDLE);
+ if (paParms[4].type == VBOX_HGCM_SVC_PARM_PTR)
+ ASSERT_GUEST_STMT_BREAK(cbWrite <= paParms[4].u.pointer.size, rc = VERR_INVALID_HANDLE);
+ else
+ ASSERT_GUEST_STMT_BREAK(cbWrite <= paParms[4].u.Pages.cb, rc = VERR_OUT_OF_RANGE);
+
+ /* Execute the function. */
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fWriting = g_pStatusLed->Actual.s.fWriting = 1;
+ }
+
+ if (paParms[4].type == VBOX_HGCM_SVC_PARM_PTR)
+ rc = vbsfWrite(pClient, idRoot, hFile, &offFile, &cbWrite, (uint8_t *)paParms[4].u.pointer.addr);
+ else
+ rc = vbsfWritePages(pClient, idRoot, hFile, &offFile, &cbWrite, &paParms[4].u.Pages);
+
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fWriting = 0;
+
+ /* Update parameters.*/
+ if (RT_SUCCESS(rc))
+ {
+ paParms[3].u.uint32 = cbWrite;
+ paParms[2].u.uint64 = offFile;
+ }
+ else
+ paParms[3].u.uint32 = 0;
+ break;
+ }
+
+ /* Lock/unlock a range in the object. */
+ case SHFL_FN_LOCK:
+ pStat = &g_StatLock;
+ pStatFail = &g_StatLockFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_LOCK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_LOCK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_64BIT /* length */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint64_t offset = paParms[2].u.uint64;
+ uint64_t length = paParms[3].u.uint64;
+ uint32_t flags = paParms[4].u.uint32;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else if (flags & SHFL_LOCK_WAIT)
+ {
+ /** @todo This should be properly implemented by the shared folders service.
+ * The service thread must never block. If an operation requires
+ * blocking, it must be processed by another thread and when it is
+ * completed, the another thread must call
+ *
+ * g_pHelpers->pfnCallComplete (callHandle, rc);
+ *
+ * The operation is async.
+ * fAsynchronousProcessing = true;
+ */
+
+ /* Here the operation must be posted to another thread. At the moment it is not implemented.
+ * Until it is implemented, try to perform the operation without waiting.
+ */
+ flags &= ~SHFL_LOCK_WAIT;
+
+ /* Execute the function. */
+ if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ rc = vbsfUnlock(pClient, root, Handle, offset, length, flags);
+ else
+ rc = vbsfLock(pClient, root, Handle, offset, length, flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* none */
+ }
+ }
+ else
+ {
+ /* Execute the function. */
+ if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ rc = vbsfUnlock(pClient, root, Handle, offset, length, flags);
+ else
+ rc = vbsfLock(pClient, root, Handle, offset, length, flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* none */
+ }
+ }
+ }
+ break;
+
+ /* List object content. */
+ case SHFL_FN_LIST:
+ {
+ pStat = &g_StatList;
+ pStatFail = &g_StatListFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_LIST\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_LIST)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* pPath */
+ || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* resumePoint */
+ || paParms[7].type != VBOX_HGCM_SVC_PARM_32BIT /* cFiles (out) */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint32_t flags = paParms[2].u.uint32;
+ uint32_t length = paParms[3].u.uint32;
+ SHFLSTRING *pPath = (paParms[4].u.pointer.size == 0) ? 0 : (SHFLSTRING *)paParms[4].u.pointer.addr;
+ uint8_t *pBuffer = (uint8_t *)paParms[5].u.pointer.addr;
+ uint32_t resumePoint = paParms[6].u.uint32;
+ uint32_t cFiles = 0;
+
+ /* Verify parameters values. */
+ if ( (length < sizeof (SHFLDIRINFO))
+ || length > paParms[5].u.pointer.size
+ || !ShflStringIsValidOrNullIn(pPath, paParms[4].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (g_pStatusLed)
+ {
+ Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC);
+ g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1;
+ }
+
+ /* Execute the function. */
+ rc = vbsfDirList (pClient, root, Handle, pPath, flags, &length, pBuffer, &resumePoint, &cFiles);
+
+ if (g_pStatusLed)
+ g_pStatusLed->Actual.s.fReading = 0;
+
+ if (rc == VERR_NO_MORE_FILES && cFiles != 0)
+ rc = VINF_SUCCESS; /* Successfully return these files. */
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = length;
+ paParms[6].u.uint32 = resumePoint;
+ paParms[7].u.uint32 = cFiles;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ paParms[6].u.uint32 = 0;
+ paParms[7].u.uint32 = cFiles;
+ }
+ }
+ }
+ break;
+ }
+
+ /* Read symlink destination */
+ case SHFL_FN_READLINK:
+ {
+ pStat = &g_StatReadLink;
+ pStatFail = &g_StatReadLinkFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_READLINK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_READLINK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t cbPath = paParms[1].u.pointer.size;
+ uint8_t *pBuffer = (uint8_t *)paParms[2].u.pointer.addr;
+ uint32_t cbBuffer = paParms[2].u.pointer.size;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidOrNullIn(pPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfReadLink (pClient, root, pPath, cbPath, pBuffer, cbBuffer);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+
+ break;
+ }
+
+ /* Legacy interface */
+ case SHFL_FN_MAP_FOLDER_OLD:
+ {
+ pStatFail = pStat = &g_StatMapFolderOld;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER_OLD\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_MAP_FOLDER_OLD)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr;
+ SHFLROOT root = (SHFLROOT)paParms[1].u.uint32;
+ RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMapFolder (pClient, pszMapName, delimiter, false, &root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.uint32 = root;
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_MAP_FOLDER:
+ {
+ pStat = &g_StatMapFolder;
+ pStatFail = &g_StatMapFolderFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER\n"));
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ Log(("SharedFolders host service: request to map folder '%s'\n",
+ ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.utf8));
+ else
+ Log(("SharedFolders host service: request to map folder '%ls'\n",
+ ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.ucs2));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_MAP_FOLDER)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* fCaseSensitive */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr;
+ SHFLROOT root = (SHFLROOT)paParms[1].u.uint32;
+ RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32;
+ bool fCaseSensitive = !!paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_INVALID_PARAMETER;
+
+ /* Fudge for windows GAs getting the length wrong by one char. */
+ if ( !(pClient->fu32Flags & SHFL_CF_UTF8)
+ && paParms[0].u.pointer.size >= sizeof(SHFLSTRING)
+ && pszMapName->u16Length >= 2
+ && pszMapName->String.ucs2[pszMapName->u16Length / 2 - 1] == 0x0000)
+ {
+ pszMapName->u16Length -= 2;
+ if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, false /*fUtf8Not16*/))
+ rc = VINF_SUCCESS;
+ else
+ pszMapName->u16Length += 2;
+ }
+ }
+
+ /* Execute the function. */
+ if (RT_SUCCESS(rc))
+ rc = vbsfMapFolder (pClient, pszMapName, delimiter, fCaseSensitive, &root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[1].u.uint32 = root;
+ }
+ }
+ Log(("SharedFolders host service: map operation result %Rrc\n", rc));
+ if (RT_SUCCESS(rc))
+ Log(("SharedFolders host service: mapped to handle %d\n", paParms[1].u.uint32));
+ break;
+ }
+
+ case SHFL_FN_UNMAP_FOLDER:
+ {
+ pStat = &g_StatUnmapFolder;
+ pStatFail = &g_StatUnmapFolderFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_UNMAP_FOLDER\n"));
+ Log(("SharedFolders host service: request to unmap folder handle %u\n",
+ paParms[0].u.uint32));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_UNMAP_FOLDER)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+
+ /* Execute the function. */
+ rc = vbsfUnmapFolder (pClient, root);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ /* nothing */
+ }
+ }
+ Log(("SharedFolders host service: unmap operation result %Rrc\n", rc));
+ break;
+ }
+
+ /* Query/set object information. */
+ case SHFL_FN_INFORMATION:
+ {
+ pStatFail = pStat = &g_StatInformationFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_INFORMATION\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_INFORMATION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+ uint32_t flags = paParms[2].u.uint32;
+ uint32_t length = paParms[3].u.uint32;
+ uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (length > paParms[4].u.pointer.size)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ if (flags & SHFL_INFO_SET)
+ {
+ rc = vbsfSetFSInfo (pClient, root, Handle, flags, &length, pBuffer);
+
+ if (flags & SHFL_INFO_FILE)
+ {
+ pStat = &g_StatInformationSetFile;
+ pStatFail = &g_StatInformationSetFileFail;
+ }
+ else if (flags & SHFL_INFO_SIZE)
+ {
+ pStat = &g_StatInformationSetSize;
+ pStatFail = &g_StatInformationSetSizeFail;
+ }
+ }
+ else /* SHFL_INFO_GET */
+ {
+ rc = vbsfQueryFSInfo (pClient, root, Handle, flags, &length, pBuffer);
+
+ if (flags & SHFL_INFO_FILE)
+ {
+ pStat = &g_StatInformationGetFile;
+ pStatFail = &g_StatInformationGetFileFail;
+ }
+ else if (flags & SHFL_INFO_VOLUME)
+ {
+ pStat = &g_StatInformationGetVolume;
+ pStatFail = &g_StatInformationGetVolumeFail;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ paParms[3].u.uint32 = length;
+ }
+ else
+ {
+ paParms[3].u.uint32 = 0; /* nothing read */
+ }
+ }
+ }
+ break;
+ }
+
+ /* Remove or rename object */
+ case SHFL_FN_REMOVE:
+ {
+ pStat = &g_StatRemove;
+ pStatFail = &g_StatRemoveFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE\n"));
+
+ /* Verify parameter count and types. */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_REMOVE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, rc = VERR_WRONG_PARAMETER_TYPE); /* path */
+ PCSHFLSTRING pStrPath = (PCSHFLSTRING)paParms[1].u.pointer.addr;
+ ASSERT_GUEST_STMT_BREAK(ShflStringIsValidIn(pStrPath, paParms[1].u.pointer.size,
+ RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)),
+ rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* flags */
+ uint32_t const fFlags = paParms[2].u.uint32;
+ ASSERT_GUEST_STMT_BREAK(!(fFlags & ~(SHFL_REMOVE_FILE | SHFL_REMOVE_DIR | SHFL_REMOVE_SYMLINK)),
+ rc = VERR_INVALID_FLAGS);
+
+ /* Execute the function. */
+ rc = vbsfRemove(pClient, paParms[0].u.uint32, pStrPath, paParms[1].u.pointer.size, fFlags, SHFL_HANDLE_NIL);
+ break;
+ }
+
+ case SHFL_FN_CLOSE_AND_REMOVE:
+ {
+ pStat = &g_StatCloseAndRemove;
+ pStatFail = &g_StatCloseAndRemoveFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_CLOSE_AND_REMOVE\n"));
+
+ /* Verify parameter count and types. */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_CLOSE_AND_REMOVE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, rc = VERR_WRONG_PARAMETER_TYPE); /* path */
+ PCSHFLSTRING pStrPath = (PCSHFLSTRING)paParms[1].u.pointer.addr;
+ ASSERT_GUEST_STMT_BREAK(ShflStringIsValidIn(pStrPath, paParms[1].u.pointer.size,
+ RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)),
+ rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* flags */
+ uint32_t const fFlags = paParms[2].u.uint32;
+ ASSERT_GUEST_STMT_BREAK(!(fFlags & ~(SHFL_REMOVE_FILE | SHFL_REMOVE_DIR | SHFL_REMOVE_SYMLINK)),
+ rc = VERR_INVALID_FLAGS);
+ SHFLHANDLE const hToClose = paParms[3].u.uint64;
+ ASSERT_GUEST_STMT_BREAK(hToClose != SHFL_HANDLE_ROOT, rc = VERR_INVALID_HANDLE);
+
+ /* Execute the function. */
+ rc = vbsfRemove(pClient, paParms[0].u.uint32, pStrPath, paParms[1].u.pointer.size, fFlags, hToClose);
+ break;
+ }
+
+ case SHFL_FN_RENAME:
+ {
+ pStat = &g_StatRename;
+ pStatFail = &g_StatRenameFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_RENAME\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_RENAME)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* src */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* dest */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pSrc = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ SHFLSTRING *pDest = (SHFLSTRING *)paParms[2].u.pointer.addr;
+ uint32_t flags = paParms[3].u.uint32;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pSrc, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || !ShflStringIsValidIn(pDest, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfRename (pClient, root, pSrc, pDest, flags);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ break;
+ }
+
+ case SHFL_FN_FLUSH:
+ {
+ pStat = &g_StatFlush;
+ pStatFail = &g_StatFlushFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_FLUSH\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_FLUSH)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLHANDLE Handle = paParms[1].u.uint64;
+
+ /* Verify parameters values. */
+ if (Handle == SHFL_HANDLE_ROOT)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ if (Handle == SHFL_HANDLE_NIL)
+ {
+ AssertMsgFailed(("Invalid handle!\n"));
+ rc = VERR_INVALID_HANDLE;
+ }
+ else
+ {
+ /* Execute the function. */
+
+ rc = vbsfFlush (pClient, root, Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Nothing to do */
+ }
+ }
+ }
+ } break;
+
+ case SHFL_FN_SET_UTF8:
+ {
+ pStatFail = pStat = &g_StatSetUtf8;
+
+ pClient->fu32Flags |= SHFL_CF_UTF8;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case SHFL_FN_SYMLINK:
+ {
+ pStat = &g_StatSymlink;
+ pStatFail = &g_StatSymlinkFail;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_SYMLINK\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_SYMLINK)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* newPath */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* oldPath */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* info */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLROOT root = (SHFLROOT)paParms[0].u.uint32;
+ SHFLSTRING *pNewPath = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ SHFLSTRING *pOldPath = (SHFLSTRING *)paParms[2].u.pointer.addr;
+ SHFLFSOBJINFO *pInfo = (SHFLFSOBJINFO *)paParms[3].u.pointer.addr;
+ uint32_t cbInfo = paParms[3].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pNewPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || !ShflStringIsValidIn(pOldPath, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
+ || (cbInfo != sizeof(SHFLFSOBJINFO))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfSymlink (pClient, root, pNewPath, pOldPath, pInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ }
+ break;
+
+ case SHFL_FN_SET_SYMLINKS:
+ {
+ pStatFail = pStat = &g_StatSetSymlinks;
+
+ pClient->fu32Flags |= SHFL_CF_SYMLINKS;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case SHFL_FN_QUERY_MAP_INFO:
+ {
+ pStatFail = pStat = &g_StatQueryMapInfo;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_QUERY_MAP_INFO\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_QUERY_MAP_INFO);
+ ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* root */
+ ASSERT_GUEST_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR); /* name */
+ PSHFLSTRING pNameBuf = (PSHFLSTRING)paParms[1].u.pointer.addr;
+ ASSERT_GUEST_BREAK(ShflStringIsValidOut(pNameBuf, paParms[1].u.pointer.size));
+ ASSERT_GUEST_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_PTR); /* mountPoint */
+ PSHFLSTRING pMntPtBuf = (PSHFLSTRING)paParms[2].u.pointer.addr;
+ ASSERT_GUEST_BREAK(ShflStringIsValidOut(pMntPtBuf, paParms[2].u.pointer.size));
+ ASSERT_GUEST_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_64BIT); /* flags */
+ ASSERT_GUEST_BREAK(!(paParms[3].u.uint64 & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH))); /* flags */
+ ASSERT_GUEST_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_32BIT); /* version */
+
+ /* Execute the function: */
+ rc = vbsfMappingsQueryInfo(pClient, paParms[0].u.uint32, pNameBuf, pMntPtBuf,
+ &paParms[3].u.uint64, &paParms[4].u.uint32);
+ break;
+ }
+
+ case SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES:
+ {
+ pStat = &g_StatWaitForMappingsChanges;
+ pStatFail = &g_StatWaitForMappingsChangesFail;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_WAIT_FOR_MAPPINGS_CHANGES);
+ ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* uFolderMappingsVersion */
+
+ /* Execute the function: */
+ rc = vbsfMappingsWaitForChanges(pClient, callHandle, paParms, g_pHelpers->pfnIsCallRestored(callHandle));
+ fAsynchronousProcessing = rc == VINF_HGCM_ASYNC_EXECUTE;
+ break;
+ }
+
+ case SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS:
+ {
+ pStatFail = pStat = &g_StatCancelMappingsChangesWait;
+ Log(("SharedFolders host service: svnCall: SHFL_FN_CANCEL_WAIT_FOR_CHANGES\n"));
+
+ /* Validate input: */
+ rc = VERR_INVALID_PARAMETER;
+ ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_CANCEL_MAPPINGS_CHANGES_WAITS);
+
+ /* Execute the function: */
+ rc = vbsfMappingsCancelChangesWaits(pClient);
+ break;
+ }
+
+ case SHFL_FN_SET_FILE_SIZE:
+ {
+ pStat = &g_StatSetFileSize;
+ pStatFail = &g_StatSetFileSizeFail;
+ Log(("SharedFolders host service: svcCall: SHFL_FN_SET_FILE_SIZE\n"));
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_SET_FILE_SIZE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* id32Root */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u64Handle */
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* cb64NewSize */
+
+ /* Execute the function: */
+ rc = vbsfSetFileSize(pClient, paParms[0].u.uint32, paParms[1].u.uint64, paParms[2].u.uint64);
+ break;
+ }
+
+ case SHFL_FN_QUERY_FEATURES:
+ {
+ pStat = pStatFail = &g_StatQueryFeatures;
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_QUERY_FEATURES, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* f64Features */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u32LastFunction */
+
+ /* Execute the function: */
+ paParms[0].u.uint64 = SHFL_FEATURE_WRITE_UPDATES_OFFSET;
+ paParms[1].u.uint32 = SHFL_FN_LAST;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case SHFL_FN_COPY_FILE:
+ {
+ pStat = &g_StatCopyFile;
+ pStatFail = &g_StatCopyFileFail;
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_COPY_FILE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* i32RootSrc */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, rc = VERR_WRONG_PARAMETER_TYPE); /* pStrPathSrc */
+ PCSHFLSTRING pStrPathSrc = (PCSHFLSTRING)paParms[1].u.pointer.addr;
+ ASSERT_GUEST_STMT_BREAK(ShflStringIsValidIn(pStrPathSrc, paParms[1].u.pointer.size,
+ RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)),
+ rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* i32RootDst */
+ ASSERT_GUEST_STMT_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_PTR, rc = VERR_WRONG_PARAMETER_TYPE); /* pStrPathDst */
+ PCSHFLSTRING pStrPathDst = (PCSHFLSTRING)paParms[3].u.pointer.addr;
+ ASSERT_GUEST_STMT_BREAK(ShflStringIsValidIn(pStrPathDst, paParms[3].u.pointer.size,
+ RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)),
+ rc = VERR_INVALID_PARAMETER);
+ ASSERT_GUEST_STMT_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* f32Flags */
+ ASSERT_GUEST_STMT_BREAK(paParms[4].u.uint32 == 0, rc = VERR_INVALID_FLAGS);
+
+ /* Execute the function: */
+ rc = vbsfCopyFile(pClient, paParms[0].u.uint32, pStrPathSrc, paParms[2].u.uint64, pStrPathDst, paParms[3].u.uint32);
+ break;
+ }
+
+
+ case SHFL_FN_COPY_FILE_PART:
+ {
+ pStat = &g_StatCopyFilePart;
+ pStatFail = &g_StatCopyFilePartFail;
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_COPY_FILE_PART, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* i32RootSrc */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u64HandleSrc */
+ ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* off64Src */
+ ASSERT_GUEST_STMT_BREAK((int64_t)paParms[2].u.uint64 >= 0, rc = VERR_NEGATIVE_SEEK);
+ ASSERT_GUEST_STMT_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* i32RootDst */
+ ASSERT_GUEST_STMT_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u64HandleDst */
+ ASSERT_GUEST_STMT_BREAK(paParms[5].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* off64Dst */
+ ASSERT_GUEST_STMT_BREAK((int64_t)paParms[5].u.uint64 >= 0, rc = VERR_NEGATIVE_SEEK);
+ ASSERT_GUEST_STMT_BREAK(paParms[6].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* cb64ToCopy */
+ ASSERT_GUEST_STMT_BREAK(paParms[6].u.uint64 < _1E, rc = VERR_OUT_OF_RANGE);
+ ASSERT_GUEST_STMT_BREAK(paParms[7].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* f32Flags */
+ ASSERT_GUEST_STMT_BREAK(paParms[7].u.uint32 == 0, rc = VERR_INVALID_FLAGS);
+
+ /* Execute the function: */
+ rc = vbsfCopyFilePart(pClient,
+ paParms[0].u.uint32, paParms[1].u.uint64, paParms[2].u.uint64,
+ paParms[3].u.uint32, paParms[4].u.uint64, paParms[5].u.uint64,
+ &paParms[6].u.uint64, paParms[7].u.uint64);
+ break;
+ }
+
+ case SHFL_FN_SET_ERROR_STYLE:
+ {
+ pStatFail = pStat = &g_StatSetErrorStyle;
+
+ /* Validate input: */
+ ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_SET_ERROR_STYLE, rc = VERR_WRONG_PARAMETER_COUNT);
+ ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* enm32Style */
+ ASSERT_GUEST_STMT_BREAK( paParms[0].u.uint32 > (uint32_t)kShflErrorStyle_Invalid
+ && paParms[0].u.uint32 < (uint32_t)kShflErrorStyle_End, rc = VERR_WRONG_PARAMETER_TYPE);
+ ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u32Reserved */
+ ASSERT_GUEST_STMT_BREAK(paParms[1].u.uint32 == 0, rc = VERR_WRONG_PARAMETER_TYPE);
+
+ /* Do the work: */
+ pClient->enmErrorStyle = (uint8_t)paParms[0].u.uint32;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ pStatFail = pStat = &g_StatUnknown;
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+ }
+
+ LogFlow(("SharedFolders host service: svcCall: rc=%Rrc\n", rc));
+
+ if ( !fAsynchronousProcessing
+ || RT_FAILURE (rc))
+ {
+ /* Complete the operation if it was unsuccessful or
+ * it was processed synchronously.
+ */
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+
+#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
+ /* Statistics: */
+ uint64_t cTicks;
+ STAM_GET_TS(cTicks);
+ cTicks -= tsStart;
+ if (RT_SUCCESS(rc))
+ STAM_REL_PROFILE_ADD_PERIOD(pStat, cTicks);
+ else
+ STAM_REL_PROFILE_ADD_PERIOD(pStatFail, cTicks);
+#endif
+
+ LogFlow(("\n")); /* Add a new line to differentiate between calls more easily. */
+}
+
+/*
+ * We differentiate between a function handler for the guest (svcCall) and one
+ * for the host. The guest is not allowed to add or remove mappings for obvious
+ * security reasons.
+ */
+static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n", u32Function, cParms, paParms));
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case SHFL_FN_ADD_MAPPING:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_ADD_MAPPING\n"));
+ LogRel(("SharedFolders host service: Adding host mapping\n"));
+ /* Verify parameter count and types. */
+ if ( (cParms != SHFL_CPARMS_ADD_MAPPING)
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* host folder path */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* map name */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* fFlags */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* auto mount point */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLSTRING *pHostPath = (SHFLSTRING *)paParms[0].u.pointer.addr;
+ SHFLSTRING *pMapName = (SHFLSTRING *)paParms[1].u.pointer.addr;
+ uint32_t fFlags = paParms[2].u.uint32;
+ SHFLSTRING *pAutoMountPoint = (SHFLSTRING *)paParms[3].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if ( !ShflStringIsValidIn(pHostPath, paParms[0].u.pointer.size, false /*fUtf8Not16*/)
+ || !ShflStringIsValidIn(pMapName, paParms[1].u.pointer.size, false /*fUtf8Not16*/)
+ || !ShflStringIsValidIn(pAutoMountPoint, paParms[3].u.pointer.size, false /*fUtf8Not16*/)
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogRel((" Host path '%ls', map name '%ls', %s, automount=%s, automntpnt=%ls, create_symlinks=%s, missing=%s\n",
+ pHostPath->String.utf16, pMapName->String.utf16,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE) ? "writable" : "read-only",
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT) ? "true" : "false",
+ pAutoMountPoint->String.utf16,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS) ? "true" : "false",
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING) ? "true" : "false"));
+
+ char *pszHostPath;
+ rc = RTUtf16ToUtf8(pHostPath->String.ucs2, &pszHostPath);
+ if (RT_SUCCESS(rc))
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsAdd(pszHostPath, pMapName,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE),
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT),
+ pAutoMountPoint,
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS),
+ RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING),
+ /* fPlaceholder = */ false);
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ RTStrFree(pszHostPath);
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ LogRel(("SharedFolders host service: Adding host mapping failed with rc=%Rrc\n", rc));
+ break;
+ }
+
+ case SHFL_FN_REMOVE_MAPPING:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE_MAPPING\n"));
+ LogRel(("SharedFolders host service: Removing host mapping '%ls'\n",
+ ((SHFLSTRING *)paParms[0].u.pointer.addr)->String.ucs2));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_REMOVE_MAPPING)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ SHFLSTRING *pString = (SHFLSTRING *)paParms[0].u.pointer.addr;
+
+ /* Verify parameters values. */
+ if (!ShflStringIsValidIn(pString, paParms[0].u.pointer.size, false /*fUtf8Not16*/))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ rc = vbsfMappingsRemove (pString);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Update parameters.*/
+ ; /* none */
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ LogRel(("SharedFolders host service: Removing host mapping failed with rc=%Rrc\n", rc));
+ break;
+ }
+
+ case SHFL_FN_SET_STATUS_LED:
+ {
+ Log(("SharedFolders host service: svcCall: SHFL_FN_SET_STATUS_LED\n"));
+
+ /* Verify parameter count and types. */
+ if (cParms != SHFL_CPARMS_SET_STATUS_LED)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Fetch parameters. */
+ PPDMLED pLed = (PPDMLED)paParms[0].u.pointer.addr;
+ uint32_t cbLed = paParms[0].u.pointer.size;
+
+ /* Verify parameters values. */
+ if ( (cbLed != sizeof (PDMLED))
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Execute the function. */
+ g_pStatusLed = pLed;
+ rc = VINF_SUCCESS;
+ }
+ }
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ LogFlow(("SharedFolders host service: svcHostCall ended with rc=%Rrc\n", rc));
+ return rc;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
+{
+ int rc = VINF_SUCCESS;
+
+ Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable = %p\n", ptable));
+
+ if (!RT_VALID_PTR(ptable))
+ {
+ LogRelFunc(("SharedFolders host service: Bad value of ptable (%p)\n", ptable));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable->cbSize = %u, ptable->u32Version = 0x%08X\n",
+ ptable->cbSize, ptable->u32Version));
+
+ if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ LogRelFunc(("SharedFolders host service: Version mismatch while loading: ptable->cbSize = %u (should be %u), ptable->u32Version = 0x%08X (should be 0x%08X)\n",
+ ptable->cbSize, sizeof (VBOXHGCMSVCFNTABLE), ptable->u32Version, VBOX_HGCM_SVC_VERSION));
+ rc = VERR_VERSION_MISMATCH;
+ }
+ else
+ {
+ g_pHelpers = ptable->pHelpers;
+
+ ptable->cbClient = sizeof (SHFLCLIENTDATA);
+
+ /* Map legacy clients to the kernel category. */
+ ptable->idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_KERNEL;
+
+ /* Only 64K pending calls per kernel client, root gets 16K and regular users 1K. */
+ ptable->acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_KERNEL] = _64K;
+ ptable->acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_ROOT] = _16K;
+ ptable->acMaxCallsPerClient[HGCM_CLIENT_CATEGORY_USER] = _1K;
+
+ /* Reduce the number of clients to SHFL_MAX_MAPPINGS + 2 in each category,
+ so the increased calls-per-client value causes less trouble.
+ ((64 + 2) * 3 * 65536 = 12 976 128) */
+ for (uintptr_t i = 0; i < RT_ELEMENTS(ptable->acMaxClients); i++)
+ ptable->acMaxClients[i] = SHFL_MAX_MAPPINGS + 2;
+
+ ptable->pfnUnload = svcUnload;
+ ptable->pfnConnect = svcConnect;
+ ptable->pfnDisconnect = svcDisconnect;
+ ptable->pfnCall = svcCall;
+ ptable->pfnHostCall = svcHostCall;
+ ptable->pfnSaveState = svcSaveState;
+ ptable->pfnLoadState = svcLoadState;
+ ptable->pfnNotify = NULL;
+ ptable->pvService = NULL;
+ }
+
+ /* Init handle table */
+ rc = vbsfInitHandleTable();
+ AssertRC(rc);
+
+ vbsfMappingInit();
+
+ /* Finally, register statistics if everything went well: */
+ if (RT_SUCCESS(rc))
+ {
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappings, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_QUERY_MAPPINGS successes", "/HGCM/VBoxSharedFolders/FnQueryMappings");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappingsFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_QUERY_MAPPINGS failures", "/HGCM/VBoxSharedFolders/FnQueryMappingsFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapName, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_QUERY_MAP_NAME", "/HGCM/VBoxSharedFolders/FnQueryMapName");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreate, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CREATE/CREATE successes", "/HGCM/VBoxSharedFolders/FnCreate");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreateFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CREATE/CREATE failures", "/HGCM/VBoxSharedFolders/FnCreateFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CREATE/LOOKUP successes", "/HGCM/VBoxSharedFolders/FnLookup");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookupFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CREATE/LOOKUP failures", "/HGCM/VBoxSharedFolders/FnLookupFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatClose, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CLOSE successes", "/HGCM/VBoxSharedFolders/FnClose");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCloseFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CLOSE failures", "/HGCM/VBoxSharedFolders/FnCloseFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_READ successes", "/HGCM/VBoxSharedFolders/FnRead");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_READ failures", "/HGCM/VBoxSharedFolders/FnReadFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_WRITE successes", "/HGCM/VBoxSharedFolders/FnWrite");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWriteFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_WRITE failures", "/HGCM/VBoxSharedFolders/FnWriteFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLock, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_LOCK successes", "/HGCM/VBoxSharedFolders/FnLock");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLockFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_LOCK failures", "/HGCM/VBoxSharedFolders/FnLockFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatList, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_LIST successes", "/HGCM/VBoxSharedFolders/FnList");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatListFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_LIST failures", "/HGCM/VBoxSharedFolders/FnListFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_READLINK successes", "/HGCM/VBoxSharedFolders/FnReadLink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_READLINK failures", "/HGCM/VBoxSharedFolders/FnReadLinkFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderOld, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_MAP_FOLDER_OLD", "/HGCM/VBoxSharedFolders/FnMapFolderOld");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_MAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnMapFolder");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_MAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnMapFolderFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_UNMAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnUnmapFolder");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_UNMAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnUnmapFolderFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION early failures", "/HGCM/VBoxSharedFolders/FnInformationFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/SET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationSetFile");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/SET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationSetFileFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSize, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/SET/SIZE successes", "/HGCM/VBoxSharedFolders/FnInformationSetSize");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSizeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/SET/SIZE failures", "/HGCM/VBoxSharedFolders/FnInformationSetSizeFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/GET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationGetFile");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/GET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationGetFileFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolume, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/GET/VOLUME successes", "/HGCM/VBoxSharedFolders/FnInformationGetVolume");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolumeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_INFORMATION/GET/VOLUME failures", "/HGCM/VBoxSharedFolders/FnInformationGetVolumeFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemove, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_REMOVE successes", "/HGCM/VBoxSharedFolders/FnRemove");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemoveFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_REMOVE failures", "/HGCM/VBoxSharedFolders/FnRemoveFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCloseAndRemove, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CLOSE_AND_REMOVE successes", "/HGCM/VBoxSharedFolders/FnCloseAndRemove");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCloseAndRemoveFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CLOSE_AND_REMOVE failures", "/HGCM/VBoxSharedFolders/FnCloseAndRemoveFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRename, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_RENAME successes", "/HGCM/VBoxSharedFolders/FnRename");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRenameFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_RENAME failures", "/HGCM/VBoxSharedFolders/FnRenameFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlush, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_FLUSH successes", "/HGCM/VBoxSharedFolders/FnFlush");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlushFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_FLUSH failures", "/HGCM/VBoxSharedFolders/FnFlushFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetErrorStyle, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_SET_ERROR_STYLE", "/HGCM/VBoxSharedFolders/FnSetErrorStyle");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetUtf8, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_SET_UTF8", "/HGCM/VBoxSharedFolders/FnSetUtf8");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_SYMLINK successes", "/HGCM/VBoxSharedFolders/FnSymlink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_SYMLINK failures", "/HGCM/VBoxSharedFolders/FnSymlinkFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetSymlinks, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_SET_SYMLINKS", "/HGCM/VBoxSharedFolders/FnSetSymlink");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapInfo, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_QUERY_MAP_INFO", "/HGCM/VBoxSharedFolders/FnQueryMapInfo");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryFeatures, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_QUERY_FEATURES", "/HGCM/VBoxSharedFolders/FnQueryFeatures");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCopyFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_COPY_FILE successes", "/HGCM/VBoxSharedFolders/FnCopyFile");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCopyFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_COPY_FILE failures", "/HGCM/VBoxSharedFolders/FnCopyFileFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCopyFilePart, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_COPY_FILE_PART successes", "/HGCM/VBoxSharedFolders/FnCopyFilePart");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCopyFilePartFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_COPY_FILE_PART failures", "/HGCM/VBoxSharedFolders/FnCopyFilePartFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChanges, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES successes", "/HGCM/VBoxSharedFolders/FnWaitForMappingsChanges");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChangesFail,STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES failures","/HGCM/VBoxSharedFolders/FnWaitForMappingsChangesFail");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCancelMappingsChangesWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS", "/HGCM/VBoxSharedFolders/FnCancelMappingsChangesWaits");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnknown, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "SHFL_FN_???", "/HGCM/VBoxSharedFolders/FnUnknown");
+ HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMsgStage1, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Time from VMMDev arrival to worker thread.","/HGCM/VBoxSharedFolders/MsgStage1");
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc
new file mode 100644
index 00000000..05a89bc8
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc
@@ -0,0 +1,61 @@
+/* $Id: VBoxSharedFoldersSvc.rc $ */
+/** @file
+ * VBoxSharedFolders - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Shared Folders Host Service\0"
+ VALUE "InternalName", "VBoxSharedFolders\0"
+ VALUE "OriginalFilename", "VBoxSharedFolders.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/SharedFolders/mappings.cpp b/src/VBox/HostServices/SharedFolders/mappings.cpp
new file mode 100644
index 00000000..6a5b80f0
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/mappings.cpp
@@ -0,0 +1,1024 @@
+/* $Id: mappings.cpp $ */
+/** @file
+ * Shared Folders Service - Mappings support.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "mappings.h"
+#include "vbsfpath.h"
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/list.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <VBox/AssertGuest.h>
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern PVBOXHGCMSVCHELPERS g_pHelpers; /* service.cpp */
+
+
+/* Shared folders order in the saved state and in the g_FolderMapping can differ.
+ * So a translation array of root handle is needed.
+ */
+
+static MAPPING g_FolderMapping[SHFL_MAX_MAPPINGS];
+static SHFLROOT g_aIndexFromRoot[SHFL_MAX_MAPPINGS];
+/**< Array running parallel to g_aIndexFromRoot and which entries are increased
+ * as an root handle is added or removed.
+ *
+ * This helps the guest figuring out that a mapping may have been reconfigured
+ * or that saved state has been restored. Entry reuse is very likely given that
+ * vbsfRootHandleAdd() always starts searching at the start for an unused entry.
+ */
+static uint32_t g_auRootHandleVersions[SHFL_MAX_MAPPINGS];
+/** Version number that is increased for every change made.
+ * This is used by the automount guest service to wait for changes.
+ * @note This does not need saving, the guest should be woken up and refresh
+ * its sate when restored. */
+static uint32_t volatile g_uFolderMappingsVersion = 0;
+
+
+/** For recording async vbsfMappingsWaitForChanges calls. */
+typedef struct SHFLMAPPINGSWAIT
+{
+ RTLISTNODE ListEntry; /**< List entry. */
+ PSHFLCLIENTDATA pClient; /**< The client that's waiting. */
+ VBOXHGCMCALLHANDLE hCall; /**< The call handle to signal completion with. */
+ PVBOXHGCMSVCPARM pParm; /**< The 32-bit unsigned parameter to stuff g_uFolderMappingsVersion into. */
+} SHFLMAPPINGSWAIT;
+/** Pointer to async mappings change wait. */
+typedef SHFLMAPPINGSWAIT *PSHFLMAPPINGSWAIT;
+/** List head for clients waiting on mapping changes (SHFLMAPPINGSWAIT). */
+static RTLISTANCHOR g_MappingsChangeWaiters;
+/** Number of clients waiting on mapping changes.
+ * We use this to limit the number of waiting calls the clients can make. */
+static uint32_t g_cMappingChangeWaiters = 0;
+static void vbsfMappingsWakeupAllWaiters(void);
+
+
+void vbsfMappingInit(void)
+{
+ unsigned root;
+
+ for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+ }
+
+ RTListInit(&g_MappingsChangeWaiters);
+}
+
+/**
+ * Called before loading mappings from saved state to drop the root IDs.
+ */
+void vbsfMappingLoadingStart(void)
+{
+ for (SHFLROOT idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
+ g_aIndexFromRoot[idRoot] = SHFL_ROOT_NIL;
+
+ for (SHFLROOT i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
+ g_FolderMapping[i].fLoadedRootId = false;
+}
+
+/**
+ * Called when a mapping is loaded to restore the root ID and make sure it
+ * exists.
+ *
+ * @returns VBox status code.
+ */
+int vbsfMappingLoaded(const MAPPING *pLoadedMapping, SHFLROOT root)
+{
+ /* Mapping loaded from the saved state with the 'root' index. Which means
+ * the guest uses the 'root' as root handle for this folder.
+ * Check whether there is the same mapping in g_FolderMapping and
+ * update the g_aIndexFromRoot.
+ *
+ * Also update the mapping properties, which were lost: cMappings.
+ */
+ if (root >= SHFL_MAX_MAPPINGS)
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+ SHFLROOT i;
+ for (i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
+ {
+ MAPPING *pMapping = &g_FolderMapping[i];
+
+ /* Equal? */
+ if ( pLoadedMapping->fValid == pMapping->fValid
+ && ShflStringSizeOfBuffer(pLoadedMapping->pMapName) == ShflStringSizeOfBuffer(pMapping->pMapName)
+ && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0)
+ {
+ Log(("vbsfMappingLoaded: root=%u i=%u (was %u) (%ls)\n",
+ root, i, g_aIndexFromRoot[root], pLoadedMapping->pMapName->String.utf16));
+
+ if (!pMapping->fLoadedRootId)
+ {
+ /* First encounter. */
+ pMapping->fLoadedRootId = true;
+
+ /* Update the mapping properties. */
+ pMapping->cMappings = pLoadedMapping->cMappings;
+ }
+ else
+ {
+ /* When pMapping->fLoadedRootId is already true it means that another HGCM client uses the same mapping. */
+ Assert(pMapping->cMappings > 1);
+ }
+
+ /* Actual index is i. Remember that when the guest uses 'root' it is actually 'i'. */
+ AssertLogRelMsg(g_aIndexFromRoot[root] == SHFL_ROOT_NIL,
+ ("idRoot=%u: current %u ([%s]), new %u (%ls [%s])\n",
+ root, g_aIndexFromRoot[root], g_FolderMapping[g_aIndexFromRoot[root]].pszFolderName,
+ i, pLoadedMapping->pMapName->String.utf16, pLoadedMapping->pszFolderName));
+ g_aIndexFromRoot[root] = i;
+
+ /* The mapping is known to the host and is used by the guest.
+ * No need for a 'placeholder'.
+ */
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* No corresponding mapping on the host but the guest still uses it.
+ * Add a 'placeholder' mapping.
+ */
+ LogRel2(("SharedFolders: mapping a placeholder for '%ls' -> '%s'\n",
+ pLoadedMapping->pMapName->String.ucs2, pLoadedMapping->pszFolderName));
+ return vbsfMappingsAdd(pLoadedMapping->pszFolderName, pLoadedMapping->pMapName,
+ pLoadedMapping->fWritable, pLoadedMapping->fAutoMount, pLoadedMapping->pAutoMountPoint,
+ pLoadedMapping->fSymlinksCreate, /* fMissing = */ true, /* fPlaceholder = */ true);
+}
+
+/**
+ * Called after loading mappings from saved state to make sure every mapping has
+ * a root ID.
+ */
+void vbsfMappingLoadingDone(void)
+{
+ for (SHFLROOT iMapping = 0; iMapping < RT_ELEMENTS(g_FolderMapping); iMapping++)
+ if (g_FolderMapping[iMapping].fValid)
+ {
+ AssertLogRel(g_FolderMapping[iMapping].pMapName);
+ AssertLogRel(g_FolderMapping[iMapping].pszFolderName);
+
+ SHFLROOT idRoot;
+ for (idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
+ if (g_aIndexFromRoot[idRoot] == iMapping)
+ break;
+ if (idRoot >= RT_ELEMENTS(g_aIndexFromRoot))
+ {
+ for (idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
+ if (g_aIndexFromRoot[idRoot] == SHFL_ROOT_NIL)
+ break;
+ if (idRoot < RT_ELEMENTS(g_aIndexFromRoot))
+ g_aIndexFromRoot[idRoot] = iMapping;
+ else
+ LogRel(("SharedFolders: Warning! No free root ID entry for mapping #%u: %ls [%s]\n", iMapping,
+ g_FolderMapping[iMapping].pMapName->String.ucs2, g_FolderMapping[iMapping].pszFolderName));
+ }
+ }
+
+ /* Log the root ID mappings: */
+ if (LogRelIs2Enabled())
+ for (SHFLROOT idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
+ {
+ SHFLROOT const iMapping = g_aIndexFromRoot[idRoot];
+ if (iMapping != SHFL_ROOT_NIL)
+ LogRel2(("SharedFolders: idRoot %u: iMapping #%u: %ls [%s]\n", idRoot, iMapping,
+ g_FolderMapping[iMapping].pMapName->String.ucs2, g_FolderMapping[iMapping].pszFolderName));
+ }
+}
+
+
+MAPPING *vbsfMappingGetByRoot(SHFLROOT root)
+{
+ if (root < RT_ELEMENTS(g_aIndexFromRoot))
+ {
+ SHFLROOT iMapping = g_aIndexFromRoot[root];
+
+ if ( iMapping != SHFL_ROOT_NIL
+ && iMapping < RT_ELEMENTS(g_FolderMapping))
+ {
+ return &g_FolderMapping[iMapping];
+ }
+ }
+
+ return NULL;
+}
+
+static SHFLROOT vbsfMappingGetRootFromIndex(SHFLROOT iMapping)
+{
+ unsigned root;
+
+ for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (iMapping == g_aIndexFromRoot[root])
+ {
+ return root;
+ }
+ }
+
+ return SHFL_ROOT_NIL;
+}
+
+static MAPPING *vbsfMappingGetByName(PRTUTF16 pwszName, SHFLROOT *pRoot)
+{
+ for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if ( g_FolderMapping[i].fValid
+ && !g_FolderMapping[i].fPlaceholder) /* Don't allow mapping placeholders. */
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pwszName))
+ {
+ SHFLROOT root = vbsfMappingGetRootFromIndex(i);
+
+ if (root != SHFL_ROOT_NIL)
+ {
+ if (pRoot)
+ {
+ *pRoot = root;
+ }
+ return &g_FolderMapping[i];
+ }
+ AssertFailed();
+ }
+ }
+ }
+ return NULL;
+}
+
+static void vbsfRootHandleAdd(SHFLROOT iMapping)
+{
+ for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (g_aIndexFromRoot[root] == SHFL_ROOT_NIL)
+ {
+ g_aIndexFromRoot[root] = iMapping;
+ g_auRootHandleVersions[root] += 1;
+ return;
+ }
+ }
+
+ AssertFailed();
+}
+
+static void vbsfRootHandleRemove(SHFLROOT iMapping)
+{
+ unsigned cFound = 0;
+
+ for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
+ {
+ if (g_aIndexFromRoot[root] == iMapping)
+ {
+ g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
+ g_auRootHandleVersions[root] += 1;
+ Log(("vbsfRootHandleRemove: Removed root=%u (iMapping=%u)\n", root, iMapping));
+
+ /* Note! Do not stop here as g_aIndexFromRoot may (at least it could
+ prior to the introduction of fLoadedRootId) contain
+ duplicates after restoring save state. */
+ cFound++;
+ }
+ }
+
+ Assert(cFound > 0); RT_NOREF(cFound);
+}
+
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_ADD_MAPPING API. Located here as a form of API
+ * documentation. */
+void testMappingsAdd(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMappingsAddBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+/*
+ * We are always executed from one specific HGCM thread. So thread safe.
+ */
+int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable,
+ bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fSymlinksCreate, bool fMissing, bool fPlaceholder)
+{
+ unsigned i;
+
+ Assert(pszFolderName && pMapName);
+
+ Log(("vbsfMappingsAdd %ls\n", pMapName->String.ucs2));
+
+ /* Check for duplicates, ignoring placeholders to give the GUI to change stuff at runtime. */
+ /** @todo bird: Not entirely sure about ignoring placeholders, but you cannot
+ * trigger auto-umounting without ignoring them. */
+ if (!fPlaceholder)
+ {
+ for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if ( g_FolderMapping[i].fValid
+ && !g_FolderMapping[i].fPlaceholder)
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+ {
+ AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2));
+ return VERR_ALREADY_EXISTS;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if (g_FolderMapping[i].fValid == false)
+ {
+ /* Make sure the folder name is an absolute path, otherwise we're
+ likely to get into trouble with buffer sizes in vbsfPathGuestToHost. */
+ char szAbsFolderName[RTPATH_MAX];
+ int rc = vbsfPathAbs(NULL, pszFolderName, szAbsFolderName, sizeof(szAbsFolderName));
+ AssertRCReturn(rc, rc);
+
+ g_FolderMapping[i].pszFolderName = RTStrDup(szAbsFolderName);
+ g_FolderMapping[i].pMapName = ShflStringDup(pMapName);
+ g_FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint);
+ if ( !g_FolderMapping[i].pszFolderName
+ || !g_FolderMapping[i].pMapName
+ || !g_FolderMapping[i].pAutoMountPoint)
+ {
+ RTStrFree(g_FolderMapping[i].pszFolderName);
+ RTMemFree(g_FolderMapping[i].pMapName);
+ RTMemFree(g_FolderMapping[i].pAutoMountPoint);
+ return VERR_NO_MEMORY;
+ }
+
+ g_FolderMapping[i].fValid = true;
+ g_FolderMapping[i].cMappings = 0;
+ g_FolderMapping[i].fWritable = fWritable;
+ g_FolderMapping[i].fAutoMount = fAutoMount;
+ g_FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
+ g_FolderMapping[i].fMissing = fMissing;
+ g_FolderMapping[i].fPlaceholder = fPlaceholder;
+ g_FolderMapping[i].fLoadedRootId = false;
+
+ /* Check if the host file system is case sensitive */
+ RTFSPROPERTIES prop;
+ prop.fCaseSensitive = false; /* Shut up MSC. */
+ rc = RTFsQueryProperties(g_FolderMapping[i].pszFolderName, &prop);
+#ifndef DEBUG_bird /* very annoying */
+ AssertRC(rc);
+#endif
+ g_FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
+ vbsfRootHandleAdd(i);
+ vbsfMappingsWakeupAllWaiters();
+ break;
+ }
+ }
+ if (i == SHFL_MAX_MAPPINGS)
+ {
+ AssertLogRelMsgFailed(("vbsfMappingsAdd: no more room to add mapping %s to %ls!!\n", pszFolderName, pMapName->String.ucs2));
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ Log(("vbsfMappingsAdd: added mapping %s to %ls (slot %u, root %u)\n",
+ pszFolderName, pMapName->String.ucs2, i, vbsfMappingGetRootFromIndex(i)));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_REMOVE_MAPPING API. Located here as a form of API
+ * documentation. */
+void testMappingsRemove(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMappingsRemoveBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfMappingsRemove(PSHFLSTRING pMapName)
+{
+ Assert(pMapName);
+ Log(("vbsfMappingsRemove %ls\n", pMapName->String.ucs2));
+
+ /*
+ * We must iterate thru the whole table as may have 0+ placeholder entries
+ * and 0-1 regular entries with the same name. Also, it is good to kick
+ * the guest automounter into action wrt to evicting placeholders.
+ */
+ int rc = VERR_FILE_NOT_FOUND;
+ for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ if (g_FolderMapping[i].fValid == true)
+ {
+ if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
+ {
+ if (g_FolderMapping[i].cMappings != 0)
+ {
+ LogRel2(("SharedFolders: removing '%ls' -> '%s'%s, which is still used by the guest\n", pMapName->String.ucs2,
+ g_FolderMapping[i].pszFolderName, g_FolderMapping[i].fPlaceholder ? " (again)" : ""));
+ g_FolderMapping[i].fMissing = true;
+ g_FolderMapping[i].fPlaceholder = true;
+ vbsfMappingsWakeupAllWaiters();
+ rc = VINF_PERMISSION_DENIED;
+ }
+ else
+ {
+ /* pMapName can be the same as g_FolderMapping[i].pMapName when
+ * called from vbsfUnmapFolder, log it before deallocating the memory. */
+ Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2));
+ bool fSame = g_FolderMapping[i].pMapName == pMapName;
+
+ RTStrFree(g_FolderMapping[i].pszFolderName);
+ RTMemFree(g_FolderMapping[i].pMapName);
+ RTMemFree(g_FolderMapping[i].pAutoMountPoint);
+ g_FolderMapping[i].pszFolderName = NULL;
+ g_FolderMapping[i].pMapName = NULL;
+ g_FolderMapping[i].pAutoMountPoint = NULL;
+ g_FolderMapping[i].fValid = false;
+ vbsfRootHandleRemove(i);
+ vbsfMappingsWakeupAllWaiters();
+ if (rc == VERR_FILE_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ if (fSame)
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+const char* vbsfMappingsQueryHostRoot(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, NULL);
+ if (pFolderMapping->fMissing)
+ return NULL;
+ return pFolderMapping->pszFolderName;
+}
+
+int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(hRoot);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+ if (pFolderMapping->fMissing)
+ return VERR_NOT_FOUND;
+ if ( pFolderMapping->pszFolderName == NULL
+ || pFolderMapping->pszFolderName[0] == 0)
+ return VERR_NOT_FOUND;
+ *ppszRoot = pFolderMapping->pszFolderName;
+ *pcbRootLen = (uint32_t)strlen(pFolderMapping->pszFolderName);
+ return VINF_SUCCESS;
+}
+
+bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, false);
+ return pFolderMapping->fGuestCaseSensitive;
+}
+
+bool vbsfIsHostMappingCaseSensitive(SHFLROOT root)
+{
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, false);
+ return pFolderMapping->fHostCaseSensitive;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_QUERY_MAPPINGS API. Located here as a form of API
+ * documentation (or should it better be inline in include/VBox/shflsvc.h?) */
+void testMappingsQuery(RTTEST hTest)
+{
+ /* The API should return all mappings if we provide enough buffers. */
+ testMappingsQuerySimple(hTest);
+ /* If we provide too few buffers that should be signalled correctly. */
+ testMappingsQueryTooFewBuffers(hTest);
+ /* The SHFL_MF_AUTOMOUNT flag means return only auto-mounted mappings. */
+ testMappingsQueryAutoMount(hTest);
+ /* The mappings return array must have numberOfMappings entries. */
+ testMappingsQueryArrayWrongSize(hTest);
+}
+#endif
+/**
+ * @note If pMappings / *pcMappings is smaller than the actual amount of
+ * mappings that *could* have been returned *pcMappings contains the
+ * required buffer size so that the caller can retry the operation if
+ * wanted.
+ */
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings)
+{
+ LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n",
+ pClient, pMappings, pcMappings, *pcMappings));
+
+ uint32_t const cMaxMappings = *pcMappings;
+ uint32_t idx = 0;
+ for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++)
+ {
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
+ if ( pFolderMapping != NULL
+ && pFolderMapping->fValid
+ && ( !fOnlyAutoMounts
+ || (pFolderMapping->fAutoMount && !pFolderMapping->fPlaceholder)) )
+ {
+ if (idx < cMaxMappings)
+ {
+ pMappings[idx].u32Status = SHFL_MS_NEW;
+ pMappings[idx].root = i;
+ }
+ idx++;
+ }
+ }
+
+ /* Return actual number of mappings, regardless whether the handed in
+ * mapping buffer was big enough. */
+ /** @todo r=bird: This is non-standard interface behaviour. We return
+ * VERR_BUFFER_OVERFLOW or at least a VINF_BUFFER_OVERFLOW here.
+ *
+ * Guess this goes well along with ORing SHFL_MF_AUTOMOUNT into
+ * pClient->fu32Flags rather than passing it as fOnlyAutoMounts...
+ * Not amused by this. */
+ *pcMappings = idx;
+
+ RT_NOREF_PV(pClient);
+ LogFlow(("vbsfMappingsQuery: returns VINF_SUCCESS (idx=%u, cMaxMappings=%u)\n", idx, cMaxMappings));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_QUERY_MAP_NAME API. Located here as a form of API
+ * documentation. */
+void testMappingsQueryName(RTTEST hTest)
+{
+ /* If we query an valid mapping it should be returned. */
+ testMappingsQueryNameValid(hTest);
+ /* If we query an invalid mapping that should be signalled. */
+ testMappingsQueryNameInvalid(hTest);
+ /* If we pass in a bad string buffer that should be detected. */
+ testMappingsQueryNameBadBuffer(hTest);
+}
+#endif
+int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString)
+{
+ LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n", pClient, root, pString));
+
+ int rc;
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping)
+ {
+ if (pFolderMapping->fValid)
+ {
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ rc = ShflStringCopyUtf16BufAsUtf8(pString, pFolderMapping->pMapName);
+ else
+ {
+ /* Not using ShlfStringCopy here as behaviour shouldn't change... */
+ if (pString->u16Size < pFolderMapping->pMapName->u16Size)
+ {
+ Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
+ pString->u16Size, pFolderMapping->pMapName->u16Size));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ pString->u16Length = pFolderMapping->pMapName->u16Length;
+ memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2,
+ pFolderMapping->pMapName->u16Size);
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ LogFlow(("vbsfMappingsQuery:Name return rc = %Rrc\n", rc));
+ return rc;
+}
+
+/** Queries fWritable flag for the given root. Returns error if the root is not accessible.
+ */
+int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryWritable: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if ( pFolderMapping->fValid
+ && !pFolderMapping->fMissing)
+ *fWritable = pFolderMapping->fWritable;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQuery:Writable return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if (pFolderMapping->fValid == true)
+ *fAutoMount = pFolderMapping->fAutoMount;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQueryAutoMount:Writable return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
+
+ if (pFolderMapping->fValid == true)
+ *fSymlinksCreate = pFolderMapping->fSymlinksCreate;
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ LogFlow(("vbsfMappingsQueryAutoMount:SymlinksCreate return rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+/**
+ * Implements SHFL_FN_QUERY_MAP_INFO.
+ * @since VBox 6.0
+ */
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+ uint64_t *pfFlags, uint32_t *puVersion)
+{
+ LogFlow(("vbsfMappingsQueryInfo: pClient=%p root=%d\n", pClient, root));
+
+ /* Resolve the root handle. */
+ int rc;
+ PMAPPING pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping)
+ {
+ if (pFolderMapping->fValid)
+ {
+ /*
+ * Produce the output.
+ */
+ *puVersion = g_auRootHandleVersions[root];
+
+ *pfFlags = 0;
+ if (pFolderMapping->fWritable)
+ *pfFlags |= SHFL_MIF_WRITABLE;
+ if (pFolderMapping->fAutoMount)
+ *pfFlags |= SHFL_MIF_AUTO_MOUNT;
+ if (pFolderMapping->fHostCaseSensitive)
+ *pfFlags |= SHFL_MIF_HOST_ICASE;
+ if (pFolderMapping->fGuestCaseSensitive)
+ *pfFlags |= SHFL_MIF_GUEST_ICASE;
+ if (pFolderMapping->fSymlinksCreate)
+ *pfFlags |= SHFL_MIF_SYMLINK_CREATION;
+
+ int rc2;
+ if (pClient->fu32Flags & SHFL_CF_UTF8)
+ {
+ rc = ShflStringCopyUtf16BufAsUtf8(pNameBuf, pFolderMapping->pMapName);
+ rc2 = ShflStringCopyUtf16BufAsUtf8(pMntPtBuf, pFolderMapping->pAutoMountPoint);
+ }
+ else
+ {
+ rc = ShflStringCopy(pNameBuf, pFolderMapping->pMapName, sizeof(RTUTF16));
+ rc2 = ShflStringCopy(pMntPtBuf, pFolderMapping->pAutoMountPoint, sizeof(RTUTF16));
+ }
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ LogFlow(("vbsfMappingsQueryInfo: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_MAP_FOLDER API. Located here as a form of API
+ * documentation. */
+void testMapFolder(RTTEST hTest)
+{
+ /* If we try to map a valid name we should get the root. */
+ testMapFolderValid(hTest);
+ /* If we try to map a valid name we should get VERR_FILE_NOT_FOUND. */
+ testMapFolderInvalid(hTest);
+ /* If we map a folder twice we can unmap it twice.
+ * Currently unmapping too often is only asserted but not signalled. */
+ testMapFolderTwice(hTest);
+ /* The delimiter should be converted in e.g. file delete operations. */
+ testMapFolderDelimiter(hTest);
+ /* Test case sensitive mapping by opening a file with the wrong case. */
+ testMapFolderCaseSensitive(hTest);
+ /* Test case insensitive mapping by opening a file with the wrong case. */
+ testMapFolderCaseInsensitive(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testMapFolderBadParameters(hTest);
+}
+#endif
+int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName,
+ RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
+{
+ MAPPING *pFolderMapping = NULL;
+
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ Log(("vbsfMapFolder %s\n", pszMapName->String.utf8));
+ }
+ else
+ {
+ Log(("vbsfMapFolder %ls\n", pszMapName->String.ucs2));
+ }
+
+ AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\',
+ ("Invalid path delimiter: %#x\n", wcDelimiter),
+ VERR_INVALID_PARAMETER);
+ if (pClient->PathDelimiter == 0)
+ {
+ pClient->PathDelimiter = wcDelimiter;
+ }
+ else
+ {
+ AssertMsgReturn(wcDelimiter == pClient->PathDelimiter,
+ ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter),
+ VERR_INVALID_PARAMETER);
+ }
+
+ SHFLROOT RootTmp;
+ if (!pRoot)
+ pRoot = &RootTmp;
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ int rc;
+ PRTUTF16 utf16Name;
+
+ rc = RTStrToUtf16((const char *) pszMapName->String.utf8, &utf16Name);
+ if (RT_FAILURE (rc))
+ return rc;
+
+ pFolderMapping = vbsfMappingGetByName(utf16Name, pRoot);
+ RTUtf16Free(utf16Name);
+ }
+ else
+ {
+ pFolderMapping = vbsfMappingGetByName(pszMapName->String.ucs2, pRoot);
+ }
+
+ if (!pFolderMapping)
+ {
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Check for reference count overflows and settings compatibility.
+ * For paranoid reasons, we don't allow modifying the case sensitivity
+ * setting while there are other mappings of a folder.
+ */
+ AssertLogRelReturn(*pRoot < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
+ AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[*pRoot] < _32K, VERR_TOO_MANY_OPENS);
+ ASSERT_GUEST_LOGREL_MSG_RETURN( pFolderMapping->cMappings == 0
+ || pFolderMapping->fGuestCaseSensitive == fCaseSensitive,
+ ("Incompatible case sensitivity setting: %s: %u mappings, %ssenitive, requested %ssenitive!\n",
+ pFolderMapping->pszFolderName, pFolderMapping->cMappings,
+ pFolderMapping->fGuestCaseSensitive ? "" : "in", fCaseSensitive ? "" : "in"),
+ VERR_INCOMPATIBLE_CONFIG);
+
+ /*
+ * Go ahead and map it.
+ */
+ if (pClient->fHasMappingCounts)
+ pClient->acMappings[*pRoot] += 1;
+ pFolderMapping->cMappings++;
+ pFolderMapping->fGuestCaseSensitive = fCaseSensitive;
+ Log(("vbsfMmapFolder (cMappings=%u, acMappings[%u]=%u)\n", pFolderMapping->cMappings, *pRoot, pClient->acMappings[*pRoot]));
+ return VINF_SUCCESS;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_UNMAP_FOLDER API. Located here as a form of API
+ * documentation. */
+void testUnmapFolder(RTTEST hTest)
+{
+ /* Unmapping a mapped folder should succeed.
+ * If the folder is not mapped this is only asserted, not signalled. */
+ testUnmapFolderValid(hTest);
+ /* Unmapping a non-existant root should fail. */
+ testUnmapFolderInvalid(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testUnmapFolderBadParameters(hTest);
+}
+#endif
+int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root)
+{
+ RT_NOREF1(pClient);
+ int rc = VINF_SUCCESS;
+
+ MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
+ if (pFolderMapping == NULL)
+ {
+ AssertFailed();
+ return VERR_FILE_NOT_FOUND;
+ }
+ Assert(pFolderMapping->fValid == true && pFolderMapping->cMappings > 0);
+
+ AssertLogRelReturn(root < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
+ AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[root] > 0, VERR_INVALID_HANDLE);
+
+ if (pClient->fHasMappingCounts)
+ pClient->acMappings[root] -= 1;
+
+ if (pFolderMapping->cMappings > 0)
+ pFolderMapping->cMappings--;
+
+ uint32_t const cMappings = pFolderMapping->cMappings;
+ if ( cMappings == 0
+ && pFolderMapping->fPlaceholder)
+ {
+ /* Automatically remove, it is not used by the guest anymore. */
+ Assert(pFolderMapping->fMissing);
+ LogRel2(("SharedFolders: unmapping placeholder '%ls' -> '%s'\n",
+ pFolderMapping->pMapName->String.ucs2, pFolderMapping->pszFolderName));
+ vbsfMappingsRemove(pFolderMapping->pMapName);
+ }
+
+ Log(("vbsfUnmapFolder (cMappings=%u, acMappings[%u]=%u)\n", cMappings, root, pClient->acMappings[root]));
+ return rc;
+}
+
+/**
+ * SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES implementation.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on change.
+ * @retval VINF_TRY_AGAIN on resume.
+ * @retval VINF_HGCM_ASYNC_EXECUTE if waiting.
+ * @retval VERR_CANCELLED if cancelled.
+ * @retval VERR_OUT_OF_RESOURCES if there are too many pending waits.
+ *
+ * @param pClient The calling client.
+ * @param hCall The call handle.
+ * @param pParm The parameter (32-bit).
+ * @param fRestored Set if this is a call restored & resubmitted from saved
+ * state.
+ * @since VBox 6.0
+ */
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored)
+{
+ /*
+ * Return immediately if the fodler mappings have changed since last call
+ * or if we got restored from saved state (adding of global folders, etc).
+ */
+ uint32_t uCurVersion = g_uFolderMappingsVersion;
+ if ( pParm->u.uint32 != uCurVersion
+ || fRestored
+ || (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) )
+ {
+ int rc = VINF_SUCCESS;
+ if (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT)
+ {
+ pClient->fu32Flags &= ~SHFL_CF_CANCEL_NEXT_WAIT;
+ rc = VERR_CANCELLED;
+ }
+ else if (fRestored)
+ {
+ rc = VINF_TRY_AGAIN;
+ if (pParm->u.uint32 == uCurVersion)
+ uCurVersion = uCurVersion != UINT32_C(0x55555555) ? UINT32_C(0x55555555) : UINT32_C(0x99999999);
+ }
+ Log(("vbsfMappingsWaitForChanges: Version %#x -> %#x, returning %Rrc immediately.\n", pParm->u.uint32, uCurVersion, rc));
+ pParm->u.uint32 = uCurVersion;
+ return rc;
+ }
+
+ /*
+ * Setup a wait if we can.
+ */
+ if (g_cMappingChangeWaiters < 64)
+ {
+ PSHFLMAPPINGSWAIT pWait = (PSHFLMAPPINGSWAIT)RTMemAlloc(sizeof(*pWait));
+ if (pWait)
+ {
+ pWait->pClient = pClient;
+ pWait->hCall = hCall;
+ pWait->pParm = pParm;
+
+ RTListAppend(&g_MappingsChangeWaiters, &pWait->ListEntry);
+ g_cMappingChangeWaiters += 1;
+ return VINF_HGCM_ASYNC_EXECUTE;
+ }
+ return VERR_NO_MEMORY;
+ }
+ LogRelMax(32, ("vbsfMappingsWaitForChanges: Too many threads waiting for changes!\n"));
+ return VERR_OUT_OF_RESOURCES;
+}
+
+/**
+ * SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS implementation.
+ *
+ * @returns VINF_SUCCESS
+ * @param pClient The calling client to cancel all waits for.
+ * @since VBox 6.0
+ */
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient)
+{
+ uint32_t const uCurVersion = g_uFolderMappingsVersion;
+
+ PSHFLMAPPINGSWAIT pCur, pNext;
+ RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+ {
+ if (pCur->pClient == pClient)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ pCur->pParm->u.uint32 = uCurVersion;
+ g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+ RTMemFree(pCur);
+ }
+ }
+
+ /* Set a flag to make sure the next SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES doesn't block.
+ This should help deal with races between this call and a thread about to do a wait. */
+ pClient->fu32Flags |= SHFL_CF_CANCEL_NEXT_WAIT;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wakes up all clients waiting on
+ */
+static void vbsfMappingsWakeupAllWaiters(void)
+{
+ uint32_t const uCurVersion = ++g_uFolderMappingsVersion;
+
+ PSHFLMAPPINGSWAIT pCur, pNext;
+ RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ pCur->pParm->u.uint32 = uCurVersion;
+ g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
+ RTMemFree(pCur);
+ }
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/mappings.h b/src/VBox/HostServices/SharedFolders/mappings.h
new file mode 100644
index 00000000..abeafb35
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/mappings.h
@@ -0,0 +1,92 @@
+/* $Id: mappings.h $ */
+/** @file
+ * Shared folders service - Mappings header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_mappings_h
+#define VBOX_INCLUDED_SRC_SharedFolders_mappings_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+typedef struct
+{
+ char *pszFolderName; /**< Directory at the host to share with the guest. */
+ PSHFLSTRING pMapName; /**< Share name for the guest. */
+ uint32_t cMappings; /**< Number of mappings. */
+ bool fValid; /**< Mapping entry is used/valid. */
+ bool fHostCaseSensitive; /**< Host file name space is case-sensitive. */
+ bool fGuestCaseSensitive; /**< Guest file name space is case-sensitive. */
+ bool fWritable; /**< Folder is writable for the guest. */
+ PSHFLSTRING pAutoMountPoint; /**< Where the guest should try auto-mount the folder. */
+ bool fAutoMount; /**< Folder will be auto-mounted by the guest. */
+ bool fSymlinksCreate; /**< Guest is able to create symlinks. */
+ bool fMissing; /**< Mapping not invalid but host path does not exist.
+ Any guest operation on such a folder fails! */
+ bool fPlaceholder; /**< Mapping does not exist in the VM settings but the guest
+ still has. fMissing is always true for this mapping. */
+ bool fLoadedRootId; /**< Set if vbsfMappingLoaded has found this mapping already. */
+} MAPPING;
+/** Pointer to a MAPPING structure. */
+typedef MAPPING *PMAPPING;
+
+void vbsfMappingInit(void);
+
+bool vbsfMappingQuery(uint32_t iMapping, PMAPPING *pMapping);
+
+int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable,
+ bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fCreateSymlinks, bool fMissing, bool fPlaceholder);
+int vbsfMappingsRemove(PSHFLSTRING pMapName);
+
+int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings);
+int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString);
+int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable);
+int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount);
+int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate);
+int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
+ uint64_t *pfFlags, uint32_t *puVersion);
+
+int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, RTUTF16 delimiter,
+ bool fCaseSensitive, SHFLROOT *pRoot);
+int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root);
+
+int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored);
+int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient);
+
+const char* vbsfMappingsQueryHostRoot(SHFLROOT root);
+int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen);
+bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root);
+bool vbsfIsHostMappingCaseSensitive(SHFLROOT root);
+
+void vbsfMappingLoadingStart(void);
+int vbsfMappingLoaded(MAPPING const *pLoadedMapping, SHFLROOT root);
+void vbsfMappingLoadingDone(void);
+PMAPPING vbsfMappingGetByRoot(SHFLROOT root);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_mappings_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/shfl.h b/src/VBox/HostServices/SharedFolders/shfl.h
new file mode 100644
index 00000000..04f83eb4
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shfl.h
@@ -0,0 +1,89 @@
+/** @file
+ * Shared Folders: Main header - Common data and function prototypes definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_shfl_h
+#define VBOX_INCLUDED_SRC_SharedFolders_shfl_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/err.h>
+#include <VBox/hgcmsvc.h>
+#include <VBox/shflsvc.h>
+
+#include <VBox/log.h>
+
+/** Shared Folders client flags.
+ * @{
+ */
+/** Client has queried mappings at least once and, therefore, the service can
+ * process its other requests too. */
+#define SHFL_CF_MAPPINGS_QUERIED (0x00000001)
+/** Mappings have been changed since last query. */
+#define SHFL_CF_MAPPINGS_CHANGED (0x00000002)
+/** Client uses UTF8 encoding, if not set then unicode 16 bit (UCS2) is used. */
+#define SHFL_CF_UTF8 (0x00000004)
+/** Client both supports and wants to use symlinks. */
+#define SHFL_CF_SYMLINKS (0x00000008)
+/** The call to SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES will return immediately
+ * because of a SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS call. */
+#define SHFL_CF_CANCEL_NEXT_WAIT (0x00000010)
+/** @} */
+
+/**
+ * @note This structure is dumped directly into the saved state, so care must be
+ * taken when extending it!
+ */
+typedef struct SHFLCLIENTDATA
+{
+ /** Client flags */
+ uint32_t fu32Flags;
+ /** Path delimiter. */
+ RTUTF16 PathDelimiter;
+ /** The error style, SHFLERRORSTYLE. */
+ uint8_t enmErrorStyle;
+ /** Set if the client has mapping usage counts.
+ * This is for helping with saved state. */
+ uint8_t fHasMappingCounts;
+ /** Mapping counts for each root ID so we can unmap the folders when the
+ * session disconnects or the VM resets. */
+ uint16_t acMappings[SHFL_MAX_MAPPINGS];
+} SHFLCLIENTDATA;
+/** Pointer to a SHFLCLIENTDATA structure. */
+typedef SHFLCLIENTDATA *PSHFLCLIENTDATA;
+
+
+/** @def SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX
+ * Whether to make windows error style adjustments on a posix host.
+ * This always returns false on windows hosts. */
+#ifdef RT_OS_WINDOWS
+# define SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(a_pClient) (false)
+#else
+# define SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(a_pClient) ((a_pClient)->enmErrorStyle == kShflErrorStyle_Windows)
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shfl_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.cpp b/src/VBox/HostServices/SharedFolders/shflhandle.cpp
new file mode 100644
index 00000000..abf734dc
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shflhandle.cpp
@@ -0,0 +1,236 @@
+/* $Id: shflhandle.cpp $ */
+/** @file
+ * Shared Folders Service - Handles helper functions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "shflhandle.h"
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Very basic and primitive handle management. Should be sufficient for our needs.
+ * Handle allocation can be rather slow, but at least lookup is fast.
+ */
+typedef struct
+{
+ uint32_t uFlags;
+ uintptr_t pvUserData;
+ PSHFLCLIENTDATA pClient;
+} SHFLINTHANDLE, *PSHFLINTHANDLE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static SHFLINTHANDLE *g_pHandles = NULL;
+static int32_t gLastHandleIndex = 0;
+static RTCRITSECT gLock;
+
+
+int vbsfInitHandleTable()
+{
+ g_pHandles = (SHFLINTHANDLE *)RTMemAllocZ (sizeof (SHFLINTHANDLE) * SHFLHANDLE_MAX);
+ if (!g_pHandles)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ /* Never return handle 0 */
+ g_pHandles[0].uFlags = SHFL_HF_TYPE_DONTUSE;
+ gLastHandleIndex = 1;
+
+ return RTCritSectInit(&gLock);
+}
+
+int vbsfFreeHandleTable()
+{
+ if (g_pHandles)
+ RTMemFree(g_pHandles);
+
+ g_pHandles = NULL;
+
+ if (RTCritSectIsInitialized(&gLock))
+ RTCritSectDelete(&gLock);
+
+ return VINF_SUCCESS;
+}
+
+SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType,
+ uintptr_t pvUserData)
+{
+ SHFLHANDLE handle;
+
+ Assert((uType & SHFL_HF_TYPE_MASK) != 0 && pvUserData);
+
+ RTCritSectEnter(&gLock);
+
+ /* Find next free handle */
+ if (gLastHandleIndex >= SHFLHANDLE_MAX-1)
+ gLastHandleIndex = 1;
+
+ /* Nice linear search */
+ for(handle=gLastHandleIndex;handle<SHFLHANDLE_MAX;handle++)
+ {
+ if (g_pHandles[handle].pvUserData == 0)
+ {
+ gLastHandleIndex = handle;
+ break;
+ }
+ }
+
+ if (handle == SHFLHANDLE_MAX)
+ {
+ /* Try once more from the start */
+ for(handle=1;handle<SHFLHANDLE_MAX;handle++)
+ {
+ if (g_pHandles[handle].pvUserData == 0)
+ {
+ gLastHandleIndex = handle;
+ break;
+ }
+ }
+ if (handle == SHFLHANDLE_MAX)
+ {
+ /* Out of handles */
+ RTCritSectLeave(&gLock);
+ AssertFailed();
+ return SHFL_HANDLE_NIL;
+ }
+ }
+ g_pHandles[handle].uFlags = (uType & SHFL_HF_TYPE_MASK) | SHFL_HF_VALID;
+ g_pHandles[handle].pvUserData = pvUserData;
+ g_pHandles[handle].pClient = pClient;
+
+ gLastHandleIndex++;
+
+ RTCritSectLeave(&gLock);
+
+ return handle;
+}
+
+static int vbsfFreeHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ {
+ g_pHandles[handle].uFlags = 0;
+ g_pHandles[handle].pvUserData = 0;
+ g_pHandles[handle].pClient = 0;
+ return VINF_SUCCESS;
+ }
+ return VERR_INVALID_HANDLE;
+}
+
+uintptr_t vbsfQueryHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle,
+ uint32_t uType)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ {
+ Assert((uType & SHFL_HF_TYPE_MASK) != 0);
+
+ if (g_pHandles[handle].uFlags & uType)
+ return g_pHandles[handle].pvUserData;
+ }
+ return 0;
+}
+
+SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle,
+ SHFL_HF_TYPE_FILE);
+}
+
+SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle,
+ SHFL_HF_TYPE_DIR);
+}
+
+uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient, SHFLHANDLE handle)
+{
+ if ( handle < SHFLHANDLE_MAX
+ && (g_pHandles[handle].uFlags & SHFL_HF_VALID)
+ && g_pHandles[handle].pClient == pClient)
+ return g_pHandles[handle].uFlags & SHFL_HF_TYPE_MASK;
+
+ return 0;
+}
+
+SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE));
+
+ if (pHandle)
+ {
+ pHandle->Header.u32Flags = SHFL_HF_TYPE_DIR;
+ return vbsfAllocHandle(pClient, pHandle->Header.u32Flags,
+ (uintptr_t)pHandle);
+ }
+
+ return SHFL_HANDLE_NIL;
+}
+
+SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE));
+
+ if (pHandle)
+ {
+ pHandle->Header.u32Flags = SHFL_HF_TYPE_FILE;
+ return vbsfAllocHandle(pClient, pHandle->Header.u32Flags,
+ (uintptr_t)pHandle);
+ }
+
+ return SHFL_HANDLE_NIL;
+}
+
+void vbsfFreeFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle)
+{
+ SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(pClient,
+ hHandle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
+
+ if (pHandle)
+ {
+ vbsfFreeHandle(pClient, hHandle);
+ RTMemFree (pHandle);
+ }
+ else
+ AssertFailed();
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.h b/src/VBox/HostServices/SharedFolders/shflhandle.h
new file mode 100644
index 00000000..c4a50fbf
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/shflhandle.h
@@ -0,0 +1,91 @@
+/* $Id: shflhandle.h $ */
+/** @file
+ * Shared Folders Host Service - Handles helper functions header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h
+#define VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+#include <iprt/dir.h>
+
+#define SHFL_HF_TYPE_MASK (0x000000FF)
+#define SHFL_HF_TYPE_DIR (0x00000001)
+#define SHFL_HF_TYPE_FILE (0x00000002)
+#define SHFL_HF_TYPE_VOLUME (0x00000004)
+#define SHFL_HF_TYPE_DONTUSE (0x00000080)
+
+#define SHFL_HF_VALID (0x80000000)
+
+#define SHFLHANDLE_MAX (4096)
+
+typedef struct _SHFLHANDLEHDR
+{
+ uint32_t u32Flags;
+} SHFLHANDLEHDR;
+
+#define ShflHandleType(__Handle) BIT_FLAG(((SHFLHANDLEHDR *)(__Handle))->u32Flags, SHFL_HF_TYPE_MASK)
+
+typedef struct _SHFLFILEHANDLE
+{
+ SHFLHANDLEHDR Header;
+ SHFLROOT root; /* Where the handle has been opened. */
+ union
+ {
+ struct
+ {
+ RTFILE Handle;
+ uint64_t fOpenFlags; /**< RTFILE_O_XXX. */
+ } file;
+ struct
+ {
+ RTDIR Handle;
+ RTDIR SearchHandle;
+ PRTDIRENTRYEX pLastValidEntry; /**< last found file in a directory search */
+ } dir;
+ };
+} SHFLFILEHANDLE;
+
+
+SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient);
+SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient);
+void vbsfFreeFileHandle (PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle);
+
+
+int vbsfInitHandleTable();
+int vbsfFreeHandleTable();
+SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType,
+ uintptr_t pvUserData);
+SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient,
+ SHFLHANDLE handle);
+SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle);
+uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient,
+ SHFLHANDLE handle);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h */
diff --git a/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk
new file mode 100644
index 00000000..c451d7a1
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk
@@ -0,0 +1,104 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Shared Folders Host Service testcases.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Structure size testcase.
+#
+PROGRAMS += tstShflSizes
+TESTING += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+ifndef VBOX_ONLY_SDK
+ ifeq ($(KBUILD_TARGET),$(KBUILD_HOST))
+ if1of ($(KBUILD_TARGET_ARCH).$(KBUILD_HOST_ARCH), x86.x86 amd64.amd64 x86.amd64)
+ OTHERS += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+ endif
+ endif
+endif
+tstShflSizes_TEMPLATE = VBoxR3AutoTest
+tstShflSizes_DEFS = VBOX_WITH_HGCM
+tstShflSizes_SOURCES = tstShflSizes.cpp
+tstShflSizes_CLEAN = $(tstShflSizes_0_OUTDIR)/tstShflSizes.run
+
+$$(tstShflSizes_0_OUTDIR)/tstShflSizes.run: $$(tstShflSizes_1_STAGE_TARGET)
+ $(tstShflSizes_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+
+
+ifdef VBOX_WITH_TESTCASES
+ #
+ # Case conversion testcase.
+ #
+ PROGRAMS += tstShflCase
+ tstShflCase_TEMPLATE = VBoxR3TstExe
+ tstShflCase_DEFS = VBOX_WITH_HGCM
+ tstShflCase_SOURCES = tstShflCase.cpp
+ tstShflCase_LIBS = $(LIB_RUNTIME)
+
+ #
+ # HGCM service testcase.
+ #
+
+ PROGRAMS += tstSharedFolderService
+ tstSharedFolderService_TEMPLATE = VBoxR3TstExe
+ tstSharedFolderService_DEFS = VBOX_WITH_HGCM UNITTEST
+ tstSharedFolderService_INCS = ..
+ tstSharedFolderService_SOURCES = \
+ tstSharedFolderService.cpp \
+ ../mappings.cpp \
+ ../VBoxSharedFoldersSvc.cpp \
+ ../shflhandle.cpp \
+ ../vbsfpathabs.cpp \
+ ../vbsfpath.cpp \
+ ../vbsf.cpp
+ tstSharedFolderService_LDFLAGS.darwin = \
+ -framework Carbon
+ tstSharedFolderService_LIBS = $(LIB_RUNTIME)
+
+ if 0 # Cannot define two RT_OS_XXX macros!
+ # As there are differences between the Windows build of the service and others,
+ # we do an additional build with RT_OS_WINDOWS defined on non-Windows targets.
+ PROGRAMS += \
+ tstSharedFolderService \
+ $(if $(eq $(KBUILD_TARGET),win),,tstSharedFolderService-win)
+ tstSharedFolderService-win_TEMPLATE = $(tstSharedFolderService_TEMPLATE)
+ tstSharedFolderService-win_DEFS = \
+ $(tstSharedFolderService_DEFS) \
+ RT_OS_WINDOWS
+ tstSharedFolderService-win_INCS = $(tstSharedFolderService_INCS)
+ tstSharedFolderService-win_SOURCES = $(tstSharedFolderService_SOURCES)
+ tstSharedFolderService-win_LDFLAGS.darwin = \
+ $(tstSharedFolderService_LDFLAGS.darwin)
+ tstSharedFolderService-win_LIBS = $(tstSharedFolderService_LIBS)
+ endif
+
+endif # VBOX_WITH_TESTCASES
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp
new file mode 100644
index 00000000..3f105284
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp
@@ -0,0 +1,1368 @@
+/* $Id: tstSharedFolderService.cpp $ */
+/** @file
+ * Testcase for the shared folder service vbsf API.
+ *
+ * Note that this is still very threadbare (there is an awful lot which should
+ * really be tested, but it already took too long to produce this much). The
+ * idea is that anyone who makes changes to the shared folders service and who
+ * cares about unit testing them should add tests to the skeleton framework to
+ * exercise the bits they change before and after changing them.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "tstSharedFolderService.h"
+#include "vbsf.h"
+
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/symlink.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "teststubs.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest = NIL_RTTEST;
+
+
+/*********************************************************************************************************************************
+* Declarations *
+*********************************************************************************************************************************/
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+
+
+/*********************************************************************************************************************************
+* Helpers *
+*********************************************************************************************************************************/
+
+/** Simple call handle structure for the guest call completion callback */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+ /** Where to store the result code */
+ int32_t rc;
+};
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+ callHandle->rc = rc;
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) stamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
+ STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list va)
+{
+ RT_NOREF(pvInstance, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) stamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va)
+{
+ RT_NOREF(pvInstance, pszPatFmt, va);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) infoRegister(void *pvInstance, const char *pszName, const char *pszDesc,
+ PFNDBGFHANDLEREXT pfnHandler, void *pvUser)
+{
+ RT_NOREF(pvInstance, pszName, pszDesc, pfnHandler, pvUser);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) infoDeregister(void *pvInstance, const char *pszName)
+{
+ RT_NOREF(pvInstance, pszName);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initialise the HGCM service table as much as we need to start the
+ * service
+ * @param pTable the table to initialise
+ */
+void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
+{
+ pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
+ pTable->u32Version = VBOX_HGCM_SVC_VERSION;
+ pHelpers->pfnCallComplete = callComplete;
+ pHelpers->pfnStamRegisterV = stamRegisterV;
+ pHelpers->pfnStamDeregisterV = stamDeregisterV;
+ pHelpers->pfnInfoRegister = infoRegister;
+ pHelpers->pfnInfoDeregister = infoDeregister;
+ pTable->pHelpers = pHelpers;
+}
+
+#define LLUIFY(a) ((unsigned long long)(a))
+
+static void bufferFromPath(char *pszDst, size_t cbDst, const char *pcszSrc)
+{
+ RTStrCopy(pszDst, cbDst, pcszSrc);
+ uintptr_t const uDstEnd = (uintptr_t)&pszDst[cbDst];
+ for (char *psz = pszDst; psz && (uintptr_t)psz < uDstEnd; ++psz)
+ if (*psz == '\\')
+ *psz = '/';
+}
+
+#define ARRAY_FROM_PATH(a, b) \
+ do { \
+ char *p = (a); NOREF(p); \
+ Assert((a) == p); /* Constant parameter */ \
+ Assert(sizeof((a)) > 0); \
+ bufferFromPath(a, sizeof(a), b); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Stub functions and data *
+*********************************************************************************************************************************/
+static bool g_fFailIfNotLowercase = false;
+
+static RTDIR g_testRTDirClose_hDir = NIL_RTDIR;
+
+extern int testRTDirClose(RTDIR hDir)
+{
+ /* RTPrintf("%s: hDir=%p\n", __PRETTY_FUNCTION__, hDir); */
+ g_testRTDirClose_hDir = hDir;
+ return VINF_SUCCESS;
+}
+
+static char g_testRTDirCreate_szPath[256];
+//static RTFMODE testRTDirCreateMode; - unused
+
+extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
+{
+ RT_NOREF2(fMode, fCreate);
+ /* RTPrintf("%s: pszPath=%s, fMode=0x%llx\n", __PRETTY_FUNCTION__, pszPath,
+ LLUIFY(fMode)); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(g_testRTDirCreate_szPath, pszPath);
+ return 0;
+}
+
+static char g_testRTDirOpen_szName[256];
+static struct TESTDIRHANDLE
+{
+ int iEntry;
+ int iDir;
+} g_aTestDirHandles[4];
+static int g_iNextDirHandle = 0;
+static RTDIR g_testRTDirOpen_hDir;
+
+extern int testRTDirOpen(RTDIR *phDir, const char *pszPath)
+{
+ /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(g_testRTDirOpen_szName, pszPath);
+ *phDir = g_testRTDirOpen_hDir;
+ g_testRTDirOpen_hDir = NIL_RTDIR;
+ if (!*phDir && g_fFailIfNotLowercase)
+ *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ if (*phDir)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir;
+ pRealDir->iEntry = 0;
+ pRealDir->iDir = 0;
+ const char *pszSlash = pszPath - 1;
+ while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL)
+ pRealDir->iDir += 1;
+ /*RTPrintf("opendir %s = %d \n", pszPath, pRealDir->iDir);*/
+ }
+ return VINF_SUCCESS;
+}
+
+/** @todo Do something useful with the last two arguments. */
+extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER, uint32_t)
+{
+ /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ ARRAY_FROM_PATH(g_testRTDirOpen_szName, pszPath);
+ *phDir = g_testRTDirOpen_hDir;
+ g_testRTDirOpen_hDir = NIL_RTDIR;
+ if (!*phDir && g_fFailIfNotLowercase)
+ *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ if (*phDir)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir;
+ pRealDir->iEntry = 0;
+ pRealDir->iDir = 0;
+ const char *pszSlash = pszPath - 1;
+ while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL)
+ pRealDir->iDir += 1;
+ pRealDir->iDir -= 1;
+ /*RTPrintf("openfiltered %s = %d\n", pszPath, pRealDir->iDir);*/
+ }
+ return VINF_SUCCESS;
+}
+
+static RTDIR g_testRTDirQueryInfo_hDir;
+static RTTIMESPEC g_testRTDirQueryInfo_ATime;
+
+extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF1(enmAdditionalAttribs);
+ /* RTPrintf("%s: hDir=%p, enmAdditionalAttribs=0x%llx\n", __PRETTY_FUNCTION__,
+ hDir, LLUIFY(enmAdditionalAttribs)); */
+ g_testRTDirQueryInfo_hDir = hDir;
+ RT_ZERO(*pObjInfo);
+ pObjInfo->AccessTime = g_testRTDirQueryInfo_ATime;
+ RT_ZERO(g_testRTDirQueryInfo_ATime);
+ return VINF_SUCCESS;
+}
+
+extern int testRTDirRemove(const char *pszPath)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+static RTDIR g_testRTDirReadEx_hDir;
+
+extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF4(pDirEntry, pcbDirEntry, enmAdditionalAttribs, fFlags);
+ /* RTPrintf("%s: hDir=%p, pcbDirEntry=%d, enmAdditionalAttribs=%llu, fFlags=0x%llx\n",
+ __PRETTY_FUNCTION__, hDir, pcbDirEntry ? (int) *pcbDirEntry : -1,
+ LLUIFY(enmAdditionalAttribs), LLUIFY(fFlags)); */
+ g_testRTDirReadEx_hDir = hDir;
+ if (g_fFailIfNotLowercase && hDir != NIL_RTDIR)
+ {
+ struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)hDir;
+ if (pRealDir->iDir == 2) /* /test/mapping/ */
+ {
+ if (pRealDir->iEntry == 0)
+ {
+ pRealDir->iEntry++;
+ RT_ZERO(*pDirEntry);
+ pDirEntry->Info.Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH;
+ pDirEntry->cbName = 4;
+ pDirEntry->cwcShortName = 4;
+ strcpy(pDirEntry->szName, "test");
+ RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "test");
+ /*RTPrintf("readdir: 'test'\n");*/
+ return VINF_SUCCESS;
+ }
+ }
+ else if (pRealDir->iDir == 3) /* /test/mapping/test/ */
+ {
+ if (pRealDir->iEntry == 0)
+ {
+ pRealDir->iEntry++;
+ RT_ZERO(*pDirEntry);
+ pDirEntry->Info.Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH;
+ pDirEntry->cbName = 4;
+ pDirEntry->cwcShortName = 4;
+ strcpy(pDirEntry->szName, "file");
+ RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "file");
+ /*RTPrintf("readdir: 'file'\n");*/
+ return VINF_SUCCESS;
+ }
+ }
+ /*else RTPrintf("%s: iDir=%d\n", pRealDir->iDir);*/
+ }
+ return VERR_NO_MORE_FILES;
+}
+
+static uint64_t g_testRTDirSetMode_fMode;
+
+extern int testRTDirSetMode(RTDIR hDir, RTFMODE fMode)
+{
+ RT_NOREF1(hDir);
+ /* RTPrintf("%s: fMode=%llu\n", __PRETTY_FUNCTION__, LLUIFY(fMode)); */
+ g_testRTDirSetMode_fMode = fMode;
+ return VINF_SUCCESS;
+}
+
+static RTTIMESPEC g_testRTDirSetTimes_ATime;
+
+extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF4(hDir, pModificationTime, pChangeTime, pBirthTime);
+ /* RTPrintf("%s: hDir=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n",
+ __PRETTY_FUNCTION__, hDir,
+ pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1,
+ pModificationTime
+ ? (long long)RTTimeSpecGetNano(pModificationTime) : -1,
+ pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1,
+ pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */
+ if (pAccessTime)
+ g_testRTDirSetTimes_ATime = *pAccessTime;
+ else
+ RT_ZERO(g_testRTDirSetTimes_ATime);
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileClose_hFile;
+
+extern int testRTFileClose(RTFILE File)
+{
+ /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */
+ g_testRTFileClose_hFile = File;
+ return 0;
+}
+
+extern int testRTFileDelete(const char *pszFilename)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+static RTFILE g_testRTFileFlush_hFile;
+
+extern int testRTFileFlush(RTFILE File)
+{
+ /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */
+ g_testRTFileFlush_hFile = File;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileLock_hFile;
+static unsigned g_testRTFileLock_fLock;
+static int64_t g_testRTFileLock_offLock;
+static uint64_t g_testRTFileLock_cbLock;
+
+extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /* RTPrintf("%s: hFile=%p, fLock=%u, offLock=%lli, cbLock=%llu\n",
+ __PRETTY_FUNCTION__, hFile, fLock, (long long) offLock,
+ LLUIFY(cbLock)); */
+ g_testRTFileLock_hFile = hFile;
+ g_testRTFileLock_fLock = fLock;
+ g_testRTFileLock_offLock = offLock;
+ g_testRTFileLock_cbLock = cbLock;
+ return VINF_SUCCESS;
+}
+
+static char g_testRTFileOpen_szName[256];
+static uint64_t g_testRTFileOpen_fOpen;
+static RTFILE g_testRTFileOpen_hFile;
+
+extern int testRTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
+{
+ /* RTPrintf("%s, pszFilename=%s, fOpen=0x%llx\n", __PRETTY_FUNCTION__,
+ pszFilename, LLUIFY(fOpen)); */
+ ARRAY_FROM_PATH(g_testRTFileOpen_szName, pszFilename);
+ g_testRTFileOpen_fOpen = fOpen;
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ *phFile = g_testRTFileOpen_hFile;
+ *penmActionTaken = RTFILEACTION_CREATED;
+ g_testRTFileOpen_hFile = 0;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileQueryInfo_hFile;
+static RTTIMESPEC g_testRTFileQueryInfo_ATime;
+static uint32_t g_testRTFileQueryInfo_fMode;
+
+extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF1(enmAdditionalAttribs);
+ /* RTPrintf("%s, hFile=%p, enmAdditionalAttribs=0x%llx\n",
+ __PRETTY_FUNCTION__, hFile, LLUIFY(enmAdditionalAttribs)); */
+ g_testRTFileQueryInfo_hFile = hFile;
+ RT_ZERO(*pObjInfo);
+ pObjInfo->AccessTime = g_testRTFileQueryInfo_ATime;
+ RT_ZERO(g_testRTDirQueryInfo_ATime);
+ pObjInfo->Attr.fMode = g_testRTFileQueryInfo_fMode;
+ g_testRTFileQueryInfo_fMode = 0;
+ return VINF_SUCCESS;
+}
+
+static const char *g_testRTFileRead_pszData;
+
+extern int testRTFileRead(RTFILE File, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RT_NOREF1(File);
+ /* RTPrintf("%s : File=%p, cbToRead=%llu\n", __PRETTY_FUNCTION__, File,
+ LLUIFY(cbToRead)); */
+ bufferFromPath((char *)pvBuf, cbToRead, g_testRTFileRead_pszData);
+ if (pcbRead)
+ *pcbRead = RT_MIN(cbToRead, strlen(g_testRTFileRead_pszData) + 1);
+ g_testRTFileRead_pszData = 0;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFileReadAt(RTFILE hFile, uint64_t offFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RT_NOREF1(hFile);
+ RT_NOREF(offFile);
+ /* RTPrintf("%s : File=%p, cbToRead=%llu\n", __PRETTY_FUNCTION__, File,
+ LLUIFY(cbToRead)); */
+ bufferFromPath((char *)pvBuf, cbToRead, g_testRTFileRead_pszData);
+ if (pcbRead)
+ *pcbRead = RT_MIN(cbToRead, strlen(g_testRTFileRead_pszData) + 1);
+ g_testRTFileRead_pszData = 0;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ RT_NOREF3(hFile, offSeek, uMethod);
+ /* RTPrintf("%s : hFile=%p, offSeek=%llu, uMethod=%u\n", __PRETTY_FUNCTION__,
+ hFile, LLUIFY(offSeek), uMethod); */
+ if (poffActual)
+ *poffActual = 0;
+ return VINF_SUCCESS;
+}
+
+static uint64_t g_testRTFileSet_fMode;
+
+extern int testRTFileSetMode(RTFILE File, RTFMODE fMode)
+{
+ RT_NOREF1(File);
+ /* RTPrintf("%s: fMode=%llu\n", __PRETTY_FUNCTION__, LLUIFY(fMode)); */
+ g_testRTFileSet_fMode = fMode;
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileSetSize_hFile;
+static RTFOFF g_testRTFileSetSize_cbSize;
+
+extern int testRTFileSetSize(RTFILE File, uint64_t cbSize)
+{
+ /* RTPrintf("%s: File=%llu, cbSize=%llu\n", __PRETTY_FUNCTION__, LLUIFY(File),
+ LLUIFY(cbSize)); */
+ g_testRTFileSetSize_hFile = File;
+ g_testRTFileSetSize_cbSize = (RTFOFF) cbSize; /* Why was this signed before? */
+ return VINF_SUCCESS;
+}
+
+static RTTIMESPEC g_testRTFileSetTimes_ATime;
+
+extern int testRTFileSetTimes(RTFILE File, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF4(File, pModificationTime, pChangeTime, pBirthTime);
+ /* RTPrintf("%s: pFile=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n",
+ __PRETTY_FUNCTION__,
+ pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1,
+ pModificationTime
+ ? (long long)RTTimeSpecGetNano(pModificationTime) : -1,
+ pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1,
+ pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */
+ if (pAccessTime)
+ g_testRTFileSetTimes_ATime = *pAccessTime;
+ else
+ RT_ZERO(g_testRTFileSetTimes_ATime);
+ return VINF_SUCCESS;
+}
+
+static RTFILE g_testRTFileUnlock_hFile;
+static int64_t g_testRTFileUnlock_offLock;
+static uint64_t g_testRTFileUnlock_cbLock;
+
+extern int testRTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock)
+{
+ /* RTPrintf("%s: hFile=%p, ofLock=%lli, cbLock=%llu\n", __PRETTY_FUNCTION__,
+ File, (long long) offLock, LLUIFY(cbLock)); */
+ g_testRTFileUnlock_hFile = File;
+ g_testRTFileUnlock_offLock = offLock;
+ g_testRTFileUnlock_cbLock = cbLock;
+ return VINF_SUCCESS;
+}
+
+static char g_testRTFileWrite_szData[256];
+
+extern int testRTFileWrite(RTFILE File, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RT_NOREF2(File, cbToWrite);
+ /* RTPrintf("%s: File=%p, pvBuf=%.*s, cbToWrite=%llu\n", __PRETTY_FUNCTION__,
+ File, cbToWrite, (const char *)pvBuf, LLUIFY(cbToWrite)); */
+ ARRAY_FROM_PATH(g_testRTFileWrite_szData, (const char *)pvBuf);
+ if (pcbWritten)
+ *pcbWritten = strlen(g_testRTFileWrite_szData) + 1;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFileWriteAt(RTFILE File, uint64_t offFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RT_NOREF3(File, cbToWrite, offFile);
+ /* RTPrintf("%s: File=%p, pvBuf=%.*s, cbToWrite=%llu\n", __PRETTY_FUNCTION__,
+ File, cbToWrite, (const char *)pvBuf, LLUIFY(cbToWrite)); */
+ ARRAY_FROM_PATH(g_testRTFileWrite_szData, (const char *)pvBuf);
+ if (pcbWritten)
+ *pcbWritten = strlen(g_testRTFileWrite_szData) + 1;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ RT_NOREF1(pszFsPath);
+ /* RTPrintf("%s, pszFsPath=%s\n", __PRETTY_FUNCTION__, pszFsPath);
+ RT_ZERO(*pProperties); */
+ pProperties->cbMaxComponent = 256;
+ pProperties->fCaseSensitive = true;
+ return VINF_SUCCESS;
+}
+
+extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ RT_NOREF2(pszFsPath, pu32Serial);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+extern int testRTFsQuerySizes(const char *pszFsPath, PRTFOFF pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ RT_NOREF5(pszFsPath, pcbTotal, pcbFree, pcbBlock, pcbSector);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF2(enmAdditionalAttribs, fFlags);
+ /* RTPrintf("%s: pszPath=%s, enmAdditionalAttribs=0x%x, fFlags=0x%x\n",
+ __PRETTY_FUNCTION__, pszPath, (unsigned) enmAdditionalAttribs,
+ (unsigned) fFlags); */
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RT_ZERO(*pObjInfo);
+ return VINF_SUCCESS;
+}
+
+extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
+{
+ RT_NOREF2(pszSymlink, fDelete);
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\")))
+ return VERR_FILE_NOT_FOUND;
+ RT_NOREF4(pszSymlink, pszTarget, cbTarget, fRead);
+ RTPrintf("%s\n", __PRETTY_FUNCTION__);
+ return 0;
+}
+
+
+/*********************************************************************************************************************************
+* Tests *
+*********************************************************************************************************************************/
+
+/* Sub-tests for testMappingsQuery(). */
+void testMappingsQuerySimple(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryTooFewBuffers(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryAutoMount(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryArrayWrongSize(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsQueryName(). */
+void testMappingsQueryNameValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryNameInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMappingsQueryNameBadBuffer(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMapFolder(). */
+void testMapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderTwice(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderDelimiter(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderCaseSensitive(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderCaseInsensitive(RTTEST hTest) { RT_NOREF1(hTest); }
+void testMapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testUnmapFolder(). */
+void testUnmapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testUnmapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); }
+void testUnmapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testCreate(). */
+void testCreateBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testClose(). */
+void testCloseBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRead(). */
+void testReadBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testWrite(). */
+void testWriteBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testLock(). */
+void testLockBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testFlush(). */
+void testFlushBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testDirList(). */
+void testDirListBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testReadLink(). */
+void testReadLinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testFSInfo(). */
+void testFSInfoBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRemove(). */
+void testRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testRename(). */
+void testRenameBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testSymlink(). */
+void testSymlinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsAdd(). */
+void testMappingsAddBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+/* Sub-tests for testMappingsRemove(). */
+void testMappingsRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); }
+
+union TESTSHFLSTRING
+{
+ SHFLSTRING string;
+ char acData[256];
+};
+
+static void fillTestShflString(union TESTSHFLSTRING *pDest,
+ const char *pcszSource)
+{
+ const size_t cchSource = strlen(pcszSource);
+ AssertRelease( cchSource * 2 + 2
+ < sizeof(*pDest) - RT_UOFFSETOF(SHFLSTRING, String));
+ pDest->string.u16Length = (uint16_t)(cchSource * sizeof(RTUTF16));
+ pDest->string.u16Size = pDest->string.u16Length + sizeof(RTUTF16);
+ /* Copy pcszSource ASCIIZ, including the trailing 0, to the UTF16 pDest->string.String.ucs2. */
+ for (unsigned i = 0; i <= cchSource; ++i)
+ pDest->string.String.ucs2[i] = (uint16_t)pcszSource[i];
+}
+
+static SHFLROOT initWithWritableMapping(RTTEST hTest,
+ VBOXHGCMSVCFNTABLE *psvcTable,
+ VBOXHGCMSVCHELPERS *psvcHelpers,
+ const char *pcszFolderName,
+ const char *pcszMapping,
+ bool fCaseSensitive = true)
+{
+ VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_ADD_MAPPING,
+ SHFL_CPARMS_MAP_FOLDER)];
+ union TESTSHFLSTRING FolderName;
+ union TESTSHFLSTRING Mapping;
+ union TESTSHFLSTRING AutoMountPoint;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ int rc;
+
+ initTable(psvcTable, psvcHelpers);
+ AssertReleaseRC(VBoxHGCMSvcLoad(psvcTable));
+ AssertRelease( psvcTable->pvService
+ = RTTestGuardedAllocTail(hTest, psvcTable->cbClient));
+ RT_BZERO(psvcTable->pvService, psvcTable->cbClient);
+ fillTestShflString(&FolderName, pcszFolderName);
+ fillTestShflString(&Mapping, pcszMapping);
+ fillTestShflString(&AutoMountPoint, "");
+ HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String)
+ + FolderName.string.u16Size);
+ HGCMSvcSetPv(&aParms[1], &Mapping, RT_UOFFSETOF(SHFLSTRING, String)
+ + Mapping.string.u16Size);
+ HGCMSvcSetU32(&aParms[2], 1);
+ HGCMSvcSetPv(&aParms[3], &AutoMountPoint, SHFLSTRING_HEADER_SIZE + AutoMountPoint.string.u16Size);
+ rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_ADD_MAPPING,
+ SHFL_CPARMS_ADD_MAPPING, aParms);
+ AssertReleaseRC(rc);
+ HGCMSvcSetPv(&aParms[0], &Mapping, RT_UOFFSETOF(SHFLSTRING, String)
+ + Mapping.string.u16Size);
+ HGCMSvcSetU32(&aParms[1], 0); /* root */
+ HGCMSvcSetU32(&aParms[2], '/'); /* delimiter */
+ HGCMSvcSetU32(&aParms[3], fCaseSensitive);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_MAP_FOLDER,
+ SHFL_CPARMS_MAP_FOLDER, aParms, 0);
+ AssertReleaseRC(callHandle.rc);
+ return aParms[1].u.uint32;
+}
+
+/** @todo Mappings should be automatically removed by unloading the service,
+ * but unloading is currently a no-op! */
+static void unmapAndRemoveMapping(RTTEST hTest, VBOXHGCMSVCFNTABLE *psvcTable,
+ SHFLROOT root, const char *pcszFolderName)
+{
+ RT_NOREF1(hTest);
+ VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_UNMAP_FOLDER,
+ SHFL_CPARMS_REMOVE_MAPPING)];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+ union TESTSHFLSTRING FolderName;
+ int rc;
+
+ HGCMSvcSetU32(&aParms[0], root);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_UNMAP_FOLDER,
+ SHFL_CPARMS_UNMAP_FOLDER, aParms, 0);
+ AssertReleaseRC(callHandle.rc);
+ fillTestShflString(&FolderName, pcszFolderName);
+ HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String)
+ + FolderName.string.u16Size);
+ rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_REMOVE_MAPPING,
+ SHFL_CPARMS_REMOVE_MAPPING, aParms);
+ AssertReleaseRC(rc);
+}
+
+static int createFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ const char *pcszFilename, uint32_t fCreateFlags,
+ SHFLHANDLE *pHandle, SHFLCREATERESULT *pResult)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_CREATE];
+ union TESTSHFLSTRING Path;
+ SHFLCREATEPARMS CreateParms;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ fillTestShflString(&Path, pcszFilename);
+ RT_ZERO(CreateParms);
+ CreateParms.CreateFlags = fCreateFlags;
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetPv(&aParms[1], &Path, RT_UOFFSETOF(SHFLSTRING, String)
+ + Path.string.u16Size);
+ HGCMSvcSetPv(&aParms[2], &CreateParms, sizeof(CreateParms));
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_CREATE,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (RT_FAILURE(callHandle.rc))
+ return callHandle.rc;
+ if (pHandle)
+ *pHandle = CreateParms.Handle;
+ if (pResult)
+ *pResult = CreateParms.Result;
+ return VINF_SUCCESS;
+}
+
+static int readFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbRead,
+ uint32_t *pcbRead, void *pvBuf, uint32_t cbBuf)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_READ];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetU64(&aParms[1], (uint64_t) hFile);
+ HGCMSvcSetU64(&aParms[2], offSeek);
+ HGCMSvcSetU32(&aParms[3], cbRead);
+ HGCMSvcSetPv(&aParms[4], pvBuf, cbBuf);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_READ,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcbRead)
+ *pcbRead = aParms[3].u.uint32;
+ return callHandle.rc;
+}
+
+static int writeFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root,
+ SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbWrite,
+ uint32_t *pcbWritten, const void *pvBuf, uint32_t cbBuf)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_WRITE];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], Root);
+ HGCMSvcSetU64(&aParms[1], (uint64_t) hFile);
+ HGCMSvcSetU64(&aParms[2], offSeek);
+ HGCMSvcSetU32(&aParms[3], cbWrite);
+ HGCMSvcSetPv(&aParms[4], (void *)pvBuf, cbBuf);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_WRITE,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcbWritten)
+ *pcbWritten = aParms[3].u.uint32;
+ return callHandle.rc;
+}
+
+static int flushFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_FLUSH];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_FLUSH,
+ SHFL_CPARMS_FLUSH, aParms, 0);
+ return callHandle.rc;
+}
+
+static int listDir(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, uint32_t fFlags,
+ const char *pcszPath, void *pvBuf, uint32_t cbBuf,
+ uint32_t resumePoint, uint32_t *pcFiles)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LIST];
+ union TESTSHFLSTRING Path;
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU32(&aParms[2], fFlags);
+ HGCMSvcSetU32(&aParms[3], cbBuf);
+ if (pcszPath)
+ {
+ fillTestShflString(&Path, pcszPath);
+ HGCMSvcSetPv(&aParms[4], &Path, RT_UOFFSETOF(SHFLSTRING, String)
+ + Path.string.u16Size);
+ }
+ else
+ HGCMSvcSetPv(&aParms[4], NULL, 0);
+ HGCMSvcSetPv(&aParms[5], pvBuf, cbBuf);
+ HGCMSvcSetU32(&aParms[6], resumePoint);
+ HGCMSvcSetU32(&aParms[7], 0);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_LIST,
+ RT_ELEMENTS(aParms), aParms, 0);
+ if (pcFiles)
+ *pcFiles = aParms[7].u.uint32;
+ return callHandle.rc;
+}
+
+static int sfInformation(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, uint32_t fFlags, uint32_t cb,
+ SHFLFSOBJINFO *pInfo)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_INFORMATION];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU32(&aParms[2], fFlags);
+ HGCMSvcSetU32(&aParms[3], cb);
+ HGCMSvcSetPv(&aParms[4], pInfo, cb);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_INFORMATION,
+ RT_ELEMENTS(aParms), aParms, 0);
+ return callHandle.rc;
+}
+
+static int lockFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root,
+ SHFLHANDLE handle, int64_t offLock, uint64_t cbLock,
+ uint32_t fFlags)
+{
+ VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LOCK];
+ VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
+
+ HGCMSvcSetU32(&aParms[0], root);
+ HGCMSvcSetU64(&aParms[1], handle);
+ HGCMSvcSetU64(&aParms[2], offLock);
+ HGCMSvcSetU64(&aParms[3], cbLock);
+ HGCMSvcSetU32(&aParms[4], fFlags);
+ psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0,
+ psvcTable->pvService, SHFL_FN_LOCK,
+ RT_ELEMENTS(aParms), aParms, 0);
+ return callHandle.rc;
+}
+
+void testCreateFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ SHFLCREATERESULT Result;
+ int rc;
+
+ RTTestSub(hTest, "Create file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, NULL,
+ &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&g_testRTFileOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/file"),
+ (hTest, "pszFilename=%s\n", &g_testRTFileOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileOpen_fOpen == 0x181,
+ (hTest, "fOpen=%llu\n", LLUIFY(g_testRTFileOpen_fOpen)));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile,
+ (hTest, "File=%u\n", (uintptr_t)g_testRTFileClose_hFile));
+}
+
+void testCreateFileSimpleCaseInsensitive(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ SHFLCREATERESULT Result;
+ int rc;
+
+ g_fFailIfNotLowercase = true;
+
+ RTTestSub(hTest, "Create file case insensitive");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname", false /*fCaseSensitive*/);
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/TesT/FilE", SHFL_CF_ACCESS_READ, NULL,
+ &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&g_testRTFileOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/file"),
+ (hTest, "pszFilename=%s\n", &g_testRTFileOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileOpen_fOpen == 0x181,
+ (hTest, "fOpen=%llu\n", LLUIFY(g_testRTFileOpen_fOpen)));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile,
+ (hTest, "File=%u\n", (uintptr_t)g_testRTFileClose_hFile));
+
+ g_fFailIfNotLowercase = false;
+}
+
+void testCreateDirSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ SHFLCREATERESULT Result;
+ int rc;
+
+ RTTestSub(hTest, "Create directory simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, NULL, &Result);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&g_testRTDirCreate_szPath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/dir"),
+ (hTest, "pszPath=%s\n", &g_testRTDirCreate_szPath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(&g_testRTDirOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0],
+ "/test/mapping/test/dir"),
+ (hTest, "pszFilename=%s\n", &g_testRTDirOpen_szName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0]));
+ RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED,
+ (hTest, "Result=%d\n", (int) Result));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testReadFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ const char *pcszReadData = "Data to read";
+ char achBuf[sizeof(pcszReadData) + 10];
+ uint32_t cbRead;
+ int rc;
+
+ RTTestSub(hTest, "Read file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ g_testRTFileRead_pszData = pcszReadData;
+ memset(achBuf, 'f', sizeof(achBuf));
+ rc = readFile(&svcTable, Root, Handle, 0, (uint32_t)strlen(pcszReadData) + 1,
+ &cbRead, achBuf, (uint32_t)sizeof(achBuf));
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strncmp(achBuf, pcszReadData, sizeof(achBuf)),
+ (hTest, "pvBuf=%.*s Handle=%#RX64\n", sizeof(achBuf), achBuf, Handle));
+ RTTEST_CHECK_MSG(hTest, cbRead == strlen(pcszReadData) + 1,
+ (hTest, "cbRead=%llu\n", LLUIFY(cbRead)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+}
+
+void testWriteFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ const char *pcszWrittenData = "Data to write";
+ uint32_t cbToWrite = (uint32_t)strlen(pcszWrittenData) + 1;
+ uint32_t cbWritten;
+ int rc;
+
+ RTTestSub(hTest, "Write file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = writeFile(&svcTable, Root, Handle, 0, cbToWrite, &cbWritten,
+ pcszWrittenData, cbToWrite);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest,
+ !strcmp(g_testRTFileWrite_szData, pcszWrittenData),
+ (hTest, "pvBuf=%s\n", g_testRTFileWrite_szData));
+ RTTEST_CHECK_MSG(hTest, cbWritten == cbToWrite,
+ (hTest, "cbWritten=%llu\n", LLUIFY(cbWritten)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+}
+
+void testFlushFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Flush file simple");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = flushFile(&svcTable, Root, Handle);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileFlush_hFile == hFile, (hTest, "File=%u\n", g_testRTFileFlush_hFile));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+}
+
+void testDirListEmpty(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ SHFLHANDLE Handle;
+ union
+ {
+ SHFLDIRINFO DirInfo;
+ uint8_t abBuffer[sizeof(SHFLDIRINFO) + 2 * sizeof(RTUTF16)];
+ } Buf;
+ uint32_t cFiles;
+ int rc;
+
+ RTTestSub(hTest, "List empty directory");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = listDir(&svcTable, Root, Handle, 0, NULL, &Buf.DirInfo, sizeof(Buf), 0, &cFiles);
+ RTTEST_CHECK_RC(hTest, rc, VERR_NO_MORE_FILES);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirReadEx_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirReadEx_hDir));
+ RTTEST_CHECK_MSG(hTest, cFiles == 0,
+ (hTest, "cFiles=%llu\n", LLUIFY(cFiles)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testFSInfoQuerySetFMode(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ const uint32_t fMode = 0660;
+ SHFLFSOBJINFO Info;
+ int rc;
+
+ RTTestSub(hTest, "Query and set file size");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ SHFLHANDLE Handle = SHFL_HANDLE_NIL;
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK_RETV(hTest, rc);
+
+ RT_ZERO(Info);
+ g_testRTFileQueryInfo_fMode = fMode;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfo_hFile == hFile, (hTest, "File=%u\n", g_testRTFileQueryInfo_hFile));
+ RTTEST_CHECK_MSG(hTest, Info.Attr.fMode == fMode,
+ (hTest, "cbObject=%llu\n", LLUIFY(Info.cbObject)));
+ RT_ZERO(Info);
+ Info.Attr.fMode = fMode;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileSet_fMode == fMode,
+ (hTest, "Size=%llu\n", LLUIFY(g_testRTFileSet_fMode)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+}
+
+void testFSInfoQuerySetDirATime(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)];
+ const int64_t ccAtimeNano = 100000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Query and set directory atime");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTDirOpen_hDir = hDir;
+ rc = createFile(&svcTable, Root, "test/dir",
+ SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&g_testRTDirQueryInfo_ATime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirQueryInfo_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirQueryInfo_hDir));
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&Info.AccessTime))));
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&g_testRTDirSetTimes_ATime)
+ == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&g_testRTDirSetTimes_ATime))));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir));
+}
+
+void testFSInfoQuerySetFileATime(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ const int64_t ccAtimeNano = 100000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Query and set file atime");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&g_testRTFileQueryInfo_ATime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info),
+ &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfo_hFile == hFile, (hTest, "File=%u\n", g_testRTFileQueryInfo_hFile));
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&Info.AccessTime))));
+ RT_ZERO(Info);
+ RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano);
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&g_testRTFileSetTimes_ATime)
+ == ccAtimeNano,
+ (hTest, "ATime=%llu\n",
+ LLUIFY(RTTimeSpecGetNano(&g_testRTFileSetTimes_ATime))));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+}
+
+void testFSInfoQuerySetEndOfFile(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ const RTFOFF cbNew = 50000;
+ SHFLFSOBJINFO Info;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Set end of file position");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RT_ZERO(Info);
+ Info.cbObject = cbNew;
+ rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_SIZE,
+ sizeof(Info), &Info);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileSetSize_hFile == hFile, (hTest, "File=%u\n", g_testRTFileSetSize_hFile));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileSetSize_cbSize == cbNew,
+ (hTest, "Size=%llu\n", LLUIFY(g_testRTFileSetSize_cbSize)));
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+}
+
+void testLockFileSimple(RTTEST hTest)
+{
+ VBOXHGCMSVCFNTABLE svcTable;
+ VBOXHGCMSVCHELPERS svcHelpers;
+ SHFLROOT Root;
+ const RTFILE hFile = (RTFILE) 0x10000;
+ const int64_t offLock = 50000;
+ const uint64_t cbLock = 4000;
+ SHFLHANDLE Handle;
+ int rc;
+
+ RTTestSub(hTest, "Simple file lock and unlock");
+ Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers,
+ "/test/mapping", "testname");
+ g_testRTFileOpen_hFile = hFile;
+ rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ,
+ &Handle, NULL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_SHARED);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+#ifdef RT_OS_WINDOWS /* Locking is a no-op elsewhere. */
+ RTTEST_CHECK_MSG(hTest, g_testRTFileLock_hFile == hFile, (hTest, "File=%u\n", g_testRTFileLock_hFile));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileLock_fLock == 0,
+ (hTest, "fLock=%u\n", g_testRTFileLock_fLock));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileLock_offLock == offLock,
+ (hTest, "Offs=%llu\n", (long long) g_testRTFileLock_offLock));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileLock_cbLock == cbLock,
+ (hTest, "Size=%llu\n", LLUIFY(g_testRTFileLock_cbLock)));
+#endif
+ rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_CANCEL);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+#ifdef RT_OS_WINDOWS
+ RTTEST_CHECK_MSG(hTest, g_testRTFileUnlock_hFile == hFile, (hTest, "File=%u\n", g_testRTFileUnlock_hFile));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileUnlock_offLock == offLock,
+ (hTest, "Offs=%llu\n",
+ (long long) g_testRTFileUnlock_offLock));
+ RTTEST_CHECK_MSG(hTest, g_testRTFileUnlock_cbLock == cbLock,
+ (hTest, "Size=%llu\n", LLUIFY(g_testRTFileUnlock_cbLock)));
+#endif
+ unmapAndRemoveMapping(hTest, &svcTable, Root, "testname");
+ AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService));
+ AssertReleaseRC(svcTable.pfnUnload(NULL));
+ RTTestGuardedFree(hTest, svcTable.pvService);
+ RTTEST_CHECK_MSG(hTest, g_testRTFileClose_hFile == hFile, (hTest, "File=%u\n", g_testRTFileClose_hFile));
+}
+
+
+/*********************************************************************************************************************************
+* Main code *
+*********************************************************************************************************************************/
+
+static void testAPI(RTTEST hTest)
+{
+ testMappingsQuery(hTest);
+ testMappingsQueryName(hTest);
+ testMapFolder(hTest);
+ testUnmapFolder(hTest);
+ testCreate(hTest);
+ testClose(hTest);
+ testRead(hTest);
+ testWrite(hTest);
+ testLock(hTest);
+ testFlush(hTest);
+ testDirList(hTest);
+ testReadLink(hTest);
+ testFSInfo(hTest);
+ testRemove(hTest);
+ testRename(hTest);
+ testSymlink(hTest);
+ testMappingsAdd(hTest);
+ testMappingsRemove(hTest);
+ /* testSetStatusLed(hTest); */
+}
+
+int main(int argc, char **argv)
+{
+ RT_NOREF1(argc);
+ RTEXITCODE rcExit = RTTestInitAndCreate(RTPathFilename(argv[0]), &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+ testAPI(g_hTest);
+ return RTTestSummaryAndDestroy(g_hTest);
+}
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h
new file mode 100644
index 00000000..3be50c23
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h
@@ -0,0 +1,140 @@
+/** @file
+ * VBox Shared Folders testcase stub redefinitions.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h
+#define VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Grumble... if the coding style let us use the anonymous "struct RTTESTINT *"
+ * instead of "PRTTEST" here we wouldn't need to unnecessarily include this. */
+#include <iprt/test.h>
+
+void testMappingsQuery(RTTEST hTest);
+/* Sub-tests for testMappingsQuery(). */
+void testMappingsQuerySimple(RTTEST hTest);
+void testMappingsQueryTooFewBuffers(RTTEST hTest);
+void testMappingsQueryAutoMount(RTTEST hTest);
+void testMappingsQueryArrayWrongSize(RTTEST hTest);
+
+void testMappingsQueryName(RTTEST hTest);
+/* Sub-tests for testMappingsQueryName(). */
+void testMappingsQueryNameValid(RTTEST hTest);
+void testMappingsQueryNameInvalid(RTTEST hTest);
+void testMappingsQueryNameBadBuffer(RTTEST hTest);
+
+void testMapFolder(RTTEST hTest);
+/* Sub-tests for testMapFolder(). */
+void testMapFolderValid(RTTEST hTest);
+void testMapFolderInvalid(RTTEST hTest);
+void testMapFolderTwice(RTTEST hTest);
+void testMapFolderDelimiter(RTTEST hTest);
+void testMapFolderCaseSensitive(RTTEST hTest);
+void testMapFolderCaseInsensitive(RTTEST hTest);
+void testMapFolderBadParameters(RTTEST hTest);
+
+void testUnmapFolder(RTTEST hTest);
+/* Sub-tests for testUnmapFolder(). */
+void testUnmapFolderValid(RTTEST hTest);
+void testUnmapFolderInvalid(RTTEST hTest);
+void testUnmapFolderBadParameters(RTTEST hTest);
+
+void testCreate(RTTEST hTest);
+/* Sub-tests for testCreate(). */
+void testCreateFileSimple(RTTEST hTest);
+void testCreateFileSimpleCaseInsensitive(RTTEST hTest);
+void testCreateDirSimple(RTTEST hTest);
+void testCreateBadParameters(RTTEST hTest);
+
+void testClose(RTTEST hTest);
+/* Sub-tests for testClose(). */
+void testCloseBadParameters(RTTEST hTest);
+
+void testRead(RTTEST hTest);
+/* Sub-tests for testRead(). */
+void testReadBadParameters(RTTEST hTest);
+void testReadFileSimple(RTTEST hTest);
+
+void testWrite(RTTEST hTest);
+/* Sub-tests for testWrite(). */
+void testWriteBadParameters(RTTEST hTest);
+void testWriteFileSimple(RTTEST hTest);
+
+void testLock(RTTEST hTest);
+/* Sub-tests for testLock(). */
+void testLockBadParameters(RTTEST hTest);
+void testLockFileSimple(RTTEST hTest);
+
+void testFlush(RTTEST hTest);
+/* Sub-tests for testFlush(). */
+void testFlushBadParameters(RTTEST hTest);
+void testFlushFileSimple(RTTEST hTest);
+
+void testDirList(RTTEST hTest);
+/* Sub-tests for testDirList(). */
+void testDirListBadParameters(RTTEST hTest);
+void testDirListEmpty(RTTEST hTest);
+
+void testReadLink(RTTEST hTest);
+/* Sub-tests for testReadLink(). */
+void testReadLinkBadParameters(RTTEST hTest);
+
+void testFSInfo(RTTEST hTest);
+/* Sub-tests for testFSInfo(). */
+void testFSInfoBadParameters(RTTEST hTest);
+void testFSInfoQuerySetFMode(RTTEST hTest);
+void testFSInfoQuerySetDirATime(RTTEST hTest);
+void testFSInfoQuerySetFileATime(RTTEST hTest);
+void testFSInfoQuerySetEndOfFile(RTTEST hTest);
+
+void testRemove(RTTEST hTest);
+/* Sub-tests for testRemove(). */
+void testRemoveBadParameters(RTTEST hTest);
+
+void testRename(RTTEST hTest);
+/* Sub-tests for testRename(). */
+void testRenameBadParameters(RTTEST hTest);
+
+void testSymlink(RTTEST hTest);
+/* Sub-tests for testSymlink(). */
+void testSymlinkBadParameters(RTTEST hTest);
+
+void testMappingsAdd(RTTEST hTest);
+/* Sub-tests for testMappingsAdd(). */
+void testMappingsAddBadParameters(RTTEST hTest);
+
+void testMappingsRemove(RTTEST hTest);
+/* Sub-tests for testMappingsRemove(). */
+void testMappingsRemoveBadParameters(RTTEST hTest);
+
+#if 0 /* Where should this go? */
+void testSetStatusLed(RTTEST hTest);
+/* Sub-tests for testStatusLed(). */
+void testSetStatusLedBadParameters(RTTEST hTest);
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h */
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp
new file mode 100644
index 00000000..052b80d7
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp
@@ -0,0 +1,452 @@
+/** @file
+ * Testcase for shared folder case conversion code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MISC
+#define LOG_ENABLED
+#include <VBox/shflsvc.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+#include <stdio.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* Override slash for non-windows hosts. */
+#undef RTPATH_DELIMITER
+#define RTPATH_DELIMITER '\\'
+
+/* Use our own RTPath and RTDir methods. */
+#define RTPathQueryInfo rtPathQueryInfo
+#define RTDirOpenFiltered rtDirOpenFiltered
+#define RTDirClose rtDirClose
+#define RTDirReadEx rtDirReadEx
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static int iDirList = 0;
+static int iDirFile = 0;
+
+static const char *g_apszDirs[] =
+{
+ "c:",
+ "c:\\test dir",
+ "c:\\test dir\\SUBDIR",
+};
+
+static const char *g_apszDirsC[] =
+{
+ ".",
+ "..",
+ "test dir"
+};
+
+static const char *g_apszTestdirEntries[] =
+{
+ ".",
+ "..",
+ "SUBDIR",
+ "a.bat",
+ "aTestJe.bat",
+ "aTestje.bat",
+ "b.bat",
+ "c.bat",
+ "d.bat",
+ "e.bat",
+ "f.bat",
+ "g.bat",
+ "h.bat",
+ "x.bat",
+ "z.bat",
+};
+
+static const char *g_apszSUBDIREntries[] =
+{
+ ".",
+ "..",
+ "a.bat",
+ "aTestJe.bat",
+ "aTestje.bat",
+ "b.bat",
+ "c.bat",
+ "d.bat",
+ "e.bat",
+ "f.bat",
+ "g.bat",
+ "h.bat",
+ "x.bat",
+ "z.bat",
+};
+
+int rtDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
+{
+ RT_NOREF2(enmFilter, fFlags);
+ if (!strcmp(pszPath, "c:\\*"))
+ iDirList = 1;
+ else if (!strcmp(pszPath, "c:\\test dir\\*"))
+ iDirList = 2;
+ else if (!strcmp(pszPath, "c:\\test dir\\SUBDIR\\*"))
+ iDirList = 3;
+ else
+ AssertFailed();
+
+ *phDir = (RTDIR)1;
+ return VINF_SUCCESS;
+}
+
+int rtDirClose(RTDIR hDir)
+{
+ RT_NOREF1(hDir);
+ iDirFile = 0;
+ return VINF_SUCCESS;
+}
+
+int rtDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ RT_NOREF4(hDir, pcbDirEntry, enmAdditionalAttribs, fFlags);
+ switch (iDirList)
+ {
+ case 1:
+ if (iDirFile == RT_ELEMENTS(g_apszDirsC))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszDirsC[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszDirsC[iDirFile++]);
+ break;
+ case 2:
+ if (iDirFile == RT_ELEMENTS(g_apszTestdirEntries))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszTestdirEntries[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszTestdirEntries[iDirFile++]);
+ break;
+ case 3:
+ if (iDirFile == RT_ELEMENTS(g_apszSUBDIREntries))
+ return VERR_NO_MORE_FILES;
+ pDirEntry->cbName = (uint16_t)strlen(g_apszSUBDIREntries[iDirFile]);
+ strcpy(pDirEntry->szName, g_apszSUBDIREntries[iDirFile++]);
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+int rtPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ RT_NOREF2(pObjInfo, enmAdditionalAttribs);
+ int cMax;
+
+ /* first try g_apszDirs */
+ for (unsigned int i=0;i<RT_ELEMENTS(g_apszDirs);i++)
+ {
+ if(!strcmp(pszPath, g_apszDirs[i]))
+ return VINF_SUCCESS;
+ }
+
+ const char **papszDirList;
+ switch (iDirList)
+ {
+ case 1:
+ cMax = RT_ELEMENTS(g_apszDirsC);
+ papszDirList = g_apszDirsC;
+ break;
+ case 2:
+ cMax = RT_ELEMENTS(g_apszTestdirEntries);
+ papszDirList = g_apszTestdirEntries;
+ break;
+ case 3:
+ cMax = RT_ELEMENTS(g_apszSUBDIREntries);
+ papszDirList = g_apszSUBDIREntries;
+ break;
+ default:
+ return VERR_FILE_NOT_FOUND;
+ }
+ for (int i = 0; i < cMax; i++)
+ {
+ if (!strcmp(pszPath, papszDirList[i]))
+ return VINF_SUCCESS;
+ }
+ return VERR_FILE_NOT_FOUND;
+}
+
+static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent)
+{
+ PRTDIRENTRYEX pDirEntry = NULL;
+ uint32_t cbDirEntry;
+ size_t cbComponent;
+ int rc = VERR_FILE_NOT_FOUND;
+ RTDIR hSearch = NIL_RTDIR;
+ char szWildCard[4];
+
+ Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
+
+ cbComponent = strlen(pszStartComponent);
+
+ cbDirEntry = 4096;
+ pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == 0)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ /** @todo this is quite inefficient, especially for directories with many files */
+ Assert(pszFullPath < pszStartComponent-1);
+ Assert(*(pszStartComponent-1) == RTPATH_DELIMITER);
+ *(pszStartComponent-1) = 0;
+ strcpy(pDirEntry->szName, pszFullPath);
+ szWildCard[0] = RTPATH_DELIMITER;
+ szWildCard[1] = '*';
+ szWildCard[2] = 0;
+ strcat(pDirEntry->szName, szWildCard);
+
+ rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ *(pszStartComponent-1) = RTPATH_DELIMITER;
+ if (RT_FAILURE(rc))
+ goto end;
+
+ for(;;)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+
+ rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+
+ if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
+ {
+ AssertFailed();
+ if (rc != VERR_NO_TRANSLATION)
+ break;
+ else
+ continue;
+ }
+
+ Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
+ if ( pDirEntry->cbName == cbComponent
+ && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
+ {
+ Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
+ strcpy(pszStartComponent, &pDirEntry->szName[0]);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ if (RT_FAILURE(rc))
+ Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
+
+end:
+ if (pDirEntry)
+ RTMemFree(pDirEntry);
+
+ if (hSearch)
+ RTDirClose(hSearch);
+ return rc;
+}
+
+
+
+int testCase(char *pszFullPath, bool fWildCard = false)
+{
+ int rc;
+ RTFSOBJINFO info;
+ char *pszWildCardComponent = NULL;
+
+ if (fWildCard)
+ {
+ /* strip off the last path component, that contains the wildcard(s) */
+ size_t len = strlen(pszFullPath);
+ char *src = pszFullPath + len - 1;
+
+ while(src > pszFullPath)
+ {
+ if (*src == RTPATH_DELIMITER)
+ break;
+ src--;
+ }
+ if (*src == RTPATH_DELIMITER)
+ {
+ bool fHaveWildcards = false;
+ char *temp = src;
+
+ while(*temp)
+ {
+ char uc = *temp;
+ /** @todo should depend on the guest OS */
+ if (uc == '*' || uc == '?' || uc == '>' || uc == '<' || uc == '"')
+ {
+ fHaveWildcards = true;
+ break;
+ }
+ temp++;
+ }
+
+ if (fHaveWildcards)
+ {
+ pszWildCardComponent = src;
+ *pszWildCardComponent = 0;
+ }
+ }
+ }
+
+ rc = RTPathQueryInfo(pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ size_t len = strlen(pszFullPath);
+ char *src = pszFullPath + len - 1;
+
+ Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
+
+ /* Find partial path that's valid */
+ while(src > pszFullPath)
+ {
+ if (*src == RTPATH_DELIMITER)
+ {
+ *src = 0;
+ rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
+ *src = RTPATH_DELIMITER;
+ if (rc == VINF_SUCCESS)
+ {
+#ifdef DEBUG
+ *src = 0;
+ Log(("Found valid partial path %s\n", pszFullPath));
+ *src = RTPATH_DELIMITER;
+#endif
+ break;
+ }
+ }
+
+ src--;
+ }
+ Assert(*src == RTPATH_DELIMITER && RT_SUCCESS(rc));
+ if ( *src == RTPATH_DELIMITER
+ && RT_SUCCESS(rc))
+ {
+ src++;
+ for(;;)
+ {
+ char *end = src;
+ bool fEndOfString = true;
+
+ while(*end)
+ {
+ if (*end == RTPATH_DELIMITER)
+ break;
+ end++;
+ }
+
+ if (*end == RTPATH_DELIMITER)
+ {
+ fEndOfString = false;
+ *end = 0;
+ rc = RTPathQueryInfo(src, &info, RTFSOBJATTRADD_NOTHING);
+ Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
+ }
+ else
+ if (end == src)
+ rc = VINF_SUCCESS; /* trailing delimiter */
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ /* path component is invalid; try to correct the casing */
+ rc = vbsfCorrectCasing(pszFullPath, src);
+ if (RT_FAILURE(rc))
+ {
+ if (!fEndOfString)
+ *end = RTPATH_DELIMITER;
+ break;
+ }
+ }
+
+ if (fEndOfString)
+ break;
+
+ *end = RTPATH_DELIMITER;
+ src = end + 1;
+ }
+ if (RT_FAILURE(rc))
+ Log(("Unable to find suitable component rc=%d\n", rc));
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ }
+ if (pszWildCardComponent)
+ *pszWildCardComponent = RTPATH_DELIMITER;
+
+ if (RT_SUCCESS(rc))
+ Log(("New valid path %s\n", pszFullPath));
+ else
+ Log(("Old invalid path %s\n", pszFullPath));
+ return rc;
+}
+
+
+int main()
+{
+ char szTest[128];
+
+ RTR3InitExeNoArguments(0);
+ RTLogFlush(NULL);
+ RTLogDestinations(NULL, "stdout");
+ RTLogGroupSettings(NULL, "misc=~0");
+ RTLogFlags(NULL, "unbuffered");
+
+ strcpy(szTest, "c:\\test Dir\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\SUBDIR\\z.bAt");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\SUBDiR\\atestje.bat");
+ testCase(szTest);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\aTestje.baT");
+ testCase(szTest);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\*");
+ testCase(szTest, true);
+ strcpy(szTest, "c:\\TEST dir\\subDiR\\");
+ testCase(szTest ,true);
+ strcpy(szTest, "c:\\test dir\\SUBDIR\\");
+ testCase(szTest);
+ strcpy(szTest, "c:\\test dir\\invalid\\SUBDIR\\test.bat");
+ testCase(szTest);
+ return 0;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp
new file mode 100644
index 00000000..c9832493
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp
@@ -0,0 +1,144 @@
+/** @file
+ * tstShflSize - Testcase for shared folder structure sizes.
+ * Run this on Linux and Windows, then compare.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/shflsvc.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+
+#define STRUCT(t, size) \
+ do { \
+ if (fPrintChecks) \
+ RTPrintf(" STRUCT(" #t ", %d);\n", (int)sizeof(t)); \
+ else if ((size) != sizeof(t)) \
+ { \
+ RTPrintf("%30s: %d expected %d!\n", #t, (int)sizeof(t), (size)); \
+ cErrors++; \
+ } \
+ else if (!fQuiet)\
+ RTPrintf("%30s: %d\n", #t, (int)sizeof(t)); \
+ } while (0)
+
+
+int main(int argc, char **argv)
+{
+ unsigned cErrors = 0;
+
+ /*
+ * Prints the code below if any argument was giving.
+ */
+ bool fQuiet = argc == 2 && !strcmp(argv[1], "quiet");
+ bool fPrintChecks = !fQuiet && argc != 1;
+
+ RTPrintf("tstShflSizes: TESTING\n");
+
+ /*
+ * The checks.
+ */
+ STRUCT(SHFLROOT, 4);
+ STRUCT(SHFLHANDLE, 8);
+ STRUCT(SHFLSTRING, 6);
+ STRUCT(SHFLCREATERESULT, 4);
+ STRUCT(SHFLCREATEPARMS, 108);
+ STRUCT(SHFLMAPPING, 8);
+ STRUCT(SHFLDIRINFO, 128);
+ STRUCT(SHFLVOLINFO, 40);
+ STRUCT(SHFLFSOBJATTR, 44);
+ STRUCT(SHFLFSOBJINFO, 92);
+#ifdef VBOX_WITH_64_BITS_GUESTS
+/* The size of the guest structures depends on the current architecture bit count (ARCH_BITS)
+ * because the HGCMFunctionParameter structure differs in 32 and 64 bit guests.
+ * The host VMMDev device takes care about this.
+ *
+ * Therefore this testcase verifies whether structure sizes are correct for the current ARCH_BITS.
+ */
+# if ARCH_BITS == 64
+ STRUCT(VBoxSFQueryMappings, 88);
+ STRUCT(VBoxSFQueryMapName, 72);
+ STRUCT(VBoxSFMapFolder_Old, 88);
+ STRUCT(VBoxSFMapFolder, 104);
+ STRUCT(VBoxSFUnmapFolder, 56);
+ STRUCT(VBoxSFCreate, 88);
+ STRUCT(VBoxSFClose, 72);
+ STRUCT(VBoxSFRead, 120);
+ STRUCT(VBoxSFWrite, 120);
+ STRUCT(VBoxSFLock, 120);
+ STRUCT(VBoxSFFlush, 72);
+ STRUCT(VBoxSFList, 168);
+ STRUCT(VBoxSFInformation, 120);
+ STRUCT(VBoxSFRemove, 88);
+ STRUCT(VBoxSFRename, 104);
+# elif ARCH_BITS == 32
+ STRUCT(VBoxSFQueryMappings, 24+52);
+ STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */
+ STRUCT(VBoxSFMapFolder_Old, 24+52);
+ STRUCT(VBoxSFMapFolder, 24+64);
+ STRUCT(VBoxSFUnmapFolder, 24+28);
+ STRUCT(VBoxSFCreate, 24+52);
+ STRUCT(VBoxSFClose, 24+40);
+ STRUCT(VBoxSFRead, 24+76);
+ STRUCT(VBoxSFWrite, 24+76);
+ STRUCT(VBoxSFLock, 24+76);
+ STRUCT(VBoxSFFlush, 24+40);
+ STRUCT(VBoxSFList, 24+112);
+ STRUCT(VBoxSFInformation, 24+76);
+ STRUCT(VBoxSFRemove, 24+52);
+ STRUCT(VBoxSFRename, 24+64);
+# else
+# error "Unsupported ARCH_BITS"
+# endif /* ARCH_BITS */
+#else
+ STRUCT(VBoxSFQueryMappings, 24+52);
+ STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */
+ STRUCT(VBoxSFMapFolder_Old, 24+52);
+ STRUCT(VBoxSFMapFolder, 24+64);
+ STRUCT(VBoxSFUnmapFolder, 24+28);
+ STRUCT(VBoxSFCreate, 24+52);
+ STRUCT(VBoxSFClose, 24+40);
+ STRUCT(VBoxSFRead, 24+76);
+ STRUCT(VBoxSFWrite, 24+76);
+ STRUCT(VBoxSFLock, 24+76);
+ STRUCT(VBoxSFFlush, 24+40);
+ STRUCT(VBoxSFList, 24+112);
+ STRUCT(VBoxSFInformation, 24+76);
+ STRUCT(VBoxSFRemove, 24+52);
+ STRUCT(VBoxSFRename, 24+64);
+#endif /* VBOX_WITH_64_BITS_GUESTS */
+
+ /*
+ * The summary.
+ */
+ if (!cErrors)
+ RTPrintf("tstShflSizes: SUCCESS\n");
+ else
+ RTPrintf("tstShflSizes: FAILURE - %d errors\n", cErrors);
+ return !!cErrors;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/teststubs.h b/src/VBox/HostServices/SharedFolders/teststubs.h
new file mode 100644
index 00000000..c7478621
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/teststubs.h
@@ -0,0 +1,104 @@
+/** @file
+ * VBox Shared Folders testcase stub redefinitions.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/**
+ * Macros for renaming iprt file operations to redirect them to testcase
+ * stub functions (mocks). The religiously correct way to do this would be
+ * to make the service use a file operations structure with function pointers
+ * but I'm not sure that would be universally appreciated. */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_teststubs_h
+#define VBOX_INCLUDED_SRC_SharedFolders_teststubs_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/time.h>
+
+#define RTDirClose testRTDirClose
+extern int testRTDirClose(RTDIR hDir);
+#define RTDirCreate testRTDirCreate
+extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate);
+#define RTDirOpen testRTDirOpen
+extern int testRTDirOpen(RTDIR *phDir, const char *pszPath);
+#define RTDirOpenFiltered testRTDirOpenFiltered
+extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags);
+#define RTDirQueryInfo testRTDirQueryInfo
+extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs);
+#define RTDirRemove testRTDirRemove
+extern int testRTDirRemove(const char *pszPath);
+#define RTDirReadEx testRTDirReadEx
+extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags);
+#define RTDirSetMode testRTDirSetMode
+extern int testRTDirSetMode(RTDIR hDir, RTFMODE fMode);
+#define RTDirSetTimes testRTDirSetTimes
+extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime);
+#define RTFileClose testRTFileClose
+extern int testRTFileClose(RTFILE hFile);
+#define RTFileDelete testRTFileDelete
+extern int testRTFileDelete(const char *pszFilename);
+#define RTFileFlush testRTFileFlush
+extern int testRTFileFlush(RTFILE hFile);
+#define RTFileLock testRTFileLock
+extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock);
+#define RTFileOpenEx testRTFileOpenEx
+extern int testRTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken);
+#define RTFileQueryInfo testRTFileQueryInfo
+extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs);
+#define RTFileRead testRTFileRead
+extern int testRTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead);
+#define RTFileReadAt testRTFileReadAt
+extern int testRTFileReadAt(RTFILE hFile, uint64_t offFile, void *pvBuf, size_t cbToRead, size_t *pcbRead);
+#define RTFileSetMode testRTFileSetMode
+extern int testRTFileSetMode(RTFILE hFile, RTFMODE fMode);
+#define RTFileSetSize testRTFileSetSize
+extern int testRTFileSetSize(RTFILE hFile, uint64_t cbSize);
+#define RTFileSetTimes testRTFileSetTimes
+extern int testRTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime);
+#define RTFileSeek testRTFileSeek
+extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual);
+#define RTFileUnlock testRTFileUnlock
+extern int testRTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock);
+#define RTFileWrite testRTFileWrite
+extern int testRTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten);
+#define RTFileWriteAt testRTFileWriteAt
+extern int testRTFileWriteAt(RTFILE hFile, uint64_t offFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten);
+#define RTFsQueryProperties testRTFsQueryProperties
+extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties);
+#define RTFsQuerySerial testRTFsQuerySerial
+extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial);
+#define RTFsQuerySizes testRTFsQuerySizes
+extern int testRTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector);
+#define RTPathQueryInfoEx testRTPathQueryInfoEx
+extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags);
+#define RTSymlinkDelete testRTSymlinkDelete
+extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete);
+#define RTSymlinkRead testRTSymlinkRead
+extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_teststubs_h */
diff --git a/src/VBox/HostServices/SharedFolders/vbsf.cpp b/src/VBox/HostServices/SharedFolders/vbsf.cpp
new file mode 100644
index 00000000..59907584
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsf.cpp
@@ -0,0 +1,2690 @@
+/* $Id: vbsf.cpp $ */
+/** @file
+ * Shared Folders - VBox Shared Folders.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "vbsfpath.h"
+#include "mappings.h"
+#include "vbsf.h"
+#include "shflhandle.h"
+
+#include <VBox/AssertGuest.h>
+#include <VBox/param.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/uni.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_DARWIN
+# include <Carbon/Carbon.h>
+#endif
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
+
+/**
+ * @todo find a better solution for supporting the execute bit for non-windows
+ * guests on windows host. Search for "0111" to find all the relevant places.
+ */
+
+
+#ifndef RT_OS_WINDOWS
+
+/**
+ * Helps to check if pszPath deserves a VERR_PATH_NOT_FOUND status when catering
+ * to windows guests.
+ */
+static bool vbsfErrorStyleIsWindowsPathNotFound(char *pszPath)
+{
+ /*
+ * Check if the parent directory actually exists. We temporarily modify the path here.
+ */
+ size_t cchParent = RTPathParentLength(pszPath);
+ char chSaved = pszPath[cchParent];
+ pszPath[cchParent] = '\0';
+ RTFSOBJINFO ObjInfo;
+ int vrc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ pszPath[cchParent] = chSaved;
+ if (RT_SUCCESS(vrc))
+ {
+ if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ return false;
+ return true;
+ }
+ if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
+ return true;
+ return false;
+}
+
+/**
+ * Helps to check if pszPath deserves a VERR_PATH_NOT_FOUND status when catering
+ * to windows guests.
+ */
+static bool vbsfErrorStyleIsWindowsPathNotFound2(char *pszSrcPath, char *pszDstPath)
+{
+ /*
+ * Do the source parent first.
+ */
+ size_t cchParent = RTPathParentLength(pszSrcPath);
+ char chSaved = pszSrcPath[cchParent];
+ pszSrcPath[cchParent] = '\0';
+ RTFSOBJINFO ObjInfo;
+ int vrc = RTPathQueryInfoEx(pszSrcPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ pszSrcPath[cchParent] = chSaved;
+ if ( (RT_SUCCESS(vrc) && !RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ || vrc == VERR_FILE_NOT_FOUND
+ || vrc == VERR_PATH_NOT_FOUND)
+ return true;
+ if (RT_FAILURE(vrc))
+ return false;
+
+ /*
+ * The source itself.
+ */
+ vrc = RTPathQueryInfoEx(pszSrcPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * The source is fine, continue with the destination.
+ */
+ cchParent = RTPathParentLength(pszDstPath);
+ chSaved = pszDstPath[cchParent];
+ pszDstPath[cchParent] = '\0';
+ vrc = RTPathQueryInfoEx(pszDstPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ pszDstPath[cchParent] = chSaved;
+ if ( (RT_SUCCESS(vrc) && !RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ || vrc == VERR_FILE_NOT_FOUND
+ || vrc == VERR_PATH_NOT_FOUND)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Helps checking if the specified path happens to exist but not be a directory.
+ */
+static bool vbsfErrorStyleIsWindowsNotADirectory(const char *pszPath)
+{
+ RTFSOBJINFO ObjInfo;
+ int vrc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (RT_SUCCESS(vrc))
+ {
+ if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Helps to check if pszPath deserves a VERR_INVALID_NAME status when catering
+ * to windows guests.
+ */
+static bool vbsfErrorStyleIsWindowsInvalidNameForNonDir(char *pszPath)
+{
+ /*
+ * This only applies to paths with trailing slashes.
+ */
+ size_t const cchPath = strlen(pszPath);
+ if (cchPath > 0 && RTPATH_IS_SLASH(pszPath[cchPath - 1]))
+ {
+ /*
+ * However it doesn't if an earlier path component is missing or not a file.
+ */
+ size_t cchParent = RTPathParentLength(pszPath);
+ char chSaved = pszPath[cchParent];
+ pszPath[cchParent] = '\0';
+ RTFSOBJINFO ObjInfo;
+ int vrc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ pszPath[cchParent] = chSaved;
+ if (RT_SUCCESS(vrc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ return true;
+ }
+ return false;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)
+{
+ RTUNICP cp;
+
+ /* Do not strip root. */
+ char *s = pszFullPath + cbFullPathRoot;
+ char *delimSecondLast = NULL;
+ char *delimLast = NULL;
+
+ LogFlowFunc(("%s -> %s\n", pszFullPath, s));
+
+ for (;;)
+ {
+ cp = RTStrGetCp(s);
+
+ if (cp == RTUNICP_INVALID || cp == 0)
+ {
+ break;
+ }
+
+ if (cp == RTPATH_DELIMITER)
+ {
+ if (delimLast != NULL)
+ {
+ delimSecondLast = delimLast;
+ }
+
+ delimLast = s;
+ }
+
+ s = RTStrNextCp(s);
+ }
+
+ if (cp == 0)
+ {
+ if (delimLast + 1 == s)
+ {
+ if (delimSecondLast)
+ {
+ *delimSecondLast = 0;
+ }
+ else if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ else
+ {
+ if (delimLast)
+ {
+ *delimLast = 0;
+ }
+ }
+ }
+
+ LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
+}
+
+static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath,
+ uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
+ bool fWildCard = false, bool fPreserveLastComponent = false)
+{
+ char *pszHostPath = NULL;
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE
+ | (fWildCard? VBSF_O_PATH_WILDCARD: 0)
+ | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0);
+
+ int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath,
+ &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags);
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc));
+ }
+ else
+ {
+ LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ppszFullPath)
+ *ppszFullPath = pszHostPath;
+ }
+ return rc;
+}
+
+static void vbsfFreeFullPath(char *pszFullPath)
+{
+ vbsfFreeHostPath(pszFullPath);
+}
+
+typedef enum VBSFCHECKACCESS
+{
+ VBSF_CHECK_ACCESS_READ = 0,
+ VBSF_CHECK_ACCESS_WRITE = 1
+} VBSFCHECKACCESS;
+
+/**
+ * Check if the handle data is valid and the operation is allowed on the shared folder.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pHandle Information about the file or directory object.
+ * @param enmCheckAccess Whether the operation needs read only or write access.
+ */
+static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root,
+ SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess)
+{
+ /* Handle from the same 'root' index? */
+ if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root))
+ { /* likely */ }
+ else
+ return VERR_INVALID_HANDLE;
+
+ /* Check if the guest is still allowed to access this share.
+ * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings.
+ */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return VERR_ACCESS_DENIED;
+
+ if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE)
+ {
+ /* Operation requires write access. Check if the shared folder is writable too. */
+ if (RT_LIKELY(fWritable))
+ { /* likely */ }
+ else
+ return VERR_WRITE_PROTECT;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
+ *
+ * @returns iprt status code
+ * @param fWritable whether the shared folder is writable
+ * @param fShflFlags shared folder create flags
+ * @param fMode file attributes
+ * @param handleInitial initial handle
+ * @param pfOpen Where to return iprt create flags
+ */
+static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint64_t *pfOpen)
+{
+ uint64_t fOpen = 0;
+ int rc = VINF_SUCCESS;
+
+ if ( (fMode & RTFS_DOS_MASK) != 0
+ && (fMode & RTFS_UNIX_MASK) == 0)
+ {
+ /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
+ * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
+ * May be better to use RTFsModeNormalize here.
+ */
+ fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
+ /* x for directories. */
+ if (fMode & RTFS_DOS_DIRECTORY)
+ fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
+ /* writable? */
+ if (!(fMode & RTFS_DOS_READONLY))
+ fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
+
+ /* Set the requested mode using only allowed bits. */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ else
+ {
+ /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
+ * and it contained random bits from stack. Detect this using the handle field value
+ * passed from the guest: old additions set it (incorrectly) to 0, new additions
+ * set it to SHFL_HANDLE_NIL(~0).
+ */
+ if (handleInitial == 0)
+ {
+ /* Old additions. Do nothing, use default mode. */
+ }
+ else
+ {
+ /* New additions or Windows additions. Set the requested mode using only allowed bits.
+ * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
+ * will be set in fOpen.
+ */
+ fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
+ }
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
+ {
+ default:
+ case SHFL_CF_ACCESS_NONE:
+ {
+#ifdef RT_OS_WINDOWS
+ if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE)
+ fOpen |= RTFILE_O_ATTR_ONLY;
+ else
+#endif
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READ:
+ {
+ fOpen |= RTFILE_O_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_WRITE:
+ {
+ fOpen |= RTFILE_O_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_READWRITE:
+ {
+ fOpen |= RTFILE_O_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
+ break;
+ }
+ }
+
+ if (fShflFlags & SHFL_CF_ACCESS_APPEND)
+ {
+ fOpen |= RTFILE_O_APPEND;
+ }
+
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
+ {
+ default:
+ case SHFL_CF_ACCESS_ATTR_NONE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READ:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_WRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
+ break;
+ }
+
+ case SHFL_CF_ACCESS_ATTR_READWRITE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
+ break;
+ }
+ }
+
+ /* Sharing mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
+ {
+ default:
+ case SHFL_CF_ACCESS_DENYNONE:
+ fOpen |= RTFILE_O_DENY_NONE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYREAD:
+ fOpen |= RTFILE_O_DENY_READ;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYWRITE:
+ fOpen |= RTFILE_O_DENY_WRITE;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
+ break;
+
+ case SHFL_CF_ACCESS_DENYALL:
+ fOpen |= RTFILE_O_DENY_ALL;
+ Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
+ break;
+ }
+
+ /* Open/Create action mask */
+ switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ {
+ case SHFL_CF_ACT_OPEN_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN;
+ Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_FAIL_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE;
+ Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_REPLACE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
+ if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_CREATE_REPLACE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
+ }
+ else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
+ Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
+ }
+ else
+ {
+ Log(("FLAGS: invalid open/create action combination\n"));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ break;
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!fWritable)
+ fOpen &= ~RTFILE_O_WRITE;
+
+ *pfOpen = fOpen;
+ }
+ return rc;
+}
+
+/**
+ * Open a file or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms Input:
+ * - @a CreateFlags: Creation or open parameters, see include/VBox/shflsvc.h
+ * - @a Info: When a new file is created this specifies the initial parameters.
+ * When a file is created or overwritten, it also specifies the
+ * initial size.
+ * Output:
+ * - @a Result: Shared folder status code, see include/VBox/shflsvc.h
+ * - @a Handle: On success the (shared folder) handle of the file opened or
+ * created
+ * - @a Info: On success the parameters of the file opened or created
+ */
+static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ RTFILEACTION enmActionTaken = RTFILEACTION_INVALID;
+ SHFLHANDLE handle = SHFL_HANDLE_NIL;
+ SHFLFILEHANDLE *pHandle = NULL;
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc))
+ fWritable = false;
+
+ uint64_t fOpen = 0;
+ rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_NO_MEMORY; /* Default error. */
+ handle = vbsfAllocFileHandle(pClient);
+ if (handle != SHFL_HANDLE_NIL)
+ {
+ pHandle = vbsfQueryFileHandle(pClient, handle);
+ if (pHandle)
+ {
+ pHandle->root = root;
+ pHandle->file.fOpenFlags = fOpen;
+ rc = RTFileOpenEx(pszPath, fOpen, &pHandle->file.Handle, &enmActionTaken);
+ }
+ }
+ }
+ bool fNoError = false;
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND:
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+#ifndef RT_OS_WINDOWS
+ if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+#endif
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+
+ case VERR_PATH_NOT_FOUND:
+#ifndef RT_OS_WINDOWS
+ if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsInvalidNameForNonDir(pszPath))
+ {
+ rc = VERR_INVALID_NAME;
+ pParms->Result = SHFL_NO_RESULT;
+ break;
+ }
+#endif
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ fNoError = true; /* Not an error either (see above). */
+ break;
+
+ case VERR_ALREADY_EXISTS:
+ {
+ RTFSOBJINFO info;
+
+ /** @todo Possible race left here. */
+ if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ pParms->Result = SHFL_FILE_EXISTS;
+
+ /* This actually isn't an error, so correct the rc before return later,
+ because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
+ fNoError = true;
+ break;
+ }
+
+ case VERR_TOO_MANY_OPEN_FILES:
+ {
+ static int s_cErrors;
+ if (s_cErrors < 32)
+ {
+ LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
+#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS)
+ if (s_cErrors < 1)
+ LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
+#endif
+ s_cErrors++;
+ }
+ pParms->Result = SHFL_NO_RESULT;
+ break;
+ }
+
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ else
+ {
+ switch (enmActionTaken)
+ {
+ default:
+ AssertFailed();
+ RT_FALL_THRU();
+ case RTFILEACTION_OPENED:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ case RTFILEACTION_CREATED:
+ pParms->Result = SHFL_FILE_CREATED;
+ break;
+ case RTFILEACTION_REPLACED:
+ case RTFILEACTION_TRUNCATED: /* not quite right */
+ pParms->Result = SHFL_FILE_REPLACED;
+ break;
+ }
+
+ if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS)
+ {
+ /* For now, we do not treat a failure here as fatal. */
+ /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
+ /** @todo r=bird: Exactly document cbObject usage and see what we can get
+ * away with here. I suspect it is only needed for windows and only
+ * with SHFL_FILE_CREATED and SHFL_FILE_REPLACED, and only if
+ * cbObject is non-zero. */
+ RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
+ }
+#if 0
+ /** @todo */
+ /* Set new attributes. */
+ if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
+ || ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
+ {
+ RTFileSetTimes(pHandle->file.Handle,
+ &pParms->Info.AccessTime,
+ &pParms->Info.ModificationTime,
+ &pParms->Info.ChangeTime,
+ &pParms->Info.BirthTime
+ );
+
+ RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
+ }
+#endif
+ RTFSOBJINFO info;
+
+ /* Get file information */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ /* Free resources if any part of the function has failed. */
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (NIL_RTFILE != pHandle->file.Handle)
+ && (0 != pHandle->file.Handle))
+ {
+ RTFileClose(pHandle->file.Handle);
+ pHandle->file.Handle = NIL_RTFILE;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+
+ /* Report the driver that all is okay, we're done here */
+ if (fNoError)
+ rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Open a folder or create and open a new one.
+ *
+ * @returns IPRT status code
+ * @param pClient Data structure describing the client accessing the shared
+ * folder
+ * @param root The index of the shared folder in the table of mappings.
+ * @param pszPath Path to the file or folder on the host.
+ * @param pParms Input: @a CreateFlags Creation or open parameters, see
+ * include/VBox/shflsvc.h
+ * Output:
+ * - @a Result: Shared folder status code, see include/VBox/shflsvc.h
+ * - @a Handle: On success the (shared folder) handle of the folder opened or
+ * created
+ * - @a Info: On success the parameters of the folder opened or created
+ *
+ * @note folders are created with fMode = 0777
+ */
+static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, char *pszPath,
+ SHFLCREATEPARMS *pParms)
+{
+ LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
+ Log(("SHFL create flags %08x\n", pParms->CreateFlags));
+
+ int rc = VERR_NO_MEMORY;
+ SHFLHANDLE handle = vbsfAllocDirHandle(pClient);
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle);
+ if (0 != pHandle)
+ {
+ pHandle->root = root;
+ pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
+ /** @todo Can anyone think of a sensible, race-less way to do this? Although
+ I suspect that the race is inherent, due to the API available... */
+ /* Try to create the folder first if "create if new" is specified. If this
+ fails, and "open if exists" is specified, then we ignore the failure and try
+ to open the folder anyway. */
+ if ( SHFL_CF_ACT_CREATE_IF_NEW
+ == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
+ {
+ /** @todo render supplied attributes.
+ * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
+ RTFMODE fMode = 0777;
+
+ pParms->Result = SHFL_FILE_CREATED;
+ rc = RTDirCreate(pszPath, fMode, 0);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo we still return 'rc' as failure here, so this is mostly pointless. */
+ switch (rc)
+ {
+ case VERR_ALREADY_EXISTS:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ break;
+ case VERR_FILE_NOT_FOUND: /* may happen on posix */
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+#ifndef RT_OS_WINDOWS
+ if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
+ {
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ rc = VERR_PATH_NOT_FOUND;
+ }
+#endif
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ else
+ rc = VINF_SUCCESS;
+ if ( RT_SUCCESS(rc)
+ || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
+ {
+ /* Open the directory now */
+ rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ }
+ }
+ else
+ {
+ /** @todo we still return 'rc' as failure here, so this is mostly pointless. */
+ switch (rc)
+ {
+ case VERR_FILE_NOT_FOUND:
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+#ifndef RT_OS_WINDOWS
+ if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsPathNotFound(pszPath))
+ {
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ rc = VERR_PATH_NOT_FOUND;
+ }
+#endif
+ break;
+ case VERR_PATH_NOT_FOUND:
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+#ifndef RT_OS_WINDOWS
+ if ( SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsNotADirectory(pszPath))
+ {
+ pParms->Result = SHFL_FILE_EXISTS;
+ rc = VERR_NOT_A_DIRECTORY;
+ break;
+ }
+#endif
+ break;
+ case VERR_ACCESS_DENIED:
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ default:
+ pParms->Result = SHFL_NO_RESULT;
+ }
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ if ( (0 != pHandle)
+ && (0 != pHandle->dir.Handle))
+ {
+ RTDirClose(pHandle->dir.Handle);
+ pHandle->dir.Handle = 0;
+ }
+ if (SHFL_HANDLE_NIL != handle)
+ {
+ vbsfFreeFileHandle(pClient, handle);
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ else
+ {
+ pParms->Handle = handle;
+ }
+ LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
+ return rc;
+}
+
+static int vbsfCloseDir(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
+ pHandle->dir.Handle, pHandle->dir.SearchHandle));
+
+ RTDirClose(pHandle->dir.Handle);
+
+ if (pHandle->dir.SearchHandle)
+ RTDirClose(pHandle->dir.SearchHandle);
+
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+ }
+
+ LogFlow(("vbsfCloseDir: rc = %d\n", rc));
+
+ return rc;
+}
+
+
+static int vbsfCloseFile(SHFLFILEHANDLE *pHandle)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCloseFile: Handle = %08X\n",
+ pHandle->file.Handle));
+
+ rc = RTFileClose(pHandle->file.Handle);
+
+ LogFlow(("vbsfCloseFile: rc = %d\n", rc));
+
+ return rc;
+}
+
+/**
+ * Look up file or folder information by host path.
+ *
+ * @returns iprt status code (currently VINF_SUCCESS)
+ * @param pClient client data
+ * @param pszPath The path of the file to be looked up
+ * @param pParms Output:
+ * - @a Result: Status of the operation (success or error)
+ * - @a Info: On success, information returned about the
+ * file
+ */
+static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
+{
+ RTFSOBJINFO info;
+ int rc;
+
+ rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("SHFL_CF_LOOKUP\n"));
+ /* Client just wants to know if the object exists. */
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ {
+#ifdef RT_OS_WINDOWS
+ info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
+ pParms->Result = SHFL_FILE_EXISTS;
+ break;
+ }
+
+ case VERR_FILE_NOT_FOUND:
+ {
+ pParms->Result = SHFL_FILE_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case VERR_PATH_NOT_FOUND:
+ {
+ pParms->Result = SHFL_PATH_NOT_FOUND;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ pParms->Handle = SHFL_HANDLE_NIL;
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CREATE API. Located here as a form of API
+ * documentation. */
+void testCreate(RTTEST hTest)
+{
+ /* Simple opening of an existing file. */
+ testCreateFileSimple(hTest);
+ testCreateFileSimpleCaseInsensitive(hTest);
+ /* Simple opening of an existing directory. */
+ /** @todo How do wildcards in the path name work? */
+ testCreateDirSimple(hTest);
+ /* If the number or types of parameters are wrong the API should fail. */
+ testCreateBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+/**
+ * Create or open a file or folder. Perform character set and case
+ * conversion on the file name if necessary.
+ *
+ * @returns IPRT status code, but see note below
+ * @param pClient Data structure describing the client accessing the
+ * shared folder
+ * @param root The index of the shared folder in the table of mappings.
+ * The host path of the shared folder is found using this.
+ * @param pPath The path of the file or folder relative to the host path
+ * indexed by root.
+ * @param cbPath Presumably the length of the path in pPath. Actually
+ * ignored, as pPath contains a length parameter.
+ * @param pParms Input: If a new file is created or an old one
+ * overwritten, set the @a Info attribute.
+ *
+ * Output:
+ * - @a Result Shared folder result code, see include/VBox/shflsvc.h
+ * - @a Handle Shared folder handle to the newly opened file
+ * - @a Info Attributes of the file or folder opened
+ *
+ * @note This function returns success if a "non-exceptional" error occurred,
+ * such as "no such file". In this case, the caller should check the
+ * pParms->Result return value and whether pParms->Handle is valid.
+ */
+int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
+ pClient, pPath, cbPath, pParms, pParms->CreateFlags));
+
+ /* Check the client access rights to the root. */
+ /** @todo */
+
+ /* Build a host full path for the given path, handle file name case issues (if the guest
+ * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
+ * necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+ if (RT_SUCCESS(rc))
+ {
+ /* Reset return value in case client forgot to do so.
+ * pParms->Handle must not be reset here, as it is used
+ * in vbsfOpenFile to detect old additions.
+ */
+ pParms->Result = SHFL_NO_RESULT;
+
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
+ {
+ rc = vbsfLookupFile(pClient, pszFullPath, pParms);
+ }
+ else
+ {
+ /* Query path information. */
+ RTFSOBJINFO info;
+
+ rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Mark it as a directory in case the caller didn't. */
+ /**
+ * @todo I left this in in order not to change the behaviour of the
+ * function too much. Is it really needed, and should it really be
+ * here?
+ */
+ if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
+ {
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ }
+
+ /**
+ * @todo This should be in the Windows Guest Additions, as no-one else
+ * needs it.
+ */
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
+ {
+ vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
+ pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
+ pParms->CreateFlags |= SHFL_CF_DIRECTORY;
+ pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
+ pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
+ }
+ }
+
+ rc = VINF_SUCCESS;
+
+ /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
+ * will cause changes.
+ *
+ * Actual operations (write, set attr, etc), which can write to a shared folder, have
+ * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
+ */
+ if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
+ || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
+ )
+ {
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
+ {
+ rc = vbsfOpenDir(pClient, root, pszFullPath, pParms);
+ }
+ else
+ {
+ rc = vbsfOpenFile(pClient, root, pszFullPath, pParms);
+ }
+ }
+ else
+ {
+ pParms->Handle = SHFL_HANDLE_NIL;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
+ * documentation. */
+void testClose(RTTEST hTest)
+{
+ /* If the API parameters are invalid the API should fail. */
+ testCloseBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n",
+ pClient, root, Handle));
+
+ int rc = VERR_INVALID_HANDLE;
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseDir(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ if (RT_LIKELY(pHandle && root == pHandle->root))
+ {
+ rc = vbsfCloseFile(pHandle);
+ vbsfFreeFileHandle(pClient, Handle);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ LogFunc(("rc = %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Helper for vbsfReadPages and vbsfWritePages that creates a S/G buffer from a
+ * pages parameter.
+ */
+static int vbsfPagesToSgBuf(VBOXHGCMSVCPARMPAGES const *pPages, uint32_t cbLeft, PRTSGBUF pSgBuf)
+{
+ PRTSGSEG paSegs = (PRTSGSEG)RTMemTmpAlloc(sizeof(paSegs[0]) * pPages->cPages);
+ if (paSegs)
+ {
+ /*
+ * Convert the pages to segments.
+ */
+ uint32_t iSeg = 0;
+ uint32_t iPage = 0;
+ for (;;)
+ {
+ Assert(iSeg < pPages->cPages);
+ Assert(iPage < pPages->cPages);
+
+ /* Current page. */
+ void *pvSeg;
+ paSegs[iSeg].pvSeg = pvSeg = pPages->papvPages[iPage];
+ uint32_t cbSeg = PAGE_SIZE - (uint32_t)((uintptr_t)pvSeg & PAGE_OFFSET_MASK);
+ iPage++;
+
+ /* Adjacent to the next page? */
+ while ( iPage < pPages->cPages
+ && (uintptr_t)pvSeg + cbSeg == (uintptr_t)pPages->papvPages[iPage])
+ {
+ iPage++;
+ cbSeg += PAGE_SIZE;
+ }
+
+ /* Adjust for max size. */
+ if (cbLeft <= cbSeg)
+ {
+ paSegs[iSeg++].cbSeg = cbLeft;
+ break;
+ }
+ paSegs[iSeg++].cbSeg = cbSeg;
+ cbLeft -= cbSeg;
+ }
+
+ /*
+ * Initialize the s/g buffer and execute the read.
+ */
+ RTSgBufInit(pSgBuf, paSegs, iSeg);
+ return VINF_SUCCESS;
+ }
+ pSgBuf->paSegs = NULL;
+ return VERR_NO_TMP_MEMORY;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READ API. Located here as a form of API
+ * documentation. */
+void testRead(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadBadParameters(hTest);
+ /* Basic reading from a file. */
+ testReadFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n",
+ pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ {
+ size_t const cbToRead = *pcbBuffer;
+ if (RT_LIKELY(cbToRead > 0))
+ {
+ size_t cbActual = 0;
+ rc = RTFileReadAt(pHandle->file.Handle, offset, pBuffer, cbToRead, &cbActual);
+ *pcbBuffer = (uint32_t)cbActual;
+ }
+ else
+ {
+ /* Reading zero bytes always succeeds. */
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ *pcbBuffer = 0;
+
+ LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+/**
+ * SHFL_FN_READ w/o bounce buffering.
+ */
+int vbsfReadPages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t offFile,
+ uint32_t *pcbRead, PVBOXHGCMSVCPARMPAGES pPages)
+{
+ LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbRead %#RX32, cPages %#x\n",
+ pClient, idRoot, hFile, offFile, *pcbRead, pPages->cPages));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ size_t cbTotal = 0;
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t const cbToRead = *pcbRead;
+ if (cbToRead > 0)
+ {
+ ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Convert to a scatter-gather buffer.
+ *
+ * We need not do any platform specific code here as the RTSGBUF
+ * segment array maps directly onto the posix iovec structure.
+ * Windows does currently benefit much from this conversion, but
+ * so be it.
+ */
+ RTSGBUF SgBuf;
+ rc = vbsfPagesToSgBuf(pPages, cbToRead, &SgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
+ while (rc == VERR_INTERRUPTED)
+ {
+ RTSgBufReset(&SgBuf);
+ rc = RTFileSgReadAt(pHandle->file.Handle, offFile, &SgBuf, cbToRead, &cbTotal);
+ }
+
+ RTMemTmpFree((void *)SgBuf.paSegs);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+
+ *pcbRead = (uint32_t)cbTotal;
+ }
+ else
+ {
+ /* Reading zero bytes always succeeds. */
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ *pcbRead = 0;
+
+ LogFunc(("%Rrc bytes read %#zx\n", rc, cbTotal));
+ return rc;
+}
+
+/**
+ * Helps with writes to RTFILE_O_APPEND files.
+ */
+static uint64_t vbsfWriteCalcPostAppendFilePosition(RTFILE hFile, uint64_t offGuessed)
+{
+ RTFSOBJINFO ObjInfo;
+ int rc2 = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc2) && (uint64_t)ObjInfo.cbObject >= offGuessed)
+ return ObjInfo.cbObject;
+ return offGuessed;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
+ * documentation. */
+void testWrite(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testWriteBadParameters(hTest);
+ /* Simple test of writing to a file. */
+ testWriteFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ uint64_t offFile = *poffFile;
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offFile 0x%RX64, bytes 0x%RX32\n",
+ pClient, idRoot, hFile, offFile, *pcbBuffer));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ size_t const cbToWrite = *pcbBuffer;
+ if (RT_LIKELY(cbToWrite != 0))
+ {
+ size_t cbWritten = 0;
+ if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
+ rc = RTFileWriteAt(pHandle->file.Handle, offFile, pBuffer, cbToWrite, &cbWritten);
+ else
+ {
+ rc = RTFileSeek(pHandle->file.Handle, offFile, RTFILE_SEEK_BEGIN, NULL);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(pHandle->file.Handle, pBuffer, cbToWrite, &cbWritten);
+ *pcbBuffer = (uint32_t)cbWritten;
+ }
+ }
+
+ /* Update the file offset (mainly for RTFILE_O_APPEND), */
+ if (RT_SUCCESS(rc))
+ {
+ offFile += cbWritten;
+ if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
+ *poffFile = offFile;
+ else
+ *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
+ }
+ }
+ else
+ {
+ /** @todo What writing zero bytes should do? */
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ *pcbBuffer = 0;
+ LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer));
+ return rc;
+}
+
+/**
+ * SHFL_FN_WRITE w/o bounce buffering.
+ */
+int vbsfWritePages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
+ uint32_t *pcbWrite, PVBOXHGCMSVCPARMPAGES pPages)
+{
+ uint64_t offFile = *poffFile;
+ LogFunc(("pClient %p, idRoot %#RX32, hFile %#RX64, offFile %#RX64, cbWrite %#RX32, cPages %#x\n",
+ pClient, idRoot, hFile, offFile, *pcbWrite, pPages->cPages));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ size_t cbTotal = 0;
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hFile);
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t const cbToWrite = *pcbWrite;
+ if (cbToWrite > 0)
+ {
+ ASSERT_GUEST_RETURN(pPages->cPages > 0, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Convert to a scatter-gather buffer.
+ *
+ * We need not do any platform specific code here as the RTSGBUF
+ * segment array maps directly onto the posix iovec structure.
+ * Windows does currently benefit much from this conversion, but
+ * so be it.
+ */
+ RTSGBUF SgBuf;
+ rc = vbsfPagesToSgBuf(pPages, cbToWrite, &SgBuf);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_LINUX
+ /* Cannot use RTFileSgWriteAt or RTFileWriteAt when opened with
+ RTFILE_O_APPEND, except for on linux where the offset is
+ then ignored by the low level kernel API. */
+ if (pHandle->file.fOpenFlags & RTFILE_O_APPEND)
+ {
+ /* paranoia */
+ RTFileSeek(pHandle->file.Handle, 0, RTFILE_SEEK_END, NULL);
+
+ for (size_t iSeg = 0; iSeg < SgBuf.cSegs; iSeg++)
+ {
+ size_t cbWrittenNow = 0;
+ do
+ rc = RTFileWrite(pHandle->file.Handle, SgBuf.paSegs[iSeg].pvSeg,
+ SgBuf.paSegs[iSeg].cbSeg, &cbWrittenNow);
+ while (rc == VERR_INTERRUPTED);
+ if (RT_SUCCESS(rc))
+ {
+ cbTotal += cbWrittenNow;
+ if (cbWrittenNow < SgBuf.paSegs[iSeg].cbSeg)
+ break;
+ }
+ else
+ {
+ if (cbTotal > 0)
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ else
+#endif
+ {
+ rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
+ while (rc == VERR_INTERRUPTED)
+ {
+ RTSgBufReset(&SgBuf);
+ rc = RTFileSgWriteAt(pHandle->file.Handle, offFile, &SgBuf, cbToWrite, &cbTotal);
+ }
+ }
+
+ RTMemTmpFree((void *)SgBuf.paSegs);
+
+ /* Update the file offset (mainly for RTFILE_O_APPEND), */
+ if (RT_SUCCESS(rc))
+ {
+ offFile += cbTotal;
+ if (!(pHandle->file.fOpenFlags & RTFILE_O_APPEND))
+ *poffFile = offFile;
+ else
+ *poffFile = vbsfWriteCalcPostAppendFilePosition(pHandle->file.Handle, offFile);
+ }
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+
+ *pcbWrite = (uint32_t)cbTotal;
+ }
+ else
+ {
+ /* Writing zero bytes always succeeds. */
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ *pcbWrite = 0;
+
+ LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
+ return rc;
+}
+
+/**
+ * Implements SHFL_FN_COPY_FILE_PART (wrapping RTFileCopyPart).
+ */
+int vbsfCopyFilePart(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, SHFLHANDLE hFileSrc, uint64_t offSrc,
+ SHFLROOT idRootDst, SHFLHANDLE hFileDst, uint64_t offDst, uint64_t *pcbToCopy, uint32_t fFlags)
+{
+ /*
+ * Validate and translates handles.
+ */
+ uint64_t const cbToCopy = *pcbToCopy;
+ *pcbToCopy = 0;
+ LogFunc(("pClient %p, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, idRootSrc %#RX32, hFileSrc %#RX64, offSrc %#RX64, cbToCopy %#RX64, fFlags %#x\n",
+ pClient, idRootSrc, hFileSrc, offSrc, idRootDst, hFileDst, offDst, cbToCopy, fFlags));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ uint64_t cbTotal = 0;
+
+ SHFLFILEHANDLE *pHandleSrc = vbsfQueryFileHandle(pClient, hFileSrc);
+ int rc = vbsfCheckHandleAccess(pClient, idRootSrc, pHandleSrc, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ {
+ SHFLFILEHANDLE *pHandleDst = vbsfQueryFileHandle(pClient, hFileDst);
+ rc = vbsfCheckHandleAccess(pClient, idRootDst, pHandleDst, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the job.
+ */
+ rc = RTFileCopyPart(pHandleSrc->file.Handle, offSrc, pHandleDst->file.Handle, offDst, cbToCopy, 0, &cbTotal);
+ *pcbToCopy = cbTotal;
+ }
+ }
+
+ RT_NOREF(fFlags);
+ LogFunc(("%Rrc bytes written %#zx\n", rc, cbTotal));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
+ * documentation. */
+void testFlush(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFlushBadParameters(hTest);
+ /* Simple opening and flushing of a file. */
+ testFlushFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
+{
+ LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n",
+ pClient, root, Handle));
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ rc = RTFileFlush(pHandle->file.Handle);
+
+ LogFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LIST API. Located here as a form of API
+ * documentation. */
+void testDirList(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testDirListBadParameters(hTest);
+ /* Test listing an empty directory (simple edge case). */
+ testDirListEmpty(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
+{
+ PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
+ uint32_t cbDirEntry, cbBufferOrg;
+ PSHFLDIRINFO pSFDEntry;
+ PRTUTF16 pwszString;
+ RTDIR hDir;
+ const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
+
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ Assert(*pIndex == 0);
+
+ cbDirEntry = 4096;
+ pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == 0)
+ {
+ AssertFailed();
+ return VERR_NO_MEMORY;
+ }
+
+ cbBufferOrg = *pcbBuffer;
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLDIRINFO)pBuffer;
+
+ *pIndex = 1; /* not yet complete */
+ *pcFiles = 0;
+
+ if (!pPath)
+ hDir = pHandle->dir.Handle;
+ else
+ {
+ if (pHandle->dir.SearchHandle == 0)
+ {
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+
+ Assert(pHandle->dir.pLastValidEntry == 0);
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+ else
+ goto end;
+ flags &= ~SHFL_LIST_RESTART;
+ }
+ Assert(pHandle->dir.SearchHandle);
+ hDir = pHandle->dir.SearchHandle;
+ }
+
+ if (flags & SHFL_LIST_RESTART)
+ {
+ rc = RTDirRewind(hDir);
+ if (RT_FAILURE(rc))
+ goto end;
+ }
+
+ while (cbBufferOrg)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+ uint32_t cbNeeded;
+
+ /* Do we still have a valid last entry for the active search? If so, then return it here */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ pDirEntry = pHandle->dir.pLastValidEntry;
+ }
+ else
+ {
+ pDirEntry = pDirEntryOrg;
+
+ rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (rc == VERR_NO_MORE_FILES)
+ {
+ *pIndex = 0; /* listing completed */
+ break;
+ }
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_NO_DIRENT_INFO)
+ {
+ //AssertFailed();
+ if ( rc == VERR_NO_TRANSLATION
+ || rc == VERR_INVALID_UTF8_ENCODING)
+ continue;
+ break;
+ }
+ }
+
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
+ if (fUtf8)
+ cbNeeded += pDirEntry->cbName + 1;
+ else
+ /* Overestimating, but that's ok */
+ cbNeeded += (pDirEntry->cbName + 1) * 2;
+
+ if (cbBufferOrg < cbNeeded)
+ {
+ /* No room, so save this directory entry, or else it's lost forever */
+ pHandle->dir.pLastValidEntry = pDirEntry;
+
+ if (*pcFiles == 0)
+ {
+ AssertFailed();
+ return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
+ }
+ return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
+ }
+
+#ifdef RT_OS_WINDOWS
+ pDirEntry->Info.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
+
+ /* The shortname (only used by OS/2 atm): */
+ Assert(pDirEntry->cwcShortName < RT_ELEMENTS(pSFDEntry->uszShortName));
+ Assert(pDirEntry->wszShortName[pDirEntry->cwcShortName] == '\0');
+ pSFDEntry->cucShortName = pDirEntry->cwcShortName;
+ if (pDirEntry->cwcShortName)
+ memcpy(pSFDEntry->uszShortName, pDirEntry->wszShortName, sizeof(pSFDEntry->uszShortName));
+
+ /* The name: */
+ if (fUtf8)
+ {
+ void *src, *dst;
+
+ src = &pDirEntry->szName[0];
+ dst = &pSFDEntry->name.String.utf8[0];
+
+ memcpy(dst, src, pDirEntry->cbName + 1);
+
+ pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
+ pSFDEntry->name.u16Length = pDirEntry->cbName;
+ }
+ else
+ {
+ pSFDEntry->name.String.ucs2[0] = 0;
+ pwszString = pSFDEntry->name.String.ucs2;
+ int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
+ AssertRC(rc2);
+
+#ifdef RT_OS_DARWIN
+/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
+ * The question is simply whether the NFD normalization is actually applied on a (virtual) file
+ * system level in darwin, or just by the user mode application libs. */
+ {
+ // Convert to
+ // Normalization Form C (composed Unicode). We need this because
+ // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
+ // while most other OS', server-side programs usually expect NFC.
+ uint16_t ucs2Length;
+ CFRange rangeCharacters;
+ CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
+
+ ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
+ ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
+ ucs2Length = ::CFStringGetLength(inStr);
+
+ rangeCharacters.location = 0;
+ rangeCharacters.length = ucs2Length;
+ ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
+ pwszString[ucs2Length] = 0x0000; // NULL terminated
+
+ CFRelease(inStr);
+ }
+#endif
+ pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
+ pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
+
+ Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
+ Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
+
+ // adjust cbNeeded (it was overestimated before)
+ cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
+ }
+
+ /* Advance */
+ pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
+ *pcbBuffer += cbNeeded;
+ cbBufferOrg-= cbNeeded;
+
+ *pcFiles += 1;
+
+ /* Free the saved last entry, that we've just returned */
+ if (pHandle->dir.pLastValidEntry)
+ {
+ RTMemFree(pHandle->dir.pLastValidEntry);
+ pHandle->dir.pLastValidEntry = NULL;
+
+ /* And use the newly allocated buffer from now. */
+ pDirEntry = pDirEntryOrg;
+ }
+
+ if (flags & SHFL_LIST_RETURN_ONE)
+ break; /* we're done */
+ }
+ Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
+
+end:
+ if (pDirEntry)
+ RTMemFree(pDirEntry);
+
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
+ * documentation. */
+void testReadLink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testReadLinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pPath == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path, handle file name case issues
+ * (if the guest expects case-insensitive paths but the host is
+ * case-sensitive) and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+ uint32_t cbFullPathRoot = 0;
+
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert the slashes in the link target to the guest path separator characters. */
+ /** @todo r=bird: for some messed up reason, we return UTF-8 here rather than
+ * the character set selected by the client. We also don't return the
+ * length, so the clients are paranoid about the zero termination behavior. */
+ char ch;
+ char *psz = (char *)pBuffer;
+ while ((ch = *psz) != '\0')
+ {
+ if (RTPATH_IS_SLASH(ch))
+ *psz = pClient->PathDelimiter;
+ psz++;
+ }
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+
+ return rc;
+}
+
+int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
+ RTFSOBJINFO fileinfo;
+
+
+ AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER);
+ AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER);
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
+
+ *pcbBuffer = 0;
+
+ if (type == SHFL_HF_TYPE_DIR)
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ }
+ else
+ {
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+#ifdef RT_OS_WINDOWS
+ if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
+ pObjInfo->Attr.fMode |= 0111;
+#endif
+ }
+ if (rc == VINF_SUCCESS)
+ {
+ vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+
+ return rc;
+}
+
+static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ int rc = VINF_SUCCESS;
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
+ || pcbBuffer == 0
+ || pBuffer == 0
+ || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
+
+ /*
+ * Get the handle.
+ */
+ SHFLFILEHANDLE *pHandle;
+ if (type == SHFL_HF_TYPE_FILE)
+ {
+ pHandle = vbsfQueryFileHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ }
+ else
+ {
+ Assert(type == SHFL_HF_TYPE_DIR);
+ pHandle = vbsfQueryDirHandle(pClient, Handle);
+ rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Any times to set?
+ */
+ if ( RTTimeSpecGetNano(&pSFDEntry->AccessTime)
+ || RTTimeSpecGetNano(&pSFDEntry->ModificationTime)
+ || RTTimeSpecGetNano(&pSFDEntry->ChangeTime)
+ || RTTimeSpecGetNano(&pSFDEntry->BirthTime))
+ {
+
+ /* Change only the time values that are not zero */
+ if (type == SHFL_HF_TYPE_FILE)
+ rc = RTFileSetTimes(pHandle->file.Handle,
+ RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
+ else
+ rc = RTDirSetTimes( pHandle->dir.Handle,
+ RTTimeSpecGetNano(&pSFDEntry->AccessTime) ? &pSFDEntry->AccessTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->ModificationTime) ? &pSFDEntry->ModificationTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->ChangeTime) ? &pSFDEntry->ChangeTime : NULL,
+ RTTimeSpecGetNano(&pSFDEntry->BirthTime) ? &pSFDEntry->BirthTime : NULL);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RT%sSetTimes failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir", rc));
+ Log(("AccessTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
+ Log(("ModificationTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
+ Log(("ChangeTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
+ Log(("BirthTime %#RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
+ /* "temporary" hack */
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Any mode changes?
+ */
+ if (pSFDEntry->Attr.fMode)
+ {
+ RTFMODE fMode = pSFDEntry->Attr.fMode;
+
+ if (type == SHFL_HF_TYPE_FILE)
+ {
+#ifndef RT_OS_WINDOWS
+ /* Don't allow the guest to clear the read own bit, otherwise the guest wouldn't
+ * be able to access this file anymore. Only for guests, which set the UNIX mode.
+ * Also, clear bits which we don't pass through for security reasons. */
+ if (fMode & RTFS_UNIX_MASK)
+ {
+ fMode |= RTFS_UNIX_IRUSR;
+ fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
+ }
+#endif
+ rc = RTFileSetMode(pHandle->file.Handle, fMode);
+ }
+ else
+ {
+#ifndef RT_OS_WINDOWS
+ /* Don't allow the guest to clear the read+execute own bits, otherwise the guest
+ * wouldn't be able to access this directory anymore. Only for guests, which set
+ * the UNIX mode. Also, clear bits which we don't pass through for security reasons. */
+ if (fMode & RTFS_UNIX_MASK)
+ {
+ fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IXUSR;
+ fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT /*?*/);
+ }
+#endif
+ rc = RTDirSetMode(pHandle->dir.Handle, fMode);
+ }
+ if (RT_FAILURE(rc))
+ {
+ Log(("RT%sSetMode %#x (%#x) failed with %Rrc\n", type == SHFL_HF_TYPE_FILE ? "File" : "Dir",
+ fMode, pSFDEntry->Attr.fMode, rc));
+ /* silent failure, because this tends to fail with e.g. windows guest & linux host */
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Return the current file info on success.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t bufsize = sizeof(*pSFDEntry);
+ rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET | SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
+ if (RT_SUCCESS(rc))
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ else
+ AssertFailed();
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Handles SHFL_FN_SET_FILE_SIZE.
+ */
+int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize)
+{
+ /*
+ * Resolve handle and validate write access.
+ */
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle);
+ ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE);
+
+ int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Execute the request.
+ */
+ rc = RTFileSetSize(pHandle->file.Handle, cbNewSize);
+ }
+ return rc;
+}
+
+
+static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags,
+ uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF1(flags);
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ SHFLFSOBJINFO *pSFDEntry;
+
+ if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ *pcbBuffer = 0;
+ pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
+
+ if (flags & SHFL_INFO_SIZE)
+ {
+ rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
+ if (rc != VINF_SUCCESS)
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+
+ if (rc == VINF_SUCCESS)
+ {
+ RTFSOBJINFO fileinfo;
+
+ /* Query the new object info and return it */
+ rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
+ if (rc == VINF_SUCCESS)
+ {
+#ifdef RT_OS_WINDOWS
+ fileinfo.Attr.fMode |= 0111;
+#endif
+ vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
+ *pcbBuffer = sizeof(SHFLFSOBJINFO);
+ }
+ else
+ AssertFailed();
+ }
+
+ return rc;
+}
+
+int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ RT_NOREF2(root, flags);
+ int rc = VINF_SUCCESS;
+ SHFLVOLINFO *pSFDEntry;
+ char *pszFullPath = NULL;
+ union
+ {
+ SHFLSTRING Dummy;
+ uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)];
+ } Buf;
+
+ if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /** @todo other options */
+ Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
+
+ *pcbBuffer = 0;
+ pSFDEntry = (PSHFLVOLINFO)pBuffer;
+
+ ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf));
+ Buf.Dummy.String.ucs2[0] = '\0';
+ rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+
+ RTFSPROPERTIES FsProperties;
+ rc = RTFsQueryProperties(pszFullPath, &FsProperties);
+ if (rc != VINF_SUCCESS)
+ goto exit;
+ vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
+
+ *pcbBuffer = sizeof(SHFLVOLINFO);
+ }
+ else AssertFailed();
+
+exit:
+ AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ return rc;
+}
+
+int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ if (pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_VOLUME)
+ return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
+ * documentation. */
+void testFSInfo(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testFSInfoBadParameters(hTest);
+ /* Basic get and set file size test. */
+ testFSInfoQuerySetFMode(hTest);
+ /* Basic get and set dir atime test. */
+ testFSInfoQuerySetDirATime(hTest);
+ /* Basic get and set file atime test. */
+ testFSInfoQuerySetFileATime(hTest);
+ /* Basic set end of file. */
+ testFSInfoQuerySetEndOfFile(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
+{
+ uint32_t type = vbsfQueryHandleType(pClient, Handle)
+ & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
+
+ if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (flags & SHFL_INFO_FILE)
+ return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+ if (flags & SHFL_INFO_SIZE)
+ return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+
+// if (flags & SHFL_INFO_VOLUME)
+// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
+ * documentation. */
+void testLock(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testLockBadParameters(hTest);
+ /* Simple file locking and unlocking test. */
+ testLockFileSimple(hTest);
+ /* Add tests as required... */
+}
+#endif
+
+int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+ uint32_t fRTLock = 0;
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock type */
+ switch(flags & SHFL_LOCK_MODE_MASK)
+ {
+ case SHFL_LOCK_SHARED:
+ fRTLock = RTFILE_LOCK_READ;
+ break;
+
+ case SHFL_LOCK_EXCLUSIVE:
+ fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
+ break;
+
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Lock wait type */
+ if (flags & SHFL_LOCK_WAIT)
+ fRTLock |= RTFILE_LOCK_WAIT;
+ else
+ fRTLock |= RTFILE_LOCK_IMMEDIATELY;
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfLock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+ return rc;
+}
+
+int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
+{
+ SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
+
+ Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
+
+ int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+
+ if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
+ || (flags & SHFL_LOCK_ENTIRE)
+ )
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
+#ifdef RT_OS_WINDOWS
+ rc = RTFileUnlock(pHandle->file.Handle, offset, length);
+ if (rc != VINF_SUCCESS)
+ Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
+#else
+ Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
+ rc = VINF_SUCCESS;
+ RT_NOREF2(offset, length);
+#endif
+
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
+ * documentation. */
+void testRemove(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRemoveBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath, uint32_t cbPath, uint32_t flags, SHFLHANDLE hToClose)
+{
+
+ /* Validate input */
+ Assert(pPath);
+ AssertReturn(pPath->u16Size > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Close the handle if specified.
+ */
+ int rc = VINF_SUCCESS;
+ if (hToClose != SHFL_HANDLE_NIL)
+ rc = vbsfClose(pClient, root, hToClose);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Build a host full path for the given path and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPath = NULL;
+ rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Is the guest allowed to write to this share?
+ */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_SUCCESS(rc) && fWritable)
+ {
+ /*
+ * Do the removal/deletion according to the type flags.
+ */
+ if (flags & SHFL_REMOVE_SYMLINK)
+ rc = RTSymlinkDelete(pszFullPath, 0);
+ else if (flags & SHFL_REMOVE_FILE)
+ rc = RTFileDelete(pszFullPath);
+ else
+ rc = RTDirRemove(pszFullPath);
+
+#if 0 //ndef RT_OS_WINDOWS
+ /* There are a few adjustments to be made here: */
+ if ( rc == VERR_FILE_NOT_FOUND
+ && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsPathNotFound(pszFullPath))
+ rc = VERR_PATH_NOT_FOUND;
+ else if ( rc == VERR_PATH_NOT_FOUND
+ && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient))
+ {
+ if (flags & (SHFL_REMOVE_FILE | SHFL_REMOVE_SYMLINK))
+ {
+ size_t cchFullPath = strlen(pszFullPath);
+ if (cchFullPath > 0 && RTPATH_IS_SLASH(pszFullPath[cchFullPath - 1]))
+ rc = VERR_INVALID_NAME;
+ }
+ else if (vbsfErrorStyleIsWindowsNotADirectory(pszFullPath))
+ rc = VERR_NOT_A_DIRECTORY;
+ }
+#endif
+ }
+ else
+ rc = VERR_WRITE_PROTECT;
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPath);
+ }
+ }
+ return rc;
+}
+
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
+ * documentation. */
+void testRename(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testRenameBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Validate input */
+ if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
+ || pSrc == 0
+ || pDest == 0)
+ {
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Build a host full path for the given path
+ * and convert ucs2 to utf8 if necessary.
+ */
+ char *pszFullPathSrc = NULL;
+ char *pszFullPathDest = NULL;
+
+ rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true);
+ if (RT_SUCCESS (rc))
+ {
+ Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
+
+ /* is the guest allowed to write to this share? */
+ bool fWritable;
+ rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
+ if (RT_FAILURE(rc) || !fWritable)
+ rc = VERR_WRITE_PROTECT;
+
+ if (RT_SUCCESS(rc))
+ {
+ if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR))
+ {
+ rc = RTPathRename(pszFullPathSrc, pszFullPathDest,
+ flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0);
+ }
+ else if (flags & SHFL_RENAME_FILE)
+ {
+ rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
+ }
+ else
+ {
+ /* NT ignores the REPLACE flag and simply return and already exists error. */
+ rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
+ ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
+ }
+#ifndef RT_OS_WINDOWS
+ if ( rc == VERR_FILE_NOT_FOUND
+ && SHFL_CLIENT_NEED_WINDOWS_ERROR_STYLE_ADJUST_ON_POSIX(pClient)
+ && vbsfErrorStyleIsWindowsPathNotFound2(pszFullPathSrc, pszFullPathDest))
+ rc = VERR_PATH_NOT_FOUND;
+#endif
+ }
+
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathDest);
+ }
+ /* free the path string */
+ vbsfFreeFullPath(pszFullPathSrc);
+ return rc;
+}
+
+/**
+ * Implements SHFL_FN_COPY_FILE (wrapping RTFileCopy).
+ */
+int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
+ SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags)
+{
+ AssertPtrReturn(pClient, VERR_INVALID_PARAMETER);
+ if (pClient->fu32Flags & SHFL_CF_UTF8)
+ LogFunc(("pClient %p, idRootSrc %#RX32, '%.*s', idRootSrc %#RX32, '%.*s', fFlags %#x\n", pClient, idRootSrc,
+ pStrPathSrc->u16Length, pStrPathSrc->String.ach, idRootDst, pStrPathDst->u16Length, pStrPathDst->String.ach, fFlags));
+ else
+ LogFunc(("pClient %p, idRootSrc %#RX32, '%.*ls', idRootSrc %#RX32, '%.*ls', fFlags %#x\n", pClient,
+ idRootSrc, pStrPathSrc->u16Length / sizeof(RTUTF16), pStrPathSrc->String.ach,
+ idRootDst, pStrPathDst->u16Length / sizeof(RTUTF16), pStrPathDst->String.ach, fFlags));
+
+ /*
+ * Build host paths.
+ */
+ char *pszPathSrc = NULL;
+ int rc = vbsfBuildFullPath(pClient, idRootSrc, pStrPathSrc, pStrPathSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathSrc, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszPathDst = NULL;
+ rc = vbsfBuildFullPath(pClient, idRootDst, pStrPathDst, pStrPathDst->u16Size + SHFLSTRING_HEADER_SIZE, &pszPathDst, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the job.
+ */
+ rc = RTFileCopy(pszPathSrc, pszPathDst);
+
+ vbsfFreeFullPath(pszPathDst);
+ }
+ vbsfFreeFullPath(pszPathSrc);
+ }
+
+ RT_NOREF(fFlags);
+ LogFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+#ifdef UNITTEST
+/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
+ * documentation. */
+void testSymlink(RTTEST hTest)
+{
+ /* If the number or types of parameters are wrong the API should fail. */
+ testSymlinkBadParameters(hTest);
+ /* Add tests as required... */
+}
+#endif
+int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
+{
+ int rc = VINF_SUCCESS;
+
+ char *pszFullNewPath = NULL;
+ char *pszFullOldPath = NULL;
+
+ /* XXX: no support for UCS2 at the moment. */
+ if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ return VERR_NOT_IMPLEMENTED;
+
+ bool fSymlinksCreate;
+ rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
+ AssertRCReturn(rc, rc);
+ if (!fSymlinksCreate)
+ return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
+
+ rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */
+ uint32_t fu32PathFlags = 0;
+ uint32_t fu32Options = 0;
+ rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE,
+ &pszFullOldPath, NULL, fu32Options, &fu32PathFlags);
+ if (RT_FAILURE(rc))
+ {
+ vbsfFreeFullPath(pszFullNewPath);
+ return rc;
+ }
+
+ /** @todo r=bird: We _must_ perform slash conversion on the target (what this
+ * code calls 'pOldPath' for some peculiar reason)! */
+
+ rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
+ RTSYMLINKTYPE_UNKNOWN, 0);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO info;
+ rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ vbfsCopyFsObjInfoFromIprt(pInfo, &info);
+ }
+
+ vbsfFreeFullPath(pszFullOldPath);
+ vbsfFreeFullPath(pszFullNewPath);
+
+ return rc;
+}
+
+/*
+ * Clean up our mess by freeing all handles that are still valid.
+ *
+ */
+int vbsfDisconnect(SHFLCLIENTDATA *pClient)
+{
+ for (int i = 0; i < SHFLHANDLE_MAX; ++i)
+ {
+ SHFLFILEHANDLE *pHandle = NULL;
+ SHFLHANDLE Handle = (SHFLHANDLE)i;
+
+ uint32_t type = vbsfQueryHandleType(pClient, Handle);
+ switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
+ {
+ case SHFL_HF_TYPE_DIR:
+ {
+ pHandle = vbsfQueryDirHandle(pClient, Handle);
+ break;
+ }
+ case SHFL_HF_TYPE_FILE:
+ {
+ pHandle = vbsfQueryFileHandle(pClient, Handle);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (pHandle)
+ {
+ LogFunc(("Opened handle 0x%08x\n", i));
+ vbsfClose(pClient, pHandle->root, Handle);
+ }
+ }
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++)
+ if (pClient->acMappings[i])
+ {
+ uint16_t cMappings = pClient->acMappings[i];
+ while (cMappings-- > 0)
+ vbsfUnmapFolder(pClient, i);
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/vbsf.h b/src/VBox/HostServices/SharedFolders/vbsf.h
new file mode 100644
index 00000000..04ef280a
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsf.h
@@ -0,0 +1,68 @@
+/* $Id: vbsf.h $ */
+/** @file
+ * VBox Shared Folders header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsf_h
+#define VBOX_INCLUDED_SRC_SharedFolders_vbsf_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms);
+
+int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle);
+
+int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfReadPages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t offFile,
+ uint32_t *pcbBuffer, PVBOXHGCMSVCPARMPAGES pPages);
+int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t *poffFile, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfWritePages(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hFile, uint64_t *poffFile,
+ uint32_t *pcbBuffer, PVBOXHGCMSVCPARMPAGES pPages);
+int vbsfCopyFilePart(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, SHFLHANDLE hFileSrc, uint64_t offSrc,
+ SHFLROOT idRootDst, SHFLHANDLE hFileDst, uint64_t offDst, uint64_t *pcbToCopy, uint32_t fFlags);
+
+int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags);
+int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags);
+int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, PCSHFLSTRING pPath, uint32_t cbPath, uint32_t flags, SHFLHANDLE hToClose);
+int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags);
+int vbsfCopyFile(SHFLCLIENTDATA *pClient, SHFLROOT idRootSrc, PCSHFLSTRING pStrPathSrc,
+ SHFLROOT idRootDst, PCSHFLSTRING pStrPathDst, uint32_t fFlags);
+int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles);
+int vbsfFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize);
+int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle);
+int vbsfDisconnect(SHFLCLIENTDATA *pClient);
+int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer);
+int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer);
+int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsf_h */
+
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.cpp b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
new file mode 100644
index 00000000..afd13c4d
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
@@ -0,0 +1,709 @@
+/* $Id: vbsfpath.cpp $ */
+/** @file
+ * Shared Folders Service - guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#ifdef UNITTEST
+# include "testcase/tstSharedFolderService.h"
+#endif
+
+#include "vbsfpath.h"
+#include "mappings.h"
+#include "vbsf.h"
+#include "shflhandle.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/fs.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/uni.h>
+#include <iprt/stream.h>
+#ifdef RT_OS_DARWIN
+# include <Carbon/Carbon.h>
+#endif
+
+#ifdef UNITTEST
+# include "teststubs.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
+
+
+/**
+ * @todo find a better solution for supporting the execute bit for non-windows
+ * guests on windows host. Search for "0111" to find all the relevant places.
+ */
+
+/**
+ * Corrects the casing of the final component
+ *
+ * @returns
+ * @param pClient .
+ * @param pszFullPath .
+ * @param pszStartComponent .
+ */
+static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
+{
+ Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
+
+ AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2);
+ AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5);
+
+ /*
+ * Allocate a buffer that can hold really long file name entries as well as
+ * the initial search pattern.
+ */
+ size_t cchComponent = strlen(pszStartComponent);
+ size_t cchParentDir = pszStartComponent - pszFullPath;
+ size_t cchFullPath = cchParentDir + cchComponent;
+ Assert(strlen(pszFullPath) == cchFullPath);
+
+ size_t cbDirEntry = 4096;
+ if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName))
+ cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4;
+
+ PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
+ if (pDirEntry == NULL)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Construct the search criteria in the szName member of pDirEntry.
+ */
+ /** @todo This is quite inefficient, especially for directories with many
+ * files. If any of the typically case sensitive host systems start
+ * supporting opendir wildcard filters, it would make sense to build
+ * one here with '?' for case foldable charaters. */
+ /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */
+ int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName),
+ pszFullPath, cchParentDir,
+ RT_STR_TUPLE("*"), RTPATH_STR_F_STYLE_HOST);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RTDIR hSearch = NULL;
+ rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ size_t cbDirEntrySize = cbDirEntry;
+
+ rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_NO_DIRENT_INFO)
+ {
+ if ( rc == VERR_NO_TRANSLATION
+ || rc == VERR_INVALID_UTF8_ENCODING)
+ continue;
+ AssertMsgFailed(("%Rrc\n", rc));
+ break;
+ }
+
+ Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
+ if ( pDirEntry->cbName == cchComponent
+ && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
+ {
+ Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
+ strcpy(pszStartComponent, &pDirEntry->szName[0]);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ RTDirClose(hSearch);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc));
+
+ RTMemFree(pDirEntry);
+
+ return rc;
+}
+
+/* Temporary stand-in for RTPathExistEx. */
+static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags)
+{
+#if 0 /** @todo Fix the symlink issue on windows! */
+ return RTPathExistsEx(pszPath, fFlags);
+#else
+ RTFSOBJINFO IgnInfo;
+ return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags);
+#endif
+}
+
+/**
+ * Helper for vbsfBuildFullPath that performs case corrections on the path
+ * that's being build.
+ *
+ * @returns VINF_SUCCESS at the moment.
+ * @param pClient The client data.
+ * @param pszFullPath Pointer to the full path. This is the path
+ * which may need case corrections. The
+ * corrections will be applied in place.
+ * @param cchFullPath The length of the full path.
+ * @param fWildCard Whether the last component may contain
+ * wildcards and thus might require exclusion
+ * from the case correction.
+ * @param fPreserveLastComponent Always exclude the last component from case
+ * correction if set.
+ */
+static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath,
+ bool fWildCard, bool fPreserveLastComponent)
+{
+ /*
+ * Hide the last path component if it needs preserving. This is required
+ * in the following cases:
+ * - Contains the wildcard(s).
+ * - Is a 'rename' target.
+ */
+ char *pszLastComponent = NULL;
+ if (fWildCard || fPreserveLastComponent)
+ {
+ char *pszSrc = pszFullPath + cchFullPath - 1;
+ Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
+ while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
+ {
+ if (*pszSrc == RTPATH_DELIMITER)
+ break;
+ pszSrc--;
+ }
+ if (*pszSrc == RTPATH_DELIMITER)
+ {
+ if ( fPreserveLastComponent
+ /* Or does it really have wildcards? */
+ || strchr(pszSrc + 1, '*') != NULL
+ || strchr(pszSrc + 1, '?') != NULL
+ || strchr(pszSrc + 1, '>') != NULL
+ || strchr(pszSrc + 1, '<') != NULL
+ || strchr(pszSrc + 1, '"') != NULL )
+ {
+ pszLastComponent = pszSrc;
+ *pszLastComponent = '\0';
+ }
+ }
+ }
+
+ /*
+ * If the path/file doesn't exist, we need to attempt case correcting it.
+ */
+ /** @todo Don't check when creating files or directories; waste of time. */
+ int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
+
+ /*
+ * Work from the end of the path to find a partial path that's valid.
+ */
+ char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1;
+ Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
+
+ while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
+ {
+ if (*pszSrc == RTPATH_DELIMITER)
+ {
+ *pszSrc = '\0';
+ rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+ *pszSrc = RTPATH_DELIMITER;
+ if (RT_SUCCESS(rc))
+ {
+#ifdef DEBUG
+ *pszSrc = '\0';
+ Log(("Found valid partial path %s\n", pszFullPath));
+ *pszSrc = RTPATH_DELIMITER;
+#endif
+ break;
+ }
+ }
+
+ pszSrc--;
+ }
+ Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc));
+ if ( *pszSrc == RTPATH_DELIMITER
+ && RT_SUCCESS(rc))
+ {
+ /*
+ * Turn around and work the other way case correcting the components.
+ */
+ pszSrc++;
+ for (;;)
+ {
+ bool fEndOfString = true;
+
+ /* Find the end of the component. */
+ char *pszEnd = pszSrc;
+ while (*pszEnd)
+ {
+ if (*pszEnd == RTPATH_DELIMITER)
+ break;
+ pszEnd++;
+ }
+
+ if (*pszEnd == RTPATH_DELIMITER)
+ {
+ fEndOfString = false;
+ *pszEnd = '\0';
+#if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */
+ rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
+#else
+ rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
+#endif
+ Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
+ }
+ else if (pszEnd == pszSrc)
+ rc = VINF_SUCCESS; /* trailing delimiter */
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ {
+ /* Path component is invalid; try to correct the casing. */
+ rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc);
+ if (RT_FAILURE(rc))
+ {
+ /* Failed, so don't bother trying any further components. */
+ if (!fEndOfString)
+ *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */
+ break;
+ }
+ }
+
+ /* Next (if any). */
+ if (fEndOfString)
+ break;
+
+ *pszEnd = RTPATH_DELIMITER;
+ pszSrc = pszEnd + 1;
+ }
+ if (RT_FAILURE(rc))
+ Log(("Unable to find suitable component rc=%d\n", rc));
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+
+ }
+
+ /* Restore the final component if it was dropped. */
+ if (pszLastComponent)
+ *pszLastComponent = RTPATH_DELIMITER;
+
+ /* might be a new file so don't fail here! */
+ return VINF_SUCCESS;
+}
+
+
+#ifdef RT_OS_DARWIN
+/* Misplaced hack! See todo! */
+
+/** Normalize the string using kCFStringNormalizationFormD.
+ *
+ * @param pwszSrc The input UTF-16 string.
+ * @param cwcSrc Length of the input string in characters.
+ * @param ppwszDst Where to store the pointer to the resulting normalized string.
+ * @param pcwcDst Where to store length of the normalized string in characters (without the trailing nul).
+ */
+static int vbsfNormalizeStringDarwin(PCRTUTF16 pwszSrc, uint32_t cwcSrc, PRTUTF16 *ppwszDst, uint32_t *pcwcDst)
+{
+ /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
+ * The question is simply whether the NFD normalization is actually applied on a (virtual) file
+ * system level in darwin, or just by the user mode application libs. */
+
+ PRTUTF16 pwszNFD;
+ uint32_t cwcNFD;
+
+ CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
+
+ /* Is 8 times length enough for decomposed in worst case...? */
+ size_t cbNFDAlloc = cwcSrc * 8 + 2;
+ pwszNFD = (PRTUTF16)RTMemAllocZ(cbNFDAlloc);
+ if (!pwszNFD)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ ::CFStringAppendCharacters(inStr, (UniChar*)pwszSrc, cwcSrc);
+ ::CFStringNormalize(inStr, kCFStringNormalizationFormD);
+ cwcNFD = ::CFStringGetLength(inStr);
+
+ CFRange rangeCharacters;
+ rangeCharacters.location = 0;
+ rangeCharacters.length = cwcNFD;
+ ::CFStringGetCharacters(inStr, rangeCharacters, pwszNFD);
+
+ pwszNFD[cwcNFD] = 0x0000; /* NULL terminated */
+
+ CFRelease(inStr);
+
+ *ppwszDst = pwszNFD;
+ *pcwcDst = cwcNFD;
+ return VINF_SUCCESS;
+}
+#endif
+
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+/* See MSDN "Naming Files, Paths, and Namespaces".
+ * '<', '>' and '"' are allowed as possible wildcards (see ANSI_DOS_STAR, etc in ntifs.h)
+ */
+static const char sachCharBlackList[] = ":/\\|";
+#else
+/* Something else. */
+static const char sachCharBlackList[] = "/";
+#endif
+
+/** Verify if the character can be used in a host file name.
+ * Wildcard characters ('?', '*') are allowed.
+ *
+ * @param c Character to verify.
+ */
+static bool vbsfPathIsValidNameChar(unsigned char c)
+{
+ /* Character 0 is not allowed too. */
+ if (c == 0 || strchr(sachCharBlackList, c))
+ {
+ return false;
+ }
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ /* Characters less than 32 are not allowed. */
+ if (c < 32)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/** Verify if the character is a wildcard.
+ *
+ * @param c Character to verify.
+ */
+static bool vbsfPathIsWildcardChar(char c)
+{
+ if ( c == '*'
+ || c == '?'
+#ifdef RT_OS_WINDOWS /* See ntifs.h */
+ || c == '<' /* ANSI_DOS_STAR */
+ || c == '>' /* ANSI_DOS_QM */
+ || c == '"' /* ANSI_DOS_DOT */
+#endif
+ )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot,
+ PCSHFLSTRING pGuestString, uint32_t cbGuestString,
+ char **ppszHostPath, uint32_t *pcbHostPathRoot,
+ uint32_t fu32Options,
+ uint32_t *pfu32PathFlags)
+{
+#ifdef VBOX_STRICT
+ /*
+ * Check that the pGuestPath has correct size and encoding.
+ */
+ if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false)
+ {
+ LogFunc(("Invalid input string\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+#else
+ NOREF(cbGuestString);
+#endif
+
+ /*
+ * Resolve the root handle into a string.
+ */
+ uint32_t cbRootLen = 0;
+ const char *pszRoot = NULL;
+ int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen);
+ if (RT_FAILURE(rc))
+ {
+ LogFunc(("invalid root\n"));
+ return rc;
+ }
+
+ AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */
+
+ /*
+ * Get the UTF8 string with the relative path provided by the guest.
+ * If guest uses UTF-16 then convert it to UTF-8.
+ */
+ uint32_t cbGuestPath = 0; /* Shut up MSC */
+ const char *pchGuestPath = NULL; /* Ditto. */
+ char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */
+ if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
+ {
+ /* UTF-8 */
+ cbGuestPath = pGuestString->u16Length;
+ pchGuestPath = pGuestString->String.ach;
+ }
+ else
+ {
+ /* UTF-16 */
+
+#ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */
+ uint32_t cwcSrc = 0;
+ PRTUTF16 pwszSrc = NULL;
+ rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0],
+ pGuestString->u16Length / sizeof(RTUTF16),
+ &pwszSrc, &cwcSrc);
+#else
+ uint32_t const cwcSrc = pGuestString->u16Length / sizeof(RTUTF16);
+ PCRTUTF16 const pwszSrc = &pGuestString->String.ucs2[0];
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc);
+ if (cbPathAsUtf8 >= cwcSrc)
+ {
+ /* Allocate buffer that will be able to contain the converted UTF-8 string. */
+ pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1);
+ if (RT_LIKELY(pchGuestPathAllocated != NULL))
+ {
+ if (RT_LIKELY(cbPathAsUtf8))
+ {
+ size_t cchActual;
+ char *pszDst = pchGuestPathAllocated;
+ rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual);
+ AssertRC(rc);
+ AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4);
+ Assert(strlen(pszDst) == cbPathAsUtf8);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Terminate the string. */
+ pchGuestPathAllocated[cbPathAsUtf8] = '\0';
+
+ cbGuestPath = (uint32_t)cbPathAsUtf8; Assert(cbGuestPath == cbPathAsUtf8);
+ pchGuestPath = pchGuestPathAllocated;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+#ifdef RT_OS_DARWIN
+ RTMemFree(pwszSrc);
+#endif
+ }
+ }
+
+ char *pszFullPath = NULL;
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath));
+
+ /*
+ * Allocate enough memory to build the host full path from the root and the relative path.
+ */
+ const uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */
+ pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc);
+ if (RT_LIKELY(pszFullPath != NULL))
+ {
+ /* Buffer for the verified guest path. */
+ char *pchVerifiedPath = (char *)RTMemAlloc(cbGuestPath + 1);
+ if (RT_LIKELY(pchVerifiedPath != NULL))
+ {
+ /* Init the pointer for the guest relative path. */
+ uint32_t cbSrc = cbGuestPath;
+ const char *pchSrc = pchGuestPath;
+
+ /* Strip leading delimiters from the path the guest specified. */
+ while ( cbSrc > 0
+ && *pchSrc == pClient->PathDelimiter)
+ {
+ ++pchSrc;
+ --cbSrc;
+ }
+
+ /*
+ * Iterate the guest path components, verify each of them replacing delimiters with the host slash.
+ */
+ char *pchDst = pchVerifiedPath;
+ bool fLastComponentHasWildcard = false;
+ for (; cbSrc > 0; --cbSrc, ++pchSrc)
+ {
+ if (RT_LIKELY(*pchSrc != pClient->PathDelimiter))
+ {
+ if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc)))
+ {
+ if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc))
+ {
+ fLastComponentHasWildcard = true;
+ }
+
+ *pchDst++ = *pchSrc;
+ }
+ else
+ {
+ rc = VERR_INVALID_NAME;
+ break;
+ }
+ }
+ else
+ {
+ /* Replace with the host slash. */
+ *pchDst++ = RTPATH_SLASH;
+
+ if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1)
+ {
+ /* Processed component has a wildcard and there are more characters in the path. */
+ *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX;
+ }
+ fLastComponentHasWildcard = false;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *pchDst++ = 0;
+
+ /* Construct the full host path removing '.' and '..'. */
+ rc = vbsfPathAbs(pszRoot, pchVerifiedPath, pszFullPath, cbFullPathAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfu32PathFlags && fLastComponentHasWildcard)
+ {
+ *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST;
+ }
+
+ /* Check if the full path is still within the shared folder. */
+ if (fu32Options & VBSF_O_PATH_CHECK_ROOT_ESCAPE)
+ {
+ if (!RTPathStartsWith(pszFullPath, pszRoot))
+ {
+ rc = VERR_INVALID_NAME;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If the host file system is case sensitive and the guest expects
+ * a case insensitive fs, then correct the path components casing.
+ */
+ if ( vbsfIsHostMappingCaseSensitive(hRoot)
+ && !vbsfIsGuestMappingCaseSensitive(hRoot))
+ {
+ const bool fWildCard = RT_BOOL(fu32Options & VBSF_O_PATH_WILDCARD);
+ const bool fPreserveLastComponent = RT_BOOL(fu32Options & VBSF_O_PATH_PRESERVE_LAST_COMPONENT);
+ rc = vbsfCorrectPathCasing(pClient, pszFullPath, strlen(pszFullPath),
+ fWildCard, fPreserveLastComponent);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("%s\n", pszFullPath));
+
+ /* Return the full host path. */
+ *ppszHostPath = pszFullPath;
+
+ if (pcbHostPathRoot)
+ {
+ /* Return the length of the root path without the trailing slash. */
+ *pcbHostPathRoot = RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1]) ?
+ cbRootLen - 1 : /* pszRoot already had the trailing slash. */
+ cbRootLen; /* pszRoot did not have the trailing slash. */
+ }
+ }
+ }
+ }
+ else
+ {
+ LogFunc(("vbsfPathAbs %Rrc\n", rc));
+ }
+ }
+
+ RTMemFree(pchVerifiedPath);
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Cleanup.
+ */
+ RTMemFree(pchGuestPathAllocated);
+
+ if (RT_SUCCESS(rc))
+ {
+ return rc;
+ }
+
+ /*
+ * Cleanup on failure.
+ */
+ RTMemFree(pszFullPath);
+
+ LogFunc(("%Rrc\n", rc));
+ return rc;
+}
+
+void vbsfFreeHostPath(char *pszHostPath)
+{
+ RTMemFree(pszHostPath);
+}
+
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.h b/src/VBox/HostServices/SharedFolders/vbsfpath.h
new file mode 100644
index 00000000..f2b4e1a6
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpath.h
@@ -0,0 +1,80 @@
+/* $Id: vbsfpath.h $ */
+/** @file
+ * Shared Folders Service - Guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h
+#define VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "shfl.h"
+#include <VBox/shflsvc.h>
+
+#define VBSF_O_PATH_WILDCARD UINT32_C(0x00000001)
+#define VBSF_O_PATH_PRESERVE_LAST_COMPONENT UINT32_C(0x00000002)
+#define VBSF_O_PATH_CHECK_ROOT_ESCAPE UINT32_C(0x00000004)
+
+#define VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX UINT32_C(0x00000001) /* A component before the last one contains a wildcard. */
+#define VBSF_F_PATH_HAS_WILDCARD_IN_LAST UINT32_C(0x00000002) /* The last component contains a wildcard. */
+
+/**
+ *
+ * @param pClient Shared folder client.
+ * @param hRoot Root handle.
+ * @param pGuestString Guest want to access the path.
+ * @param cbGuestString Size of pGuestString memory buffer.
+ * @param ppszHostPath Returned full host path: root prefix + guest path.
+ * @param pcbHostPathRoot Length of the root prefix in bytes. Optional, can be NULL.
+ * @param fu32Options Options.
+ * @param pfu32PathFlags VBSF_F_PATH_* flags. Optional, can be NULL.
+ */
+int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot,
+ PCSHFLSTRING pGuestString, uint32_t cbGuestString,
+ char **ppszHostPath, uint32_t *pcbHostPathRoot,
+ uint32_t fu32Options, uint32_t *pfu32PathFlags);
+
+/** Free the host path returned by vbsfPathGuestToHost.
+ *
+ * @param pszHostPath Host path string.
+ */
+void vbsfFreeHostPath(char *pszHostPath);
+
+/**
+ * Build the absolute path by combining an absolute pszRoot and a relative pszPath.
+ * The resulting path does not contain '.' and '..' components.
+ * Similar to RTPathAbsEx but with support for Windows extended-length paths ("\\?\" prefix).
+ * Uses RTPathAbsEx for regular paths and on non-Windows hosts.
+ *
+ * @param pszRoot The absolute prefix. It is copied to the pszAbsPath without any processing.
+ * If NULL then the pszPath must be converted to the absolute path.
+ * @param pszPath The relative path to be appended to pszRoot. Already has correct delimiters (RTPATH_SLASH).
+ * @param pszAbsPath Where to store the resulting absolute path.
+ * @param cbAbsPath Size of pszAbsBuffer in bytes.
+ */
+int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath);
+
+#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h */
diff --git a/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp
new file mode 100644
index 00000000..ce6f9391
--- /dev/null
+++ b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp
@@ -0,0 +1,200 @@
+/* $Id: vbsfpathabs.cpp $ */
+/** @file
+ * Shared Folders Service - guest/host path convertion and verification.
+ */
+
+/*
+ * Copyright (C) 2017-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 <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+
+#if defined(RT_OS_WINDOWS)
+static void vbsfPathResolveRelative(char *pszPathBegin)
+{
+ char *pszCur = pszPathBegin;
+ char * const pszTop = pszCur;
+
+ /*
+ * Get rid of double dot path components by evaluating them.
+ */
+ for (;;)
+ {
+ char const chFirst = pszCur[0];
+ if ( chFirst == '.'
+ && pszCur[1] == '.'
+ && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
+ {
+ /* rewind to the previous component if any */
+ char *pszPrev = pszCur;
+ if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
+ {
+ pszPrev--;
+ while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
+ && pszPrev[-1] != RTPATH_SLASH)
+ pszPrev--;
+ }
+ if (!pszCur[2])
+ {
+ if (pszPrev != pszTop)
+ pszPrev[-1] = '\0';
+ else
+ *pszPrev = '\0';
+ break;
+ }
+ Assert(pszPrev[-1] == RTPATH_SLASH);
+ memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
+ pszCur = pszPrev - 1;
+ }
+ else if ( chFirst == '.'
+ && (!pszCur[1] || pszCur[1] == RTPATH_SLASH))
+ {
+ /* remove unnecessary '.' */
+ if (!pszCur[1])
+ {
+ if (pszCur != pszTop)
+ pszCur[-1] = '\0';
+ else
+ *pszCur = '\0';
+ break;
+ }
+ memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1);
+ continue;
+ }
+ else
+ {
+ /* advance to end of component. */
+ while (*pszCur && *pszCur != RTPATH_SLASH)
+ pszCur++;
+ }
+
+ if (!*pszCur)
+ break;
+
+ /* skip the slash */
+ ++pszCur;
+ }
+}
+#endif /* RT_OS_WINDOWS */
+
+int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath)
+{
+#if defined(RT_OS_WINDOWS)
+ /** @todo This code is not needed in 6.0 and later as IPRT translates paths
+ * to //./ (inverted slashes for doxygen) format if they're too long. */
+ const char *pszPathStart = pszRoot? pszRoot: pszPath;
+
+ /* Windows extended-length paths. */
+ if ( RTPATH_IS_SLASH(pszPathStart[0])
+ && RTPATH_IS_SLASH(pszPathStart[1])
+ && pszPathStart[2] == '?'
+ && RTPATH_IS_SLASH(pszPathStart[3])
+ )
+ {
+ /* Maximum total path length of 32,767 characters. */
+ if (cbAbsPath > _32K)
+ cbAbsPath = _32K;
+
+ /* Copy the root to pszAbsPath buffer. */
+ size_t cchRoot = pszRoot? strlen(pszRoot): 0;
+ if (cchRoot >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ if (pszRoot)
+ {
+ /* Caller must ensure that the path is relative, without the leading path separator. */
+ if (RTPATH_IS_SLASH(pszPath[0]))
+ return VERR_INVALID_PARAMETER;
+
+ if (cchRoot)
+ memcpy(pszAbsPath, pszRoot, cchRoot);
+
+ if (cchRoot == 0 || !RTPATH_IS_SLASH(pszAbsPath[cchRoot - 1]))
+ {
+ /* Append path separator after the root. */
+ ++cchRoot;
+ if (cchRoot >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ pszAbsPath[cchRoot - 1] = RTPATH_SLASH;
+ }
+ }
+
+ /* Append the path to the pszAbsPath buffer. */
+ const size_t cchPath = strlen(pszPath);
+ if (cchRoot + cchPath >= cbAbsPath)
+ return VERR_FILENAME_TOO_LONG;
+
+ memcpy(&pszAbsPath[cchRoot], pszPath, cchPath + 1); /* Including trailing 0. */
+
+ /* Find out where the actual path begins, i.e. skip the root spec. */
+ char *pszPathBegin = &pszAbsPath[4]; /* Skip the extended-length path prefix "\\?\" */
+ if ( pszPathBegin[0]
+ && RTPATH_IS_VOLSEP(pszPathBegin[1])
+ && pszPathBegin[2] == RTPATH_SLASH)
+ {
+ /* "\\?\C:\" */
+ pszPathBegin += 3;
+ }
+ else if ( pszPathBegin[0] == 'U'
+ && pszPathBegin[1] == 'N'
+ && pszPathBegin[2] == 'C'
+ && pszPathBegin[3] == RTPATH_SLASH)
+ {
+ /* "\\?\UNC\server\share" */
+ pszPathBegin += 4;
+
+ /* Skip "server\share" too. */
+ while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin)
+ ++pszPathBegin;
+ if (*pszPathBegin == RTPATH_SLASH)
+ {
+ ++pszPathBegin;
+ while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin)
+ ++pszPathBegin;
+ if (*pszPathBegin == RTPATH_SLASH)
+ ++pszPathBegin;
+ }
+ }
+ else
+ return VERR_INVALID_NAME;
+
+ /* Process pszAbsPath in place. */
+ vbsfPathResolveRelative(pszPathBegin);
+
+ return VINF_SUCCESS;
+ }
+#endif /* RT_OS_WINDOWS */
+
+ /* Fallback for the common paths. */
+
+ if (*pszPath != '\0')
+ return RTPathAbsEx(pszRoot, pszPath, RTPATH_STR_F_STYLE_HOST, pszAbsPath, &cbAbsPath);
+ return RTPathAbsEx(NULL, pszRoot, RTPATH_STR_F_STYLE_HOST, pszAbsPath, &cbAbsPath);
+}