VirtualBox

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

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

VBoxService,VBoxControl: OS/2 build fixes.

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