VirtualBox

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

Last change on this file since 108754 was 108754, checked in by vboxsync, 6 weeks ago

VBoxService/Shared Folders: Skip initializing / enabling the 'automount' sub service on <= NT4 guests, as we don't support Shared Folders on those.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette