VirtualBox

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

Last change on this file since 51250 was 51250, checked in by vboxsync, 11 years ago

shared folders: several fixes, cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/** @file
2 * Shared Folders: Mappings support.
3 */
4
5/*
6 * Copyright (C) 2006-2012 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#ifdef UNITTEST
18# include "testcase/tstSharedFolderService.h"
19#endif
20
21#include "mappings.h"
22#include <iprt/alloc.h>
23#include <iprt/assert.h>
24#include <iprt/string.h>
25
26#ifdef UNITTEST
27# include "teststubs.h"
28#endif
29
30/* Shared folders order in the saved state and in the FolderMapping can differ.
31 * So a translation array of root handle is needed.
32 */
33
34static MAPPING FolderMapping[SHFL_MAX_MAPPINGS];
35static SHFLROOT aIndexFromRoot[SHFL_MAX_MAPPINGS];
36
37void vbsfMappingInit(void)
38{
39 unsigned root;
40
41 for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
42 {
43 aIndexFromRoot[root] = SHFL_ROOT_NIL;
44 }
45}
46
47int vbsfMappingLoaded(const PMAPPING pLoadedMapping, SHFLROOT root)
48{
49 /* Mapping loaded from the saved state with the index. Which means
50 * the guest uses the iMapping as root handle for this folder.
51 * Check whether there is the same mapping in FolderMapping and
52 * update the aIndexFromRoot.
53 *
54 * Also update the mapping properties, which were lost: cMappings.
55 */
56 if (root >= SHFL_MAX_MAPPINGS)
57 {
58 return VERR_INVALID_PARAMETER;
59 }
60
61 SHFLROOT i;
62 for (i = 0; i < RT_ELEMENTS(FolderMapping); i++)
63 {
64 MAPPING *pMapping = &FolderMapping[i];
65
66 /* Equal? */
67 if ( pLoadedMapping->fValid == pMapping->fValid
68 && ShflStringSizeOfBuffer(pLoadedMapping->pMapName) == ShflStringSizeOfBuffer(pMapping->pMapName)
69 && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0)
70 {
71 /* Actual index is i. */
72 aIndexFromRoot[root] = i;
73
74 /* Update the mapping properties. */
75 pMapping->cMappings = pLoadedMapping->cMappings;
76
77 return VINF_SUCCESS;
78 }
79 }
80
81 return VERR_INVALID_PARAMETER;
82}
83
84MAPPING *vbsfMappingGetByRoot(SHFLROOT root)
85{
86 if (root < RT_ELEMENTS(aIndexFromRoot))
87 {
88 SHFLROOT iMapping = aIndexFromRoot[root];
89
90 if ( iMapping != SHFL_ROOT_NIL
91 && iMapping < RT_ELEMENTS(FolderMapping))
92 {
93 return &FolderMapping[iMapping];
94 }
95 }
96
97 return NULL;
98}
99
100static SHFLROOT vbsfMappingGetRootFromIndex(SHFLROOT iMapping)
101{
102 unsigned root;
103
104 for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
105 {
106 if (iMapping == aIndexFromRoot[root])
107 {
108 return root;
109 }
110 }
111
112 return SHFL_ROOT_NIL;
113}
114
115static MAPPING *vbsfMappingGetByName (PRTUTF16 pwszName, SHFLROOT *pRoot)
116{
117 unsigned i;
118
119 for (i=0; i<SHFL_MAX_MAPPINGS; i++)
120 {
121 if (FolderMapping[i].fValid == true)
122 {
123 if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pwszName))
124 {
125 SHFLROOT root = vbsfMappingGetRootFromIndex(i);
126
127 if (root != SHFL_ROOT_NIL)
128 {
129 if (pRoot)
130 {
131 *pRoot = root;
132 }
133 return &FolderMapping[i];
134 }
135 else
136 {
137 AssertFailed();
138 }
139 }
140 }
141 }
142
143 return NULL;
144}
145
146static void vbsfRootHandleAdd(SHFLROOT iMapping)
147{
148 unsigned root;
149
150 for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
151 {
152 if (aIndexFromRoot[root] == SHFL_ROOT_NIL)
153 {
154 aIndexFromRoot[root] = iMapping;
155 return;
156 }
157 }
158
159 AssertFailed();
160}
161
162static void vbsfRootHandleRemove(SHFLROOT iMapping)
163{
164 unsigned root;
165
166 for (root = 0; root < RT_ELEMENTS(aIndexFromRoot); root++)
167 {
168 if (aIndexFromRoot[root] == iMapping)
169 {
170 aIndexFromRoot[root] = SHFL_ROOT_NIL;
171 return;
172 }
173 }
174
175 AssertFailed();
176}
177
178
179
180#ifdef UNITTEST
181/** Unit test the SHFL_FN_ADD_MAPPING API. Located here as a form of API
182 * documentation. */
183void testMappingsAdd(RTTEST hTest)
184{
185 /* If the number or types of parameters are wrong the API should fail. */
186 testMappingsAddBadParameters(hTest);
187 /* Add tests as required... */
188}
189#endif
190/*
191 * We are always executed from one specific HGCM thread. So thread safe.
192 */
193int vbsfMappingsAdd(PSHFLSTRING pFolderName, PSHFLSTRING pMapName,
194 bool fWritable, bool fAutoMount, bool fSymlinksCreate, bool fMissing)
195{
196 unsigned i;
197
198 Assert(pFolderName && pMapName);
199
200 Log(("vbsfMappingsAdd %ls\n", pMapName->String.ucs2));
201
202 /* check for duplicates */
203 for (i=0; i<SHFL_MAX_MAPPINGS; i++)
204 {
205 if (FolderMapping[i].fValid == true)
206 {
207 if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
208 {
209 AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2));
210 return VERR_ALREADY_EXISTS;
211 }
212 }
213 }
214
215 for (i=0; i<SHFL_MAX_MAPPINGS; i++)
216 {
217 if (FolderMapping[i].fValid == false)
218 {
219 int rc = RTUtf16ToUtf8(pFolderName->String.ucs2, &FolderMapping[i].pszFolderName);
220 AssertRCReturn(rc, rc);
221
222 FolderMapping[i].pMapName = (PSHFLSTRING)RTMemAlloc(ShflStringSizeOfBuffer(pMapName));
223 if (!FolderMapping[i].pMapName)
224 {
225 RTStrFree(FolderMapping[i].pszFolderName);
226 AssertFailed();
227 return VERR_NO_MEMORY;
228 }
229
230 FolderMapping[i].pMapName->u16Length = pMapName->u16Length;
231 FolderMapping[i].pMapName->u16Size = pMapName->u16Size;
232 memcpy(FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2, pMapName->u16Size);
233
234 FolderMapping[i].fValid = true;
235 FolderMapping[i].cMappings = 0;
236 FolderMapping[i].fWritable = fWritable;
237 FolderMapping[i].fAutoMount = fAutoMount;
238 FolderMapping[i].fSymlinksCreate = fSymlinksCreate;
239 FolderMapping[i].fMissing = fMissing;
240
241 /* Check if the host file system is case sensitive */
242 RTFSPROPERTIES prop;
243 char *pszAsciiRoot;
244
245 rc = RTStrUtf8ToCurrentCP(&pszAsciiRoot, FolderMapping[i].pszFolderName);
246 if (RT_SUCCESS(rc))
247 {
248 rc = RTFsQueryProperties(pszAsciiRoot, &prop);
249 AssertRC(rc);
250 RTStrFree(pszAsciiRoot);
251 }
252
253 FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false;
254 vbsfRootHandleAdd(i);
255 break;
256 }
257 }
258 if (i == SHFL_MAX_MAPPINGS)
259 {
260 AssertMsgFailed(("vbsfMappingsAdd: no more room to add mapping %ls to %ls!!\n", pFolderName->String.ucs2, pMapName->String.ucs2));
261 return VERR_TOO_MUCH_DATA;
262 }
263
264 Log(("vbsfMappingsAdd: added mapping %ls to %ls\n", pFolderName->String.ucs2, pMapName->String.ucs2));
265 return VINF_SUCCESS;
266}
267
268#ifdef UNITTEST
269/** Unit test the SHFL_FN_REMOVE_MAPPING API. Located here as a form of API
270 * documentation. */
271void testMappingsRemove(RTTEST hTest)
272{
273 /* If the number or types of parameters are wrong the API should fail. */
274 testMappingsRemoveBadParameters(hTest);
275 /* Add tests as required... */
276}
277#endif
278int vbsfMappingsRemove(PSHFLSTRING pMapName)
279{
280 unsigned i;
281
282 Assert(pMapName);
283
284 Log(("vbsfMappingsRemove %ls\n", pMapName->String.ucs2));
285 for (i=0; i<SHFL_MAX_MAPPINGS; i++)
286 {
287 if (FolderMapping[i].fValid == true)
288 {
289 if (!RTUtf16LocaleICmp(FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2))
290 {
291 if (FolderMapping[i].cMappings != 0)
292 {
293 Log(("vbsfMappingsRemove: trying to remove active share %ls\n", pMapName->String.ucs2));
294 return VERR_PERMISSION_DENIED;
295 }
296
297 RTStrFree(FolderMapping[i].pszFolderName);
298 RTMemFree(FolderMapping[i].pMapName);
299 FolderMapping[i].pszFolderName = NULL;
300 FolderMapping[i].pMapName = NULL;
301 FolderMapping[i].fValid = false;
302 vbsfRootHandleRemove(i);
303 break;
304 }
305 }
306 }
307
308 if (i == SHFL_MAX_MAPPINGS)
309 {
310 AssertMsgFailed(("vbsfMappingsRemove: mapping %ls not found!!!!\n", pMapName->String.ucs2));
311 return VERR_FILE_NOT_FOUND;
312 }
313 Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2));
314 return VINF_SUCCESS;
315}
316
317const char* vbsfMappingsQueryHostRoot(SHFLROOT root)
318{
319 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
320 AssertReturn(pFolderMapping, NULL);
321 if (pFolderMapping->fMissing)
322 return NULL;
323 return pFolderMapping->pszFolderName;
324}
325
326bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root)
327{
328 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
329 AssertReturn(pFolderMapping, false);
330 return pFolderMapping->fGuestCaseSensitive;
331}
332
333bool vbsfIsHostMappingCaseSensitive(SHFLROOT root)
334{
335 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
336 AssertReturn(pFolderMapping, false);
337 return pFolderMapping->fHostCaseSensitive;
338}
339
340#ifdef UNITTEST
341/** Unit test the SHFL_FN_QUERY_MAPPINGS API. Located here as a form of API
342 * documentation (or should it better be inline in include/VBox/shflsvc.h?) */
343void testMappingsQuery(RTTEST hTest)
344{
345 /* The API should return all mappings if we provide enough buffers. */
346 testMappingsQuerySimple(hTest);
347 /* If we provide too few buffers that should be signalled correctly. */
348 testMappingsQueryTooFewBuffers(hTest);
349 /* The SHFL_MF_AUTOMOUNT flag means return only auto-mounted mappings. */
350 testMappingsQueryAutoMount(hTest);
351 /* The mappings return array must have numberOfMappings entries. */
352 testMappingsQueryArrayWrongSize(hTest);
353}
354#endif
355/**
356 * Note: If pMappings / *pcMappings is smaller than the actual amount of mappings
357 * that *could* have been returned *pcMappings contains the required buffer size
358 * so that the caller can retry the operation if wanted.
359 */
360int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, PSHFLMAPPING pMappings, uint32_t *pcMappings)
361{
362 int rc = VINF_SUCCESS;
363
364 uint32_t cMappings = 0; /* Will contain actual valid mappings. */
365 uint32_t idx = 0; /* Current index in mappings buffer. */
366
367 LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n",
368 pClient, pMappings, pcMappings, *pcMappings));
369
370 for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++)
371 {
372 MAPPING *pFolderMapping = vbsfMappingGetByRoot(i);
373 if ( pFolderMapping != NULL
374 && pFolderMapping->fValid == true)
375 {
376 if (idx < *pcMappings)
377 {
378 /* Skip mappings which are not marked for auto-mounting if
379 * the SHFL_MF_AUTOMOUNT flag ist set. */
380 if ( (pClient->fu32Flags & SHFL_MF_AUTOMOUNT)
381 && !pFolderMapping->fAutoMount)
382 continue;
383
384 pMappings[idx].u32Status = SHFL_MS_NEW;
385 pMappings[idx].root = i;
386 idx++;
387 }
388 cMappings++;
389 }
390 }
391
392 /* Return actual number of mappings, regardless whether the handed in
393 * mapping buffer was big enough. */
394 *pcMappings = cMappings;
395
396 LogFlow(("vbsfMappingsQuery: return rc = %Rrc\n", rc));
397 return rc;
398}
399
400#ifdef UNITTEST
401/** Unit test the SHFL_FN_QUERY_MAP_NAME API. Located here as a form of API
402 * documentation. */
403void testMappingsQueryName(RTTEST hTest)
404{
405 /* If we query an valid mapping it should be returned. */
406 testMappingsQueryNameValid(hTest);
407 /* If we query an invalid mapping that should be signalled. */
408 testMappingsQueryNameInvalid(hTest);
409 /* If we pass in a bad string buffer that should be detected. */
410 testMappingsQueryNameBadBuffer(hTest);
411}
412#endif
413int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString)
414{
415 int rc = VINF_SUCCESS;
416
417 LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n",
418 pClient, root, pString));
419
420 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
421 if (pFolderMapping == NULL)
422 {
423 return VERR_INVALID_PARAMETER;
424 }
425
426 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
427 {
428 /* Not implemented. */
429 AssertFailed();
430 return VERR_INVALID_PARAMETER;
431 }
432
433 if (pFolderMapping->fValid == true)
434 {
435 if (pString->u16Size < pFolderMapping->pMapName->u16Size)
436 {
437 Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n",
438 pString->u16Size, pFolderMapping->pMapName->u16Size));
439 rc = VERR_INVALID_PARAMETER;
440 }
441 else
442 {
443 pString->u16Length = pFolderMapping->pMapName->u16Length;
444 memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2,
445 pFolderMapping->pMapName->u16Size);
446 }
447 }
448 else
449 rc = VERR_FILE_NOT_FOUND;
450
451 LogFlow(("vbsfMappingsQuery:Name return rc = %Rrc\n", rc));
452
453 return rc;
454}
455
456int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable)
457{
458 int rc = VINF_SUCCESS;
459
460 LogFlow(("vbsfMappingsQueryWritable: pClient = %p, root = %d\n", pClient, root));
461
462 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
463 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
464
465 if ( pFolderMapping->fValid
466 && !pFolderMapping->fMissing)
467 *fWritable = pFolderMapping->fWritable;
468 else
469 rc = VERR_FILE_NOT_FOUND;
470
471 LogFlow(("vbsfMappingsQuery:Writable return rc = %Rrc\n", rc));
472
473 return rc;
474}
475
476int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount)
477{
478 int rc = VINF_SUCCESS;
479
480 LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
481
482 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
483 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
484
485 if (pFolderMapping->fValid == true)
486 *fAutoMount = pFolderMapping->fAutoMount;
487 else
488 rc = VERR_FILE_NOT_FOUND;
489
490 LogFlow(("vbsfMappingsQueryAutoMount:Writable return rc = %Rrc\n", rc));
491
492 return rc;
493}
494
495int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate)
496{
497 int rc = VINF_SUCCESS;
498
499 LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root));
500
501 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
502 AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER);
503
504 if (pFolderMapping->fValid == true)
505 *fSymlinksCreate = pFolderMapping->fSymlinksCreate;
506 else
507 rc = VERR_FILE_NOT_FOUND;
508
509 LogFlow(("vbsfMappingsQueryAutoMount:SymlinksCreate return rc = %Rrc\n", rc));
510
511 return rc;
512}
513
514#ifdef UNITTEST
515/** Unit test the SHFL_FN_MAP_FOLDER API. Located here as a form of API
516 * documentation. */
517void testMapFolder(RTTEST hTest)
518{
519 /* If we try to map a valid name we should get the root. */
520 testMapFolderValid(hTest);
521 /* If we try to map a valid name we should get VERR_FILE_NOT_FOUND. */
522 testMapFolderInvalid(hTest);
523 /* If we map a folder twice we can unmap it twice.
524 * Currently unmapping too often is only asserted but not signalled. */
525 testMapFolderTwice(hTest);
526 /* The delimiter should be converted in e.g. file delete operations. */
527 testMapFolderDelimiter(hTest);
528 /* Test case sensitive mapping by opening a file with the wrong case. */
529 testMapFolderCaseSensitive(hTest);
530 /* Test case insensitive mapping by opening a file with the wrong case. */
531 testMapFolderCaseInsensitive(hTest);
532 /* If the number or types of parameters are wrong the API should fail. */
533 testMapFolderBadParameters(hTest);
534}
535#endif
536int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName,
537 RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)
538{
539 MAPPING *pFolderMapping = NULL;
540
541 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
542 {
543 Log(("vbsfMapFolder %s\n", pszMapName->String.utf8));
544 }
545 else
546 {
547 Log(("vbsfMapFolder %ls\n", pszMapName->String.ucs2));
548 }
549
550 AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\',
551 ("Invalid path delimiter: %#x\n", wcDelimiter),
552 VERR_INVALID_PARAMETER);
553 if (pClient->PathDelimiter == 0)
554 {
555 pClient->PathDelimiter = wcDelimiter;
556 }
557 else
558 {
559 AssertMsgReturn(wcDelimiter == pClient->PathDelimiter,
560 ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter),
561 VERR_INVALID_PARAMETER);
562 }
563
564 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
565 {
566 int rc;
567 PRTUTF16 utf16Name;
568
569 rc = RTStrToUtf16 ((const char *) pszMapName->String.utf8, &utf16Name);
570 if (RT_FAILURE (rc))
571 return rc;
572
573 pFolderMapping = vbsfMappingGetByName(utf16Name, pRoot);
574 RTUtf16Free (utf16Name);
575 }
576 else
577 {
578 pFolderMapping = vbsfMappingGetByName(pszMapName->String.ucs2, pRoot);
579 }
580
581 if (!pFolderMapping)
582 {
583 return VERR_FILE_NOT_FOUND;
584 }
585
586 pFolderMapping->cMappings++;
587 Assert(pFolderMapping->cMappings == 1 || pFolderMapping->fGuestCaseSensitive == fCaseSensitive);
588 pFolderMapping->fGuestCaseSensitive = fCaseSensitive;
589 return VINF_SUCCESS;
590}
591
592#ifdef UNITTEST
593/** Unit test the SHFL_FN_UNMAP_FOLDER API. Located here as a form of API
594 * documentation. */
595void testUnmapFolder(RTTEST hTest)
596{
597 /* Unmapping a mapped folder should succeed.
598 * If the folder is not mapped this is only asserted, not signalled. */
599 testUnmapFolderValid(hTest);
600 /* Unmapping a non-existant root should fail. */
601 testUnmapFolderInvalid(hTest);
602 /* If the number or types of parameters are wrong the API should fail. */
603 testUnmapFolderBadParameters(hTest);
604}
605#endif
606int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root)
607{
608 int rc = VINF_SUCCESS;
609
610 MAPPING *pFolderMapping = vbsfMappingGetByRoot(root);
611 if (pFolderMapping == NULL)
612 {
613 AssertFailed();
614 return VERR_FILE_NOT_FOUND;
615 }
616
617 Assert(pFolderMapping->fValid == true && pFolderMapping->cMappings > 0);
618 if (pFolderMapping->cMappings > 0)
619 pFolderMapping->cMappings--;
620
621 Log(("vbsfUnmapFolder\n"));
622 return rc;
623}
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