VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp

Last change on this file was 100331, checked in by vboxsync, 10 months ago

Additions: Get rid of MAX_MNTOPT_STR and replace with RTSystemGetPageSize() in the shared folders mounting tools, bugref:10476 [Solaris build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.4 KB
Line 
1/* $Id: VBoxServiceAutoMount.cpp 100331 2023-06-29 12:11:23Z vboxsync $ */
2/** @file
3 * VBoxService - Auto-mounting for Shared Folders, only Linux & Solaris atm.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/** @page pg_vgsvc_automount VBoxService - Shared Folder Automounter
30 *
31 * The Shared Folder Automounter subservice mounts shared folders upon request
32 * from the host.
33 *
34 * This retrieves shared folder automount requests from Main via the VMMDev.
35 * The current implemention only does this once, for some inexplicable reason,
36 * so the run-time addition of automounted shared folders are not heeded.
37 *
38 * This subservice is only used on linux and solaris. On Windows the current
39 * thinking is this is better of done from VBoxTray, some one argue that for
40 * drive letter assigned shared folders it would be better to do some magic here
41 * (obviously not involving NDAddConnection).
42 *
43 */
44
45
46/*********************************************************************************************************************************
47* Header Files *
48*********************************************************************************************************************************/
49#include <iprt/assert.h>
50#include <iprt/ctype.h>
51#include <iprt/dir.h>
52#include <iprt/mem.h>
53#include <iprt/path.h>
54#include <iprt/semaphore.h>
55#include <iprt/sort.h>
56#include <iprt/string.h>
57#include <iprt/system.h>
58#include <VBox/err.h>
59#include <VBox/VBoxGuestLib.h>
60#include <VBox/shflsvc.h>
61#include "VBoxServiceInternal.h"
62#include "VBoxServiceUtils.h"
63
64#ifdef RT_OS_WINDOWS
65#elif defined(RT_OS_OS2)
66# define INCL_DOSFILEMGR
67# define INCL_ERRORS
68# define OS2EMX_PLAIN_CHAR
69# include <os2emx.h>
70#else
71# include <alloca.h>
72# include <errno.h>
73# include <grp.h>
74# include <sys/mount.h>
75# ifdef RT_OS_SOLARIS
76# include <sys/mntent.h>
77# include <sys/mnttab.h>
78# include <sys/vfs.h>
79# elif defined(RT_OS_LINUX)
80# include <mntent.h>
81# include <paths.h>
82# include <sys/utsname.h>
83RT_C_DECLS_BEGIN
84# include "../../linux/sharedfolders/vbsfmount.h"
85RT_C_DECLS_END
86# else
87# error "Port me!"
88# endif
89# include <unistd.h>
90#endif
91
92
93
94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
97/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
98 * Default mount directory (unix only).
99 */
100#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
101# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/media"
102#endif
103
104/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
105 * Default mount prefix (unix only).
106 */
107#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
108# define VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX "sf_"
109#endif
110
111#ifndef _PATH_MOUNTED
112# ifdef RT_OS_SOLARIS
113# define _PATH_MOUNTED "/etc/mnttab"
114# else
115# define _PATH_MOUNTED "/etc/mtab"
116# endif
117#endif
118
119/** @def VBOXSERVICE_AUTOMOUNT_MIQF
120 * The drive letter / path mount point flag. */
121#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
122# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_DRIVE_LETTER
123#else
124# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_PATH
125#endif
126
127
128/*********************************************************************************************************************************
129* Structures and Typedefs *
130*********************************************************************************************************************************/
131/**
132 * Automounter mount table entry.
133 *
134 * This holds the information returned by SHFL_FN_QUERY_MAP_INFO and
135 * additional mount state info. We only keep entries for mounted mappings.
136 */
137typedef struct VBSVCAUTOMOUNTERENTRY
138{
139 /** The root ID. */
140 uint32_t idRoot;
141 /** The root ID version. */
142 uint32_t uRootIdVersion;
143 /** Map info flags, SHFL_MIF_XXX. */
144 uint64_t fFlags;
145 /** The shared folder (mapping) name. */
146 char *pszName;
147 /** The configured mount point, NULL if none. */
148 char *pszMountPoint;
149 /** The actual mount point, NULL if not mount. */
150 char *pszActualMountPoint;
151} VBSVCAUTOMOUNTERENTRY;
152/** Pointer to an automounter entry. */
153typedef VBSVCAUTOMOUNTERENTRY *PVBSVCAUTOMOUNTERENTRY;
154
155/** Automounter mount table. */
156typedef struct VBSVCAUTOMOUNTERTABLE
157{
158 /** Current number of entries in the array. */
159 uint32_t cEntries;
160 /** Max number of entries the array can hold w/o growing it. */
161 uint32_t cAllocated;
162 /** Pointer to an array of entry pointers. */
163 PVBSVCAUTOMOUNTERENTRY *papEntries;
164} VBSVCAUTOMOUNTERTABLE;
165/** Pointer to an automounter mount table. */
166typedef VBSVCAUTOMOUNTERTABLE *PVBSVCAUTOMOUNTERTABLE;
167
168
169/*********************************************************************************************************************************
170* Global Variables *
171*********************************************************************************************************************************/
172/** The semaphore we're blocking on. */
173static RTSEMEVENTMULTI g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
174/** The Shared Folders service client ID. */
175static uint32_t g_idClientSharedFolders = 0;
176/** Set if we can wait on changes to the mappings. */
177static bool g_fHostSupportsWaitAndInfoQuery = false;
178
179#ifdef RT_OS_OS2
180/** The attachment tag we use to identify attchments that belongs to us. */
181static char const g_szTag[] = "VBoxAutomounter";
182#elif defined(RT_OS_LINUX)
183/** Tag option value that lets us identify mounts that belongs to us. */
184static char const g_szTag[] = "VBoxAutomounter";
185#elif defined(RT_OS_SOLARIS)
186/** Dummy mount option that lets us identify mounts that belongs to us. */
187static char const g_szTag[] = "VBoxAutomounter";
188#endif
189
190
191
192/**
193 * @interface_method_impl{VBOXSERVICE,pfnInit}
194 */
195static DECLCALLBACK(int) vbsvcAutomounterInit(void)
196{
197 VGSvcVerbose(3, "vbsvcAutomounterInit\n");
198
199 int rc = RTSemEventMultiCreate(&g_hAutoMountEvent);
200 AssertRCReturn(rc, rc);
201
202 rc = VbglR3SharedFolderConnect(&g_idClientSharedFolders);
203 if (RT_SUCCESS(rc))
204 {
205 VGSvcVerbose(3, "vbsvcAutomounterInit: Service Client ID: %#x\n", g_idClientSharedFolders);
206 g_fHostSupportsWaitAndInfoQuery = RT_SUCCESS(VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders));
207 }
208 else
209 {
210 /* If the service was not found, we disable this service without
211 causing VBoxService to fail. */
212 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
213 {
214 VGSvcVerbose(0, "vbsvcAutomounterInit: Shared Folders service is not available\n");
215 rc = VERR_SERVICE_DISABLED;
216 }
217 else
218 VGSvcError("Control: Failed to connect to the Shared Folders service! Error: %Rrc\n", rc);
219 RTSemEventMultiDestroy(g_hAutoMountEvent);
220 g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
221 }
222
223 return rc;
224}
225
226
227#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) /* The old code: */
228
229/**
230 * @todo Integrate into RTFsQueryMountpoint()?
231 */
232static bool vbsvcAutoMountShareIsMountedOld(const char *pszShare, char *pszMountPoint, size_t cbMountPoint)
233{
234 AssertPtrReturn(pszShare, false);
235 AssertPtrReturn(pszMountPoint, false);
236 AssertReturn(cbMountPoint, false);
237
238 bool fMounted = false;
239
240# if defined(RT_OS_SOLARIS)
241 /** @todo What to do if we have a relative path in mtab instead
242 * of an absolute one ("temp" vs. "/media/temp")?
243 * procfs contains the full path but not the actual share name ...
244 * FILE *pFh = setmntent("/proc/mounts", "r+t"); */
245 FILE *pFh = fopen(_PATH_MOUNTED, "r");
246 if (!pFh)
247 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
248 else
249 {
250 mnttab mntTab;
251 while ((getmntent(pFh, &mntTab)))
252 {
253 if (!RTStrICmp(mntTab.mnt_special, pszShare))
254 {
255 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp)
256 ? true : false;
257 break;
258 }
259 }
260 fclose(pFh);
261 }
262# elif defined(RT_OS_LINUX)
263 FILE *pFh = setmntent(_PATH_MOUNTED, "r+t"); /** @todo r=bird: why open it for writing? (the '+') */
264 if (pFh == NULL)
265 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
266 else
267 {
268 mntent *pMntEnt;
269 while ((pMntEnt = getmntent(pFh)))
270 {
271 if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare))
272 {
273 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir)
274 ? true : false;
275 break;
276 }
277 }
278 endmntent(pFh);
279 }
280# else
281# error "PORTME!"
282# endif
283
284 VGSvcVerbose(4, "vbsvcAutoMountShareIsMountedOld: Share '%s' at mount point '%s' = %s\n",
285 pszShare, fMounted ? pszMountPoint : "<None>", fMounted ? "Yes" : "No");
286 return fMounted;
287}
288
289
290/**
291 * Unmounts a shared folder.
292 *
293 * @returns VBox status code
294 * @param pszMountPoint The shared folder mount point.
295 */
296static int vbsvcAutoMountUnmountOld(const char *pszMountPoint)
297{
298 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
299
300 int rc = VINF_SUCCESS;
301 uint8_t uTries = 0;
302 int r;
303 while (uTries++ < 3)
304 {
305 r = umount(pszMountPoint);
306 if (r == 0)
307 break;
308/** @todo r=bird: Why do sleep 5 seconds after the final retry?
309 * May also be a good idea to check for EINVAL or other signs that someone
310 * else have already unmounted the share. */
311 RTThreadSleep(5000); /* Wait a while ... */
312 }
313 if (r == -1) /** @todo r=bird: RTThreadSleep set errno. */
314 rc = RTErrConvertFromErrno(errno);
315 return rc;
316}
317
318
319/**
320 * Prepares a mount point (create it, set group and mode).
321 *
322 * @returns VBox status code
323 * @param pszMountPoint The mount point.
324 * @param pszShareName Unused.
325 * @param gidGroup The group ID.
326 */
327static int vbsvcAutoMountPrepareMountPointOld(const char *pszMountPoint, const char *pszShareName, RTGID gidGroup)
328{
329 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
330 AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER);
331
332 /** @todo r=bird: There is no reason why gidGroup should have write access?
333 * Seriously, what kind of non-sense is this? */
334
335 RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */
336 int rc = RTDirCreateFullPath(pszMountPoint, fMode);
337 if (RT_SUCCESS(rc))
338 {
339 rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, gidGroup, RTPATH_F_ON_LINK);
340 if (RT_SUCCESS(rc))
341 {
342 rc = RTPathSetMode(pszMountPoint, fMode);
343 if (RT_FAILURE(rc))
344 {
345 if (rc == VERR_WRITE_PROTECT)
346 {
347 VGSvcVerbose(3, "vbsvcAutoMountPrepareMountPointOld: Mount directory '%s' already is used/mounted\n",
348 pszMountPoint);
349 rc = VINF_SUCCESS;
350 }
351 else
352 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set mode %RTfmode for mount directory '%s', rc = %Rrc\n",
353 fMode, pszMountPoint, rc);
354 }
355 }
356 else
357 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set permissions for mount directory '%s', rc = %Rrc\n",
358 pszMountPoint, rc);
359 }
360 else
361 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not create mount directory '%s' with mode %RTfmode, rc = %Rrc\n",
362 pszMountPoint, fMode, rc);
363 return rc;
364}
365
366
367/**
368 * Mounts a shared folder.
369 *
370 * @returns VBox status code reflecting unmount and mount point preparation
371 * results, but not actual mounting
372 *
373 * @param pszShareName The shared folder name.
374 * @param pszMountPoint The mount point.
375 */
376static int vbsvcAutoMountSharedFolderOld(const char *pszShareName, const char *pszMountPoint)
377{
378 /*
379 * Linux and solaris share the same mount structure.
380 */
381 struct group *grp_vboxsf = getgrnam("vboxsf");
382 if (!grp_vboxsf)
383 {
384 VGSvcError("vbsvcAutoMountWorker: Group 'vboxsf' does not exist\n");
385 return VINF_SUCCESS;
386 }
387
388 int rc = vbsvcAutoMountPrepareMountPointOld(pszMountPoint, pszShareName, grp_vboxsf->gr_gid);
389 if (RT_SUCCESS(rc))
390 {
391 uint32_t cbOpts = RTSystemGetPageSize();
392 char *pszOpts = (char *)alloca(cbOpts);
393 RT_BZERO(pszOpts, cbOpts);
394
395# ifdef RT_OS_SOLARIS
396 int const fFlags = MS_OPTIONSTR;
397 RTStrPrintf(pszOpts, cbOpts, "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000", grp_vboxsf->gr_gid);
398 int r = mount(pszShareName,
399 pszMountPoint,
400 fFlags,
401 "vboxfs",
402 NULL, /* char *dataptr */
403 0, /* int datalen */
404 pszOpts,
405 cbOpts);
406 if (r == 0)
407 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
408 else if (errno != EBUSY) /* Share is already mounted? Then skip error msg. */
409 VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s', error = %s\n",
410 pszShareName, pszMountPoint, strerror(errno));
411
412# else /* RT_OS_LINUX */
413 struct utsname uts;
414 AssertStmt(uname(&uts) != -1, strcpy(uts.release, "4.4.0"));
415
416 unsigned long const fFlags = MS_NODEV;
417 ssize_t cchOpts = RTStrPrintf2(pszOpts, cbOpts, "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000",
418 grp_vboxsf->gr_gid);
419 if (cchOpts > 0 && RTStrVersionCompare(uts.release, "2.6.0") < 0)
420 cchOpts = RTStrPrintf2(&pszOpts[cchOpts], cbOpts - cchOpts, ",sf_name=%s", pszShareName);
421 if (cchOpts <= 0)
422 {
423 VGSvcError("vbsvcAutomounterMountIt: pszOpts overflow! %zd (share %s)\n", cchOpts, pszShareName);
424 return VERR_BUFFER_OVERFLOW;
425 }
426
427 int r = mount(pszShareName,
428 pszMountPoint,
429 "vboxsf",
430 fFlags,
431 pszOpts);
432 if (r == 0)
433 {
434 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
435
436 r = vbsfmount_complete(pszShareName, pszMountPoint, fFlags, pszOpts);
437 switch (r)
438 {
439 case 0: /* Success. */
440 errno = 0; /* Clear all errors/warnings. */
441 break;
442 case 1:
443 VGSvcError("vbsvcAutoMountWorker: Could not update mount table (malloc failure)\n");
444 break;
445 case 2:
446 VGSvcError("vbsvcAutoMountWorker: Could not open mount table for update: %s\n", strerror(errno));
447 break;
448 case 3:
449 /* VGSvcError("vbsvcAutoMountWorker: Could not add an entry to the mount table: %s\n", strerror(errno)); */
450 errno = 0;
451 break;
452 default:
453 VGSvcError("vbsvcAutoMountWorker: Unknown error while completing mount operation: %d\n", r);
454 break;
455 }
456 }
457 else /* r == -1, we got some error in errno. */
458 {
459 switch (errno)
460 {
461 /* If we get EINVAL here, the system already has mounted the Shared Folder to another
462 * mount point. */
463 case EINVAL:
464 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' is already mounted!\n", pszShareName);
465 /* Ignore this error! */
466 break;
467 case EBUSY:
468 /* Ignore these errors! */
469 break;
470 default:
471 VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s': %s (%d)\n",
472 pszShareName, pszMountPoint, strerror(errno), errno);
473 rc = RTErrConvertFromErrno(errno);
474 break;
475 }
476 }
477# endif
478 }
479 VGSvcVerbose(3, "vbsvcAutoMountWorker: Mounting returned with rc=%Rrc\n", rc);
480 return rc;
481}
482
483
484/**
485 * Processes shared folder mappings retrieved from the host.
486 *
487 * @returns VBox status code.
488 * @param paMappings The mappings.
489 * @param cMappings The number of mappings.
490 * @param pszMountDir The mount directory.
491 * @param pszSharePrefix The share prefix.
492 * @param uClientID The shared folder service (HGCM) client ID.
493 */
494static int vbsvcAutoMountProcessMappingsOld(PCVBGLR3SHAREDFOLDERMAPPING paMappings, uint32_t cMappings,
495 const char *pszMountDir, const char *pszSharePrefix, uint32_t uClientID)
496{
497 if (cMappings == 0)
498 return VINF_SUCCESS;
499 AssertPtrReturn(paMappings, VERR_INVALID_PARAMETER);
500 AssertPtrReturn(pszMountDir, VERR_INVALID_PARAMETER);
501 AssertPtrReturn(pszSharePrefix, VERR_INVALID_PARAMETER);
502 AssertReturn(uClientID > 0, VERR_INVALID_PARAMETER);
503
504 /** @todo r=bird: Why is this loop schitzoid about status codes? It quits if
505 * RTPathJoin fails (i.e. if the user specifies a very long name), but happily
506 * continues if RTStrAPrintf failes (mem alloc).
507 *
508 * It also happily continues if the 'vboxsf' group is missing, which is a waste
509 * of effort... In fact, retrieving the group ID could probably be done up
510 * front, outside the loop. */
511 int rc = VINF_SUCCESS;
512 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
513 {
514 char *pszShareName = NULL;
515 rc = VbglR3SharedFolderGetName(uClientID, paMappings[i].u32Root, &pszShareName);
516 if ( RT_SUCCESS(rc)
517 && *pszShareName)
518 {
519 VGSvcVerbose(3, "vbsvcAutoMountWorker: Connecting share %u (%s) ...\n", i+1, pszShareName);
520
521 /** @todo r=bird: why do you copy things twice here and waste heap space?
522 * szMountPoint has a fixed size.
523 * @code
524 * char szMountPoint[RTPATH_MAX];
525 * rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, *pszSharePrefix ? pszSharePrefix : pszShareName);
526 * if (RT_SUCCESS(rc) && *pszSharePrefix)
527 * rc = RTStrCat(szMountPoint, sizeof(szMountPoint), pszShareName);
528 * @endcode */
529 char *pszShareNameFull = NULL;
530 if (RTStrAPrintf(&pszShareNameFull, "%s%s", pszSharePrefix, pszShareName) > 0)
531 {
532 char szMountPoint[RTPATH_MAX];
533 rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, pszShareNameFull);
534 if (RT_SUCCESS(rc))
535 {
536 VGSvcVerbose(4, "vbsvcAutoMountWorker: Processing mount point '%s'\n", szMountPoint);
537
538 /*
539 * Already mounted?
540 */
541 /** @todo r-bird: this does not take into account that a shared folder could
542 * be mounted twice... We're really just interested in whether the
543 * folder is mounted on 'szMountPoint', no where else... */
544 bool fSkip = false;
545 char szAlreadyMountedOn[RTPATH_MAX];
546 if (vbsvcAutoMountShareIsMountedOld(pszShareName, szAlreadyMountedOn, sizeof(szAlreadyMountedOn)))
547 {
548 /* Do if it not mounted to our desired mount point */
549 if (RTStrICmp(szMountPoint, szAlreadyMountedOn))
550 {
551 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', unmounting ...\n",
552 pszShareName, szAlreadyMountedOn);
553 rc = vbsvcAutoMountUnmountOld(szAlreadyMountedOn);
554 if (RT_SUCCESS(rc))
555 fSkip = false;
556 else
557 VGSvcError("vbsvcAutoMountWorker: Failed to unmount '%s', %s (%d)! (rc=%Rrc)\n",
558 szAlreadyMountedOn, strerror(errno), errno, rc); /** @todo errno isn't reliable at this point */
559 }
560 if (fSkip)
561 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', skipping\n",
562 pszShareName, szAlreadyMountedOn);
563 }
564 if (!fSkip)
565 {
566 /*
567 * Mount it.
568 */
569 rc = vbsvcAutoMountSharedFolderOld(pszShareName, szMountPoint);
570 }
571 }
572 else
573 VGSvcError("vbsvcAutoMountWorker: Unable to join mount point/prefix/shrae, rc = %Rrc\n", rc);
574 RTStrFree(pszShareNameFull);
575 }
576 else
577 VGSvcError("vbsvcAutoMountWorker: Unable to allocate full share name\n");
578 RTStrFree(pszShareName);
579 }
580 else
581 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
582 paMappings[i].u32Root, rc);
583 } /* for cMappings. */
584 return rc;
585}
586
587#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) - the old code*/
588
589
590/**
591 * Service worker function for old host.
592 *
593 * This only mount stuff on startup.
594 *
595 * @returns VBox status code.
596 * @param pfShutdown Shutdown indicator.
597 */
598static int vbsvcAutoMountWorkerOld(bool volatile *pfShutdown)
599{
600#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
601 /*
602 * We only do a single pass here.
603 */
604 uint32_t cMappings;
605 PVBGLR3SHAREDFOLDERMAPPING paMappings;
606 int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /* Only process auto-mounted folders */,
607 &paMappings, &cMappings);
608 if ( RT_SUCCESS(rc)
609 && cMappings)
610 {
611 char *pszMountDir;
612 rc = VbglR3SharedFolderGetMountDir(&pszMountDir);
613 if (rc == VERR_NOT_FOUND)
614 rc = RTStrDupEx(&pszMountDir, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR);
615 if (RT_SUCCESS(rc))
616 {
617 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount dir set to '%s'\n", pszMountDir);
618
619 char *pszSharePrefix;
620 rc = VbglR3SharedFolderGetMountPrefix(&pszSharePrefix);
621 if (RT_SUCCESS(rc))
622 {
623 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount prefix set to '%s'\n", pszSharePrefix);
624# ifdef USE_VIRTUAL_SHARES
625 /* Check for a fixed/virtual auto-mount share. */
626 if (VbglR3SharedFolderExists(g_idClientSharedFolders, "vbsfAutoMount"))
627 VGSvcVerbose(3, "vbsvcAutoMountWorker: Host supports auto-mount root\n");
628 else
629 {
630# endif
631 VGSvcVerbose(3, "vbsvcAutoMountWorker: Got %u shared folder mappings\n", cMappings);
632 rc = vbsvcAutoMountProcessMappingsOld(paMappings, cMappings, pszMountDir, pszSharePrefix,
633 g_idClientSharedFolders);
634# ifdef USE_VIRTUAL_SHARES
635 }
636# endif
637 RTStrFree(pszSharePrefix);
638 } /* Mount share prefix. */
639 else
640 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mount prefix, rc = %Rrc\n", rc);
641 RTStrFree(pszMountDir);
642 }
643 else
644 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder directory, rc = %Rrc\n", rc);
645 VbglR3SharedFolderFreeMappings(paMappings);
646 }
647 else if (RT_FAILURE(rc))
648 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mappings, rc = %Rrc\n", rc);
649 else
650 VGSvcVerbose(3, "vbsvcAutoMountWorker: No shared folder mappings found\n");
651
652#else
653 int rc = VINF_SUCCESS;
654#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) */
655
656
657 /*
658 * Wait on shutdown (this used to be a silly RTThreadSleep(500) loop).
659 */
660 while (!*pfShutdown)
661 {
662 rc = RTSemEventMultiWait(g_hAutoMountEvent, RT_MS_1MIN);
663 if (rc != VERR_TIMEOUT)
664 break;
665 }
666
667 VGSvcVerbose(3, "vbsvcAutoMountWorkerOld: Finished with rc=%Rrc\n", rc);
668 return rc;
669}
670
671#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
672/**
673 * Assembles the mount directory and prefix into @a pszDst.
674 *
675 * Will fall back on defaults if we have trouble with the configuration from the
676 * host. This ASSUMES that @a cbDst is rather large and won't cause trouble
677 * with the default.
678 *
679 * @returns IPRT status code.
680 * @param pszDst Where to return the prefix.
681 * @param cbDst The size of the prefix buffer.
682 */
683static int vbsvcAutomounterQueryMountDirAndPrefix(char *pszDst, size_t cbDst)
684{
685 /*
686 * Query the config first.
687 */
688 /* Mount directory: */
689 const char *pszDir = VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR;
690 char *pszCfgDir;
691 int rc = VbglR3SharedFolderGetMountDir(&pszCfgDir);
692 if (RT_SUCCESS(rc))
693 {
694 if (*pszCfgDir == '/')
695 pszDir = pszCfgDir;
696 }
697 else
698 pszCfgDir = NULL;
699
700 /* Prefix: */
701 const char *pszPrefix = VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX;
702 char *pszCfgPrefix;
703 rc = VbglR3SharedFolderGetMountPrefix(&pszCfgPrefix);
704 if (RT_SUCCESS(rc))
705 {
706 if ( strchr(pszCfgPrefix, '/') == NULL
707 && strchr(pszCfgPrefix, '\\') == NULL
708 && strcmp(pszCfgPrefix, "..") != 0)
709 pszPrefix = pszCfgPrefix;
710 }
711 else
712 pszCfgPrefix = NULL;
713
714 /*
715 * Try combine the two.
716 */
717 rc = RTPathAbs(pszDir, pszDst, cbDst);
718 if (RT_SUCCESS(rc))
719 {
720 if (*pszPrefix)
721 {
722 rc = RTPathAppend(pszDst, cbDst, pszPrefix);
723 if (RT_FAILURE(rc))
724 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAppend(%s,,%s) -> %Rrc\n", pszDst, pszPrefix, rc);
725 }
726 else
727 {
728 rc = RTPathEnsureTrailingSeparator(pszDst, cbDst);
729 if (RT_FAILURE(rc))
730 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathEnsureTrailingSeparator(%s) -> %Rrc\n", pszDst, rc);
731 }
732 }
733 else
734 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAbs(%s) -> %Rrc\n", pszDir, rc);
735
736
737 /*
738 * Return the default dir + prefix if the above failed.
739 */
740 if (RT_FAILURE(rc))
741 {
742 rc = RTStrCopy(pszDst, cbDst, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/" VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX);
743 AssertRC(rc);
744 }
745
746 RTStrFree(pszCfgDir);
747 RTStrFree(pszCfgPrefix);
748 return rc;
749}
750#endif /* !RT_OS_WINDOW && !RT_OS_OS2 */
751
752
753/**
754 * @callback_method_impl{FNRTSORTCMP, For sorting mount table by root ID. }
755 */
756static DECLCALLBACK(int) vbsvcAutomounterCompareEntry(void const *pvElement1, void const *pvElement2, void *pvUser)
757{
758 RT_NOREF_PV(pvUser);
759 PVBSVCAUTOMOUNTERENTRY pEntry1 = (PVBSVCAUTOMOUNTERENTRY)pvElement1;
760 PVBSVCAUTOMOUNTERENTRY pEntry2 = (PVBSVCAUTOMOUNTERENTRY)pvElement2;
761 return pEntry1->idRoot < pEntry2->idRoot ? -1
762 : pEntry1->idRoot > pEntry2->idRoot ? 1 : 0;
763}
764
765
766/**
767 * Worker for vbsvcAutomounterPopulateTable for adding discovered entries.
768 *
769 * This is puts dummies in for missing values, depending on
770 * vbsvcAutomounterPopulateTable to query them later.
771 *
772 * @returns VINF_SUCCESS or VERR_NO_MEMORY;
773 * @param pMountTable The mount table to add an entry to.
774 * @param pszName The shared folder name.
775 * @param pszMountPoint The mount point.
776 */
777static int vbsvcAutomounterAddEntry(PVBSVCAUTOMOUNTERTABLE pMountTable, const char *pszName, const char *pszMountPoint)
778{
779 VGSvcVerbose(2, "vbsvcAutomounterAddEntry: %s -> %s\n", pszMountPoint, pszName);
780 PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
781 pEntry->idRoot = UINT32_MAX;
782 pEntry->uRootIdVersion = UINT32_MAX;
783 pEntry->fFlags = UINT64_MAX;
784 pEntry->pszName = RTStrDup(pszName);
785 pEntry->pszMountPoint = NULL;
786 pEntry->pszActualMountPoint = RTStrDup(pszMountPoint);
787 if (pEntry->pszName && pEntry->pszActualMountPoint)
788 {
789 if (pMountTable->cEntries + 1 <= pMountTable->cAllocated)
790 {
791 pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
792 return VINF_SUCCESS;
793 }
794
795 void *pvNew = RTMemRealloc(pMountTable->papEntries, (pMountTable->cAllocated + 8) * sizeof(pMountTable->papEntries[0]));
796 if (pvNew)
797 {
798 pMountTable->cAllocated += 8;
799 pMountTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvNew;
800
801 pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
802 return VINF_SUCCESS;
803 }
804 }
805 RTMemFree(pEntry->pszActualMountPoint);
806 RTMemFree(pEntry->pszName);
807 RTMemFree(pEntry);
808 return VERR_NO_MEMORY;
809}
810
811
812/**
813 * Populates the mount table as best we can with existing automount entries.
814 *
815 * @returns VINF_SUCCESS or VERR_NO_MEMORY;
816 * @param pMountTable The mount table (empty).
817 */
818static int vbsvcAutomounterPopulateTable(PVBSVCAUTOMOUNTERTABLE pMountTable)
819{
820 int rc;
821
822#ifdef RT_OS_WINDOWS
823 /*
824 * Loop thru the drive letters and check out each of them using QueryDosDeviceW.
825 */
826 static const char s_szDevicePath[] = "\\Device\\VBoxMiniRdr\\;";
827 for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
828 {
829 RTUTF16 const wszMountPoint[4] = { (RTUTF16)chDrive, ':', '\0', '\0' };
830 RTUTF16 wszTargetPath[RTPATH_MAX];
831 DWORD const cwcResult = QueryDosDeviceW(wszMountPoint, wszTargetPath, RT_ELEMENTS(wszTargetPath));
832 if ( cwcResult > sizeof(s_szDevicePath)
833 && RTUtf16NICmpAscii(wszTargetPath, RT_STR_TUPLE(s_szDevicePath)) == 0)
834 {
835 PCRTUTF16 pwsz = &wszTargetPath[RT_ELEMENTS(s_szDevicePath) - 1];
836 Assert(pwsz[-1] == ';');
837 if ( (pwsz[0] & ~(RTUTF16)0x20) == chDrive
838 && pwsz[1] == ':'
839 && pwsz[2] == '\\')
840 {
841 /* For now we'll just use the special capitalization of the
842 "server" name to identify it as our work. We could check
843 if the symlink is from \Global?? or \??, but that trick does
844 work for older OS versions (<= XP) or when running the
845 service manually for testing/wathever purposes. */
846 /** @todo Modify the windows shared folder driver to allow tagging drives.*/
847 if (RTUtf16NCmpAscii(&pwsz[3], RT_STR_TUPLE("VBoxSvr\\")) == 0)
848 {
849 pwsz += 3 + 8;
850 if (*pwsz != '\\' && *pwsz)
851 {
852 /* The shared folder name should follow immediately after the server prefix. */
853 char *pszMountedName = NULL;
854 rc = RTUtf16ToUtf8(pwsz, &pszMountedName);
855 if (RT_SUCCESS(rc))
856 {
857 char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
858 rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
859 RTStrFree(pszMountedName);
860 }
861 if (RT_FAILURE(rc))
862 return rc;
863 }
864 else
865 VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Malformed, not ours: %ls -> %ls\n",
866 wszMountPoint, wszTargetPath);
867 }
868 else
869 VGSvcVerbose(3, "vbsvcAutomounterPopulateTable: Not ours: %ls -> %ls\n", wszMountPoint, wszTargetPath);
870 }
871 }
872 }
873
874#elif defined(RT_OS_OS2)
875 /*
876 * Just loop thru the drive letters and check the attachment of each.
877 */
878 for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
879 {
880 char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
881 union
882 {
883 FSQBUFFER2 FsQueryBuf;
884 char achPadding[1024];
885 } uBuf;
886 RT_ZERO(uBuf);
887 ULONG cbBuf = sizeof(uBuf) - 2;
888 APIRET rcOs2 = DosQueryFSAttach(szMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
889 if (rcOs2 == NO_ERROR)
890 {
891 const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
892 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
893 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
894 {
895 const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
896 const char *pszTag = pszMountedName + strlen(pszMountedName) + 1; /* (Safe. Always two trailing zero bytes, see above.) */
897 if (strcmp(pszTag, g_szTag) == 0)
898 {
899 rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
900 if (RT_FAILURE(rc))
901 return rc;
902 }
903 }
904 }
905 }
906
907#elif defined(RT_OS_LINUX)
908 /*
909 * Scan the mount table file for the mount point and then match file system
910 * and device/share. We identify our mounts by mount path + prefix for now,
911 * but later we may use the same approach as on solaris.
912 */
913 FILE *pFile = setmntent("/proc/mounts", "r");
914 int iErrMounts = errno;
915 if (!pFile)
916 pFile = setmntent("/etc/mtab", "r");
917 if (pFile)
918 {
919 rc = VWRN_NOT_FOUND;
920 struct mntent *pEntry;
921 while ((pEntry = getmntent(pFile)) != NULL)
922 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
923 if (strstr(pEntry->mnt_opts, g_szTag) != NULL)
924 {
925 rc = vbsvcAutomounterAddEntry(pMountTable, pEntry->mnt_fsname, pEntry->mnt_dir);
926 if (RT_FAILURE(rc))
927 {
928 endmntent(pFile);
929 return rc;
930 }
931 }
932 endmntent(pFile);
933 }
934 else
935 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d) or '/proc/mounts' (errno=%d)\n",
936 _PATH_MOUNTED, errno, iErrMounts);
937
938#elif defined(RT_OS_SOLARIS)
939 /*
940 * Look thru the system mount table and inspect the vboxsf mounts.
941 */
942 FILE *pFile = fopen(_PATH_MOUNTED, "r");
943 if (pFile)
944 {
945 rc = VINF_SUCCESS;
946 struct mnttab Entry;
947 while (getmntent(pFile, &Entry) == 0)
948 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
949 {
950 /* Look for the dummy automounter option. */
951 if ( Entry.mnt_mntopts != NULL
952 && strstr(Entry.mnt_mntopts, g_szTag) != NULL)
953 {
954 rc = vbsvcAutomounterAddEntry(pMountTable, Entry.mnt_special, Entry.mnt_mountp);
955 if (RT_FAILURE(rc))
956 {
957 fclose(pFile);
958 return rc;
959 }
960 }
961 }
962 fclose(pFile);
963 }
964 else
965 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
966
967#else
968# error "PORTME!"
969#endif
970
971 /*
972 * Try reconcile the detected folders with data from the host.
973 */
974 uint32_t cMappings = 0;
975 PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
976 rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
977 if (RT_SUCCESS(rc))
978 {
979 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
980 {
981 uint32_t const idRootSrc = paMappings[i].u32Root;
982
983 uint32_t uRootIdVer = UINT32_MAX;
984 uint64_t fFlags = 0;
985 char *pszName = NULL;
986 char *pszMntPt = NULL;
987 int rc2 = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
988 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
989 if (RT_SUCCESS(rc2))
990 {
991 uint32_t iPrevHit = UINT32_MAX;
992 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
993 {
994 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
995 if (RTStrICmp(pEntry->pszName, pszName) == 0)
996 {
997 VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Identified %s -> %s: idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
998 pEntry->pszActualMountPoint, pEntry->pszName, idRootSrc, uRootIdVer, fFlags, pszMntPt);
999 pEntry->fFlags = fFlags;
1000 pEntry->idRoot = idRootSrc;
1001 pEntry->uRootIdVersion = uRootIdVer;
1002 RTStrFree(pEntry->pszMountPoint);
1003 pEntry->pszMountPoint = RTStrDup(pszMntPt);
1004 if (!pEntry->pszMountPoint)
1005 {
1006 rc = VERR_NO_MEMORY;
1007 break;
1008 }
1009
1010 /* If multiple mappings of the same folder, pick the first or the one
1011 with matching mount point. */
1012 if (iPrevHit == UINT32_MAX)
1013 iPrevHit = iTable;
1014 else if (RTPathCompare(pszMntPt, pEntry->pszActualMountPoint) == 0)
1015 {
1016 if (iPrevHit != UINT32_MAX)
1017 pMountTable->papEntries[iPrevHit]->uRootIdVersion -= 1;
1018 iPrevHit = iTable;
1019 }
1020 else
1021 pEntry->uRootIdVersion -= 1;
1022 }
1023 }
1024
1025 RTStrFree(pszName);
1026 RTStrFree(pszMntPt);
1027 }
1028 else
1029 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderQueryFolderInfo(%u) failed: %Rrc\n", idRootSrc, rc2);
1030 }
1031
1032 VbglR3SharedFolderFreeMappings(paMappings);
1033
1034 /*
1035 * Sort the table by root ID.
1036 */
1037 if (pMountTable->cEntries > 1)
1038 RTSortApvShell((void **)pMountTable->papEntries, pMountTable->cEntries, vbsvcAutomounterCompareEntry, NULL);
1039
1040 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
1041 {
1042 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
1043 if (pMountTable->papEntries[iTable]->idRoot != UINT32_MAX)
1044 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
1045 iTable, pEntry->pszActualMountPoint, pEntry->pszName, pEntry->idRoot, pEntry->uRootIdVersion,
1046 pEntry->fFlags, pEntry->pszMountPoint);
1047 else
1048 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s - not identified!\n",
1049 iTable, pEntry->pszActualMountPoint, pEntry->pszName);
1050 }
1051 }
1052 else
1053 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
1054 return rc;
1055}
1056
1057
1058/**
1059 * Checks whether the shared folder @a pszName is mounted on @a pszMountPoint.
1060 *
1061 * @returns Exactly one of the following IPRT status codes;
1062 * @retval VINF_SUCCESS if mounted
1063 * @retval VWRN_NOT_FOUND if nothing is mounted at @a pszMountPoint.
1064 * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
1065 * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
1066 * there.
1067 *
1068 * @param pszMountPoint The mount point to check.
1069 * @param pszName The name of the shared folder (mapping).
1070 */
1071static int vbsvcAutomounterQueryMountPoint(const char *pszMountPoint, const char *pszName)
1072{
1073 VGSvcVerbose(4, "vbsvcAutomounterQueryMountPoint: pszMountPoint=%s pszName=%s\n", pszMountPoint, pszName);
1074
1075#ifdef RT_OS_WINDOWS
1076 /*
1077 * We could've used RTFsQueryType here but would then have to
1078 * calling RTFsQueryLabel for the share name hint, ending up
1079 * doing the same work twice. We could also use QueryDosDeviceW,
1080 * but output is less clear...
1081 */
1082 PRTUTF16 pwszMountPoint = NULL;
1083 int rc = RTStrToUtf16(pszMountPoint, &pwszMountPoint);
1084 if (RT_SUCCESS(rc))
1085 {
1086 DWORD uSerial = 0;
1087 DWORD cchCompMax = 0;
1088 DWORD fFlags = 0;
1089 RTUTF16 wszLabel[512];
1090 RTUTF16 wszFileSystem[256];
1091 RT_ZERO(wszLabel);
1092 RT_ZERO(wszFileSystem);
1093 if (GetVolumeInformationW(pwszMountPoint, wszLabel, RT_ELEMENTS(wszLabel) - 1, &uSerial, &cchCompMax, &fFlags,
1094 wszFileSystem, RT_ELEMENTS(wszFileSystem) - 1))
1095 {
1096 if (RTUtf16ICmpAscii(wszFileSystem, "VBoxSharedFolderFS") == 0)
1097 {
1098 char *pszLabel = NULL;
1099 rc = RTUtf16ToUtf8(wszLabel, &pszLabel);
1100 if (RT_SUCCESS(rc))
1101 {
1102 const char *pszMountedName = pszLabel;
1103 if (RTStrStartsWith(pszMountedName, "VBOX_"))
1104 pszMountedName += sizeof("VBOX_") - 1;
1105 if (RTStrICmp(pszMountedName, pszName) == 0)
1106 {
1107 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1108 pszName, pszMountPoint);
1109 rc = VINF_SUCCESS;
1110 }
1111 else
1112 {
1113 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1114 pszMountedName, pszMountPoint, pszName);
1115 rc = VERR_RESOURCE_BUSY;
1116 }
1117 RTStrFree(pszLabel);
1118 }
1119 else
1120 {
1121 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: RTUtf16ToUtf8(%ls,) failed: %Rrc\n", wszLabel, rc);
1122 rc = VERR_RESOURCE_BUSY;
1123 }
1124 }
1125 else
1126 {
1127 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%ls' with label '%ls' mount at '%s', not '%s'...\n",
1128 wszFileSystem, wszLabel, pszMountPoint, pszName);
1129 rc = VERR_ACCESS_DENIED;
1130 }
1131 }
1132 else
1133 {
1134 rc = GetLastError();
1135 if (rc != ERROR_PATH_NOT_FOUND || g_cVerbosity >= 4)
1136 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: GetVolumeInformationW('%ls',,,,) failed: %u\n", pwszMountPoint, rc);
1137 if (rc == ERROR_PATH_NOT_FOUND)
1138 rc = VWRN_NOT_FOUND;
1139 else if ( RT_C_IS_ALPHA(pszMountPoint[0])
1140 && pszMountPoint[1] == ':'
1141 && ( pszMountPoint[2] == '\0'
1142 || (RTPATH_IS_SLASH(pszMountPoint[2]) && pszMountPoint[3] == '\0')))
1143 {
1144 /* See whether QueryDosDeviceW thinks its a malfunctioning shared folder or
1145 something else (it doesn't access the file system). We've seen
1146 VERR_NET_HOST_NOT_FOUND here for shared folders that was removed on the
1147 host side.
1148
1149 Note! This duplicates code from vbsvcAutomounterPopulateTable. */
1150 rc = VERR_ACCESS_DENIED;
1151 static const char s_szDevicePath[] = "\\Device\\VBoxMiniRdr\\;";
1152 wszFileSystem[0] = pwszMountPoint[0];
1153 wszFileSystem[1] = pwszMountPoint[1];
1154 wszFileSystem[2] = '\0';
1155 DWORD const cwcResult = QueryDosDeviceW(wszFileSystem, wszLabel, RT_ELEMENTS(wszLabel));
1156 if ( cwcResult > sizeof(s_szDevicePath)
1157 && RTUtf16NICmpAscii(wszLabel, RT_STR_TUPLE(s_szDevicePath)) == 0)
1158 {
1159 PCRTUTF16 pwsz = &wszLabel[RT_ELEMENTS(s_szDevicePath) - 1];
1160 Assert(pwsz[-1] == ';');
1161 if ( (pwsz[0] & ~(RTUTF16)0x20) == (wszFileSystem[0] & ~(RTUTF16)0x20)
1162 && pwsz[1] == ':'
1163 && pwsz[2] == '\\')
1164 {
1165 if (RTUtf16NICmpAscii(&pwsz[3], RT_STR_TUPLE("VBoxSvr\\")) == 0)
1166 {
1167 pwsz += 3 + 8;
1168 char *pszMountedName = NULL;
1169 rc = RTUtf16ToUtf8(pwsz, &pszMountedName);
1170 if (RT_SUCCESS(rc))
1171 {
1172 if (RTStrICmp(pszMountedName, pszName) == 0)
1173 {
1174 rc = VINF_SUCCESS;
1175 VGSvcVerbose(2, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s' (using QueryDosDeviceW).\n",
1176 pszName, pszMountPoint);
1177 }
1178 else
1179 {
1180 VGSvcVerbose(2, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s' (using QueryDosDeviceW), not '%s'...\n",
1181 pszMountedName, pszMountPoint, pszName);
1182 rc = VERR_RESOURCE_BUSY;
1183 }
1184 RTStrFree(pszMountedName);
1185 }
1186 else
1187 {
1188 VGSvcVerbose(2, "vbsvcAutomounterQueryMountPoint: RTUtf16ToUtf8 failed: %Rrc\n", rc);
1189 AssertRC(rc);
1190 rc = VERR_RESOURCE_BUSY;
1191 }
1192 }
1193 }
1194 }
1195 }
1196 else
1197 rc = VERR_ACCESS_DENIED;
1198 }
1199 RTUtf16Free(pwszMountPoint);
1200 }
1201 else
1202 {
1203 VGSvcError("vbsvcAutomounterQueryMountPoint: RTStrToUtf16(%s,) -> %Rrc\n", pszMountPoint, rc);
1204 rc = VWRN_NOT_FOUND;
1205 }
1206 return rc;
1207
1208#elif defined(RT_OS_OS2)
1209 /*
1210 * Query file system attachment info for the given drive letter.
1211 */
1212 union
1213 {
1214 FSQBUFFER2 FsQueryBuf;
1215 char achPadding[512];
1216 } uBuf;
1217 RT_ZERO(uBuf);
1218
1219 ULONG cbBuf = sizeof(uBuf);
1220 APIRET rcOs2 = DosQueryFSAttach(pszMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
1221 int rc;
1222 if (rcOs2 == NO_ERROR)
1223 {
1224 const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
1225 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
1226 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
1227 {
1228 const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
1229 if (RTStrICmp(pszMountedName, pszName) == 0)
1230 {
1231 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1232 pszName, pszMountPoint);
1233 rc = VINF_SUCCESS;
1234 }
1235 else
1236 {
1237 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1238 pszMountedName, pszMountPoint, pszName);
1239 rc = VERR_RESOURCE_BUSY;
1240 }
1241 }
1242 else
1243 {
1244 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' type %u mount at '%s', not '%s'...\n",
1245 pszFsdName, uBuf.FsQueryBuf.iType, pszMountPoint, pszName);
1246 rc = VERR_ACCESS_DENIED;
1247 }
1248 }
1249 else
1250 {
1251 rc = VWRN_NOT_FOUND;
1252 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: DosQueryFSAttach(%s) -> %u\n", pszMountPoint, rcOs2);
1253 AssertMsgStmt(rcOs2 != ERROR_BUFFER_OVERFLOW && rcOs2 != ERROR_INVALID_PARAMETER,
1254 ("%s -> %u\n", pszMountPoint, rcOs2), rc = VERR_ACCESS_DENIED);
1255 }
1256 return rc;
1257
1258#elif defined(RT_OS_LINUX)
1259 /*
1260 * Scan one of the mount table file for the mount point and then
1261 * match file system and device/share.
1262 */
1263 FILE *pFile = setmntent("/proc/mounts", "r");
1264 int rc = errno;
1265 if (!pFile)
1266 pFile = setmntent(_PATH_MOUNTED, "r");
1267 if (pFile)
1268 {
1269 rc = VWRN_NOT_FOUND;
1270 struct mntent *pEntry;
1271 while ((pEntry = getmntent(pFile)) != NULL)
1272 if (RTPathCompare(pEntry->mnt_dir, pszMountPoint) == 0)
1273 {
1274 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
1275 {
1276 if (RTStrICmp(pEntry->mnt_fsname, pszName) == 0)
1277 {
1278 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1279 pszName, pszMountPoint);
1280 rc = VINF_SUCCESS;
1281 }
1282 else
1283 {
1284 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1285 pEntry->mnt_fsname, pszMountPoint, pszName);
1286 rc = VERR_RESOURCE_BUSY;
1287 }
1288 }
1289 else
1290 {
1291 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1292 pEntry->mnt_type, pEntry->mnt_fsname, pszMountPoint, pszName);
1293 rc = VERR_ACCESS_DENIED;
1294 }
1295 /* We continue searching in case of stacked mounts, we want the last one. */
1296 }
1297 endmntent(pFile);
1298 }
1299 else
1300 {
1301 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '/proc/mounts' (errno=%d) or '%s' (errno=%d)\n",
1302 rc, _PATH_MOUNTED, errno);
1303 rc = VERR_ACCESS_DENIED;
1304 }
1305 return rc;
1306
1307#elif defined(RT_OS_SOLARIS)
1308 /*
1309 * Similar to linux.
1310 */
1311 int rc;
1312 FILE *pFile = fopen(_PATH_MOUNTED, "r");
1313 if (pFile)
1314 {
1315 rc = VWRN_NOT_FOUND;
1316 struct mnttab Entry;
1317 while (getmntent(pFile, &Entry) == 0)
1318 if (RTPathCompare(Entry.mnt_mountp, pszMountPoint) == 0)
1319 {
1320 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
1321 {
1322 if (RTStrICmp(Entry.mnt_special, pszName) == 0)
1323 {
1324 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1325 pszName, pszMountPoint);
1326 rc = VINF_SUCCESS;
1327 }
1328 else
1329 {
1330 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1331 Entry.mnt_special, pszMountPoint, pszName);
1332 rc = VERR_RESOURCE_BUSY;
1333 }
1334 }
1335 else
1336 {
1337 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1338 Entry.mnt_fstype, Entry.mnt_special, pszMountPoint, pszName);
1339 rc = VERR_ACCESS_DENIED;
1340 }
1341 /* We continue searching in case of stacked mounts, we want the last one. */
1342 }
1343 fclose(pFile);
1344 }
1345 else
1346 {
1347 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
1348 rc = VERR_ACCESS_DENIED;
1349 }
1350 return rc;
1351#else
1352# error "PORTME"
1353#endif
1354}
1355
1356
1357/**
1358 * Worker for vbsvcAutomounterMountNewEntry that does the OS mounting.
1359 *
1360 * @returns IPRT status code.
1361 * @param pEntry The entry to try mount.
1362 */
1363static int vbsvcAutomounterMountIt(PVBSVCAUTOMOUNTERENTRY pEntry)
1364{
1365 VGSvcVerbose(3, "vbsvcAutomounterMountIt: Trying to mount '%s' (idRoot=%#x) on '%s'...\n",
1366 pEntry->pszName, pEntry->idRoot, pEntry->pszActualMountPoint);
1367#ifdef RT_OS_WINDOWS
1368 /*
1369 * Attach the shared folder using WNetAddConnection2W.
1370 *
1371 * According to google we should get a drive symlink in \\GLOBAL?? when
1372 * we are running under the system account. Otherwise it will be a session
1373 * local link (\\??).
1374 */
1375 Assert(RT_C_IS_UPPER(pEntry->pszActualMountPoint[0]) && pEntry->pszActualMountPoint[1] == ':' && pEntry->pszActualMountPoint[2] == '\0');
1376 RTUTF16 wszDrive[4] = { (RTUTF16)pEntry->pszActualMountPoint[0], ':', '\0', '\0' };
1377
1378 RTUTF16 wszPrefixedName[RTPATH_MAX];
1379 int rc = RTUtf16CopyAscii(wszPrefixedName, RT_ELEMENTS(wszPrefixedName), "\\\\VBoxSvr\\");
1380 AssertRC(rc);
1381
1382 size_t const offName = RTUtf16Len(wszPrefixedName);
1383 PRTUTF16 pwszName = &wszPrefixedName[offName];
1384 rc = RTStrToUtf16Ex(pEntry->pszName, RTSTR_MAX, &pwszName, sizeof(wszPrefixedName) - offName, NULL);
1385 if (RT_FAILURE(rc))
1386 {
1387 VGSvcError("vbsvcAutomounterMountIt: RTStrToUtf16Ex failed on '%s': %Rrc\n", pEntry->pszName, rc);
1388 return rc;
1389 }
1390
1391 VGSvcVerbose(3, "vbsvcAutomounterMountIt: wszDrive='%ls', wszPrefixedName='%ls'\n",
1392 wszDrive, wszPrefixedName);
1393
1394 NETRESOURCEW NetRsrc;
1395 RT_ZERO(NetRsrc);
1396 NetRsrc.dwType = RESOURCETYPE_DISK;
1397 NetRsrc.lpLocalName = wszDrive;
1398 NetRsrc.lpRemoteName = wszPrefixedName;
1399 NetRsrc.lpProvider = (PWSTR)L"VirtualBox Shared Folders"; /* Only try our provider. */
1400 NetRsrc.lpComment = pwszName;
1401
1402 DWORD dwErr = WNetAddConnection2W(&NetRsrc, NULL /*pwszPassword*/, NULL /*pwszUserName*/, 0 /*dwFlags*/);
1403 if (dwErr == NO_ERROR)
1404 {
1405 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1406 pEntry->pszName, pEntry->pszActualMountPoint);
1407 return VINF_SUCCESS;
1408 }
1409 VGSvcError("vbsvcAutomounterMountIt: Failed to attach '%s' to '%s': %Rrc (%u)\n",
1410 pEntry->pszName, pEntry->pszActualMountPoint, RTErrConvertFromWin32(dwErr), dwErr);
1411 return VERR_OPEN_FAILED;
1412
1413#elif defined(RT_OS_OS2)
1414 /*
1415 * It's a rather simple affair on OS/2.
1416 *
1417 * In order to be able to detect our mounts we add a 2nd string after
1418 * the folder name that tags the attachment. The IFS will remember this
1419 * and return it when DosQueryFSAttach is called.
1420 *
1421 * Note! Kernel currently accepts limited 7-bit ASCII names. We could
1422 * change that to UTF-8 if we like as that means no extra string
1423 * encoding conversion fun here.
1424 */
1425 char szzNameAndTag[256];
1426 size_t cchName = strlen(pEntry->pszName);
1427 if (cchName + 1 + sizeof(g_szTag) <= sizeof(szzNameAndTag))
1428 {
1429 memcpy(szzNameAndTag, pEntry->pszName, cchName);
1430 szzNameAndTag[cchName] = '\0';
1431 memcpy(&szzNameAndTag[cchName + 1], g_szTag, sizeof(g_szTag));
1432
1433 APIRET rc = DosFSAttach(pEntry->pszActualMountPoint, "VBOXSF", szzNameAndTag, cchName + 1 + sizeof(g_szTag), FS_ATTACH);
1434 if (rc == NO_ERROR)
1435 {
1436 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1437 pEntry->pszName, pEntry->pszActualMountPoint);
1438 return VINF_SUCCESS;
1439 }
1440 VGSvcError("vbsvcAutomounterMountIt: DosFSAttach failed to attach '%s' to '%s': %u\n",
1441 pEntry->pszName, pEntry->pszActualMountPoint, rc);
1442 }
1443 else
1444 VGSvcError("vbsvcAutomounterMountIt: Share name for attach to '%s' is too long: %u chars - '%s'\n",
1445 pEntry->pszActualMountPoint, cchName, pEntry->pszName);
1446 return VERR_OPEN_FAILED;
1447
1448#else
1449 /*
1450 * Common work for unix-like systems: Get group, make sure mount directory exist.
1451 */
1452 int rc = RTDirCreateFullPath(pEntry->pszActualMountPoint,
1453 RTFS_UNIX_IRWXU | RTFS_UNIX_IXGRP | RTFS_UNIX_IRGRP | RTFS_UNIX_IXOTH | RTFS_UNIX_IROTH);
1454 if (RT_FAILURE(rc))
1455 {
1456 VGSvcError("vbsvcAutomounterMountIt: Failed to create mount path '%s' for share '%s': %Rrc\n",
1457 pEntry->pszActualMountPoint, pEntry->pszName, rc);
1458 return rc;
1459 }
1460
1461 gid_t gidMount;
1462 struct group *grp_vboxsf = getgrnam("vboxsf");
1463 if (grp_vboxsf)
1464 gidMount = grp_vboxsf->gr_gid;
1465 else
1466 {
1467 VGSvcError("vbsvcAutomounterMountIt: Group 'vboxsf' does not exist\n");
1468 gidMount = 0;
1469 }
1470
1471 /*
1472 * Allocate options string buffer which is limited to a page on most systems.
1473 */
1474 uint32_t cbOpts = RTSystemGetPageSize();
1475 char *pszOpts = (char *)alloca(cbOpts);
1476
1477# if defined(RT_OS_LINUX)
1478 /*
1479 * Linux a bit more work...
1480 */
1481 struct utsname uts;
1482 AssertStmt(uname(&uts) != -1, strcpy(uts.release, "4.4.0"));
1483
1484 /* Built mount option string. Need st_name for pre 2.6.0 kernels. */
1485 unsigned long const fFlags = MS_NODEV;
1486 ssize_t cchOpts = RTStrPrintf2(pszOpts, cbOpts,
1487 "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000,tag=%s", gidMount, g_szTag);
1488 if (RTStrVersionCompare(uts.release, "2.6.0") < 0 && cchOpts > 0)
1489 cchOpts += RTStrPrintf2(&pszOpts[cchOpts], cbOpts - cchOpts, ",sf_name=%s", pEntry->pszName);
1490 if (cchOpts <= 0)
1491 {
1492 VGSvcError("vbsvcAutomounterMountIt: pszOpts overflow! %zd\n", cchOpts);
1493 return VERR_BUFFER_OVERFLOW;
1494 }
1495
1496 /* Do the mounting. The fallback w/o tag is for the Linux vboxsf fork
1497 which lagged a lot behind when it first appeared in 5.6. */
1498 errno = 0;
1499 rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, "vboxsf", fFlags, pszOpts);
1500 if (rc != 0 && errno == EINVAL && RTStrVersionCompare(uts.release, "5.6.0") >= 0)
1501 {
1502 VGSvcVerbose(2, "vbsvcAutomounterMountIt: mount returned EINVAL, retrying without the tag.\n");
1503 *strstr(pszOpts, ",tag=") = '\0';
1504 errno = 0;
1505 rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, "vboxsf", fFlags, pszOpts);
1506 if (rc == 0)
1507 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Running outdated vboxsf module without support for the 'tag' option?\n");
1508 }
1509 if (rc == 0)
1510 {
1511 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1512 pEntry->pszName, pEntry->pszActualMountPoint);
1513
1514 errno = 0;
1515 rc = vbsfmount_complete(pEntry->pszName, pEntry->pszActualMountPoint, fFlags, pszOpts);
1516 if (rc != 0) /* Ignorable. /etc/mtab is probably a link to /proc/mounts. */
1517 VGSvcVerbose(1, "vbsvcAutomounterMountIt: vbsfmount_complete failed: %s (%d/%d)\n",
1518 rc == 1 ? "malloc" : rc == 2 ? "setmntent" : rc == 3 ? "addmntent" : "unknown", rc, errno);
1519 return VINF_SUCCESS;
1520 }
1521
1522 if (errno == EINVAL)
1523 VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s' because it is probably mounted elsewhere arleady! (%d,%d)\n",
1524 pEntry->pszName, pEntry->pszActualMountPoint, rc, errno);
1525 else
1526 VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s': %s (%d,%d)\n",
1527 pEntry->pszName, pEntry->pszActualMountPoint, strerror(errno), rc, errno);
1528 return VERR_WRITE_ERROR;
1529
1530# elif defined(RT_OS_SOLARIS)
1531 /*
1532 * Solaris is rather simple compared to linux.
1533 *
1534 * The ',VBoxService=auto' option (g_szTag) is ignored by the kernel but helps
1535 * us identify our own mounts on restart. See vbsvcAutomounterPopulateTable().
1536 *
1537 * Note! Must pass MAX_MNTOPT_STR rather than cchOpts to mount, as it may fail
1538 * with EOVERFLOW in vfs_buildoptionstr() during domount() otherwise.
1539 */
1540 ssize_t cchOpts = RTStrPrintf2(pszOpts, cbOpts,
1541 "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000,tag=%s", gidMount, g_szTag);
1542 if (cchOpts <= 0)
1543 {
1544 VGSvcError("vbsvcAutomounterMountIt: pszOpts overflow! %zd\n", cchOpts);
1545 return VERR_BUFFER_OVERFLOW;
1546 }
1547
1548 rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, MS_OPTIONSTR, "vboxfs",
1549 NULL /*dataptr*/, 0 /* datalen */, pszOpts, MAX_MNTOPT_STR);
1550 if (rc == 0)
1551 {
1552 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1553 pEntry->pszName, pEntry->pszActualMountPoint);
1554 return VINF_SUCCESS;
1555 }
1556
1557 rc = errno;
1558 VGSvcError("vbsvcAutomounterMountIt: mount failed for '%s' on '%s' (pszOpts=%s): %s (%d)\n",
1559 pEntry->pszName, pEntry->pszActualMountPoint, pszOpts, strerror(rc), rc);
1560 return VERR_OPEN_FAILED;
1561
1562# else
1563# error "PORTME!"
1564# endif
1565#endif
1566}
1567
1568
1569/**
1570 * Attempts to mount the given shared folder, adding it to the mount table on
1571 * success.
1572 *
1573 * @returns iTable + 1 on success, iTable on failure.
1574 * @param pTable The mount table.
1575 * @param iTable The mount table index at which to add the mount.
1576 * @param pszName The name of the shared folder mapping.
1577 * @param pszMntPt The mount point (hint) specified by the host.
1578 * @param fFlags The shared folder flags, SHFL_MIF_XXX.
1579 * @param idRoot The root ID.
1580 * @param uRootIdVersion The root ID version.
1581 * @param fAutoMntPt Whether to try automatically assign a mount point if
1582 * pszMntPt doesn't work out. This is set in pass \#3.
1583 */
1584static uint32_t vbsvcAutomounterMountNewEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable,
1585 const char *pszName, const char *pszMntPt, uint64_t fFlags,
1586 uint32_t idRoot, uint32_t uRootIdVersion, bool fAutoMntPt)
1587{
1588 VGSvcVerbose(3, "vbsvcAutomounterMountNewEntry: #%u: '%s' at '%s'%s\n",
1589 iTable, pszName, pszMntPt, fAutoMntPt ? " auto-assign" : "");
1590
1591 /*
1592 * First we need to figure out the actual mount point.
1593 */
1594 char szActualMountPoint[RTPATH_MAX];
1595
1596#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
1597 /*
1598 * Drive letter based. We only care about the first two characters
1599 * and ignore the rest (see further down).
1600 */
1601 char chNextLetter = 'Z';
1602 if (RT_C_IS_ALPHA(pszMntPt[0]) && pszMntPt[1] == ':')
1603 szActualMountPoint[0] = RT_C_TO_UPPER(pszMntPt[0]);
1604 else if (!fAutoMntPt)
1605 return iTable;
1606 else
1607 szActualMountPoint[0] = chNextLetter--;
1608 szActualMountPoint[1] = ':';
1609 szActualMountPoint[2] = '\0';
1610
1611 int rc;
1612 for (;;)
1613 {
1614 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1615 if (rc == VWRN_NOT_FOUND)
1616 break;
1617
1618 /* next */
1619 if (chNextLetter == 'A' || !fAutoMntPt)
1620 return iTable;
1621 szActualMountPoint[0] = chNextLetter--;
1622 }
1623
1624#else
1625 /*
1626 * Path based #1: Host specified mount point.
1627 */
1628
1629 /* Skip DOS drive letter if there is a UNIX mount point path following it: */
1630 if ( pszMntPt[0] != '/'
1631 && pszMntPt[0] != '\0'
1632 && pszMntPt[1] == ':'
1633 && pszMntPt[2] == '/')
1634 pszMntPt += 2;
1635
1636 /* Try specified mount point if it starts with a UNIX slash: */
1637 int rc = VERR_ACCESS_DENIED;
1638 if (*pszMntPt == '/')
1639 {
1640 rc = RTPathAbs(pszMntPt, szActualMountPoint, sizeof(szActualMountPoint));
1641 if (RT_SUCCESS(rc))
1642 {
1643 static const char * const s_apszBlacklist[] =
1644 { "/", "/dev", "/bin", "/sbin", "/lib", "/etc", "/var", "/tmp", "/usr", "/usr/bin", "/usr/sbin", "/usr/lib" };
1645 for (size_t i = 0; i < RT_ELEMENTS(s_apszBlacklist); i++)
1646 if (strcmp(szActualMountPoint, s_apszBlacklist[i]) == 0)
1647 {
1648 rc = VERR_ACCESS_DENIED;
1649 break;
1650 }
1651 if (RT_SUCCESS(rc))
1652 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1653 }
1654 }
1655 if (rc != VWRN_NOT_FOUND)
1656 {
1657 if (!fAutoMntPt)
1658 return iTable;
1659
1660 /*
1661 * Path based #2: Mount dir + prefix + share.
1662 */
1663 rc = vbsvcAutomounterQueryMountDirAndPrefix(szActualMountPoint, sizeof(szActualMountPoint));
1664 if (RT_SUCCESS(rc))
1665 {
1666 /* Append a sanitized share name: */
1667 size_t const offShare = strlen(szActualMountPoint);
1668 size_t offDst = offShare;
1669 size_t offSrc = 0;
1670 for (;;)
1671 {
1672 char ch = pszName[offSrc++];
1673 if (ch == ' ' || ch == '/' || ch == '\\' || ch == ':' || ch == '$')
1674 ch = '_';
1675 else if (!ch)
1676 break;
1677 else if (ch < 0x20 || ch == 0x7f)
1678 continue;
1679 if (offDst < sizeof(szActualMountPoint) - 1)
1680 szActualMountPoint[offDst++] = ch;
1681 }
1682 szActualMountPoint[offDst] = '\0';
1683 if (offDst > offShare)
1684 {
1685 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1686 if (rc != VWRN_NOT_FOUND)
1687 {
1688 /*
1689 * Path based #3: Mount dir + prefix + share + _ + number.
1690 */
1691 if (offDst + 2 >= sizeof(szActualMountPoint))
1692 return iTable;
1693
1694 szActualMountPoint[offDst++] = '_';
1695 for (uint32_t iTry = 1; iTry < 10 && rc != VWRN_NOT_FOUND; iTry++)
1696 {
1697 szActualMountPoint[offDst] = '0' + iTry;
1698 szActualMountPoint[offDst + 1] = '\0';
1699 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1700 }
1701 if (rc != VWRN_NOT_FOUND)
1702 return iTable;
1703 }
1704 }
1705 else
1706 VGSvcError("vbsvcAutomounterMountNewEntry: Bad share name: %.*Rhxs", strlen(pszName), pszName);
1707 }
1708 else
1709 VGSvcError("vbsvcAutomounterMountNewEntry: Failed to construct basic auto mount point for '%s'", pszName);
1710 }
1711#endif
1712
1713 /*
1714 * Prepare a table entry and ensure space in the table..
1715 */
1716 if (pTable->cEntries + 1 > pTable->cAllocated)
1717 {
1718 void *pvEntries = RTMemRealloc(pTable->papEntries, sizeof(pTable->papEntries[0]) * (pTable->cAllocated + 8));
1719 if (!pvEntries)
1720 {
1721 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for growing table (size %u)\n", pTable->cAllocated);
1722 return iTable;
1723 }
1724 pTable->cAllocated += 8;
1725 pTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvEntries;
1726 }
1727
1728 PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
1729 if (pEntry)
1730 {
1731 pEntry->idRoot = idRoot;
1732 pEntry->uRootIdVersion = uRootIdVersion;
1733 pEntry->fFlags = fFlags;
1734 pEntry->pszName = RTStrDup(pszName);
1735 pEntry->pszMountPoint = RTStrDup(pszMntPt);
1736 pEntry->pszActualMountPoint = RTStrDup(szActualMountPoint);
1737 if (pEntry->pszName && pEntry->pszMountPoint && pEntry->pszActualMountPoint)
1738 {
1739 /*
1740 * Now try mount it.
1741 */
1742 rc = vbsvcAutomounterMountIt(pEntry);
1743 if (RT_SUCCESS(rc))
1744 {
1745 uint32_t cToMove = pTable->cEntries - iTable;
1746 if (cToMove > 0)
1747 memmove(&pTable->papEntries[iTable + 1], &pTable->papEntries[iTable], cToMove * sizeof(pTable->papEntries[0]));
1748 pTable->papEntries[iTable] = pEntry;
1749 pTable->cEntries++;
1750 return iTable + 1;
1751 }
1752 }
1753 else
1754 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
1755 RTMemFree(pEntry->pszActualMountPoint);
1756 RTMemFree(pEntry->pszMountPoint);
1757 RTMemFree(pEntry->pszName);
1758 RTMemFree(pEntry);
1759 }
1760 else
1761 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
1762 return iTable;
1763}
1764
1765
1766
1767/**
1768 * Does the actual unmounting.
1769 *
1770 * @returns Exactly one of the following IPRT status codes;
1771 * @retval VINF_SUCCESS if successfully umounted or nothing was mounted there.
1772 * @retval VERR_TRY_AGAIN if the shared folder is busy.
1773 * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
1774 * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
1775 * there.
1776 *
1777 * @param pszMountPoint The mount point.
1778 * @param pszName The shared folder (mapping) name.
1779 */
1780static int vbsvcAutomounterUnmount(const char *pszMountPoint, const char *pszName)
1781{
1782 /*
1783 * Retry for 5 seconds in a hope that busy mounts will quiet down.
1784 */
1785 for (unsigned iTry = 0; ; iTry++)
1786 {
1787 /*
1788 * Check what's mounted there before we start umounting stuff.
1789 */
1790 int rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
1791 if (rc == VINF_SUCCESS)
1792 { /* pszName is mounted there */ }
1793 else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
1794 return VINF_SUCCESS;
1795 else
1796 {
1797 Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
1798 return VERR_RESOURCE_BUSY;
1799 }
1800
1801 /*
1802 * Do host specific unmounting.
1803 */
1804#ifdef RT_OS_WINDOWS
1805 Assert(RT_C_IS_UPPER(pszMountPoint[0]) && pszMountPoint[1] == ':' && pszMountPoint[2] == '\0');
1806 RTUTF16 const wszDrive[4] = { (RTUTF16)pszMountPoint[0], ':', '\0', '\0' };
1807 DWORD dwErr = WNetCancelConnection2W(wszDrive, 0 /*dwFlags*/, FALSE /*fForce*/);
1808 if (dwErr == NO_ERROR)
1809 return VINF_SUCCESS;
1810 VGSvcVerbose(2, "vbsvcAutomounterUnmount: WNetCancelConnection2W returns %u for '%s' ('%s')\n", dwErr, pszMountPoint, pszName);
1811 if (dwErr == ERROR_NOT_CONNECTED)
1812 return VINF_SUCCESS;
1813
1814#elif defined(RT_OS_OS2)
1815 APIRET rcOs2 = DosFSAttach(pszMountPoint, "VBOXSF", NULL, 0, FS_DETACH);
1816 if (rcOs2 == NO_ERROR)
1817 return VINF_SUCCESS;
1818 VGSvcVerbose(2, "vbsvcAutomounterUnmount: DosFSAttach failed on '%s' ('%s'): %u\n", pszMountPoint, pszName, rcOs2);
1819 if (rcOs2 == ERROR_INVALID_FSD_NAME)
1820 return VERR_ACCESS_DENIED;
1821 if ( rcOs2 == ERROR_INVALID_DRIVE
1822 || rcOs2 == ERROR_INVALID_PATH)
1823 return VERR_TRY_AGAIN;
1824
1825#else
1826 int rc2 = umount(pszMountPoint);
1827 if (rc2 == 0)
1828 {
1829 /* Remove the mount directory if not directly under the root dir. */
1830 RTPATHPARSED Parsed;
1831 RT_ZERO(Parsed);
1832 RTPathParse(pszMountPoint, &Parsed, sizeof(Parsed), RTPATH_STR_F_STYLE_HOST);
1833 if (Parsed.cComps >= 3)
1834 RTDirRemove(pszMountPoint);
1835
1836 return VINF_SUCCESS;
1837 }
1838 rc2 = errno;
1839 VGSvcVerbose(2, "vbsvcAutomounterUnmount: umount failed on '%s' ('%s'): %d\n", pszMountPoint, pszName, rc2);
1840 if (rc2 != EBUSY && rc2 != EAGAIN)
1841 return VERR_ACCESS_DENIED;
1842#endif
1843
1844 /*
1845 * Check what's mounted there before we start delaying.
1846 */
1847 RTThreadSleep(8); /* fudge */
1848 rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
1849 if (rc == VINF_SUCCESS)
1850 { /* pszName is mounted there */ }
1851 else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
1852 return VINF_SUCCESS;
1853 else
1854 {
1855 Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
1856 return VERR_RESOURCE_BUSY;
1857 }
1858
1859 if (iTry >= 5)
1860 return VERR_TRY_AGAIN;
1861 RTThreadSleep(1000);
1862 }
1863}
1864
1865
1866/**
1867 * Unmounts a mount table entry and evicts it from the table if successful.
1868 *
1869 * @returns The next iTable (same value on success, +1 on failure).
1870 * @param pTable The mount table.
1871 * @param iTable The table entry.
1872 * @param pszReason Why we're here.
1873 */
1874static uint32_t vbsvcAutomounterUnmountEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable, const char *pszReason)
1875{
1876 Assert(iTable < pTable->cEntries);
1877 PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
1878 VGSvcVerbose(2, "vbsvcAutomounterUnmountEntry: #%u: '%s' at '%s' (reason: %s)\n",
1879 iTable, pEntry->pszName, pEntry->pszActualMountPoint, pszReason);
1880
1881 /*
1882 * Do we need to umount the entry? Return if unmount fails and we .
1883 */
1884 if (pEntry->pszActualMountPoint)
1885 {
1886 int rc = vbsvcAutomounterUnmount(pEntry->pszActualMountPoint, pEntry->pszName);
1887 if (rc == VERR_TRY_AGAIN)
1888 {
1889 VGSvcVerbose(1, "vbsvcAutomounterUnmountEntry: Keeping '%s' -> '%s' (VERR_TRY_AGAIN)\n",
1890 pEntry->pszActualMountPoint, pEntry->pszName);
1891 return iTable + 1;
1892 }
1893 }
1894
1895 /*
1896 * Remove the entry by shifting up the ones after it.
1897 */
1898 pTable->cEntries -= 1;
1899 uint32_t cAfter = pTable->cEntries - iTable;
1900 if (cAfter)
1901 memmove(&pTable->papEntries[iTable], &pTable->papEntries[iTable + 1], cAfter * sizeof(pTable->papEntries[0]));
1902 pTable->papEntries[pTable->cEntries] = NULL;
1903
1904 RTStrFree(pEntry->pszActualMountPoint);
1905 pEntry->pszActualMountPoint = NULL;
1906 RTStrFree(pEntry->pszMountPoint);
1907 pEntry->pszMountPoint = NULL;
1908 RTStrFree(pEntry->pszName);
1909 pEntry->pszName = NULL;
1910 RTMemFree(pEntry);
1911
1912 return iTable;
1913}
1914
1915
1916/**
1917 * @callback_method_impl{FNRTSORTCMP, For sorting the mappings by ID. }
1918 */
1919static DECLCALLBACK(int) vbsvcSharedFolderMappingCompare(void const *pvElement1, void const *pvElement2, void *pvUser)
1920{
1921 RT_NOREF_PV(pvUser);
1922 PVBGLR3SHAREDFOLDERMAPPING pMapping1 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement1;
1923 PVBGLR3SHAREDFOLDERMAPPING pMapping2 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement2;
1924 return pMapping1->u32Root < pMapping2->u32Root ? -1 : pMapping1->u32Root != pMapping2->u32Root ? 1 : 0;
1925}
1926
1927
1928/**
1929 * Refreshes the mount table.
1930 *
1931 * @returns true if we've processed the current config, false if we failed to
1932 * query the mappings.
1933 * @param pTable The mount table to refresh.
1934 */
1935static bool vbsvcAutomounterRefreshTable(PVBSVCAUTOMOUNTERTABLE pTable)
1936{
1937 /*
1938 * Query the root IDs of all auto-mountable shared folder mappings.
1939 */
1940 uint32_t cMappings = 0;
1941 PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
1942 int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
1943 if (RT_FAILURE(rc))
1944 {
1945 VGSvcError("vbsvcAutomounterRefreshTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
1946 return false;
1947 }
1948
1949 /*
1950 * Walk the table and the mappings in parallel, so we have to make sure
1951 * they are both sorted by root ID.
1952 */
1953 if (cMappings > 1)
1954 RTSortShell(paMappings, cMappings, sizeof(paMappings[0]), vbsvcSharedFolderMappingCompare, NULL);
1955
1956 /*
1957 * Pass #1: Do all the umounting.
1958 *
1959 * By doing the umount pass separately from the mount pass, we can
1960 * better handle changing involving the same mount points (switching
1961 * mount points between two shares, new share on same mount point but
1962 * with lower root ID, ++).
1963 */
1964 uint32_t iTable = 0;
1965 for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
1966 {
1967 /*
1968 * Unmount table entries up to idRootSrc.
1969 */
1970 uint32_t const idRootSrc = paMappings[iSrc].u32Root;
1971 while ( iTable < pTable->cEntries
1972 && pTable->papEntries[iTable]->idRoot < idRootSrc)
1973 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped");
1974
1975 /*
1976 * If the paMappings entry and the mount table entry has the same
1977 * root ID, umount if anything has changed or if we cannot query
1978 * the mapping data.
1979 */
1980 if (iTable < pTable->cEntries)
1981 {
1982 PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
1983 if (pEntry->idRoot == idRootSrc)
1984 {
1985 uint32_t uRootIdVer = UINT32_MAX;
1986 uint64_t fFlags = 0;
1987 char *pszName = NULL;
1988 char *pszMntPt = NULL;
1989 rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
1990 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
1991 if (RT_FAILURE(rc))
1992 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "VbglR3SharedFolderQueryFolderInfo failed");
1993 else if (pEntry->uRootIdVersion != uRootIdVer)
1994 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "root ID version changed");
1995 else if (RTPathCompare(pEntry->pszMountPoint, pszMntPt) != 0)
1996 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "mount point changed");
1997 else if (RTStrICmp(pEntry->pszName, pszName) != 0)
1998 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "name changed");
1999 else
2000 {
2001 VGSvcVerbose(3, "vbsvcAutomounterRefreshTable: Unchanged: %s -> %s\n", pEntry->pszMountPoint, pEntry->pszName);
2002 iTable++;
2003 }
2004 if (RT_SUCCESS(rc))
2005 {
2006 RTStrFree(pszName);
2007 RTStrFree(pszMntPt);
2008 }
2009 }
2010 }
2011 }
2012
2013 while (iTable < pTable->cEntries)
2014 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped (tail)");
2015
2016 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u entries in mount table after pass #1.\n", pTable->cEntries);
2017
2018 /*
2019 * Pass #2: Try mount new folders that has mount points assigned.
2020 * Pass #3: Try mount new folders not mounted in pass #2.
2021 */
2022 for (uint32_t iPass = 2; iPass <= 3; iPass++)
2023 {
2024 iTable = 0;
2025 for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
2026 {
2027 uint32_t const idRootSrc = paMappings[iSrc].u32Root;
2028
2029 /*
2030 * Skip tabel entries we couldn't umount in pass #1.
2031 */
2032 while ( iTable < pTable->cEntries
2033 && pTable->papEntries[iTable]->idRoot < idRootSrc)
2034 {
2035 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Skipping idRoot=%u %s\n",
2036 iPass, iSrc, iTable, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
2037 iTable++;
2038 }
2039
2040 /*
2041 * New share?
2042 */
2043 if ( iTable >= pTable->cEntries
2044 || pTable->papEntries[iTable]->idRoot != idRootSrc)
2045 {
2046 uint32_t uRootIdVer = UINT32_MAX;
2047 uint64_t fFlags = 0;
2048 char *pszName = NULL;
2049 char *pszMntPt = NULL;
2050 rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
2051 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
2052 if (RT_SUCCESS(rc))
2053 {
2054 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Mounting idRoot=%u/%u %s\n", iPass, iSrc, iTable,
2055 idRootSrc, iTable >= pTable->cEntries ? UINT32_MAX : pTable->papEntries[iTable]->idRoot, pszName);
2056 iTable = vbsvcAutomounterMountNewEntry(pTable, iTable, pszName, pszMntPt, fFlags,
2057 idRootSrc, uRootIdVer, iPass == 3);
2058
2059 RTStrFree(pszName);
2060 RTStrFree(pszMntPt);
2061 }
2062 else
2063 VGSvcVerbose(1, "vbsvcAutomounterRefreshTable: VbglR3SharedFolderQueryFolderInfo failed: %Rrc\n", rc);
2064 }
2065 else
2066 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: idRootSrc=%u vs idRoot=%u %s\n", iPass, iSrc,
2067 iTable, idRootSrc, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
2068 }
2069 }
2070
2071 VbglR3SharedFolderFreeMappings(paMappings);
2072 return true;
2073}
2074
2075
2076/**
2077 * @interface_method_impl{VBOXSERVICE,pfnWorker}
2078 */
2079static DECLCALLBACK(int) vbsvcAutomounterWorker(bool volatile *pfShutdown)
2080{
2081 /*
2082 * Tell the control thread that it can continue spawning services.
2083 */
2084 RTThreadUserSignal(RTThreadSelf());
2085
2086 /* Divert old hosts to original auto-mount code. */
2087 if (!g_fHostSupportsWaitAndInfoQuery)
2088 return vbsvcAutoMountWorkerOld(pfShutdown);
2089
2090 /*
2091 * Initialize the state in case we're restarted...
2092 */
2093 VBSVCAUTOMOUNTERTABLE MountTable = { 0, 0, NULL };
2094 int rc = vbsvcAutomounterPopulateTable(&MountTable);
2095 if (RT_FAILURE(rc))
2096 {
2097 VGSvcError("vbsvcAutomounterWorker: vbsvcAutomounterPopulateTable failed (%Rrc), quitting!\n", rc);
2098 return rc;
2099 }
2100
2101 /*
2102 * Work loop.
2103 */
2104 uint32_t uConfigVer = UINT32_MAX;
2105 uint32_t uNewVersion = 0;
2106 bool fForceRefresh = true;
2107 while (!*pfShutdown)
2108 {
2109 /*
2110 * Update the mounts.
2111 */
2112 if ( uConfigVer != uNewVersion
2113 || fForceRefresh)
2114 {
2115 fForceRefresh = !vbsvcAutomounterRefreshTable(&MountTable);
2116 uConfigVer = uNewVersion;
2117 }
2118
2119 /*
2120 * Wait for more to do.
2121 */
2122 if (!*pfShutdown)
2123 {
2124 uNewVersion = uConfigVer - 1;
2125 VGSvcVerbose(2, "vbsvcAutomounterWorker: Waiting with uConfigVer=%u\n", uConfigVer);
2126 rc = VbglR3SharedFolderWaitForMappingsChanges(g_idClientSharedFolders, uConfigVer, &uNewVersion);
2127 VGSvcVerbose(2, "vbsvcAutomounterWorker: Woke up with uNewVersion=%u and rc=%Rrc\n", uNewVersion, rc);
2128
2129 /* Delay a little before doing a table refresh so the GUI can finish
2130 all its updates. Delay a little longer on non-shutdown failure to
2131 avoid eating too many CPU cycles if something goes wrong here... */
2132 if (!*pfShutdown)
2133 RTSemEventMultiWait(g_hAutoMountEvent, RT_SUCCESS(rc) ? 256 : 1000);
2134 }
2135 }
2136
2137 /*
2138 * Destroy the mount table.
2139 */
2140 while (MountTable.cEntries-- > 0)
2141 RTMemFree(MountTable.papEntries[MountTable.cEntries]);
2142 MountTable.papEntries = NULL;
2143
2144 VGSvcVerbose(3, "vbsvcAutomounterWorker: Finished\n");
2145 return VINF_SUCCESS;
2146}
2147
2148
2149/**
2150 * @interface_method_impl{VBOXSERVICE,pfnStop}
2151 */
2152static DECLCALLBACK(void) vbsvcAutomounterStop(void)
2153{
2154 RTSemEventMultiSignal(g_hAutoMountEvent);
2155 if (g_fHostSupportsWaitAndInfoQuery)
2156 VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
2157}
2158
2159
2160/**
2161 * @interface_method_impl{VBOXSERVICE,pfnTerm}
2162 */
2163static DECLCALLBACK(void) vbsvcAutomounterTerm(void)
2164{
2165 VGSvcVerbose(3, "vbsvcAutoMountTerm\n");
2166
2167 if (g_fHostSupportsWaitAndInfoQuery)
2168 VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
2169
2170 VbglR3SharedFolderDisconnect(g_idClientSharedFolders);
2171 g_idClientSharedFolders = 0;
2172
2173 if (g_hAutoMountEvent != NIL_RTSEMEVENTMULTI)
2174 {
2175 RTSemEventMultiDestroy(g_hAutoMountEvent);
2176 g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
2177 }
2178}
2179
2180
2181/**
2182 * The 'automount' service description.
2183 */
2184VBOXSERVICE g_AutoMount =
2185{
2186 /* pszName. */
2187 "automount",
2188 /* pszDescription. */
2189 "Automounter for Shared Folders",
2190 /* pszUsage. */
2191 NULL,
2192 /* pszOptions. */
2193 NULL,
2194 /* methods */
2195 VGSvcDefaultPreInit,
2196 VGSvcDefaultOption,
2197 vbsvcAutomounterInit,
2198 vbsvcAutomounterWorker,
2199 vbsvcAutomounterStop,
2200 vbsvcAutomounterTerm
2201};
2202
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use