VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedFolders/mappings.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: 37.1 KB
Line 
1/* $Id: mappings.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * Shared Folders Service - Mappings support.
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 "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