VirtualBox

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

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

VBoxService/Shared Folders: Resolve ERROR_BAD_PROVIDER before calling RTErrConvertFromWin32() in vbsvcAutomounterMountIt(), to avoid assertions in debug builds.

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