VirtualBox

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

Last change on this file since 105087 was 105087, checked in by vboxsync, 8 months ago

doc/manual,include/VBox,Frontends/VBoxManage,HostServices/SharedFolders,
Main/{include,SharedFolder,Console,Machine,VirtualBox.xidl}: Add a
new attribute to ISharedFolder for specifying a symbolic link creation
policy to restrict the source pathname when creating symbolic links
within a guest. The symbolic link policies are represented by a new
enumeration of type SymlinkPolicy_T which includes values for no
restrictions ('any'), symlink sources only within the subtree of the
share ('subtree'), symlink sources as any relative path ('relative'),
and no symlinks allowed ('forbidden'). The symlink policy can only be
applied to permanent shared folders at this stage. bugref:10619

  • 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 105087 2024-07-01 23:27:59Z vboxsync $ */
2/** @file
3 * Shared Folders Service - guest/host path convertion and verification.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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