VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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