VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedFolders/mappings.cpp@ 105745

Last change on this file since 105745 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: 37.1 KB
Line 
1/* $Id: mappings.cpp 105087 2024-07-01 23:27:59Z vboxsync $ */
2/** @file
3 * Shared Folders Service - Mappings support.
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 "mappings.h"
38#include "vbsfpath.h"
39#include <iprt/alloc.h>
40#include <iprt/assert.h>
41#include <iprt/list.h>
42#include <iprt/path.h>
43#include <iprt/string.h>
44#include <VBox/AssertGuest.h>
45
46#ifdef UNITTEST
47# include "teststubs.h"
48#endif
49
50
51/*********************************************************************************************************************************
52* Global Variables *
53*********************************************************************************************************************************/
54extern PVBOXHGCMSVCHELPERS g_pHelpers; /* service.cpp */
55
56
57/* Shared folders order in the saved state and in the g_FolderMapping can differ.
58 * So a translation array of root handle is needed.
59 */
60
61static MAPPING g_FolderMapping[SHFL_MAX_MAPPINGS];
62static SHFLROOT g_aIndexFromRoot[SHFL_MAX_MAPPINGS];
63/**< Array running parallel to g_aIndexFromRoot and which entries are increased
64 * as an root handle is added or removed.
65 *
66 * This helps the guest figuring out that a mapping may have been reconfigured
67 * or that saved state has been restored. Entry reuse is very likely given that
68 * vbsfRootHandleAdd() always starts searching at the start for an unused entry.
69 */
70static uint32_t g_auRootHandleVersions[SHFL_MAX_MAPPINGS];
71/** Version number that is increased for every change made.
72 * This is used by the automount guest service to wait for changes.
73 * @note This does not need saving, the guest should be woken up and refresh
74 * its sate when restored. */
75static uint32_t volatile g_uFolderMappingsVersion = 0;
76
77
78/** For recording async vbsfMappingsWaitForChanges calls. */
79typedef struct SHFLMAPPINGSWAIT
80{
81 RTLISTNODE ListEntry; /**< List entry. */
82 PSHFLCLIENTDATA pClient; /**< The client that's waiting. */
83 VBOXHGCMCALLHANDLE hCall; /**< The call handle to signal completion with. */
84 PVBOXHGCMSVCPARM pParm; /**< The 32-bit unsigned parameter to stuff g_uFolderMappingsVersion into. */
85} SHFLMAPPINGSWAIT;
86/** Pointer to async mappings change wait. */
87typedef SHFLMAPPINGSWAIT *PSHFLMAPPINGSWAIT;
88/** List head for clients waiting on mapping changes (SHFLMAPPINGSWAIT). */
89static RTLISTANCHOR g_MappingsChangeWaiters;
90/** Number of clients waiting on mapping changes.
91 * We use this to limit the number of waiting calls the clients can make. */
92static uint32_t g_cMappingChangeWaiters = 0;
93static void vbsfMappingsWakeupAllWaiters(void);
94
95
96void vbsfMappingInit(void)
97{
98 unsigned root;
99
100 for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
101 {
102 g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
103 }
104
105 RTListInit(&g_MappingsChangeWaiters);
106}
107
108/**
109 * Called before loading mappings from saved state to drop the root IDs.
110 */
111void vbsfMappingLoadingStart(void)
112{
113 for (SHFLROOT idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
114 g_aIndexFromRoot[idRoot] = SHFL_ROOT_NIL;
115
116 for (SHFLROOT i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
117 g_FolderMapping[i].fLoadedRootId = false;
118}
119
120/**
121 * Called when a mapping is loaded to restore the root ID and make sure it
122 * exists.
123 *
124 * @returns VBox status code.
125 */
126int vbsfMappingLoaded(const MAPPING *pLoadedMapping, SHFLROOT root)
127{
128 /* Mapping loaded from the saved state with the 'root' index. Which means
129 * the guest uses the 'root' as root handle for this folder.
130 * Check whether there is the same mapping in g_FolderMapping and
131 * update the g_aIndexFromRoot.
132 *
133 * Also update the mapping properties, which were lost: cMappings.
134 */
135 if (root >= SHFL_MAX_MAPPINGS)
136 {
137 return VERR_INVALID_PARAMETER;
138 }
139
140 SHFLROOT i;
141 for (i = 0; i < RT_ELEMENTS(g_FolderMapping); i++)
142 {
143 MAPPING *pMapping = &g_FolderMapping[i];
144
145 /* Equal? */
146 if ( pLoadedMapping->fValid == pMapping->fValid
147 && ShflStringSizeOfBuffer(pLoadedMapping->pMapName) == ShflStringSizeOfBuffer(pMapping->pMapName)
148 && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0)
149 {
150 Log(("vbsfMappingLoaded: root=%u i=%u (was %u) (%ls)\n",
151 root, i, g_aIndexFromRoot[root], pLoadedMapping->pMapName->String.utf16));
152
153 if (!pMapping->fLoadedRootId)
154 {
155 /* First encounter. */
156 pMapping->fLoadedRootId = true;
157
158 /* Update the mapping properties. */
159 pMapping->cMappings = pLoadedMapping->cMappings;
160 }
161 else
162 {
163 /* When pMapping->fLoadedRootId is already true it means that another HGCM client uses the same mapping. */
164 Assert(pMapping->cMappings > 1);
165 }
166
167 /* Actual index is i. Remember that when the guest uses 'root' it is actually 'i'. */
168 AssertLogRelMsg(g_aIndexFromRoot[root] == SHFL_ROOT_NIL,
169 ("idRoot=%u: current %u ([%s]), new %u (%ls [%s])\n",
170 root, g_aIndexFromRoot[root], g_FolderMapping[g_aIndexFromRoot[root]].pszFolderName,
171 i, pLoadedMapping->pMapName->String.utf16, pLoadedMapping->pszFolderName));
172 g_aIndexFromRoot[root] = i;
173
174 /* The mapping is known to the host and is used by the guest.
175 * No need for a 'placeholder'.
176 */
177 return VINF_SUCCESS;
178 }
179 }
180
181 /* No corresponding mapping on the host but the guest still uses it.
182 * Add a 'placeholder' mapping.
183 */
184 LogRel2(("SharedFolders: mapping a placeholder for '%ls' -> '%s'\n",
185 pLoadedMapping->pMapName->String.utf16, pLoadedMapping->pszFolderName));
186 return vbsfMappingsAdd(pLoadedMapping->pszFolderName, pLoadedMapping->pMapName,
187 pLoadedMapping->fWritable, pLoadedMapping->fAutoMount, pLoadedMapping->pAutoMountPoint,
188 pLoadedMapping->fSymlinksCreate, /* fMissing = */ true, /* fPlaceholder = */ true,
189 pLoadedMapping->enmSymlinkPolicy);
190}
191
192/**
193 * Called after loading mappings from saved state to make sure every mapping has
194 * a root ID.
195 */
196void vbsfMappingLoadingDone(void)
197{
198 for (SHFLROOT iMapping = 0; iMapping < RT_ELEMENTS(g_FolderMapping); iMapping++)
199 if (g_FolderMapping[iMapping].fValid)
200 {
201 AssertLogRel(g_FolderMapping[iMapping].pMapName);
202 AssertLogRel(g_FolderMapping[iMapping].pszFolderName);
203
204 SHFLROOT idRoot;
205 for (idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
206 if (g_aIndexFromRoot[idRoot] == iMapping)
207 break;
208 if (idRoot >= RT_ELEMENTS(g_aIndexFromRoot))
209 {
210 for (idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
211 if (g_aIndexFromRoot[idRoot] == SHFL_ROOT_NIL)
212 break;
213 if (idRoot < RT_ELEMENTS(g_aIndexFromRoot))
214 g_aIndexFromRoot[idRoot] = iMapping;
215 else
216 LogRel(("SharedFolders: Warning! No free root ID entry for mapping #%u: %ls [%s]\n", iMapping,
217 g_FolderMapping[iMapping].pMapName->String.utf16, g_FolderMapping[iMapping].pszFolderName));
218 }
219 }
220
221 /* Log the root ID mappings: */
222 if (LogRelIs2Enabled())
223 for (SHFLROOT idRoot = 0; idRoot < RT_ELEMENTS(g_aIndexFromRoot); idRoot++)
224 {
225 SHFLROOT const iMapping = g_aIndexFromRoot[idRoot];
226 if (iMapping != SHFL_ROOT_NIL)
227 LogRel2(("SharedFolders: idRoot %u: iMapping #%u: %ls [%s]\n", idRoot, iMapping,
228 g_FolderMapping[iMapping].pMapName->String.utf16, g_FolderMapping[iMapping].pszFolderName));
229 }
230}
231
232
233MAPPING *vbsfMappingGetByRoot(SHFLROOT root)
234{
235 if (root < RT_ELEMENTS(g_aIndexFromRoot))
236 {
237 SHFLROOT iMapping = g_aIndexFromRoot[root];
238
239 if ( iMapping != SHFL_ROOT_NIL
240 && iMapping < RT_ELEMENTS(g_FolderMapping))
241 {
242 return &g_FolderMapping[iMapping];
243 }
244 }
245
246 return NULL;
247}
248
249static SHFLROOT vbsfMappingGetRootFromIndex(SHFLROOT iMapping)
250{
251 unsigned root;
252
253 for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
254 {
255 if (iMapping == g_aIndexFromRoot[root])
256 {
257 return root;
258 }
259 }
260
261 return SHFL_ROOT_NIL;
262}
263
264static MAPPING *vbsfMappingGetByName(PRTUTF16 pwszName, SHFLROOT *pRoot)
265{
266 for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
267 {
268 if ( g_FolderMapping[i].fValid
269 && !g_FolderMapping[i].fPlaceholder) /* Don't allow mapping placeholders. */
270 {
271 if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.utf16, pwszName))
272 {
273 SHFLROOT root = vbsfMappingGetRootFromIndex(i);
274
275 if (root != SHFL_ROOT_NIL)
276 {
277 if (pRoot)
278 {
279 *pRoot = root;
280 }
281 return &g_FolderMapping[i];
282 }
283 AssertFailed();
284 }
285 }
286 }
287 return NULL;
288}
289
290static void vbsfRootHandleAdd(SHFLROOT iMapping)
291{
292 for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
293 {
294 if (g_aIndexFromRoot[root] == SHFL_ROOT_NIL)
295 {
296 g_aIndexFromRoot[root] = iMapping;
297 g_auRootHandleVersions[root] += 1;
298 return;
299 }
300 }
301
302 AssertFailed();
303}
304
305static void vbsfRootHandleRemove(SHFLROOT iMapping)
306{
307 unsigned cFound = 0;
308
309 for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++)
310 {
311 if (g_aIndexFromRoot[root] == iMapping)
312 {
313 g_aIndexFromRoot[root] = SHFL_ROOT_NIL;
314 g_auRootHandleVersions[root] += 1;
315 Log(("vbsfRootHandleRemove: Removed root=%u (iMapping=%u)\n", root, iMapping));
316
317 /* Note! Do not stop here as g_aIndexFromRoot may (at least it could
318 prior to the introduction of fLoadedRootId) contain
319 duplicates after restoring save state. */
320 cFound++;
321 }
322 }
323
324 Assert(cFound > 0); RT_NOREF(cFound);
325}
326
327
328
329#ifdef UNITTEST
330/** Unit test the SHFL_FN_ADD_MAPPING API. Located here as a form of API
331 * documentation. */
332void testMappingsAdd(RTTEST hTest)
333{
334 /* If the number or types of parameters are wrong the API should fail. */
335 testMappingsAddBadParameters(hTest);
336 /* Add tests as required... */
337}
338#endif
339/*
340 * We are always executed from one specific HGCM thread. So thread safe.
341 */
342int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable,
343 bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fSymlinksCreate, bool fMissing, bool fPlaceholder,
344 SymlinkPolicy_T enmSymlinkPolicy)
345{
346 unsigned i;
347
348 Assert(pszFolderName && pMapName);
349
350 Log(("vbsfMappingsAdd %ls\n", pMapName->String.utf16));
351
352 /* Check for duplicates, ignoring placeholders to give the GUI to change stuff at runtime. */
353 /** @todo bird: Not entirely sure about ignoring placeholders, but you cannot
354 * trigger auto-umounting without ignoring them. */
355 if (!fPlaceholder)
356 {
357 for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
358 {
359 if ( g_FolderMapping[i].fValid
360 && !g_FolderMapping[i].fPlaceholder)
361 {
362 if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.utf16, pMapName->String.utf16))
363 {
364 AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.utf16));
365 return VERR_ALREADY_EXISTS;
366 }
367 }
368 }
369 }
370
371 for (i = 0; i < SHFL_MAX_MAPPINGS; i++)
372 {
373 if (!g_FolderMapping[i].fValid)
374 {
375 /* Make sure the folder name is an absolute path, otherwise we're
376 likely to get into trouble with buffer sizes in vbsfPathGuestToHost. */
377 char szAbsFolderName[RTPATH_MAX];
378 int rc = vbsfPathAbs(NULL, pszFolderName, szAbsFolderName, sizeof(szAbsFolderName));
379 AssertRCReturn(rc, rc);
380
381 g_FolderMapping[i].pszFolderName = RTStrDup(szAbsFolderName);
382 g_FolderMapping[i].pMapName = ShflStringDup(pMapName);
383 g_FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint);
384 if ( !g_FolderMapping[i].pszFolderName
385 || !g_FolderMapping[i].pMapName
386 || !g_FolderMapping[i].pAutoMountPoint)
387 {
388 RTStrFree(g_FolderMapping[i].pszFolderName);
389 RTMemFree(g_FolderMapping[i].pMapName);
390 RTMemFree(g_FolderMapping[i].pAutoMountPoint);
391 return VERR_NO_MEMORY;
392 }
393
394 g_FolderMapping[i].fValid = true;
395 g_FolderMapping[i].cMappings = 0;
396 g_FolderMapping[i].fWritable = fWritable;
397 g_FolderMapping[i].fAutoMount = fAutoMount;
398 g_FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
399 g_FolderMapping[i].fMissing = fMissing;
400 g_FolderMapping[i].fPlaceholder = fPlaceholder;
401 g_FolderMapping[i].fLoadedRootId = false;
402 g_FolderMapping[i].enmSymlinkPolicy = enmSymlinkPolicy;
403
404 /* Check if the host file system is case sensitive */
405 RTFSPROPERTIES prop;
406 prop.fCaseSensitive = false; /* Shut up MSC. */
407 rc = RTFsQueryProperties(g_FolderMapping[i].pszFolderName, &prop);
408#ifndef DEBUG_bird /* very annoying */
409 AssertRC(rc);
410#endif
411 g_FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
412 vbsfRootHandleAdd(i);
413 vbsfMappingsWakeupAllWaiters();
414 break;
415 }
416 }
417 if (i == SHFL_MAX_MAPPINGS)
418 {
419 AssertLogRelMsgFailed(("vbsfMappingsAdd: no more room to add mapping %s to %ls!!\n", pszFolderName, pMapName->String.utf16));
420 return VERR_TOO_MUCH_DATA;
421 }
422
423 Log(("vbsfMappingsAdd: added mapping %s to %ls (slot %u, root %u)\n",
424 pszFolderName, pMapName->String.utf16, i, vbsfMappingGetRootFromIndex(i)));
425 return VINF_SUCCESS;
426}
427
428#ifdef UNITTEST
429/** Unit test the SHFL_FN_REMOVE_MAPPING API. Located here as a form of API
430 * documentation. */
431void testMappingsRemove(RTTEST hTest)
432{
433 /* If the number or types of parameters are wrong the API should fail. */
434 testMappingsRemoveBadParameters(hTest);
435 /* Add tests as required... */
436}
437#endif
438int vbsfMappingsRemove(PSHFLSTRING pMapName)
439{
440 Assert(pMapName);
441 Log(("vbsfMappingsRemove %ls\n", pMapName->String.utf16));
442
443 /*
444 * We must iterate thru the whole table as may have 0+ placeholder entries
445 * and 0-1 regular entries with the same name. Also, it is good to kick
446 * the guest automounter into action wrt to evicting placeholders.
447 */
448 int rc = VERR_FILE_NOT_FOUND;
449 for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++)
450 {
451 if (g_FolderMapping[i].fValid)
452 {
453 if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.utf16, pMapName->String.utf16))
454 {
455 if (g_FolderMapping[i].cMappings != 0)
456 {
457 LogRel2(("SharedFolders: removing '%ls' -> '%s'%s, which is still used by the guest\n", pMapName->String.utf16,
458 g_FolderMapping[i].pszFolderName, g_FolderMapping[i].fPlaceholder ? " (again)" : ""));
459 g_FolderMapping[i].fMissing = true;
460 g_FolderMapping[i].fPlaceholder = true;
461 vbsfMappingsWakeupAllWaiters();
462 rc = VINF_PERMISSION_DENIED;
463 }
464 else
465 {
466 /* pMapName can be the same as g_FolderMapping[i].pMapName when
467 * called from vbsfUnmapFolder, log it before deallocating the memory. */
468 Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.utf16));
469 bool fSame = g_FolderMapping[i].pMapName == pMapName;
470
471 RTStrFree(g_FolderMapping[i].pszFolderName);
472 RTMemFree(g_FolderMapping[i].pMapName);
473 RTMemFree(g_FolderMapping[i].pAutoMountPoint);
474 g_FolderMapping[i].pszFolderName = NULL;
475 g_FolderMapping[i].pMapName = NULL;
476 g_FolderMapping[i].pAutoMountPoint = NULL;
477 g_FolderMapping[i].fValid = false;
478 vbsfRootHandleRemove(i);
479 vbsfMappingsWakeupAllWaiters();
480 if (rc == VERR_FILE_NOT_FOUND)
481 rc = VINF_SUCCESS;
482 if (fSame)
483 break;
484 }
485 }
486 }
487 }
488
489 return rc;
490}
491
492const char* vbsfMappingsQueryHostRoot(SHFLROOT root)
493{
494 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
495 AssertReturn(pFolderMapping, NULL);
496 if (pFolderMapping->fMissing)
497 return NULL;
498 return pFolderMapping->pszFolderName;
499}
500
501int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen)
502{
503 MAPPING *pFolderMapping = vbsfMappingGetByRoot(hRoot);
504 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
505 if (pFolderMapping->fMissing)
506 return VERR_NOT_FOUND;
507 if ( pFolderMapping->pszFolderName == NULL
508 || pFolderMapping->pszFolderName[0] == 0)
509 return VERR_NOT_FOUND;
510 *ppszRoot = pFolderMapping->pszFolderName;
511 *pcbRootLen = (uint32_t)strlen(pFolderMapping->pszFolderName);
512 return VINF_SUCCESS;
513}
514
515bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root)
516{
517 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
518 AssertReturn(pFolderMapping, false);
519 return pFolderMapping->fGuestCaseSensitive;
520}
521
522bool vbsfIsHostMappingCaseSensitive(SHFLROOT root)
523{
524 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
525 AssertReturn(pFolderMapping, false);
526 return pFolderMapping->fHostCaseSensitive;
527}
528
529#ifdef UNITTEST
530/** Unit test the SHFL_FN_QUERY_MAPPINGS API. Located here as a form of API
531 * documentation (or should it better be inline in include/VBox/shflsvc.h?) */
532void testMappingsQuery(RTTEST hTest)
533{
534 /* The API should return all mappings if we provide enough buffers. */
535 testMappingsQuerySimple(hTest);
536 /* If we provide too few buffers that should be signalled correctly. */
537 testMappingsQueryTooFewBuffers(hTest);
538 /* The SHFL_MF_AUTOMOUNT flag means return only auto-mounted mappings. */
539 testMappingsQueryAutoMount(hTest);
540 /* The mappings return array must have numberOfMappings entries. */
541 testMappingsQueryArrayWrongSize(hTest);
542}
543#endif
544/**
545 * @note If pMappings / *pcMappings is smaller than the actual amount of
546 * mappings that *could* have been returned *pcMappings contains the
547 * required buffer size so that the caller can retry the operation if
548 * wanted.
549 */
550int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings)
551{
552 LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n",
553 pClient, pMappings, pcMappings, *pcMappings));
554
555 uint32_t const cMaxMappings = *pcMappings;
556 uint32_t idx = 0;
557 for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++)
558 {
559 MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
560 if ( pFolderMapping != NULL
561 && pFolderMapping->fValid
562 && ( !fOnlyAutoMounts
563 || (pFolderMapping->fAutoMount && !pFolderMapping->fPlaceholder)) )
564 {
565 if (idx < cMaxMappings)
566 {
567 pMappings[idx].u32Status = SHFL_MS_NEW;
568 pMappings[idx].root = i;
569 }
570 idx++;
571 }
572 }
573
574 /* Return actual number of mappings, regardless whether the handed in
575 * mapping buffer was big enough. */
576 /** @todo r=bird: This is non-standard interface behaviour. We return
577 * VERR_BUFFER_OVERFLOW or at least a VINF_BUFFER_OVERFLOW here.
578 *
579 * Guess this goes well along with ORing SHFL_MF_AUTOMOUNT into
580 * pClient->fu32Flags rather than passing it as fOnlyAutoMounts...
581 * Not amused by this. */
582 *pcMappings = idx;
583
584 RT_NOREF_PV(pClient);
585 LogFlow(("vbsfMappingsQuery: returns VINF_SUCCESS (idx=%u, cMaxMappings=%u)\n", idx, cMaxMappings));
586 return VINF_SUCCESS;
587}
588
589#ifdef UNITTEST
590/** Unit test the SHFL_FN_QUERY_MAP_NAME API. Located here as a form of API
591 * documentation. */
592void testMappingsQueryName(RTTEST hTest)
593{
594 /* If we query an valid mapping it should be returned. */
595 testMappingsQueryNameValid(hTest);
596 /* If we query an invalid mapping that should be signalled. */
597 testMappingsQueryNameInvalid(hTest);
598 /* If we pass in a bad string buffer that should be detected. */
599 testMappingsQueryNameBadBuffer(hTest);
600}
601#endif
602int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString)
603{
604 LogFlow(("vbsfMappingsQueryName: pClient = %p, root = %d, *pString = %p\n", pClient, root, pString));
605
606 int rc;
607 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
608 if (pFolderMapping)
609 {
610 if (pFolderMapping->fValid)
611 {
612 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
613 rc = ShflStringCopyUtf16BufAsUtf8(pString, pFolderMapping->pMapName);
614 else
615 {
616 /* Not using ShlfStringCopy here as behaviour shouldn't change... */
617 if (pString->u16Size < pFolderMapping->pMapName->u16Size)
618 {
619 Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
620 pString->u16Size, pFolderMapping->pMapName->u16Size));
621 rc = VERR_INVALID_PARAMETER;
622 }
623 else
624 {
625 pString->u16Length = pFolderMapping->pMapName->u16Length;
626 memcpy(pString->String.utf16, pFolderMapping->pMapName->String.utf16,
627 pFolderMapping->pMapName->u16Size);
628 rc = VINF_SUCCESS;
629 }
630 }
631 }
632 else
633 rc = VERR_FILE_NOT_FOUND;
634 }
635 else
636 rc = VERR_INVALID_PARAMETER;
637
638 LogFlow(("vbsfMappingsQueryName returns rc = %Rrc\n", rc));
639 return rc;
640}
641
642/** Queries fWritable flag for the given root. Returns error if the root is not accessible.
643 */
644int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable)
645{
646 RT_NOREF1(pClient);
647 int rc = VINF_SUCCESS;
648
649 LogFlow(("vbsfMappingsQueryWritable: pClient = %p, root = %d\n", pClient, root));
650
651 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
652 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
653
654 if ( pFolderMapping->fValid
655 && !pFolderMapping->fMissing)
656 *fWritable = pFolderMapping->fWritable;
657 else
658 rc = VERR_FILE_NOT_FOUND;
659
660 LogFlow(("vbsfMappingsQueryWritable returns rc = %Rrc\n", rc));
661
662 return rc;
663}
664
665int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount)
666{
667 RT_NOREF1(pClient);
668 int rc = VINF_SUCCESS;
669
670 LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
671
672 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
673 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
674
675 if (pFolderMapping->fValid)
676 *fAutoMount = pFolderMapping->fAutoMount;
677 else
678 rc = VERR_FILE_NOT_FOUND;
679
680 LogFlow(("vbsfMappingsQueryAutoMount returns rc = %Rrc\n", rc));
681
682 return rc;
683}
684
685int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate)
686{
687 RT_NOREF1(pClient);
688 int rc = VINF_SUCCESS;
689
690 LogFlow(("vbsfMappingsQuerySymlinksCreate: pClient = %p, root = %d\n", pClient, root));
691
692 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
693 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
694
695 if (pFolderMapping->fValid)
696 *fSymlinksCreate = pFolderMapping->fSymlinksCreate;
697 else
698 rc = VERR_FILE_NOT_FOUND;
699
700 LogFlow(("vbsfMappingsQuerySymlinksCreate returns rc = %Rrc\n", rc));
701
702 return rc;
703}
704
705int vbsfMappingsQuerySymlinkPolicy(PSHFLCLIENTDATA pClient, SHFLROOT root, SymlinkPolicy_T *enmSymlinkPolicy)
706{
707 RT_NOREF1(pClient);
708 int rc = VINF_SUCCESS;
709
710 LogFlow(("vbsfMappingsQuerySymlinkPolicy: pClient = %p, root = %d\n", pClient, root));
711
712 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
713 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
714
715 if (pFolderMapping->fValid)
716 *enmSymlinkPolicy = pFolderMapping->enmSymlinkPolicy;
717 else
718 rc = VERR_FILE_NOT_FOUND;
719
720 LogFlow(("vbsfMappingsQuerySymlinkPolicy returns rc = %Rrc\n", rc));
721
722 return rc;
723}
724
725/**
726 * Implements SHFL_FN_QUERY_MAP_INFO.
727 * @since VBox 6.0
728 */
729int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf,
730 uint64_t *pfFlags, uint32_t *puVersion)
731{
732 LogFlow(("vbsfMappingsQueryInfo: pClient=%p root=%d\n", pClient, root));
733
734 /* Resolve the root handle. */
735 int rc;
736 PMAPPING pFolderMapping = vbsfMappingGetByRoot(root);
737 if (pFolderMapping)
738 {
739 if (pFolderMapping->fValid)
740 {
741 /*
742 * Produce the output.
743 */
744 *puVersion = g_auRootHandleVersions[root];
745
746 *pfFlags = 0;
747 if (pFolderMapping->fWritable)
748 *pfFlags |= SHFL_MIF_WRITABLE;
749 if (pFolderMapping->fAutoMount)
750 *pfFlags |= SHFL_MIF_AUTO_MOUNT;
751 if (pFolderMapping->fHostCaseSensitive)
752 *pfFlags |= SHFL_MIF_HOST_ICASE;
753 if (pFolderMapping->fGuestCaseSensitive)
754 *pfFlags |= SHFL_MIF_GUEST_ICASE;
755 if (pFolderMapping->fSymlinksCreate)
756 *pfFlags |= SHFL_MIF_SYMLINK_CREATION;
757
758 int rc2;
759 if (pClient->fu32Flags & SHFL_CF_UTF8)
760 {
761 rc = ShflStringCopyUtf16BufAsUtf8(pNameBuf, pFolderMapping->pMapName);
762 rc2 = ShflStringCopyUtf16BufAsUtf8(pMntPtBuf, pFolderMapping->pAutoMountPoint);
763 }
764 else
765 {
766 rc = ShflStringCopy(pNameBuf, pFolderMapping->pMapName, sizeof(RTUTF16));
767 rc2 = ShflStringCopy(pMntPtBuf, pFolderMapping->pAutoMountPoint, sizeof(RTUTF16));
768 }
769 if (RT_SUCCESS(rc))
770 rc = rc2;
771 }
772 else
773 rc = VERR_FILE_NOT_FOUND;
774 }
775 else
776 rc = VERR_INVALID_PARAMETER;
777 LogFlow(("vbsfMappingsQueryInfo: returns %Rrc\n", rc));
778 return rc;
779}
780
781
782
783#ifdef UNITTEST
784/** Unit test the SHFL_FN_MAP_FOLDER API. Located here as a form of API
785 * documentation. */
786void testMapFolder(RTTEST hTest)
787{
788 /* If we try to map a valid name we should get the root. */
789 testMapFolderValid(hTest);
790 /* If we try to map a valid name we should get VERR_FILE_NOT_FOUND. */
791 testMapFolderInvalid(hTest);
792 /* If we map a folder twice we can unmap it twice.
793 * Currently unmapping too often is only asserted but not signalled. */
794 testMapFolderTwice(hTest);
795 /* The delimiter should be converted in e.g. file delete operations. */
796 testMapFolderDelimiter(hTest);
797 /* Test case sensitive mapping by opening a file with the wrong case. */
798 testMapFolderCaseSensitive(hTest);
799 /* Test case insensitive mapping by opening a file with the wrong case. */
800 testMapFolderCaseInsensitive(hTest);
801 /* If the number or types of parameters are wrong the API should fail. */
802 testMapFolderBadParameters(hTest);
803}
804#endif
805int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName,
806 RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
807{
808 MAPPING *pFolderMapping = NULL;
809
810 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
811 Log(("vbsfMapFolder %s\n", pszMapName->String.utf8));
812 else
813 Log(("vbsfMapFolder %ls\n", pszMapName->String.utf16));
814
815 AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\',
816 ("Invalid path delimiter: %#x\n", wcDelimiter),
817 VERR_INVALID_PARAMETER);
818 if (pClient->PathDelimiter == 0)
819 {
820 pClient->PathDelimiter = wcDelimiter;
821 }
822 else
823 {
824 AssertMsgReturn(wcDelimiter == pClient->PathDelimiter,
825 ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter),
826 VERR_INVALID_PARAMETER);
827 }
828
829 SHFLROOT RootTmp;
830 if (!pRoot)
831 pRoot = &RootTmp;
832 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
833 {
834 int rc;
835 PRTUTF16 utf16Name;
836
837 rc = RTStrToUtf16((const char *) pszMapName->String.utf8, &utf16Name);
838 if (RT_FAILURE (rc))
839 return rc;
840
841 pFolderMapping = vbsfMappingGetByName(utf16Name, pRoot);
842 RTUtf16Free(utf16Name);
843 }
844 else
845 {
846 pFolderMapping = vbsfMappingGetByName(pszMapName->String.utf16, pRoot);
847 }
848
849 if (!pFolderMapping)
850 {
851 return VERR_FILE_NOT_FOUND;
852 }
853
854 /*
855 * Check for reference count overflows and settings compatibility.
856 * For paranoid reasons, we don't allow modifying the case sensitivity
857 * setting while there are other mappings of a folder.
858 */
859 AssertLogRelReturn(*pRoot < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
860 AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[*pRoot] < _32K, VERR_TOO_MANY_OPENS);
861 ASSERT_GUEST_LOGREL_MSG_RETURN( pFolderMapping->cMappings == 0
862 || pFolderMapping->fGuestCaseSensitive == fCaseSensitive,
863 ("Incompatible case sensitivity setting: %s: %u mappings, %ssenitive, requested %ssenitive!\n",
864 pFolderMapping->pszFolderName, pFolderMapping->cMappings,
865 pFolderMapping->fGuestCaseSensitive ? "" : "in", fCaseSensitive ? "" : "in"),
866 VERR_INCOMPATIBLE_CONFIG);
867
868 /*
869 * Go ahead and map it.
870 */
871 if (pClient->fHasMappingCounts)
872 pClient->acMappings[*pRoot] += 1;
873 pFolderMapping->cMappings++;
874 pFolderMapping->fGuestCaseSensitive = fCaseSensitive;
875 Log(("vbsfMmapFolder (cMappings=%u, acMappings[%u]=%u)\n", pFolderMapping->cMappings, *pRoot, pClient->acMappings[*pRoot]));
876 return VINF_SUCCESS;
877}
878
879#ifdef UNITTEST
880/** Unit test the SHFL_FN_UNMAP_FOLDER API. Located here as a form of API
881 * documentation. */
882void testUnmapFolder(RTTEST hTest)
883{
884 /* Unmapping a mapped folder should succeed.
885 * If the folder is not mapped this is only asserted, not signalled. */
886 testUnmapFolderValid(hTest);
887 /* Unmapping a non-existant root should fail. */
888 testUnmapFolderInvalid(hTest);
889 /* If the number or types of parameters are wrong the API should fail. */
890 testUnmapFolderBadParameters(hTest);
891}
892#endif
893int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root)
894{
895 RT_NOREF1(pClient);
896 int rc = VINF_SUCCESS;
897
898 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
899 if (pFolderMapping == NULL)
900 {
901 AssertFailed();
902 return VERR_FILE_NOT_FOUND;
903 }
904 Assert(pFolderMapping->fValid && pFolderMapping->cMappings > 0);
905
906 AssertLogRelReturn(root < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR);
907 AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[root] > 0, VERR_INVALID_HANDLE);
908
909 if (pClient->fHasMappingCounts)
910 pClient->acMappings[root] -= 1;
911
912 if (pFolderMapping->cMappings > 0)
913 pFolderMapping->cMappings--;
914
915 uint32_t const cMappings = pFolderMapping->cMappings;
916 if ( cMappings == 0
917 && pFolderMapping->fPlaceholder)
918 {
919 /* Automatically remove, it is not used by the guest anymore. */
920 Assert(pFolderMapping->fMissing);
921 LogRel2(("SharedFolders: unmapping placeholder '%ls' -> '%s'\n",
922 pFolderMapping->pMapName->String.utf16, pFolderMapping->pszFolderName));
923 vbsfMappingsRemove(pFolderMapping->pMapName);
924 }
925
926 Log(("vbsfUnmapFolder (cMappings=%u, acMappings[%u]=%u)\n", cMappings, root, pClient->acMappings[root]));
927 return rc;
928}
929
930/**
931 * SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES implementation.
932 *
933 * @returns VBox status code.
934 * @retval VINF_SUCCESS on change.
935 * @retval VINF_TRY_AGAIN on resume.
936 * @retval VINF_HGCM_ASYNC_EXECUTE if waiting.
937 * @retval VERR_CANCELLED if cancelled.
938 * @retval VERR_OUT_OF_RESOURCES if there are too many pending waits.
939 *
940 * @param pClient The calling client.
941 * @param hCall The call handle.
942 * @param pParm The parameter (32-bit).
943 * @param fRestored Set if this is a call restored & resubmitted from saved
944 * state.
945 * @since VBox 6.0
946 */
947int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored)
948{
949 /*
950 * Return immediately if the fodler mappings have changed since last call
951 * or if we got restored from saved state (adding of global folders, etc).
952 */
953 uint32_t uCurVersion = g_uFolderMappingsVersion;
954 if ( pParm->u.uint32 != uCurVersion
955 || fRestored
956 || (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) )
957 {
958 int rc = VINF_SUCCESS;
959 if (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT)
960 {
961 pClient->fu32Flags &= ~SHFL_CF_CANCEL_NEXT_WAIT;
962 rc = VERR_CANCELLED;
963 }
964 else if (fRestored)
965 {
966 rc = VINF_TRY_AGAIN;
967 if (pParm->u.uint32 == uCurVersion)
968 uCurVersion = uCurVersion != UINT32_C(0x55555555) ? UINT32_C(0x55555555) : UINT32_C(0x99999999);
969 }
970 Log(("vbsfMappingsWaitForChanges: Version %#x -> %#x, returning %Rrc immediately.\n", pParm->u.uint32, uCurVersion, rc));
971 pParm->u.uint32 = uCurVersion;
972 return rc;
973 }
974
975 /*
976 * Setup a wait if we can.
977 */
978 if (g_cMappingChangeWaiters < 64)
979 {
980 PSHFLMAPPINGSWAIT pWait = (PSHFLMAPPINGSWAIT)RTMemAlloc(sizeof(*pWait));
981 if (pWait)
982 {
983 pWait->pClient = pClient;
984 pWait->hCall = hCall;
985 pWait->pParm = pParm;
986
987 RTListAppend(&g_MappingsChangeWaiters, &pWait->ListEntry);
988 g_cMappingChangeWaiters += 1;
989 return VINF_HGCM_ASYNC_EXECUTE;
990 }
991 return VERR_NO_MEMORY;
992 }
993 LogRelMax(32, ("vbsfMappingsWaitForChanges: Too many threads waiting for changes!\n"));
994 return VERR_OUT_OF_RESOURCES;
995}
996
997/**
998 * SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS implementation.
999 *
1000 * @returns VINF_SUCCESS
1001 * @param pClient The calling client to cancel all waits for.
1002 * @since VBox 6.0
1003 */
1004int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient)
1005{
1006 uint32_t const uCurVersion = g_uFolderMappingsVersion;
1007
1008 PSHFLMAPPINGSWAIT pCur, pNext;
1009 RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
1010 {
1011 if (pCur->pClient == pClient)
1012 {
1013 RTListNodeRemove(&pCur->ListEntry);
1014 pCur->pParm->u.uint32 = uCurVersion;
1015 g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
1016 RTMemFree(pCur);
1017 }
1018 }
1019
1020 /* Set a flag to make sure the next SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES doesn't block.
1021 This should help deal with races between this call and a thread about to do a wait. */
1022 pClient->fu32Flags |= SHFL_CF_CANCEL_NEXT_WAIT;
1023
1024 return VINF_SUCCESS;
1025}
1026
1027/**
1028 * Wakes up all clients waiting on
1029 */
1030static void vbsfMappingsWakeupAllWaiters(void)
1031{
1032 uint32_t const uCurVersion = ++g_uFolderMappingsVersion;
1033
1034 PSHFLMAPPINGSWAIT pCur, pNext;
1035 RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry)
1036 {
1037 RTListNodeRemove(&pCur->ListEntry);
1038 pCur->pParm->u.uint32 = uCurVersion;
1039 g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED);
1040 RTMemFree(pCur);
1041 }
1042}
1043
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