VirtualBox

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

Last change on this file since 78003 was 77498, checked in by vboxsync, 6 years ago

HostServices/SharedFolders: fixed loading of shared folder mappings to make sure that they are not duplicated when two or more HGCM clients use the same mapping

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