Index: /trunk/include/VBox/shflsvc.h
===================================================================
--- /trunk/include/VBox/shflsvc.h	(revision 51249)
+++ /trunk/include/VBox/shflsvc.h	(revision 51250)
@@ -205,5 +205,5 @@
 
 /**
- * Validates a HGCM string parameter.
+ * Validates a HGCM string output parameter.
  *
  * @returns true if valid, false if not.
@@ -212,5 +212,5 @@
  * @param   cbBuf       The buffer size from the parameter.
  */
-DECLINLINE(bool) ShflStringIsValid(PCSHFLSTRING pString, uint32_t cbBuf)
+DECLINLINE(bool) ShflStringIsValidOut(PCSHFLSTRING pString, uint32_t cbBuf)
 {
     if (RT_UNLIKELY(cbBuf <= RT_UOFFSETOF(SHFLSTRING, String)))
@@ -220,15 +220,48 @@
     if (RT_UNLIKELY(pString->u16Length >= pString->u16Size))
         return false;
-    /** @todo r=bird: Check that u16Length is a multiple of two if UTF-16 input? */
-    /** @todo r=bird: Do we require the string to have a NUL terminator char, if
-     *        so check for it!! (Just had a problem with too small (/2) u16Length
-     *        and code behaving incorrectly because it worked up to the terminator
-     *        instead of the length.) */
-    /** @todo r=bird: Who checks for valid UTF-8 encoding of strings? */
     return true;
 }
 
 /**
- * Validates an optional HGCM string parameter.
+ * Validates a HGCM string input parameter.
+ *
+ * @returns true if valid, false if not.
+ *
+ * @param   pString     The string buffer pointer.
+ * @param   cbBuf       The buffer size from the parameter.
+ * @param   fUtf8Not16  Set if UTF-8 encoding, clear if UTF-16 encoding.
+ */
+DECLINLINE(bool) ShflStringIsValidIn(PCSHFLSTRING pString, uint32_t cbBuf, bool fUtf8Not16)
+{
+    int rc;
+    if (RT_UNLIKELY(cbBuf <= RT_UOFFSETOF(SHFLSTRING, String)))
+        return false;
+    if (RT_UNLIKELY((uint32_t)pString->u16Size + RT_UOFFSETOF(SHFLSTRING, String) > cbBuf))
+        return false;
+    if (fUtf8Not16)
+    {
+        /* UTF-8: */
+        if (RT_UNLIKELY(pString->u16Length >= pString->u16Size))
+            return false;
+        rc = RTStrValidateEncodingEx((const char *)&pString->String.utf8[0], pString->u16Length + 1,
+                                     RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+    }
+    else
+    {
+        /* UTF-16: */
+        if (RT_UNLIKELY(pString->u16Length & 1))
+            return false;
+        if (RT_UNLIKELY((uint32_t)sizeof(RTUTF16) + pString->u16Length > pString->u16Size))
+            return false;
+        rc = RTUtf16ValidateEncodingEx(&pString->String.ucs2[0], pString->u16Length / 2 + 1,
+                                       RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+    }
+    if (RT_FAILURE(rc))
+        return false;
+    return true;
+}
+
+/**
+ * Validates an optional HGCM string input parameter.
  *
  * @returns true if valid, false if not.
@@ -236,9 +269,10 @@
  * @param   pString     The string buffer pointer. Can be NULL.
  * @param   cbBuf       The buffer size from the parameter.
- */
-DECLINLINE(bool) ShflStringIsValidOrNull(PCSHFLSTRING pString, uint32_t cbBuf)
+ * @param   fUtf8Not16  Set if UTF-8 encoding, clear if UTF-16 encoding.
+ */
+DECLINLINE(bool) ShflStringIsValidOrNullIn(PCSHFLSTRING pString, uint32_t cbBuf, bool fUtf8Not16)
 {
     if (pString)
-        return ShflStringIsValid(pString, cbBuf);
+        return ShflStringIsValidIn(pString, cbBuf, fUtf8Not16);
     if (RT_UNLIKELY(cbBuf > 0))
         return false;
Index: /trunk/src/VBox/HostServices/SharedFolders/mappings.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedFolders/mappings.cpp	(revision 51249)
+++ /trunk/src/VBox/HostServices/SharedFolders/mappings.cpp	(revision 51250)
@@ -535,5 +535,5 @@
 #endif
 int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName,
-                  RTUTF16 pwszDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
+                  RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
 {
     MAPPING *pFolderMapping = NULL;
@@ -548,11 +548,16 @@
     }
 
+    AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\',
+                    ("Invalid path delimiter: %#x\n", wcDelimiter),
+                    VERR_INVALID_PARAMETER);
     if (pClient->PathDelimiter == 0)
     {
-        pClient->PathDelimiter = pwszDelimiter;
+        pClient->PathDelimiter = wcDelimiter;
     }
     else
     {
-        Assert(pwszDelimiter == pClient->PathDelimiter);
+        AssertMsgReturn(wcDelimiter == pClient->PathDelimiter,
+                        ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter),
+                        VERR_INVALID_PARAMETER);
     }
 
Index: /trunk/src/VBox/HostServices/SharedFolders/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedFolders/service.cpp	(revision 51249)
+++ /trunk/src/VBox/HostServices/SharedFolders/service.cpp	(revision 51250)
@@ -385,5 +385,5 @@
 
                 /* Verify parameters values. */
-                if (!ShflStringIsValid(pString, paParms[1].u.pointer.size))
+                if (!ShflStringIsValidOut(pString, paParms[1].u.pointer.size))
                 {
                     rc = VERR_INVALID_PARAMETER;
@@ -431,5 +431,5 @@
 
                 /* Verify parameters values. */
-                if (   !ShflStringIsValid(pPath, cbPath)
+                if (   !ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
                     || (cbParms != sizeof (SHFLCREATEPARMS))
                    )
@@ -442,5 +442,4 @@
                 {
                     /* Execute the function. */
-
                     rc = vbsfCreate (pClient, root, pPath, cbPath, pParms);
 
@@ -490,5 +489,4 @@
                 {
                     /* Execute the function. */
-
                     rc = vbsfClose (pClient, root, Handle);
 
@@ -762,5 +760,5 @@
                 if (   (length < sizeof (SHFLDIRINFO))
                     ||  length > paParms[5].u.pointer.size
-                    ||  !ShflStringIsValidOrNull(pPath, paParms[4].u.pointer.size)
+                    ||  !ShflStringIsValidOrNullIn(pPath, paParms[4].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))
                    )
                 {
@@ -830,5 +828,5 @@
 
                 /* Verify parameters values. */
-                if (!ShflStringIsValidOrNull(pPath, paParms[1].u.pointer.size))
+                if (!ShflStringIsValidOrNullIn(pPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
                 {
                     rc = VERR_INVALID_PARAMETER;
@@ -875,5 +873,5 @@
 
                 /* Verify parameters values. */
-                if (!ShflStringIsValid(pszMapName, paParms[0].u.pointer.size))
+                if (!ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
                 {
                     rc = VERR_INVALID_PARAMETER;
@@ -926,19 +924,34 @@
 
                 /* Verify parameters values. */
-                if (!ShflStringIsValid(pszMapName, paParms[0].u.pointer.size))
-                {
-                    rc = VERR_INVALID_PARAMETER;
-                }
-                else
-                {
-
-                    /* Execute the function. */
+                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;
-                    }
+                if (RT_SUCCESS(rc))
+                {
+                    /* Update parameters.*/
+                    paParms[1].u.uint32 = root;
                 }
             }
@@ -1065,5 +1078,5 @@
 
                 /* Verify parameters values. */
-                if (!ShflStringIsValid(pPath, cbPath))
+                if (!ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)))
                 {
                     rc = VERR_INVALID_PARAMETER;
@@ -1109,6 +1122,6 @@
 
                 /* Verify parameters values. */
-                if (    !ShflStringIsValid(pSrc, paParms[1].u.pointer.size)
-                    ||  !ShflStringIsValid(pDest, paParms[2].u.pointer.size)
+                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))
                    )
                 {
@@ -1209,6 +1222,6 @@
 
                 /* Verify parameters values. */
-                if (    !ShflStringIsValid(pNewPath, paParms[1].u.pointer.size)
-                    ||  !ShflStringIsValid(pOldPath, paParms[2].u.pointer.size)
+                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))
                    )
@@ -1306,6 +1319,6 @@
 
             /* Verify parameters values. */
-            if (    !ShflStringIsValid(pFolderName, paParms[0].u.pointer.size)
-                ||  !ShflStringIsValid(pMapName, paParms[1].u.pointer.size)
+            if (    !ShflStringIsValidIn(pFolderName, paParms[0].u.pointer.size, false /*fUtf8Not16*/)
+                ||  !ShflStringIsValidIn(pMapName, paParms[1].u.pointer.size, false /*fUtf8Not16*/)
                )
             {
@@ -1362,5 +1375,5 @@
 
             /* Verify parameters values. */
-            if (!ShflStringIsValid(pString, paParms[0].u.pointer.size))
+            if (!ShflStringIsValidIn(pString, paParms[0].u.pointer.size, false /*fUtf8Not16*/))
             {
                 rc = VERR_INVALID_PARAMETER;
Index: /trunk/src/VBox/HostServices/SharedFolders/vbsf.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedFolders/vbsf.cpp	(revision 51249)
+++ /trunk/src/VBox/HostServices/SharedFolders/vbsf.cpp	(revision 51250)
@@ -107,79 +107,279 @@
 }
 
+/**
+ * Corrects the casing of the final component
+ *
+ * @returns
+ * @param   pClient             .
+ * @param   pszFullPath         .
+ * @param   pszStartComponent   .
+ */
 static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
 {
-    PRTDIRENTRYEX  pDirEntry = NULL;
-    uint32_t       cbDirEntry, cbComponent;
-    int            rc = VERR_FILE_NOT_FOUND;
-    PRTDIR         hSearch = 0;
-    char           szWildCard[4];
-
     Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
 
-    cbComponent = (uint32_t) strlen(pszStartComponent);
-
-    cbDirEntry = 4096;
-    pDirEntry  = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
-    if (pDirEntry == 0)
-    {
-        AssertFailed();
+    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;
-    }
-
-    /** @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);
-    *(pszStartComponent-1) = RTPATH_DELIMITER;
+
+    /*
+     * 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("*"));
+    AssertRC(rc);
+    if (RT_SUCCESS(rc))
+    {
+        PRTDIR hSearch = NULL;
+        rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0);
+        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)
+                {
+                    AssertFailed();
+                    if (   rc == VERR_NO_TRANSLATION
+                        || rc == VERR_INVALID_UTF8_ENCODING)
+                        continue;
+                    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))
-        goto end;
-
-    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)
-        {
-            AssertFailed();
-            if (   rc == VERR_NO_TRANSLATION
-                || rc == VERR_INVALID_UTF8_ENCODING)
-                continue;
-            break;
-        }
-
-        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;
-        }
-    }
-
-end:
-    if (RT_FAILURE(rc))
-        Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
-
-    if (pDirEntry)
-        RTMemFree(pDirEntry);
-
-    if (hSearch)
-        RTDirClose(hSearch);
+        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;
+}
+
+
+/** @def VBSF_IS_PATH_SLASH
+ * Checks if @a a_ch is a path delimiter (slash, not volume spearator) for the
+ * guest or the host.
+ * @param   a_pClient   Pointer to the client data (SHFLCLIENTDATA).
+ * @param   a_ch        The character to inspect.
+ */
+#if 1
+# define VBSF_IS_PATH_SLASH(a_pClient, a_ch)     ( (a_ch) == '\\' || (a_ch) == '/' || RTPATH_IS_SLASH(a_ch) )
+#else
+# define VBSF_IS_PATH_SLASH(a_pClient, a_ch)     ( (RTUTF16)(a_ch) == (a_pClient)->PathDelimiter || RTPATH_IS_SLASH(a_ch) )
+#endif
+
 
 /**
@@ -193,7 +393,6 @@
  * @retval  VERR_INVALID_NAME
  *
- * @param   pUtf8Path   The path to check.
- * @param   cchPath     The length of the path in chars (not code points, but
- *                      the C type) excluding the string terminator.
+ * @param   pszPath         The (UTF-8) path to check.  Slashes has been convert
+ *                          to host slashes by this time.
  *
  * @remarks This function assumes that the path will be appended to the root
@@ -201,9 +400,10 @@
  *          checking absolute paths!
  */
-static int vbsfPathCheck(const char *pUtf8Path, size_t cchPath)
-{
-    int rc = VINF_SUCCESS;
-
-    size_t i = 0;
+static int vbsfPathCheck(const char *pszPath)
+{
+    /*
+     * Walk the path, component by component and check for escapes.
+     */
+
     int cComponents = 0; /* How many normal path components. */
     int cParentDirs = 0; /* How many '..' components. */
@@ -211,51 +411,48 @@
     for (;;)
     {
+        char ch;
+
         /* Skip leading path delimiters. */
-        while (   i < cchPath
-               && (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))
-            i++;
-
-        if (i >= cchPath)
-            break;
+        do
+            ch = *pszPath++;
+        while (RTPATH_IS_SLASH(ch));
+        if (ch == '\0')
+            return VINF_SUCCESS;
 
         /* Check if that is a dot component. */
         int cDots = 0;
-        while (i < cchPath && pUtf8Path[i] == '.')
+        while (ch == '.')
         {
             cDots++;
-            i++;
-        }
-
-        if (   cDots >= 2 /* Consider all multidots sequences as a 'parent dir'. */
-            && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')))
-        {
-            cParentDirs++;
-        }
-        else if (   cDots == 1
-                 && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')))
-        {
-            /* Single dot, nothing changes. */
+            ch = *pszPath++;
+        }
+
+        if (   cDots >= 1
+            && (ch == '\0' || RTPATH_IS_SLASH(ch)) )
+        {
+            if (cDots >= 2) /* Consider all multidots sequences as a 'parent dir'. */
+            {
+                cParentDirs++;
+
+                /* Escaping? */
+                if (cParentDirs > cComponents)
+                    return VERR_INVALID_NAME;
+            }
+            /* else: Single dot, nothing changes. */
         }
         else
         {
-            /* Skip this component. */
-            while (   i < cchPath
-                   && (pUtf8Path[i] != '\\' && pUtf8Path[i] != '/'))
-                i++;
-
+            /* Not a dot component, skip to the end of it. */
+            while (ch != '\0' && !RTPATH_IS_SLASH(ch))
+                ch = *pszPath++;
             cComponents++;
         }
-
-        Assert(i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'));
-
-        /* Verify counters for every component. */
-        if (cParentDirs > cComponents)
-        {
-            rc = VERR_INVALID_NAME;
-            break;
-        }
-    }
-
-    return rc;
+        Assert(cComponents >= cParentDirs);
+
+        /* The end? */
+        Assert(ch == '\0' || RTPATH_IS_SLASH(ch));
+        if (ch == '\0')
+            return VINF_SUCCESS;
+    }
 }
 
@@ -264,52 +461,67 @@
                              bool fWildCard = false, bool fPreserveLastComponent = false)
 {
-    int rc = VINF_SUCCESS;
-    char *pszFullPath = NULL;
-    size_t cbRoot;
+    /* Resolve the root prefix into a string. */
     const char *pszRoot = vbsfMappingsQueryHostRoot(root);
-
     if (   !pszRoot
-        || !(cbRoot = strlen(pszRoot)))
+        || !*pszRoot)
     {
         Log(("vbsfBuildFullPath: invalid root!\n"));
         return VERR_INVALID_PARAMETER;
     }
-
+    uint32_t cchRoot = (uint32_t)strlen(pszRoot);
+#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
+    Assert(!strchr(pszRoot, '/'));
+#endif
+
+    /*
+     * Combine the root prefix with the guest path into a full UTF-8 path in a
+     * buffer pointed to by pszFullPath.  cchRoot may be adjusted in the process.
+     */
+    int    rc;
+    size_t cchFullPath;
+    char  *pszFullPath = NULL;
     if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
     {
-        /* Verify that the path is under the root directory. */
-        rc = vbsfPathCheck((const char *)&pPath->String.utf8[0], pPath->u16Length);
-        if (RT_SUCCESS(rc))
-        {
-            size_t cbUtf8FullPath = cbRoot + 1 + pPath->u16Length + 1;
-            char *utf8FullPath = (char *) RTMemAllocZ(cbUtf8FullPath);
-
-            if (!utf8FullPath)
-            {
-                rc = VERR_NO_MEMORY;
-                *ppszFullPath = NULL;
-                Log(("RTMemAllocZ %x failed!!\n", cbUtf8FullPath));
-            }
-            else
-            {
-                /** @todo r-bird: Pardon me for asking, but who validates the UTF-8 encoding?*/
-                memcpy(utf8FullPath, pszRoot, cbRoot);
-                utf8FullPath[cbRoot] = '/';
-                memcpy(utf8FullPath + cbRoot + 1, &pPath->String.utf8[0], pPath->u16Length);
-                utf8FullPath[cbUtf8FullPath - 1] = 0;
-                pszFullPath = utf8FullPath;
-
-                if (pcbFullPathRoot)
-                    *pcbFullPathRoot = (uint32_t)cbRoot; /* Must index the path delimiter. */
-            }
+        /* Strip leading slashes from the path the guest specified. */
+        uint32_t    cchSrc = pPath->u16Length;
+        const char *pszSrc = (char *)&pPath->String.utf8[0];
+        Log(("Root %s path %.*s\n", pszRoot, cchSrc, pszSrc));
+
+        while (   cchSrc > 0
+               && VBSF_IS_PATH_SLASH(pClient, *pszSrc))
+        {
+            pszSrc++;
+            cchSrc--;
+        }
+
+        /* Allocate a buffer that will be able to contain the root prefix and
+           the path specified by the guest. */
+        cchFullPath = cchRoot + cchSrc;
+        pszFullPath = (char *)RTMemAlloc(cchFullPath + 1 + 1);
+        if (RT_LIKELY(pszFullPath != NULL))
+        {
+            memcpy(pszFullPath, pszRoot, cchRoot);
+            if (!RTPATH_IS_SLASH(pszRoot[-1]))
+            {
+                pszFullPath[cchRoot++] = RTPATH_DELIMITER;
+                cchFullPath++;
+            }
+
+            if (cchSrc)
+                memcpy(&pszFullPath[cchRoot], pszSrc, cchSrc);
+
+            /* Terminate the string. */
+            pszFullPath[cchRoot + cchSrc] = '\0';
+            rc = VINF_SUCCESS;
         }
         else
         {
-            Log(("vbsfBuildFullPath: vbsfPathCheck failed with %Rrc\n", rc));
-        }
-    }
-    else
-    {
-#ifdef RT_OS_DARWIN
+            Log(("RTMemAlloc %x failed!!\n", cchFullPath + 1));
+            rc = VERR_NO_MEMORY;
+        }
+    }
+    else /* Client speaks UTF-16. */
+    {
+#ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */
 /** @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
@@ -345,91 +557,62 @@
         CFRelease(inStr);
 #endif
-        /* Client sends us UCS2, so convert it to UTF8. */
-        Log(("Root %s path %.*ls\n", pszRoot, pPath->u16Length/sizeof(pPath->String.ucs2[0]), pPath->String.ucs2));
+
+        /* Strip leading slashes and calculate the UTF-8 length. */
+        size_t      cwcSrc  = pPath->u16Length / sizeof(RTUTF16);
+        PRTUTF16    pwszSrc = &pPath->String.ucs2[0];
+        Log(("Root %s path %.*ls\n", pszRoot, cwcSrc, pwszSrc));
+
+        while (   cwcSrc > 0
+               && *pwszSrc < 0x80
+               && VBSF_IS_PATH_SLASH(pClient, (char)*pwszSrc))
+        {
+            pwszSrc++;
+            cwcSrc--;
+        }
+
+        size_t      cchPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc);
+#ifdef RT_OS_DARWIN
+        AssertReturnStmt(cchPathAsUtf8 >= cwcSrc, RTMemFree(pPath), VERR_INTERNAL_ERROR_3);
+#else
+        AssertReturn(cchPathAsUtf8 >= cwcSrc, VERR_INTERNAL_ERROR_3);
+#endif
 
         /* Allocate buffer that will be able to contain the root prefix and
-         * the pPath converted to UTF8. Expect a 2 bytes UCS2 to be converted
-         * to 8 bytes UTF8 in the worst case.
-         */
-        uint32_t cbFullPath = (cbRoot + ShflStringLength(pPath)) * 4;
-        pszFullPath = (char *)RTMemAllocZ(cbFullPath);
-        if (!pszFullPath)
-        {
+         * the pPath converted to UTF-8. */
+        cchFullPath = cchRoot + cchPathAsUtf8;
+        pszFullPath = (char *)RTMemAlloc(cchFullPath + 1 + 1);
+        if (RT_LIKELY(pszFullPath != NULL))
+        {
+            /* Copy the root prefix into the result buffer and make sure it
+               ends with a path separator. */
+            memcpy(pszFullPath, pszRoot, cchRoot);
+            if (!RTPATH_IS_SLASH(pszFullPath[cchRoot - 1]))
+            {
+                pszFullPath[cchRoot++] = RTPATH_DELIMITER;
+                cchFullPath++;
+            }
+
+            /* Append the path specified by the guest (if any). */
+            if (cchPathAsUtf8)
+            {
+                size_t cchActual;
+                char *pszDst = &pszFullPath[cchRoot];
+                rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cchFullPath - cchRoot + 1, &cchActual);
+                AssertRC(rc);
+                AssertStmt(RT_FAILURE(rc) || cchActual == cchPathAsUtf8, rc = VERR_INTERNAL_ERROR_4);
+                Assert(strlen(pszDst) == cchPathAsUtf8);
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            /* Terminate the string. */
+            pszFullPath[cchRoot + cchPathAsUtf8] = '\0';
+        }
+        else
+        {
+            Log(("RTMemAlloc %x failed!!\n", cchFullPath + 1));
             rc = VERR_NO_MEMORY;
         }
-        else
-        {
-            memcpy(pszFullPath, pszRoot, cbRoot + 1);
-            char *pszDst = pszFullPath;
-            size_t cbDst = strlen(pszDst);
-            size_t cb    = cbFullPath;
-            if (pszDst[cbDst - 1] != RTPATH_DELIMITER)
-            {
-                pszDst[cbDst] = RTPATH_DELIMITER;
-                cbDst++;
-            }
-
-            if (pcbFullPathRoot)
-                *pcbFullPathRoot = cbDst - 1; /* Must index the path delimiter.  */
-
-            pszDst += cbDst;
-            cb     -= cbDst;
-
-            if (pPath->u16Length)
-            {
-                /* Convert and copy components. */
-                size_t   cwcSrc  = pPath->u16Length / sizeof(RTUTF16);
-                PRTUTF16 pwszSrc = &pPath->String.ucs2[0];
-
-                /* Correct path delimiters */
-                if (pClient->PathDelimiter != RTPATH_DELIMITER)
-                {
-                    LogFlow(("Correct path delimiter in %ls\n", pwszSrc));
-                    while (*pwszSrc)
-                    {
-                        if (*pwszSrc == pClient->PathDelimiter)
-                            *pwszSrc = RTPATH_DELIMITER;
-                        pwszSrc++;
-                    }
-                    pwszSrc = &pPath->String.ucs2[0];
-                    LogFlow(("Corrected string %ls\n", pwszSrc));
-                }
-                if (*pwszSrc == RTPATH_DELIMITER)
-                {
-                    pwszSrc++;  /* we already appended a delimiter to the first part */
-                    cwcSrc--;
-                }
-
-                rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cb, &cbDst);
-                if (RT_FAILURE(rc))
-                {
-                    AssertFailed();
-#ifdef RT_OS_DARWIN
-                    RTMemFree(pPath);
-                    pPath = pPathParameter;
-#endif
-                    return rc;
-                }
-                Assert(cbDst == strlen(pszDst));
-
-                /* Verify that the path is under the root directory. */
-                rc = vbsfPathCheck(pszDst, cbDst);
-                if (RT_FAILURE(rc))
-                {
-#ifdef RT_OS_DARWIN
-                    RTMemFree(pPath);
-#endif
-                    return rc;
-                }
-
-                cb     -= cbDst;
-                pszDst += cbDst;
-
-                Assert(cb > 0);
-            }
-
-            /* Nul terminate the string */
-            *pszDst = 0;
-        }
+
 #ifdef RT_OS_DARWIN
         RTMemFree(pPath);
@@ -437,147 +620,60 @@
 #endif
     }
-
     if (RT_SUCCESS(rc))
     {
-        /* When the host file system is case sensitive and the guest expects
-         * a case insensitive fs, then problems can occur */
-        if (     vbsfIsHostMappingCaseSensitive(root)
-            &&  !vbsfIsGuestMappingCaseSensitive(root))
-        {
-            RTFSOBJINFO info;
-            char *pszLastComponent = NULL;
-
-            if (fWildCard || fPreserveLastComponent)
-            {
-                /* strip off the last path component, that has to be preserved:
-                 * contains the wildcard(s) or a 'rename' target. */
-                size_t cb = strlen(pszFullPath);
-                char *pszSrc = pszFullPath + cb - 1;
-
-                while (pszSrc > pszFullPath)
-                {
-                    if (*pszSrc == RTPATH_DELIMITER)
-                        break;
-                    pszSrc--;
-                }
-                if (*pszSrc == RTPATH_DELIMITER)
-                {
-                    bool fHaveWildcards = false;
-                    char *psz = pszSrc;
-
-                    while (*psz)
-                    {
-                        char ch = *psz;
-                        if (ch == '*' || ch == '?' || ch == '>' || ch == '<' || ch == '"')
-                        {
-                            fHaveWildcards = true;
-                            break;
-                        }
-                        psz++;
-                    }
-
-                    if (fHaveWildcards || fPreserveLastComponent)
-                    {
-                        pszLastComponent = pszSrc;
-                        *pszLastComponent = 0;
-                    }
-                }
-            }
-
-            /** @todo don't check when creating files or directories; waste of time */
-            rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
-            if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
-            {
-                size_t cb = strlen(pszFullPath);
-                char   *pszSrc = pszFullPath + cb - 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 (pszSrc > pszFullPath)
-                {
-                    if (*pszSrc == RTPATH_DELIMITER)
-                    {
-                        *pszSrc = 0;
-                        rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, 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))
-                {
-                    pszSrc++;
-                    for (;;)
-                    {
-                        char *pszEnd = pszSrc;
-                        bool fEndOfString = true;
-
-                        while (*pszEnd)
-                        {
-                            if (*pszEnd == RTPATH_DELIMITER)
-                                break;
-                            pszEnd++;
-                        }
-
-                        if (*pszEnd == RTPATH_DELIMITER)
-                        {
-                            fEndOfString = false;
-                            *pszEnd = 0;
-                            rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
-                            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))
-                            {
-                                if (!fEndOfString)
-                                    *pszEnd = RTPATH_DELIMITER; /* restore the original full path */
-                                break;
-                            }
-                        }
-
-                        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;
-
-            }
-            if (pszLastComponent)
-                *pszLastComponent = RTPATH_DELIMITER;
-
-            /* might be a new file so don't fail here! */
-            rc = VINF_SUCCESS;
-        }
-        *ppszFullPath = pszFullPath;
-
-        LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
-    }
-
+        Assert(strlen(pszFullPath) == cchFullPath);
+        Assert(RTPATH_IS_SLASH(pszFullPath[cchRoot - 1])); /* includes delimiter. */
+
+        if (pcbFullPathRoot)
+            *pcbFullPathRoot = cchRoot - 1; /* Must index the path delimiter. */
+
+        /*
+         * Convert guest path delimiters into host ones and check for attempts
+         * to escape the shared folder root directory.
+         *
+         * After this, there will only be RTPATH_DELIMITER slashes in the path!
+         *
+         * ASSUMES that the root path only has RTPATH_DELIMITER as well.
+         */
+        char  ch;
+        char *pszTmp = &pszFullPath[cchRoot];
+        while ((ch = *pszTmp) != '\0')
+        {
+            if (VBSF_IS_PATH_SLASH(pClient, ch))
+                *pszTmp = RTPATH_DELIMITER;
+            pszTmp++;
+        }
+        LogFlow(("Corrected string %s\n", pszFullPath));
+
+        rc = vbsfPathCheck(&pszFullPath[cchRoot]);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * When the host file system is case sensitive and the guest expects
+             * a case insensitive fs, then problems can occur.
+             */
+            if (    vbsfIsHostMappingCaseSensitive(root)
+                && !vbsfIsGuestMappingCaseSensitive(root))
+                rc = vbsfCorrectPathCasing(pClient, pszFullPath, cchFullPath, fWildCard, fPreserveLastComponent);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * We're good.
+                 */
+                *ppszFullPath = pszFullPath;
+                LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
+                return rc;
+            }
+
+            /* Failed, clean up. */
+            Log(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
+        }
+        else
+            Log(("vbsfBuildPath: Caught escape attempt: (%.*s) '%s'\n", cchRoot, pszFullPath, &pszFullPath[cchRoot]));
+    }
+
+    if (pszFullPath)
+        RTMemFree(pszFullPath);
+    *ppszFullPath = NULL;
     return rc;
 }
@@ -1960,4 +2056,5 @@
 
     ShflStringInitBuffer(&dummy, sizeof(dummy));
+    dummy.String.ucs2[0] = '\0';
     rc = vbsfBuildFullPath(pClient, root, &dummy, 0, &pszFullPath, NULL);
 
