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