VirtualBox

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

Last change on this file since 76585 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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