VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp@ 106238

Last change on this file since 106238 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.8 KB
Line 
1/** @file
2 * Testcase for shared folder case conversion code.
3 *
4 * @todo r=andy Testcase needs to be rewritten using the test framework.
5 * Otherwise it's almost pointless to run this on the testboxes.
6 */
7
8/*
9 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define LOG_GROUP LOG_GROUP_MISC
35#define LOG_ENABLED
36#include <VBox/shflsvc.h>
37#include <VBox/log.h>
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/file.h>
41#include <iprt/fs.h>
42#include <iprt/dir.h>
43#include <iprt/initterm.h>
44#include <iprt/mem.h>
45#include <iprt/path.h>
46#include <iprt/string.h>
47#include <iprt/test.h>
48#include <iprt/uni.h>
49#include <stdio.h>
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55/* Override slash for non-windows hosts. */
56#undef RTPATH_DELIMITER
57#define RTPATH_DELIMITER '\\'
58
59/* Use our own RTPath and RTDir methods. */
60#define RTPathQueryInfo rtPathQueryInfo
61#define RTDirOpenFiltered rtDirOpenFiltered
62#define RTDirClose rtDirClose
63#define RTDirReadEx rtDirReadEx
64
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69static int iDirList = 0;
70static int iDirFile = 0;
71
72static const char *g_apszDirs[] =
73{
74 "c:",
75 "c:\\test dir",
76 "c:\\test dir\\SUBDIR",
77};
78
79static const char *g_apszDirsC[] =
80{
81 ".",
82 "..",
83 "test dir"
84};
85
86static const char *g_apszTestdirEntries[] =
87{
88 ".",
89 "..",
90 "SUBDIR",
91 "a.bat",
92 "aTestJe.bat",
93 "aTestje.bat",
94 "b.bat",
95 "c.bat",
96 "d.bat",
97 "e.bat",
98 "f.bat",
99 "g.bat",
100 "h.bat",
101 "x.bat",
102 "z.bat",
103};
104
105static const char *g_apszSUBDIREntries[] =
106{
107 ".",
108 "..",
109 "a.bat",
110 "aTestJe.bat",
111 "aTestje.bat",
112 "b.bat",
113 "c.bat",
114 "d.bat",
115 "e.bat",
116 "f.bat",
117 "g.bat",
118 "h.bat",
119 "x.bat",
120 "z.bat",
121};
122
123static int rtDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
124{
125 RT_NOREF2(enmFilter, fFlags);
126 if (!strcmp(pszPath, "c:\\*"))
127 iDirList = 1;
128 else if (!strcmp(pszPath, "c:\\test dir\\*"))
129 iDirList = 2;
130 else if (!strcmp(pszPath, "c:\\test dir\\SUBDIR\\*"))
131 iDirList = 3;
132 else
133 AssertFailed();
134
135 *phDir = (RTDIR)1;
136 return VINF_SUCCESS;
137}
138
139static int rtDirClose(RTDIR hDir)
140{
141 RT_NOREF1(hDir);
142 iDirFile = 0;
143 return VINF_SUCCESS;
144}
145
146static int rtDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
147{
148 RT_NOREF4(hDir, pcbDirEntry, enmAdditionalAttribs, fFlags);
149 const char *pszSrcName;
150 switch (iDirList)
151 {
152 case 1:
153 if (iDirFile == RT_ELEMENTS(g_apszDirsC))
154 return VERR_NO_MORE_FILES;
155 pszSrcName = g_apszDirsC[iDirFile];
156 break;
157 case 2:
158 if (iDirFile == RT_ELEMENTS(g_apszTestdirEntries))
159 return VERR_NO_MORE_FILES;
160 pszSrcName = g_apszTestdirEntries[iDirFile];
161 break;
162 case 3:
163 if (iDirFile == RT_ELEMENTS(g_apszSUBDIREntries))
164 return VERR_NO_MORE_FILES;
165 pszSrcName = g_apszSUBDIREntries[iDirFile];
166 break;
167 default:
168 AssertFailed();
169 return VERR_NO_MORE_FILES;
170 }
171
172 size_t const cbDirEntry = *pcbDirEntry;
173 size_t const cbNameAvail = cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
174 size_t const cchSrcName = strlen(pszSrcName);
175 *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchSrcName + 1;
176 pDirEntry->cbName = (uint16_t)cchSrcName;
177 AssertReturn(cchSrcName < cbNameAvail, VERR_BUFFER_OVERFLOW);
178
179 memcpy(pDirEntry->szName, pszSrcName, cchSrcName + 1);
180 iDirFile++;
181 return VINF_SUCCESS;
182}
183
184static int rtPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
185{
186 RT_NOREF2(pObjInfo, enmAdditionalAttribs);
187 int cMax;
188
189 /* first try g_apszDirs */
190 for (unsigned int i=0;i<RT_ELEMENTS(g_apszDirs);i++)
191 {
192 if(!strcmp(pszPath, g_apszDirs[i]))
193 return VINF_SUCCESS;
194 }
195
196 const char **papszDirList;
197 switch (iDirList)
198 {
199 case 1:
200 cMax = RT_ELEMENTS(g_apszDirsC);
201 papszDirList = g_apszDirsC;
202 break;
203 case 2:
204 cMax = RT_ELEMENTS(g_apszTestdirEntries);
205 papszDirList = g_apszTestdirEntries;
206 break;
207 case 3:
208 cMax = RT_ELEMENTS(g_apszSUBDIREntries);
209 papszDirList = g_apszSUBDIREntries;
210 break;
211 default:
212 return VERR_FILE_NOT_FOUND;
213 }
214 for (int i = 0; i < cMax; i++)
215 {
216 if (!strcmp(pszPath, papszDirList[i]))
217 return VINF_SUCCESS;
218 }
219 return VERR_FILE_NOT_FOUND;
220}
221
222static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent)
223{
224 size_t const cchComponent = strlen(pszStartComponent);
225
226 Log2(("vbsfCorrectCasing: '%s' '%s' (%zu)\n", pszFullPath, pszStartComponent, cchComponent));
227
228 uint32_t const cbDirEntry = 4096;
229 uint32_t const cbDirEntryName = cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
230 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
231 AssertReturn(pDirEntry, VERR_NO_MEMORY);
232
233 /** @todo this is quite inefficient, especially for directories with many files */
234 Assert(pszFullPath < pszStartComponent - 1);
235 Assert(pszStartComponent[-1] == RTPATH_DELIMITER);
236 pszStartComponent[-1] = '\0';
237 int rc = RTStrCopy(pDirEntry->szName, cbDirEntryName - sizeof(RTPATH_SLASH_STR "*"), pszFullPath);
238 pszStartComponent[-1] = RTPATH_DELIMITER;
239 if (RT_SUCCESS(rc))
240 {
241 char szWildCard[4];
242 szWildCard[0] = RTPATH_DELIMITER;
243 szWildCard[1] = '*';
244 szWildCard[2] = '\0';
245 rc = RTStrCat(pDirEntry->szName, cbDirEntryName, szWildCard);
246 }
247 if (RT_SUCCESS(rc))
248 {
249 RTDIR hSearch = NIL_RTDIR;
250 rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/);
251 if (RT_SUCCESS(rc))
252 {
253 for(;;)
254 {
255 size_t cbDirEntryRet = cbDirEntry;
256 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntryRet, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
257 if (rc == VERR_NO_MORE_FILES)
258 break;
259
260 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
261 {
262 AssertFailed();
263 if (rc != VERR_NO_TRANSLATION)
264 break;
265 continue;
266 }
267
268 Log2(("vbsfCorrectCasing: found '%s'\n", &pDirEntry->szName[0]));
269 if ( pDirEntry->cbName == cchComponent
270 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
271 {
272 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
273 memcpy(pszStartComponent, &pDirEntry->szName[0], cchComponent);
274 pszStartComponent[cchComponent] = '\0'; /* paranoia */
275 rc = VINF_SUCCESS;
276 break;
277 }
278 }
279 if (RT_FAILURE(rc))
280 Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
281
282 RTDirClose(hSearch);
283 }
284 }
285
286 RTMemFree(pDirEntry);
287 return rc;
288}
289
290
291
292static int testCase(char *pszFullPath, bool fWildCard = false)
293{
294 int rc;
295 RTFSOBJINFO info;
296 char *pszWildCardComponent = NULL;
297
298 if (fWildCard)
299 {
300 /* strip off the last path component, that contains the wildcard(s) */
301 size_t cchLlen = strlen(pszFullPath);
302 char *pszSrc = pszFullPath + cchLlen - 1;
303
304 while(pszSrc > pszFullPath)
305 {
306 if (*pszSrc == RTPATH_DELIMITER)
307 break;
308 pszSrc--;
309 }
310 if (*pszSrc == RTPATH_DELIMITER)
311 {
312 bool fHaveWildcards = false;
313 char *temp = pszSrc;
314
315 while(*temp)
316 {
317 char uc = *temp;
318 /** @todo should depend on the guest OS */
319 if (uc == '*' || uc == '?' || uc == '>' || uc == '<' || uc == '"')
320 {
321 fHaveWildcards = true;
322 break;
323 }
324 temp++;
325 }
326
327 if (fHaveWildcards)
328 {
329 pszWildCardComponent = pszSrc;
330 *pszWildCardComponent = 0;
331 }
332 }
333 }
334
335 rc = RTPathQueryInfo(pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
336 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
337 {
338 char *pszSrc = &pszFullPath[strlen(pszFullPath) - 1];
339
340 Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
341
342 /* Find partial path that's valid */
343 while (pszSrc > pszFullPath)
344 {
345 if (*pszSrc == RTPATH_DELIMITER)
346 {
347 *pszSrc = '\0';
348 rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
349 *pszSrc = RTPATH_DELIMITER;
350 if (rc == VINF_SUCCESS)
351 {
352#ifdef DEBUG
353 *pszSrc = '\0';
354 Log(("Found valid partial path %s\n", pszFullPath));
355 *pszSrc = RTPATH_DELIMITER;
356#endif
357 break;
358 }
359 }
360
361 pszSrc--;
362 }
363 Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc));
364 if ( *pszSrc == RTPATH_DELIMITER
365 && RT_SUCCESS(rc))
366 {
367 pszSrc++;
368 for(;;)
369 {
370 char *end = pszSrc;
371 bool fEndOfString = true;
372
373 while(*end)
374 {
375 if (*end == RTPATH_DELIMITER)
376 break;
377 end++;
378 }
379
380 if (*end == RTPATH_DELIMITER)
381 {
382 fEndOfString = false;
383 *end = 0;
384 rc = RTPathQueryInfo(pszSrc, &info, RTFSOBJATTRADD_NOTHING);
385 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
386 }
387 else if (end == pszSrc)
388 rc = VINF_SUCCESS; /* trailing delimiter */
389 else
390 rc = VERR_FILE_NOT_FOUND;
391
392 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
393 {
394 /* path component is invalid; try to correct the casing */
395 rc = vbsfCorrectCasing(pszFullPath, pszSrc);
396 if (RT_FAILURE(rc))
397 {
398 if (!fEndOfString)
399 *end = RTPATH_DELIMITER;
400 break;
401 }
402 }
403
404 if (fEndOfString)
405 break;
406
407 *end = RTPATH_DELIMITER;
408 pszSrc = end + 1;
409 }
410 if (RT_FAILURE(rc))
411 Log(("Unable to find suitable component rc=%d\n", rc));
412 }
413 else
414 rc = VERR_FILE_NOT_FOUND;
415
416 }
417 if (pszWildCardComponent)
418 *pszWildCardComponent = RTPATH_DELIMITER;
419
420 if (RT_SUCCESS(rc))
421 Log(("New valid path %s\n", pszFullPath));
422 else
423 Log(("Old invalid path %s\n", pszFullPath));
424 return rc;
425}
426
427
428int main()
429{
430 RTTEST hTest;
431 RTEXITCODE rcExit = RTTestInitAndCreate("tstShflCase", &hTest);
432 if (rcExit != RTEXITCODE_SUCCESS)
433 return rcExit;
434 RTTestBanner(hTest);
435
436 RTLogFlush(NULL);
437 RTLogDestinations(NULL, "stdout");
438 RTLogGroupSettings(NULL, "misc=~0");
439 RTLogFlags(NULL, "unbuffered");
440
441 static const struct
442 {
443 int rcExpect;
444 bool fWildcard;
445 const char *pszPath;
446 size_t cchPath;
447 } s_aTests[] =
448 {
449 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\test Dir\\z.bAt") },
450 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\test dir\\z.bAt") },
451 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\test dir\\SUBDIR\\z.bAt") },
452 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\test dir\\SUBDiR\\atestje.bat") },
453 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\TEST dir\\subDiR\\aTestje.baT") },
454 { VINF_SUCCESS, true, RT_STR_TUPLE("c:\\TEST dir\\subDiR\\*") },
455 { VINF_SUCCESS, true, RT_STR_TUPLE("c:\\TEST dir\\subDiR\\") },
456 { VINF_SUCCESS, false, RT_STR_TUPLE("c:\\test dir\\SUBDIR\\") },
457 { VERR_NO_MORE_FILES, false, RT_STR_TUPLE("c:\\test dir\\invalid\\SUBDIR\\test.bat") },
458 };
459 for (size_t i = 0; i < RT_ELEMENTS(s_aTests); i++)
460 {
461 char szTest[128];
462 AssertContinueStmt(s_aTests[i].cchPath < sizeof(szTest), RTTestFailed(hTest, "too long: #%zu", i));
463 memcpy(szTest, s_aTests[i].pszPath, s_aTests[i].cchPath + 1);
464 int rc = testCase(szTest, s_aTests[i].fWildcard);
465 if (rc != s_aTests[i].rcExpect)
466 RTTestFailed(hTest, "testCase returned %Rrc, expected %Rrc (path: %s)",
467 rc, s_aTests[i].rcExpect, s_aTests[i].pszPath);
468 }
469 return RTTestSummaryAndDestroy(hTest);
470}
471
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette