VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedFolders/vbsfpath.cpp@ 60121

Last change on this file since 60121 was 57849, checked in by vboxsync, 9 years ago

HostServices/SharedFolders: allow colon character for darwin host.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.9 KB
Line 
1/* $Id: vbsfpath.cpp 57849 2015-09-22 08:39:31Z vboxsync $ */
2/** @file
3 * Shared Folders - guest/host path convertion and verification.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef UNITTEST
19# include "testcase/tstSharedFolderService.h"
20#endif
21
22#include "vbsfpath.h"
23#include "mappings.h"
24#include "vbsf.h"
25#include "shflhandle.h"
26
27#include <iprt/alloc.h>
28#include <iprt/asm.h>
29#include <iprt/assert.h>
30#include <iprt/fs.h>
31#include <iprt/dir.h>
32#include <iprt/file.h>
33#include <iprt/path.h>
34#include <iprt/string.h>
35#include <iprt/symlink.h>
36#include <iprt/uni.h>
37#include <iprt/stream.h>
38#ifdef RT_OS_DARWIN
39# include <Carbon/Carbon.h>
40#endif
41
42#ifdef UNITTEST
43# include "teststubs.h"
44#endif
45
46#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
47
48/**
49 * @todo find a better solution for supporting the execute bit for non-windows
50 * guests on windows host. Search for "0111" to find all the relevant places.
51 */
52
53/**
54 * Corrects the casing of the final component
55 *
56 * @returns
57 * @param pClient .
58 * @param pszFullPath .
59 * @param pszStartComponent .
60 */
61static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
62{
63 Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
64
65 AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2);
66 AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5);
67
68 /*
69 * Allocate a buffer that can hold really long file name entries as well as
70 * the initial search pattern.
71 */
72 size_t cchComponent = strlen(pszStartComponent);
73 size_t cchParentDir = pszStartComponent - pszFullPath;
74 size_t cchFullPath = cchParentDir + cchComponent;
75 Assert(strlen(pszFullPath) == cchFullPath);
76
77 size_t cbDirEntry = 4096;
78 if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName))
79 cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4;
80
81 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
82 if (pDirEntry == NULL)
83 return VERR_NO_MEMORY;
84
85 /*
86 * Construct the search criteria in the szName member of pDirEntry.
87 */
88 /** @todo This is quite inefficient, especially for directories with many
89 * files. If any of the typically case sensitive host systems start
90 * supporting opendir wildcard filters, it would make sense to build
91 * one here with '?' for case foldable charaters. */
92 /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */
93 int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName),
94 pszFullPath, cchParentDir,
95 RT_STR_TUPLE("*"));
96 AssertRC(rc);
97 if (RT_SUCCESS(rc))
98 {
99 PRTDIR hSearch = NULL;
100 rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0);
101 if (RT_SUCCESS(rc))
102 {
103 for (;;)
104 {
105 size_t cbDirEntrySize = cbDirEntry;
106
107 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
108 if (rc == VERR_NO_MORE_FILES)
109 break;
110
111 if ( rc != VINF_SUCCESS
112 && rc != VWRN_NO_DIRENT_INFO)
113 {
114 AssertFailed();
115 if ( rc == VERR_NO_TRANSLATION
116 || rc == VERR_INVALID_UTF8_ENCODING)
117 continue;
118 break;
119 }
120
121 Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
122 if ( pDirEntry->cbName == cchComponent
123 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
124 {
125 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
126 strcpy(pszStartComponent, &pDirEntry->szName[0]);
127 rc = VINF_SUCCESS;
128 break;
129 }
130 }
131
132 RTDirClose(hSearch);
133 }
134 }
135
136 if (RT_FAILURE(rc))
137 Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc));
138
139 RTMemFree(pDirEntry);
140
141 return rc;
142}
143
144/* Temporary stand-in for RTPathExistEx. */
145static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags)
146{
147#if 0 /** @todo Fix the symlink issue on windows! */
148 return RTPathExistsEx(pszPath, fFlags);
149#else
150 RTFSOBJINFO IgnInfo;
151 return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags);
152#endif
153}
154
155/**
156 * Helper for vbsfBuildFullPath that performs case corrections on the path
157 * that's being build.
158 *
159 * @returns VINF_SUCCESS at the moment.
160 * @param pClient The client data.
161 * @param pszFullPath Pointer to the full path. This is the path
162 * which may need case corrections. The
163 * corrections will be applied in place.
164 * @param cchFullPath The length of the full path.
165 * @param fWildCard Whether the last component may contain
166 * wildcards and thus might require exclusion
167 * from the case correction.
168 * @param fPreserveLastComponent Always exclude the last component from case
169 * correction if set.
170 */
171static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath,
172 bool fWildCard, bool fPreserveLastComponent)
173{
174 /*
175 * Hide the last path component if it needs preserving. This is required
176 * in the following cases:
177 * - Contains the wildcard(s).
178 * - Is a 'rename' target.
179 */
180 char *pszLastComponent = NULL;
181 if (fWildCard || fPreserveLastComponent)
182 {
183 char *pszSrc = pszFullPath + cchFullPath - 1;
184 Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
185 while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
186 {
187 if (*pszSrc == RTPATH_DELIMITER)
188 break;
189 pszSrc--;
190 }
191 if (*pszSrc == RTPATH_DELIMITER)
192 {
193 if ( fPreserveLastComponent
194 /* Or does it really have wildcards? */
195 || strchr(pszSrc + 1, '*') != NULL
196 || strchr(pszSrc + 1, '?') != NULL
197 || strchr(pszSrc + 1, '>') != NULL
198 || strchr(pszSrc + 1, '<') != NULL
199 || strchr(pszSrc + 1, '"') != NULL )
200 {
201 pszLastComponent = pszSrc;
202 *pszLastComponent = '\0';
203 }
204 }
205 }
206
207 /*
208 * If the path/file doesn't exist, we need to attempt case correcting it.
209 */
210 /** @todo Don't check when creating files or directories; waste of time. */
211 int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
212 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
213 {
214 Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
215
216 /*
217 * Work from the end of the path to find a partial path that's valid.
218 */
219 char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1;
220 Assert(strchr(pszFullPath, '\0') == pszSrc + 1);
221
222 while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath)
223 {
224 if (*pszSrc == RTPATH_DELIMITER)
225 {
226 *pszSrc = '\0';
227 rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
228 *pszSrc = RTPATH_DELIMITER;
229 if (RT_SUCCESS(rc))
230 {
231#ifdef DEBUG
232 *pszSrc = '\0';
233 Log(("Found valid partial path %s\n", pszFullPath));
234 *pszSrc = RTPATH_DELIMITER;
235#endif
236 break;
237 }
238 }
239
240 pszSrc--;
241 }
242 Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc));
243 if ( *pszSrc == RTPATH_DELIMITER
244 && RT_SUCCESS(rc))
245 {
246 /*
247 * Turn around and work the other way case correcting the components.
248 */
249 pszSrc++;
250 for (;;)
251 {
252 bool fEndOfString = true;
253
254 /* Find the end of the component. */
255 char *pszEnd = pszSrc;
256 while (*pszEnd)
257 {
258 if (*pszEnd == RTPATH_DELIMITER)
259 break;
260 pszEnd++;
261 }
262
263 if (*pszEnd == RTPATH_DELIMITER)
264 {
265 fEndOfString = false;
266 *pszEnd = '\0';
267#if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */
268 rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
269#else
270 rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient));
271#endif
272 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
273 }
274 else if (pszEnd == pszSrc)
275 rc = VINF_SUCCESS; /* trailing delimiter */
276 else
277 rc = VERR_FILE_NOT_FOUND;
278
279 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
280 {
281 /* Path component is invalid; try to correct the casing. */
282 rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc);
283 if (RT_FAILURE(rc))
284 {
285 /* Failed, so don't bother trying any further components. */
286 if (!fEndOfString)
287 *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */
288 break;
289 }
290 }
291
292 /* Next (if any). */
293 if (fEndOfString)
294 break;
295
296 *pszEnd = RTPATH_DELIMITER;
297 pszSrc = pszEnd + 1;
298 }
299 if (RT_FAILURE(rc))
300 Log(("Unable to find suitable component rc=%d\n", rc));
301 }
302 else
303 rc = VERR_FILE_NOT_FOUND;
304
305 }
306
307 /* Restore the final component if it was dropped. */
308 if (pszLastComponent)
309 *pszLastComponent = RTPATH_DELIMITER;
310
311 /* might be a new file so don't fail here! */
312 return VINF_SUCCESS;
313}
314
315
316#ifdef RT_OS_DARWIN
317/* Misplaced hack! See todo! */
318
319/** Normalize the string using kCFStringNormalizationFormD.
320 *
321 * @param pwszSrc The input UTF-16 string.
322 * @param cwcSrc Length of the input string in characters.
323 * @param ppwszDst Where to store the pointer to the resulting normalized string.
324 * @param pcwcDst Where to store length of the normalized string in characters (without the trailing nul).
325 */
326static int vbsfNormalizeStringDarwin(const PRTUTF16 pwszSrc, uint32_t cwcSrc, PRTUTF16 *ppwszDst, uint32_t *pcwcDst)
327{
328 /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
329 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
330 * system level in darwin, or just by the user mode application libs. */
331
332 PRTUTF16 pwszNFD;
333 uint32_t cwcNFD;
334
335 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
336
337 /* Is 8 times length enough for decomposed in worst case...? */
338 size_t cbNFDAlloc = cwcSrc * 8 + 2;
339 pwszNFD = (PRTUTF16)RTMemAllocZ(cbNFDAlloc);
340 if (!pwszNFD)
341 {
342 return VERR_NO_MEMORY;
343 }
344
345 ::CFStringAppendCharacters(inStr, (UniChar*)pwszSrc, cwcSrc);
346 ::CFStringNormalize(inStr, kCFStringNormalizationFormD);
347 cwcNFD = ::CFStringGetLength(inStr);
348
349 CFRange rangeCharacters;
350 rangeCharacters.location = 0;
351 rangeCharacters.length = cwcNFD;
352 ::CFStringGetCharacters(inStr, rangeCharacters, pwszNFD);
353
354 pwszNFD[cwcNFD] = 0x0000; /* NULL terminated */
355
356 CFRelease(inStr);
357
358 *ppwszDst = pwszNFD;
359 *pcwcDst = cwcNFD;
360 return VINF_SUCCESS;
361}
362#endif
363
364
365/**
366 * Check the given UTF-8 path for root escapes.
367 *
368 * Verify that the path is within the root directory of the mapping. Count '..'
369 * and other path components and check that we do not go over the root.
370 *
371 * @returns VBox status code.
372 * @retval VINF_SUCCESS
373 * @retval VERR_INVALID_NAME
374 *
375 * @param pszPath The (UTF-8) path to check. Slashes has been convert
376 * to host slashes by this time.
377 *
378 * @remarks This function assumes that the path will be appended to the root
379 * directory of the shared folder mapping. Keep that in mind when
380 * checking absolute paths!
381 */
382static int vbsfPathCheckRootEscape(const char *pszPath)
383{
384 /*
385 * Walk the path, component by component and check for escapes.
386 */
387
388 int cComponents = 0; /* How many normal path components. */
389 int cParentDirs = 0; /* How many '..' components. */
390
391 for (;;)
392 {
393 char ch;
394
395 /* Skip leading path delimiters. */
396 do
397 ch = *pszPath++;
398 while (RTPATH_IS_SLASH(ch));
399 if (ch == '\0')
400 return VINF_SUCCESS;
401
402 /* Check if that is a dot component. */
403 int cDots = 0;
404 while (ch == '.')
405 {
406 cDots++;
407 ch = *pszPath++;
408 }
409
410 if ( cDots >= 1
411 && (ch == '\0' || RTPATH_IS_SLASH(ch)) )
412 {
413 if (cDots >= 2) /* Consider all multidots sequences as a 'parent dir'. */
414 {
415 cParentDirs++;
416
417 /* Escaping? */
418 if (cParentDirs > cComponents)
419 return VERR_INVALID_NAME;
420 }
421 /* else: Single dot, nothing changes. */
422 }
423 else
424 {
425 /* Not a dot component, skip to the end of it. */
426 while (ch != '\0' && !RTPATH_IS_SLASH(ch))
427 ch = *pszPath++;
428 cComponents++;
429 }
430 Assert(cComponents >= cParentDirs);
431
432 /* The end? */
433 Assert(ch == '\0' || RTPATH_IS_SLASH(ch));
434 if (ch == '\0')
435 return VINF_SUCCESS;
436 }
437}
438
439#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
440/* See MSDN "Naming Files, Paths, and Namespaces".
441 * '<', '>' and '"' are allowed as possible wildcards (see ANSI_DOS_STAR, etc in ntifs.h)
442 */
443static const char sachCharBlackList[] = ":/\\|";
444#else
445/* Something else. */
446static const char sachCharBlackList[] = "/";
447#endif
448
449/** Verify if the character can be used in a host file name.
450 * Wildcard characters ('?', '*') are allowed.
451 *
452 * @param c Character to verify.
453 */
454static bool vbsfPathIsValidNameChar(unsigned char c)
455{
456 /* Character 0 is not allowed too. */
457 if (c == 0 || strchr(sachCharBlackList, c))
458 {
459 return false;
460 }
461
462#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
463 /* Characters less than 32 are not allowed. */
464 if (c < 32)
465 {
466 return false;
467 }
468#endif
469
470 return true;
471}
472
473/** Verify if the character is a wildcard.
474 *
475 * @param c Character to verify.
476 */
477static bool vbsfPathIsWildcardChar(char c)
478{
479 if ( c == '*'
480 || c == '?'
481#ifdef RT_OS_WINDOWS /* See ntifs.h */
482 || c == '<' /* ANSI_DOS_STAR */
483 || c == '>' /* ANSI_DOS_QM */
484 || c == '"' /* ANSI_DOS_DOT */
485#endif
486 )
487 {
488 return true;
489 }
490
491 return false;
492}
493
494int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot,
495 PSHFLSTRING pGuestString, uint32_t cbGuestString,
496 char **ppszHostPath, uint32_t *pcbHostPathRoot,
497 uint32_t fu32Options,
498 uint32_t *pfu32PathFlags)
499{
500#ifdef VBOX_STRICT
501 /*
502 * Check that the pGuestPath has correct size and encoding.
503 */
504 if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false)
505 {
506 LogFunc(("Invalid input string\n"));
507 return VERR_INTERNAL_ERROR;
508 }
509#else
510 NOREF(cbGuestString);
511#endif
512
513 /*
514 * Resolve the root handle into a string.
515 */
516 uint32_t cbRootLen = 0;
517 const char *pszRoot = NULL;
518 int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen);
519 if (RT_FAILURE(rc))
520 {
521 LogFunc(("invalid root\n"));
522 return rc;
523 }
524
525 AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */
526
527 /*
528 * Get the UTF8 string with the relative path provided by the guest.
529 * If guest uses UTF-16 then convert it to UTF-8.
530 */
531 uint32_t cbGuestPath;
532 const char *pchGuestPath;
533 char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */
534 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
535 {
536 /* UTF-8 */
537 cbGuestPath = pGuestString->u16Length;
538 pchGuestPath = (char *)&pGuestString->String.utf8[0];
539 }
540 else
541 {
542 /* UTF-16 */
543 uint32_t cwcSrc;
544 PRTUTF16 pwszSrc;
545
546#ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */
547 cwcSrc = 0;
548 pwszSrc = NULL;
549 rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0],
550 pGuestString->u16Length / sizeof(RTUTF16),
551 &pwszSrc, &cwcSrc);
552#else
553 cwcSrc = pGuestString->u16Length / sizeof(RTUTF16);
554 pwszSrc = &pGuestString->String.ucs2[0];
555#endif
556
557 if (RT_SUCCESS(rc))
558 {
559 size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc);
560 if (cbPathAsUtf8 >= cwcSrc)
561 {
562 /* Allocate buffer that will be able to contain the converted UTF-8 string. */
563 pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1);
564 if (RT_LIKELY(pchGuestPathAllocated != NULL))
565 {
566 if (RT_LIKELY(cbPathAsUtf8))
567 {
568 size_t cchActual;
569 char *pszDst = pchGuestPathAllocated;
570 rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual);
571 AssertRC(rc);
572 AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4);
573 Assert(strlen(pszDst) == cbPathAsUtf8);
574 }
575
576 if (RT_SUCCESS(rc))
577 {
578 /* Terminate the string. */
579 pchGuestPathAllocated[cbPathAsUtf8] = '\0';
580
581 cbGuestPath = cbPathAsUtf8;
582 pchGuestPath = pchGuestPathAllocated;
583 }
584 }
585 else
586 {
587 rc = VERR_NO_MEMORY;
588 }
589 }
590 else
591 {
592 AssertFailed();
593 rc = VERR_INTERNAL_ERROR_3;
594 }
595
596#ifdef RT_OS_DARWIN
597 RTMemFree(pwszSrc);
598#endif
599 }
600 }
601
602 char *pszFullPath = NULL;
603
604 if (RT_SUCCESS(rc))
605 {
606 LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath));
607
608 /*
609 * Allocate enough memory to build the host full path from the root and the relative path.
610 */
611 uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */
612 pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc);
613 if (RT_LIKELY(pszFullPath != NULL))
614 {
615 /* Copy the root. */
616 memcpy(pszFullPath, pszRoot, cbRootLen);
617 if (!RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1]))
618 {
619 pszFullPath[cbRootLen++] = RTPATH_SLASH;
620 }
621
622 /* Init the pointer for the relative path. */
623 char *pchDst = &pszFullPath[cbRootLen];
624
625 uint32_t cbSrc = cbGuestPath;
626 const char *pchSrc = pchGuestPath;
627
628 /* Strip leading delimiters from the path the guest specified. */
629 while ( cbSrc > 0
630 && *pchSrc == pClient->PathDelimiter)
631 {
632 ++pchSrc;
633 --cbSrc;
634 }
635
636 /*
637 * Iterate the guest path components, verify each of them
638 * and append to the host full path replacing delimiters with host slash.
639 */
640 bool fLastComponentHasWildcard = false;
641 for (; cbSrc > 0; --cbSrc, ++pchSrc)
642 {
643 if (RT_LIKELY(*pchSrc != pClient->PathDelimiter))
644 {
645 if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc)))
646 {
647 if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc))
648 {
649 fLastComponentHasWildcard = true;
650 }
651
652 *pchDst++ = *pchSrc;
653 }
654 else
655 {
656 rc = VERR_INVALID_NAME;
657 break;
658 }
659 }
660 else
661 {
662 /* Replace with the host slash. */
663 *pchDst++ = RTPATH_SLASH;
664
665 if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1)
666 {
667 /* Processed component has a wildcard and there are more characters in the path. */
668 *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX;
669 }
670 fLastComponentHasWildcard = false;
671 }
672 }
673
674 if (RT_SUCCESS(rc))
675 {
676 const size_t cbFullPathLength = pchDst - &pszFullPath[0]; /* As strlen(pszFullPath). */
677
678 *pchDst++ = 0;
679
680 if (pfu32PathFlags && fLastComponentHasWildcard)
681 {
682 *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST;
683 }
684
685 /* Check the appended path for root escapes. */
686 if (fu32Options & VBSF_O_PATH_CHECK_ROOT_ESCAPE)
687 {
688 rc = vbsfPathCheckRootEscape(&pszFullPath[cbRootLen]);
689 }
690 if (RT_SUCCESS(rc))
691 {
692 /*
693 * When the host file system is case sensitive and the guest expects
694 * a case insensitive fs, then problems can occur.
695 */
696 if ( vbsfIsHostMappingCaseSensitive(hRoot)
697 && !vbsfIsGuestMappingCaseSensitive(hRoot))
698 {
699 bool fWildCard = RT_BOOL(fu32Options & VBSF_O_PATH_WILDCARD);
700 bool fPreserveLastComponent = RT_BOOL(fu32Options & VBSF_O_PATH_PRESERVE_LAST_COMPONENT);
701 rc = vbsfCorrectPathCasing(pClient, pszFullPath, cbFullPathLength, fWildCard, fPreserveLastComponent);
702 }
703
704 if (RT_SUCCESS(rc))
705 {
706 LogFunc(("%s\n", pszFullPath));
707
708 /* Return the full host path. */
709 *ppszHostPath = pszFullPath;
710
711 if (pcbHostPathRoot)
712 {
713 *pcbHostPathRoot = cbRootLen - 1; /* Must index the path delimiter. */
714 }
715 }
716 }
717 }
718 }
719 else
720 {
721 rc = VERR_NO_MEMORY;
722 }
723 }
724
725 /*
726 * Cleanup.
727 */
728 RTMemFree(pchGuestPathAllocated);
729
730 if (RT_SUCCESS(rc))
731 {
732 return rc;
733 }
734
735 /*
736 * Cleanup on failure.
737 */
738 RTMemFree(pszFullPath);
739
740 return rc;
741}
742
743void vbsfFreeHostPath(char *pszHostPath)
744{
745 RTMemFree(pszHostPath);
746}
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