VirtualBox

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

Last change on this file since 91503 was 86396, checked in by vboxsync, 4 years ago

SharedFoldersSvc: Must free pAutoMountPoint in vbsfMappingsRemove. bugref:9841

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