VirtualBox

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

Last change on this file since 77138 was 77138, checked in by vboxsync, 6 years ago

linux/vboxsf: More read code tweaking, making the max read/write buffer size configurable (mount option maxiopages). Also added code for dealing with the host running out of heap. bugref:9172

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