VirtualBox

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

Last change on this file since 98709 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

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