summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/SharedFolders/vbsfpath.cpp')
-rw-r--r--src/VBox/HostServices/SharedFolders/vbsfpath.cpp709
1 files changed, 709 insertions, 0 deletions
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);
+}
+