VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use