VirtualBox

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

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