VirtualBox

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

Last change on this file since 108900 was 108755, checked in by vboxsync, 4 weeks ago

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

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