VirtualBox

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

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

VBoxControl,VBoxService: Some OS/2 bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.5 KB
Line 
1/* $Id: VBoxServiceAutoMount.cpp 75467 2018-11-14 20:45:16Z 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# include <os2emx.h>
57#else
58# include <errno.h>
59# include <grp.h>
60# include <sys/mount.h>
61# ifdef RT_OS_SOLARIS
62# include <sys/mntent.h>
63# include <sys/mnttab.h>
64# include <sys/vfs.h>
65RT_C_DECLS_BEGIN /* Only needed for old code.*/
66# include "../../linux/sharedfolders/vbsfmount.h"
67RT_C_DECLS_END
68# elif defined(RT_OS_LINUX)
69# include <mntent.h>
70# include <paths.h>
71RT_C_DECLS_BEGIN
72# include "../../linux/sharedfolders/vbsfmount.h"
73RT_C_DECLS_END
74# else
75# error "Port me!"
76# endif
77# include <unistd.h>
78#endif
79
80
81
82/*********************************************************************************************************************************
83* Defined Constants And Macros *
84*********************************************************************************************************************************/
85/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
86 * Default mount directory (unix only).
87 */
88#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
89# ifdef RT_OS_SOLARIS
90# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/mnt"
91# else
92# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/media"
93# endif
94#endif
95
96/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
97 * Default mount prefix (unix only).
98 */
99#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
100# define VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX "sf_"
101#endif
102
103#ifndef _PATH_MOUNTED
104# ifdef RT_OS_SOLARIS
105# define _PATH_MOUNTED "/etc/mnttab"
106# else
107# define _PATH_MOUNTED "/etc/mtab"
108# endif
109#endif
110
111/** @def VBOXSERVICE_AUTOMOUNT_MIQF
112 * The drive letter / path mount point flag. */
113#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
114# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_DRIVE_LETTER
115#else
116# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_PATH
117#endif
118
119
120/*********************************************************************************************************************************
121* Structures and Typedefs *
122*********************************************************************************************************************************/
123/**
124 * Automounter mount table entry.
125 *
126 * This holds the information returned by SHFL_FN_QUERY_MAP_INFO and
127 * additional mount state info. We only keep entries for mounted mappings.
128 */
129typedef struct VBSVCAUTOMOUNTERENTRY
130{
131 /** The root ID. */
132 uint32_t idRoot;
133 /** The root ID version. */
134 uint32_t uRootIdVersion;
135 /** Map info flags, SHFL_MIF_XXX. */
136 uint64_t fFlags;
137 /** The shared folder (mapping) name. */
138 char *pszName;
139 /** The configured mount point, NULL if none. */
140 char *pszMountPoint;
141 /** The actual mount point, NULL if not mount. */
142 char *pszActualMountPoint;
143} VBSVCAUTOMOUNTERENTRY;
144/** Pointer to an automounter entry. */
145typedef VBSVCAUTOMOUNTERENTRY *PVBSVCAUTOMOUNTERENTRY;
146
147/** Automounter mount table. */
148typedef struct VBSVCAUTOMOUNTERTABLE
149{
150 /** Current number of entries in the array. */
151 uint32_t cEntries;
152 /** Max number of entries the array can hold w/o growing it. */
153 uint32_t cAllocated;
154 /** Pointer to an array of entry pointers. */
155 PVBSVCAUTOMOUNTERENTRY *papEntries;
156} VBSVCAUTOMOUNTERTABLE;
157/** Pointer to an automounter mount table. */
158typedef VBSVCAUTOMOUNTERTABLE *PVBSVCAUTOMOUNTERTABLE;
159
160
161/*********************************************************************************************************************************
162* Global Variables *
163*********************************************************************************************************************************/
164/** The semaphore we're blocking on. */
165static RTSEMEVENTMULTI g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
166/** The Shared Folders service client ID. */
167static uint32_t g_idClientSharedFolders = 0;
168/** Set if we can wait on changes to the mappings. */
169static bool g_fHostSupportsWaitAndInfoQuery = false;
170
171#ifdef RT_OS_OS2
172/** The attachment tag we use to identify attchments that belongs to us. */
173static char const g_szTag[] = "VBoxAutomounter";
174#elif defined(RT_OS_LINUX)
175/** Tag option value that lets us identify mounts that belongs to us. */
176static char const g_szTag[] = "VBoxAutomounter";
177#elif defined(RT_OS_SOLARIS)
178/** Dummy mount option that lets us identify mounts that belongs to us. */
179static char const g_szTag[] = "VBoxAutomounter";
180#endif
181
182
183
184/**
185 * @interface_method_impl{VBOXSERVICE,pfnInit}
186 */
187static DECLCALLBACK(int) vbsvcAutomounterInit(void)
188{
189 VGSvcVerbose(3, "vbsvcAutomounterInit\n");
190
191 int rc = RTSemEventMultiCreate(&g_hAutoMountEvent);
192 AssertRCReturn(rc, rc);
193
194 rc = VbglR3SharedFolderConnect(&g_idClientSharedFolders);
195 if (RT_SUCCESS(rc))
196 {
197 VGSvcVerbose(3, "vbsvcAutomounterInit: Service Client ID: %#x\n", g_idClientSharedFolders);
198 g_fHostSupportsWaitAndInfoQuery = RT_SUCCESS(VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders));
199 }
200 else
201 {
202 /* If the service was not found, we disable this service without
203 causing VBoxService to fail. */
204 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
205 {
206 VGSvcVerbose(0, "vbsvcAutomounterInit: Shared Folders service is not available\n");
207 rc = VERR_SERVICE_DISABLED;
208 }
209 else
210 VGSvcError("Control: Failed to connect to the Shared Folders service! Error: %Rrc\n", rc);
211 RTSemEventMultiDestroy(g_hAutoMountEvent);
212 g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
213 }
214
215 return rc;
216}
217
218
219#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) /* The old code: */
220
221/**
222 * @todo Integrate into RTFsQueryMountpoint()?
223 */
224static bool vbsvcAutoMountShareIsMountedOld(const char *pszShare, char *pszMountPoint, size_t cbMountPoint)
225{
226 AssertPtrReturn(pszShare, false);
227 AssertPtrReturn(pszMountPoint, false);
228 AssertReturn(cbMountPoint, false);
229
230 bool fMounted = false;
231
232# if defined(RT_OS_SOLARIS)
233 /** @todo What to do if we have a relative path in mtab instead
234 * of an absolute one ("temp" vs. "/media/temp")?
235 * procfs contains the full path but not the actual share name ...
236 * FILE *pFh = setmntent("/proc/mounts", "r+t"); */
237 FILE *pFh = fopen(_PATH_MOUNTED, "r");
238 if (!pFh)
239 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
240 else
241 {
242 mnttab mntTab;
243 while ((getmntent(pFh, &mntTab)))
244 {
245 if (!RTStrICmp(mntTab.mnt_special, pszShare))
246 {
247 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp)
248 ? true : false;
249 break;
250 }
251 }
252 fclose(pFh);
253 }
254# elif defined(RT_OS_LINUX)
255 FILE *pFh = setmntent(_PATH_MOUNTED, "r+t"); /** @todo r=bird: why open it for writing? (the '+') */
256 if (pFh == NULL)
257 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
258 else
259 {
260 mntent *pMntEnt;
261 while ((pMntEnt = getmntent(pFh)))
262 {
263 if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare))
264 {
265 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir)
266 ? true : false;
267 break;
268 }
269 }
270 endmntent(pFh);
271 }
272# else
273# error "PORTME!"
274# endif
275
276 VGSvcVerbose(4, "vbsvcAutoMountShareIsMountedOld: Share '%s' at mount point '%s' = %s\n",
277 pszShare, fMounted ? pszMountPoint : "<None>", fMounted ? "Yes" : "No");
278 return fMounted;
279}
280
281
282/**
283 * Unmounts a shared folder.
284 *
285 * @returns VBox status code
286 * @param pszMountPoint The shared folder mount point.
287 */
288static int vbsvcAutoMountUnmountOld(const char *pszMountPoint)
289{
290 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
291
292 int rc = VINF_SUCCESS;
293 uint8_t uTries = 0;
294 int r;
295 while (uTries++ < 3)
296 {
297 r = umount(pszMountPoint);
298 if (r == 0)
299 break;
300/** @todo r=bird: Why do sleep 5 seconds after the final retry?
301 * May also be a good idea to check for EINVAL or other signs that someone
302 * else have already unmounted the share. */
303 RTThreadSleep(5000); /* Wait a while ... */
304 }
305 if (r == -1) /** @todo r=bird: RTThreadSleep set errno. */
306 rc = RTErrConvertFromErrno(errno);
307 return rc;
308}
309
310
311/**
312 * Prepares a mount point (create it, set group and mode).
313 *
314 * @returns VBox status code
315 * @param pszMountPoint The mount point.
316 * @param pszShareName Unused.
317 * @param pOpts For getting the group ID.
318 */
319static int vbsvcAutoMountPrepareMountPointOld(const char *pszMountPoint, const char *pszShareName, vbsf_mount_opts *pOpts)
320{
321 AssertPtrReturn(pOpts, VERR_INVALID_PARAMETER);
322 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
323 AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER);
324
325 RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */
326 int rc = RTDirCreateFullPath(pszMountPoint, fMode);
327 if (RT_SUCCESS(rc))
328 {
329 rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, pOpts->gid, RTPATH_F_ON_LINK);
330 if (RT_SUCCESS(rc))
331 {
332 rc = RTPathSetMode(pszMountPoint, fMode);
333 if (RT_FAILURE(rc))
334 {
335 if (rc == VERR_WRITE_PROTECT)
336 {
337 VGSvcVerbose(3, "vbsvcAutoMountPrepareMountPointOld: Mount directory '%s' already is used/mounted\n",
338 pszMountPoint);
339 rc = VINF_SUCCESS;
340 }
341 else
342 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set mode %RTfmode for mount directory '%s', rc = %Rrc\n",
343 fMode, pszMountPoint, rc);
344 }
345 }
346 else
347 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set permissions for mount directory '%s', rc = %Rrc\n",
348 pszMountPoint, rc);
349 }
350 else
351 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not create mount directory '%s' with mode %RTfmode, rc = %Rrc\n",
352 pszMountPoint, fMode, rc);
353 return rc;
354}
355
356
357/**
358 * Mounts a shared folder.
359 *
360 * @returns VBox status code reflecting unmount and mount point preparation
361 * results, but not actual mounting
362 *
363 * @param pszShareName The shared folder name.
364 * @param pszMountPoint The mount point.
365 */
366static int vbsvcAutoMountSharedFolderOld(const char *pszShareName, const char *pszMountPoint)
367{
368 /*
369 * Linux and solaris share the same mount structure.
370 */
371 struct group *grp_vboxsf = getgrnam("vboxsf");
372 if (!grp_vboxsf)
373 {
374 VGSvcError("vbsvcAutoMountWorker: Group 'vboxsf' does not exist\n");
375 return VINF_SUCCESS;
376 }
377
378 struct vbsf_mount_opts Opts =
379 {
380 0, /* uid */
381 (int)grp_vboxsf->gr_gid, /* gid */
382 0, /* ttl */
383 0770, /* dmode, owner and group "vboxsf" have full access */
384 0770, /* fmode, owner and group "vboxsf" have full access */
385 0, /* dmask */
386 0, /* fmask */
387 0, /* ronly */
388 0, /* sloppy */
389 0, /* noexec */
390 0, /* nodev */
391 0, /* nosuid */
392 0, /* remount */
393 "\0", /* nls_name */
394 NULL, /* convertcp */
395 };
396
397 int rc = vbsvcAutoMountPrepareMountPointOld(pszMountPoint, pszShareName, &Opts);
398 if (RT_SUCCESS(rc))
399 {
400# ifdef RT_OS_SOLARIS
401 int fFlags = 0;
402 if (Opts.ronly)
403 fFlags |= MS_RDONLY;
404 char szOptBuf[MAX_MNTOPT_STR] = { '\0', };
405 RTStrPrintf(szOptBuf, sizeof(szOptBuf), "uid=%d,gid=%d,dmode=%0o,fmode=%0o,dmask=%0o,fmask=%0o",
406 Opts.uid, Opts.gid, Opts.dmode, Opts.fmode, Opts.dmask, Opts.fmask);
407 int r = mount(pszShareName,
408 pszMountPoint,
409 fFlags | MS_OPTIONSTR,
410 "vboxfs",
411 NULL, /* char *dataptr */
412 0, /* int datalen */
413 szOptBuf,
414 sizeof(szOptBuf));
415 if (r == 0)
416 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
417 else if (errno != EBUSY) /* Share is already mounted? Then skip error msg. */
418 VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s', error = %s\n",
419 pszShareName, pszMountPoint, strerror(errno));
420
421# else /* RT_OS_LINUX */
422 unsigned long fFlags = MS_NODEV;
423
424 /*const char *szOptions = { "rw" }; - ??? */
425 struct vbsf_mount_info_new mntinf;
426 RT_ZERO(mntinf);
427
428 mntinf.nullchar = '\0';
429 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
430 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
431 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
432 mntinf.length = sizeof(mntinf);
433
434 mntinf.uid = Opts.uid;
435 mntinf.gid = Opts.gid;
436 mntinf.ttl = Opts.ttl;
437 mntinf.dmode = Opts.dmode;
438 mntinf.fmode = Opts.fmode;
439 mntinf.dmask = Opts.dmask;
440 mntinf.fmask = Opts.fmask;
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", 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, &cbBuf);
943 if (rcOs2 == NO_ERROR)
944 {
945 const char *pszFsdName = &uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
946 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
947 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
948 {
949 const char *pszMountedName = &pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
950 const char *pszTag = 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 if (!pFile)
969 pFile = setmntent("/etc/mtab", "r");
970 if (pFile)
971 {
972 rc = VWRN_NOT_FOUND;
973 struct mntent *pEntry;
974 while ((pEntry = getmntent(pFile)) != NULL)
975 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
976 if (strstr(pEntry->mnt_opts, g_szTag) != NULL)
977 {
978 rc = vbsvcAutomounterAddEntry(pMountTable, pEntry->mnt_fsname, pEntry->mnt_dir);
979 if (RT_FAILURE(rc))
980 {
981 endmntent(pFile);
982 return rc;
983 }
984 }
985 endmntent(pFile);
986 }
987 else
988 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d) or '/proc/mounts' (errno=%d)\n",
989 _PATH_MOUNTED, errno);
990
991#elif defined(RT_OS_SOLARIS)
992 /*
993 * Look thru the system mount table and inspect the vboxsf mounts.
994 */
995 FILE *pFile = fopen(_PATH_MOUNTED, "r");
996 if (pFile)
997 {
998 rc = VINF_SUCCESS;
999 struct mnttab Entry;
1000 while (getmntent(pFile, &Entry) == 0)
1001 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
1002 {
1003 /* Look for the dummy automounter option. */
1004 if ( Entry.mnt_mntopts != NULL
1005 && strstr(Entry.mnt_mntopts, g_szTag) != NULL)
1006 {
1007 rc = vbsvcAutomounterAddEntry(pMountTable, Entry.mnt_special, Entry.mnt_mountp);
1008 if (RT_FAILURE(rc))
1009 {
1010 fclose(pFile);
1011 return rc;
1012 }
1013 }
1014 }
1015 fclose(pFile);
1016 }
1017 else
1018 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
1019
1020#else
1021# error "PORTME!"
1022#endif
1023
1024 /*
1025 * Try reconcile the detected folders with data from the host.
1026 */
1027 uint32_t cMappings = 0;
1028 PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
1029 rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
1030 if (RT_SUCCESS(rc))
1031 {
1032 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
1033 {
1034 uint32_t const idRootSrc = paMappings[i].u32Root;
1035
1036 uint32_t uRootIdVer = UINT32_MAX;
1037 uint64_t fFlags = 0;
1038 char *pszName = NULL;
1039 char *pszMntPt = NULL;
1040 int rc2 = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
1041 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
1042 if (RT_SUCCESS(rc2))
1043 {
1044 uint32_t iPrevHit = UINT32_MAX;
1045 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
1046 {
1047 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
1048 if (RTStrICmp(pEntry->pszName, pszName) == 0)
1049 {
1050 VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Identified %s -> %s: idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
1051 pEntry->pszActualMountPoint, pEntry->pszName, idRootSrc, uRootIdVer, fFlags, pszMntPt);
1052 pEntry->fFlags = fFlags;
1053 pEntry->idRoot = idRootSrc;
1054 pEntry->uRootIdVersion = uRootIdVer;
1055 RTStrFree(pEntry->pszMountPoint);
1056 pEntry->pszMountPoint = RTStrDup(pszMntPt);
1057 if (!pEntry->pszMountPoint)
1058 {
1059 rc = VERR_NO_MEMORY;
1060 break;
1061 }
1062
1063 /* If multiple mappings of the same folder, pick the first or the one
1064 with matching mount point. */
1065 if (iPrevHit == UINT32_MAX)
1066 iPrevHit = iTable;
1067 else if (RTPathCompare(pszMntPt, pEntry->pszActualMountPoint) == 0)
1068 {
1069 if (iPrevHit != UINT32_MAX)
1070 pMountTable->papEntries[iPrevHit]->uRootIdVersion -= 1;
1071 iPrevHit = iTable;
1072 }
1073 else
1074 pEntry->uRootIdVersion -= 1;
1075 }
1076 }
1077
1078 RTStrFree(pszName);
1079 RTStrFree(pszMntPt);
1080 }
1081 else
1082 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderQueryFolderInfo(%u) failed: %Rrc\n", idRootSrc, rc2);
1083 }
1084
1085 VbglR3SharedFolderFreeMappings(paMappings);
1086
1087 /*
1088 * Sort the table by root ID.
1089 */
1090 if (pMountTable->cEntries > 1)
1091 RTSortApvShell((void **)pMountTable->papEntries, pMountTable->cEntries, vbsvcAutomounterCompareEntry, NULL);
1092
1093 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
1094 {
1095 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
1096 if (pMountTable->papEntries[iTable]->idRoot != UINT32_MAX)
1097 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
1098 iTable, pEntry->pszActualMountPoint, pEntry->pszName, pEntry->idRoot, pEntry->uRootIdVersion,
1099 pEntry->fFlags, pEntry->pszMountPoint);
1100 else
1101 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s - not identified!\n",
1102 iTable, pEntry->pszActualMountPoint, pEntry->pszName);
1103 }
1104 }
1105 else
1106 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
1107 return rc;
1108}
1109
1110
1111/**
1112 * Checks whether the shared folder @a pszName is mounted on @a pszMountPoint.
1113 *
1114 * @returns Exactly one of the following IPRT status codes;
1115 * @retval VINF_SUCCESS if mounted
1116 * @retval VWRN_NOT_FOUND if nothing is mounted at @a pszMountPoint.
1117 * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
1118 * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
1119 * there.
1120 *
1121 * @param pszMountPoint The mount point to check.
1122 * @param pszName The name of the shared folder (mapping).
1123 */
1124static int vbsvcAutomounterQueryMountPoint(const char *pszMountPoint, const char *pszName)
1125{
1126 VGSvcVerbose(4, "vbsvcAutomounterQueryMountPoint: pszMountPoint=%s pszName=%s\n", pszMountPoint, pszName);
1127
1128#ifdef RT_OS_WINDOWS
1129 /*
1130 * We could've used RTFsQueryType here but would then have to
1131 * calling RTFsQueryLabel for the share name hint, ending up
1132 * doing the same work twice. We could also use QueryDosDeviceW,
1133 * but output is less clear...
1134 */
1135 PRTUTF16 pwszMountPoint = NULL;
1136 int rc = RTStrToUtf16(pszMountPoint, &pwszMountPoint);
1137 if (RT_SUCCESS(rc))
1138 {
1139 DWORD uSerial = 0;
1140 DWORD cchCompMax = 0;
1141 DWORD fFlags = 0;
1142 RTUTF16 wszLabel[512];
1143 RTUTF16 wszFileSystem[256];
1144 RT_ZERO(wszLabel);
1145 RT_ZERO(wszFileSystem);
1146 if (GetVolumeInformationW(pwszMountPoint, wszLabel, RT_ELEMENTS(wszLabel) - 1, &uSerial, &cchCompMax, &fFlags,
1147 wszFileSystem, RT_ELEMENTS(wszFileSystem) - 1))
1148 {
1149 if (RTUtf16ICmpAscii(wszFileSystem, "VBoxSharedFolderFS") == 0)
1150 {
1151 char *pszLabel = NULL;
1152 rc = RTUtf16ToUtf8(wszLabel, &pszLabel);
1153 if (RT_SUCCESS(rc))
1154 {
1155 const char *pszMountedName = pszLabel;
1156 if (RTStrStartsWith(pszMountedName, "VBOX_"))
1157 pszMountedName += sizeof("VBOX_") - 1;
1158 if (RTStrICmp(pszMountedName, pszName) == 0)
1159 {
1160 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1161 pszName, pszMountPoint);
1162 rc = VINF_SUCCESS;
1163 }
1164 else
1165 {
1166 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1167 pszMountedName, pszMountPoint, pszName);
1168 rc = VERR_RESOURCE_BUSY;
1169 }
1170 RTStrFree(pszLabel);
1171 }
1172 else
1173 {
1174 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: RTUtf16ToUtf8(%ls,) failed: %Rrc\n", wszLabel, rc);
1175 rc = VERR_RESOURCE_BUSY;
1176 }
1177 }
1178 else
1179 {
1180 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%ls' with label '%ls' mount at '%s', not '%s'...\n",
1181 wszFileSystem, wszLabel, pszMountPoint, pszName);
1182 rc = VERR_ACCESS_DENIED;
1183 }
1184 }
1185 else
1186 {
1187 rc = GetLastError();
1188 if (rc != ERROR_PATH_NOT_FOUND || g_cVerbosity >= 4)
1189 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: GetVolumeInformationW('%ls',,,,) failed: %u\n", pwszMountPoint, rc);
1190 rc = VWRN_NOT_FOUND;
1191 }
1192 RTUtf16Free(pwszMountPoint);
1193 }
1194 else
1195 {
1196 VGSvcError("vbsvcAutomounterQueryMountPoint: RTStrToUtf16(%s,) -> %Rrc\n", pszMountPoint, rc);
1197 rc = VWRN_NOT_FOUND;
1198 }
1199 return rc;
1200
1201#elif defined(RT_OS_OS2)
1202 /*
1203 * Query file system attachment info for the given drive letter.
1204 */
1205 union
1206 {
1207 FSQBUFFER2 FsQueryBuf;
1208 char achPadding[512];
1209 } uBuf;
1210 RT_ZERO(uBuf);
1211
1212 ULONG cbBuf = sizeof(uBuf);
1213 APIRET rcOs2 = DosQueryFSAttach((PCSZ)pFsInfo->szMountpoint, 0, FSAIL_QUERYNAME, uBuf, &cbBuf);
1214 int rc;
1215 if (rcOs2 == NO_ERROR)
1216 {
1217 const char *pszFsdName = &uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
1218 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
1219 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
1220 {
1221 const char *pszMountedName = &pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
1222 if (RTStrICmp(pszMountedName, pszName) == 0)
1223 {
1224 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1225 pszName, pszMountPoint);
1226 rc = VINF_SUCCESS;
1227 }
1228 else
1229 {
1230 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1231 pszMountedName, pszMountPoint, pszName);
1232 rc = VERR_RESOURCE_BUSY;
1233 }
1234 }
1235 else
1236 {
1237 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' type %u mount at '%s', not '%s'...\n",
1238 pszFsdName, uBuf.FsQueryBuf.iType, pszMountPoint, pszName);
1239 rc = VERR_ACCESS_DENIED;
1240 }
1241 }
1242 else
1243 {
1244 rc = VWRN_NOT_FOUND;
1245 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: DosQueryFSAttach(%s) -> %u\n", pszMountPoint, rcOs2);
1246 AssertMsgStmt(rcOs2 != ERROR_BUFFER_OVERFLOW && rcOs2 != ERROR_INVALID_PARAMETER,
1247 ("%s -> %u\n", pszMountPoint, rcOs2), rc = VERR_ACCESS_DENIED);
1248 }
1249 return rc;
1250
1251#elif defined(RT_OS_LINUX)
1252 /*
1253 * Scan one of the mount table file for the mount point and then
1254 * match file system and device/share.
1255 */
1256 FILE *pFile = setmntent("/proc/mounts", "r");
1257 int rc = errno;
1258 if (!pFile)
1259 pFile = setmntent(_PATH_MOUNTED, "r");
1260 if (pFile)
1261 {
1262 rc = VWRN_NOT_FOUND;
1263 struct mntent *pEntry;
1264 while ((pEntry = getmntent(pFile)) != NULL)
1265 if (RTPathCompare(pEntry->mnt_dir, pszMountPoint) == 0)
1266 {
1267 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
1268 {
1269 if (RTStrICmp(pEntry->mnt_fsname, pszName) == 0)
1270 {
1271 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1272 pszName, pszMountPoint);
1273 rc = VINF_SUCCESS;
1274 }
1275 else
1276 {
1277 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1278 pEntry->mnt_fsname, pszMountPoint, pszName);
1279 rc = VERR_RESOURCE_BUSY;
1280 }
1281 }
1282 else
1283 {
1284 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1285 pEntry->mnt_type, pEntry->mnt_fsname, pszMountPoint, pszName);
1286 rc = VERR_ACCESS_DENIED;
1287 }
1288 /* We continue searching in case of stacked mounts, we want the last one. */
1289 }
1290 endmntent(pFile);
1291 }
1292 else
1293 {
1294 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '/proc/mounts' (errno=%d) or '%s' (errno=%d)\n",
1295 rc, _PATH_MOUNTED, errno);
1296 rc = VERR_ACCESS_DENIED;
1297 }
1298 return rc;
1299
1300#elif defined(RT_OS_SOLARIS)
1301 /*
1302 * Similar to linux.
1303 */
1304 int rc;
1305 FILE *pFile = fopen(_PATH_MOUNTED, "r");
1306 if (pFile)
1307 {
1308 rc = VWRN_NOT_FOUND;
1309 struct mnttab Entry;
1310 while (getmntent(pFile, &Entry) == 0)
1311 if (RTPathCompare(Entry.mnt_mountp, pszMountPoint) == 0)
1312 {
1313 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
1314 {
1315 if (RTStrICmp(Entry.mnt_special, pszName) == 0)
1316 {
1317 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1318 pszName, pszMountPoint);
1319 rc = VINF_SUCCESS;
1320 }
1321 else
1322 {
1323 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1324 Entry.mnt_special, pszMountPoint, pszName);
1325 rc = VERR_RESOURCE_BUSY;
1326 }
1327 }
1328 else
1329 {
1330 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1331 Entry.mnt_fstype, Entry.mnt_special, pszMountPoint, pszName);
1332 rc = VERR_ACCESS_DENIED;
1333 }
1334 /* We continue searching in case of stacked mounts, we want the last one. */
1335 }
1336 fclose(pFile);
1337 }
1338 else
1339 {
1340 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
1341 rc = VERR_ACCESS_DENIED;
1342 }
1343 return rc;
1344#else
1345# error "PORTME"
1346#endif
1347}
1348
1349
1350/**
1351 * Worker for vbsvcAutomounterMountNewEntry that does the OS mounting.
1352 *
1353 * @returns IPRT status code.
1354 * @param pEntry The entry to try mount.
1355 */
1356static int vbsvcAutomounterMountIt(PVBSVCAUTOMOUNTERENTRY pEntry)
1357{
1358 VGSvcVerbose(3, "vbsvcAutomounterMountIt: Trying to mount '%s' (idRoot=%#x) on '%s'...\n",
1359 pEntry->pszName, pEntry->idRoot, pEntry->pszActualMountPoint);
1360#ifdef RT_OS_WINDOWS
1361 /*
1362 * Attach the shared folder using WNetAddConnection2W.
1363 *
1364 * According to google we should get a drive symlink in \\GLOBAL?? when
1365 * we are running under the system account. Otherwise it will a session
1366 * local link (\\??).
1367 */
1368 Assert(RT_C_IS_UPPER(pEntry->pszActualMountPoint[0]) && pEntry->pszActualMountPoint[1] == ':' && pEntry->pszActualMountPoint[2] == '\0');
1369 RTUTF16 wszDrive[4] = { pEntry->pszActualMountPoint[0], ':', '\0', '\0' };
1370
1371 RTUTF16 wszPrefixedName[RTPATH_MAX];
1372 int rc = RTUtf16CopyAscii(wszPrefixedName, RT_ELEMENTS(wszPrefixedName), "\\\\VBoxSvr\\");
1373 AssertRC(rc);
1374
1375 PRTUTF16 pwszName = &wszPrefixedName[RTUtf16Len(wszPrefixedName)];
1376 rc = RTStrToUtf16Ex(pEntry->pszName, RTSTR_MAX, &pwszName, pwszName - wszPrefixedName, NULL);
1377 if (RT_FAILURE(rc))
1378 {
1379 VGSvcError("vbsvcAutomounterMountIt: RTStrToUtf16Ex failed on '%s': %Rrc\n", pEntry->pszName, rc);
1380 return rc;
1381 }
1382
1383 NETRESOURCEW NetRsrc;
1384 RT_ZERO(NetRsrc);
1385 NetRsrc.dwType = RESOURCETYPE_DISK;
1386 NetRsrc.lpLocalName = wszDrive;
1387 NetRsrc.lpRemoteName = wszPrefixedName;
1388 NetRsrc.lpProvider = L"VirtualBox Shared Folders"; /* Only try our provider. */
1389 NetRsrc.lpComment = pwszName;
1390
1391 DWORD dwErr = WNetAddConnection2W(&NetRsrc, NULL /*pwszPassword*/, NULL /*pwszUserName*/, 0 /*dwFlags*/);
1392 if (dwErr == NO_ERROR)
1393 {
1394 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1395 pEntry->pszName, pEntry->pszActualMountPoint);
1396 return VINF_SUCCESS;
1397 }
1398 VGSvcError("vbsvcAutomounterMountIt: Failed to attach '%s' to '%s': %u\n",
1399 pEntry->pszName, pEntry->pszActualMountPoint, rc);
1400 return VERR_OPEN_FAILED;
1401
1402#elif defined(RT_OS_OS2)
1403 /*
1404 * It's a rather simple affair on OS/2.
1405 *
1406 * In order to be able to detect our mounts we add a 2nd string after
1407 * the folder name that tags the attachment. The IFS will remember this
1408 * and return it when DosQueryFSAttach is called.
1409 *
1410 * Note! Kernel currently accepts limited 7-bit ASCII names. We could
1411 * change that to UTF-8 if we like as that means no extra string
1412 * encoding conversion fun here.
1413 */
1414 char szzNameAndTag[256];
1415 size_t cchName = strlen(pEntry->pszName);
1416 if (cchName + 1 + sizeof(g_szTag) <= sizeof(szzNameAndTag))
1417 {
1418 memcpy(szzNameAndTag, pEntry->pszName, cchName);
1419 szzNameAndTag[cchName] = '\0';
1420 memcpy(&szzNameAndTag[cchName + 1], g_szTag, sizeof(g_szTag));
1421
1422 APIRET rc = DosFSAttach(pEntry->pszActualMountPoint, "VBOXSF", szzNameAndTag, cchName + 1 + sizeof(g_szTag), FS_ATTACH);
1423 if (rc == NO_ERROR)
1424 {
1425 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1426 pEntry->pszName, pEntry->pszActualMountPoint);
1427 return VINF_SUCCESS;
1428 }
1429 VGSvcError("vbsvcAutomounterMountIt: DosFSAttach failed to attach '%s' to '%s': %u\n",
1430 pEntry->pszName, pEntry->pszActualMountPoint, rc);
1431 }
1432 else
1433 VGSvcError("vbsvcAutomounterMountIt: Share name for attach to '%s' is too long: %u chars - '%s'\n",
1434 pEntry->pszActualMountPoint, cchName, pEntry->pszName;
1435 return VERR_OPEN_FAILED;
1436
1437#else
1438 /*
1439 * Common work for unix-like systems: Get group, make sure mount directory exist.
1440 */
1441 int rc = RTDirCreateFullPath(pEntry->pszActualMountPoint,
1442 RTFS_UNIX_IRWXU | RTFS_UNIX_IXGRP | RTFS_UNIX_IRGRP | RTFS_UNIX_IXOTH | RTFS_UNIX_IROTH);
1443 if (RT_FAILURE(rc))
1444 {
1445 VGSvcError("vbsvcAutomounterMountIt: Failed to create mount path '%s' for share '%s': %Rrc\n",
1446 pEntry->pszActualMountPoint, pEntry->pszName, rc);
1447 return rc;
1448 }
1449
1450 gid_t gidMount;
1451 struct group *grp_vboxsf = getgrnam("vboxsf");
1452 if (grp_vboxsf)
1453 gidMount = grp_vboxsf->gr_gid;
1454 else
1455 {
1456 VGSvcError("vbsvcAutomounterMountIt: Group 'vboxsf' does not exist\n");
1457 gidMount = 0;
1458 }
1459
1460# if defined(RT_OS_LINUX)
1461 /*
1462 * Linux a bit more work...
1463 */
1464 struct vbsf_mount_info_new MntInfo;
1465 RT_ZERO(MntInfo);
1466 struct vbsf_mount_opts MntOpts;
1467 RT_ZERO(MntOpts);
1468 MntInfo.nullchar = '\0';
1469 MntInfo.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
1470 MntInfo.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
1471 MntInfo.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
1472 MntInfo.length = sizeof(MntInfo);
1473 MntInfo.uid = MntOpts.uid = 0;
1474 MntInfo.gid = MntOpts.gid = gidMount;
1475 MntInfo.dmode = MntOpts.dmode = 0770;
1476 MntInfo.fmode = MntOpts.fmode = 0770;
1477 MntInfo.dmask = MntOpts.dmask = 0000;
1478 MntInfo.fmask = MntOpts.fmask = 0000;
1479 memcpy(MntInfo.tag, g_szTag, sizeof(g_szTag)); AssertCompile(sizeof(MntInfo.tag) >= sizeof(g_szTag));
1480 rc = RTStrCopy(MntInfo.name, sizeof(MntInfo.name), pEntry->pszName);
1481 if (RT_FAILURE(rc))
1482 {
1483 VGSvcError("vbsvcAutomounterMountIt: Share name '%s' is too long for the MntInfo.name field!\n", pEntry->pszName);
1484 return rc;
1485 }
1486
1487 errno = 0;
1488 unsigned long fFlags = MS_NODEV;
1489 rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, "vboxsf", fFlags, &MntInfo);
1490 if (rc == 0)
1491 {
1492 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1493 pEntry->pszName, pEntry->pszActualMountPoint);
1494
1495 errno = 0;
1496 rc = vbsfmount_complete(pEntry->pszName, pEntry->pszActualMountPoint, fFlags, &MntOpts);
1497 if (rc != 0) /* Ignorable. /etc/mtab is probably a link to /proc/mounts. */
1498 VGSvcVerbose(1, "vbsvcAutomounterMountIt: vbsfmount_complete failed: %s (%d/%d)\n",
1499 rc == 1 ? "open_memstream" : rc == 2 ? "setmntent" : rc == 3 ? "addmntent" : "unknown", rc, errno);
1500 return VINF_SUCCESS;
1501 }
1502 else if (errno == EINVAL)
1503 VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s' because it is probably mounted elsewhere arleady! (%d,%d)\n",
1504 pEntry->pszName, pEntry->pszActualMountPoint, rc, errno);
1505 else
1506 VGSvcError("vbsvcAutomounterMountIt: Failed to mount '%s' on '%s': %s (%d,%d)\n",
1507 pEntry->pszName, pEntry->pszActualMountPoint, strerror(errno), rc, errno);
1508 return VERR_WRITE_ERROR;
1509
1510# elif defined(RT_OS_SOLARIS)
1511 /*
1512 * Solaris is rather simple compared to linux.
1513 *
1514 * The ',VBoxService=auto' option (g_szTag) is ignored by the kernel but helps
1515 * us identify our own mounts on restart. See vbsvcAutomounterPopulateTable().
1516 *
1517 * Note! Must pass MAX_MNTOPT_STR rather than cchOpts to mount, as it may fail
1518 * with EOVERFLOW in vfs_buildoptionstr() during domount() otherwise.
1519 */
1520 char szOpts[MAX_MNTOPT_STR] = { '\0', };
1521 ssize_t cchOpts = RTStrPrintf2(szOpts, sizeof(szOpts),
1522 "uid=0,gid=%d,dmode=0770,fmode=0770,dmask=0000,fmask=0000,tag=%s", gidMount, g_szTag);
1523 if (cchOpts <= 0)
1524 {
1525 VGSvcError("vbsvcAutomounterMountIt: szOpts overflow! %zd\n", cchOpts);
1526 return VERR_BUFFER_OVERFLOW;
1527 }
1528
1529 rc = mount(pEntry->pszName, pEntry->pszActualMountPoint, MS_OPTIONSTR, "vboxfs",
1530 NULL /*dataptr*/, 0 /* datalen */, szOpts, MAX_MNTOPT_STR);
1531 if (rc == 0)
1532 {
1533 VGSvcVerbose(0, "vbsvcAutomounterMountIt: Successfully mounted '%s' on '%s'\n",
1534 pEntry->pszName, pEntry->pszActualMountPoint);
1535 return VINF_SUCCESS;
1536 }
1537
1538 rc = errno;
1539 VGSvcError("vbsvcAutomounterMountIt: mount failed for '%s' on '%s' (szOpts=%s): %s (%d)\n",
1540 pEntry->pszName, pEntry->pszActualMountPoint, szOpts, strerror(rc), rc);
1541 return VERR_OPEN_FAILED;
1542
1543# else
1544# error "PORTME!"
1545# endif
1546#endif
1547}
1548
1549
1550/**
1551 * Attempts to mount the given shared folder, adding it to the mount table on
1552 * success.
1553 *
1554 * @returns iTable + 1 on success, iTable on failure.
1555 * @param pTable The mount table.
1556 * @param iTable The mount table index at which to add the mount.
1557 * @param pszName The name of the shared folder mapping.
1558 * @param pszMntPt The mount point (hint) specified by the host.
1559 * @param fFlags The shared folder flags, SHFL_MIF_XXX.
1560 * @param idRoot The root ID.
1561 * @param uRootIdVersion The root ID version.
1562 * @param fAutoMntPt Whether to try automatically assign a mount point if
1563 * pszMntPt doesn't work out. This is set in pass \#3.
1564 */
1565static uint32_t vbsvcAutomounterMountNewEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable,
1566 const char *pszName, const char *pszMntPt, uint64_t fFlags,
1567 uint32_t idRoot, uint32_t uRootIdVersion, bool fAutoMntPt)
1568{
1569 VGSvcVerbose(3, "vbsvcAutomounterMountNewEntry: #%u: '%s' at '%s'%s\n",
1570 iTable, pszName, pszMntPt, fAutoMntPt ? " auto-assign" : "");
1571
1572 /*
1573 * First we need to figure out the actual mount point.
1574 */
1575 char szActualMountPoint[RTPATH_MAX];
1576
1577#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
1578 /*
1579 * Drive letter based. We only care about the first two characters
1580 * and ignore the rest (see further down).
1581 */
1582 char chNextLetter = 'Z';
1583 if (RT_C_IS_ALPHA(pszMntPt[0]) && pszMntPt[1] == ':')
1584 szActualMountPoint[0] = RT_C_TO_UPPER(pszMntPt[0]);
1585 else if (!fAutoMntPt)
1586 return iTable;
1587 else
1588 szActualMountPoint[0] = chNextLetter--;
1589 szActualMountPoint[1] = ':';
1590 szActualMountPoint[2] = '\0';
1591
1592 int rc;
1593 for (;;)
1594 {
1595 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1596 if (rc == VWRN_NOT_FOUND)
1597 break;
1598
1599 /* next */
1600 if (chNextLetter == 'A' || !fAutoMntPt)
1601 return iTable;
1602 szActualMountPoint[0] = chNextLetter--;
1603 }
1604
1605#else
1606 /*
1607 * Path based #1: Host specified mount point.
1608 */
1609
1610 /* Skip DOS drive letter if there is a UNIX mount point path following it: */
1611 if ( pszMntPt[0] != '/'
1612 && pszMntPt[0] != '\0'
1613 && pszMntPt[1] == ':'
1614 && pszMntPt[2] == '/')
1615 pszMntPt += 2;
1616
1617 /* Try specified mount point if it starts with a UNIX slash: */
1618 int rc = VERR_ACCESS_DENIED;
1619 if (*pszMntPt == '/')
1620 {
1621 rc = RTPathAbs(pszMntPt, szActualMountPoint, sizeof(szActualMountPoint));
1622 if (RT_SUCCESS(rc))
1623 {
1624 static const char * const s_apszBlacklist[] =
1625 { "/", "/dev", "/bin", "/sbin", "/lib", "/etc", "/var", "/tmp", "/usr", "/usr/bin", "/usr/sbin", "/usr/lib" };
1626 for (size_t i = 0; i < RT_ELEMENTS(s_apszBlacklist); i++)
1627 if (strcmp(szActualMountPoint, s_apszBlacklist[i]) == 0)
1628 {
1629 rc = VERR_ACCESS_DENIED;
1630 break;
1631 }
1632 if (RT_SUCCESS(rc))
1633 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1634 }
1635 }
1636 if (rc != VWRN_NOT_FOUND)
1637 {
1638 if (!fAutoMntPt)
1639 return iTable;
1640
1641 /*
1642 * Path based #2: Mount dir + prefix + share.
1643 */
1644 rc = vbsvcAutomounterQueryMountDirAndPrefix(szActualMountPoint, sizeof(szActualMountPoint));
1645 if (RT_SUCCESS(rc))
1646 {
1647 /* Append a sanitized share name: */
1648 size_t const offShare = strlen(szActualMountPoint);
1649 size_t offDst = offShare;
1650 size_t offSrc = 0;
1651 for (;;)
1652 {
1653 char ch = pszName[offSrc++];
1654 if (ch == ' ' || ch == '/' || ch == '\\' || ch == ':' || ch == '$')
1655 ch = '_';
1656 else if (!ch)
1657 break;
1658 else if (ch < 0x20 || ch == 0x7f)
1659 continue;
1660 if (offDst < sizeof(szActualMountPoint) - 1)
1661 szActualMountPoint[offDst++] = ch;
1662 }
1663 szActualMountPoint[offDst] = '\0';
1664 if (offDst > offShare)
1665 {
1666 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1667 if (rc != VWRN_NOT_FOUND)
1668 {
1669 /*
1670 * Path based #3: Mount dir + prefix + share + _ + number.
1671 */
1672 if (offDst + 2 >= sizeof(szActualMountPoint))
1673 return iTable;
1674
1675 szActualMountPoint[offDst++] = '_';
1676 for (uint32_t iTry = 1; iTry < 10 && rc != VWRN_NOT_FOUND; iTry++)
1677 {
1678 szActualMountPoint[offDst] = '0' + iTry;
1679 szActualMountPoint[offDst + 1] = '\0';
1680 rc = vbsvcAutomounterQueryMountPoint(szActualMountPoint, pszName);
1681 }
1682 if (rc != VWRN_NOT_FOUND)
1683 return iTable;
1684 }
1685 }
1686 else
1687 VGSvcError("vbsvcAutomounterMountNewEntry: Bad share name: %.*Rhxs", strlen(pszName), pszName);
1688 }
1689 else
1690 VGSvcError("vbsvcAutomounterMountNewEntry: Failed to construct basic auto mount point for '%s'", pszName);
1691 }
1692#endif
1693
1694 /*
1695 * Prepare a table entry and ensure space in the table..
1696 */
1697 if (pTable->cEntries + 1 > pTable->cAllocated)
1698 {
1699 void *pvEntries = RTMemRealloc(pTable->papEntries, sizeof(pTable->papEntries[0]) * (pTable->cAllocated + 8));
1700 if (!pvEntries)
1701 {
1702 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for growing table (size %u)\n", pTable->cAllocated);
1703 return iTable;
1704 }
1705 pTable->cAllocated += 8;
1706 pTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvEntries;
1707 }
1708
1709 PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
1710 if (pEntry)
1711 {
1712 pEntry->idRoot = idRoot;
1713 pEntry->uRootIdVersion = uRootIdVersion;
1714 pEntry->fFlags = fFlags;
1715 pEntry->pszName = RTStrDup(pszName);
1716 pEntry->pszMountPoint = RTStrDup(pszMntPt);
1717 pEntry->pszActualMountPoint = RTStrDup(szActualMountPoint);
1718 if (pEntry->pszName && pEntry->pszMountPoint && pEntry->pszActualMountPoint)
1719 {
1720 /*
1721 * Now try mount it.
1722 */
1723 rc = vbsvcAutomounterMountIt(pEntry);
1724 if (RT_SUCCESS(rc))
1725 {
1726 uint32_t cToMove = pTable->cEntries - iTable;
1727 if (cToMove > 0)
1728 memmove(&pTable->papEntries[iTable + 1], &pTable->papEntries[iTable], cToMove * sizeof(pTable->papEntries[0]));
1729 pTable->papEntries[iTable] = pEntry;
1730 pTable->cEntries++;
1731 return iTable + 1;
1732 }
1733 }
1734 else
1735 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
1736 RTMemFree(pEntry->pszActualMountPoint);
1737 RTMemFree(pEntry->pszMountPoint);
1738 RTMemFree(pEntry->pszName);
1739 RTMemFree(pEntry);
1740 }
1741 else
1742 VGSvcError("vbsvcAutomounterMountNewEntry: Out of memory for table entry!\n");
1743 return iTable;
1744}
1745
1746
1747
1748/**
1749 * Does the actual unmounting.
1750 *
1751 * @returns Exactly one of the following IPRT status codes;
1752 * @retval VINF_SUCCESS if successfully umounted or nothing was mounted there.
1753 * @retval VERR_TRY_AGAIN if the shared folder is busy.
1754 * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
1755 * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
1756 * there.
1757 *
1758 * @param pszMountPoint The mount point.
1759 * @param pszName The shared folder (mapping) name.
1760 */
1761static int vbsvcAutomounterUnmount(const char *pszMountPoint, const char *pszName)
1762{
1763 /*
1764 * Retry for 5 seconds in a hope that busy mounts will quiet down.
1765 */
1766 for (unsigned iTry = 0; ; iTry++)
1767 {
1768 /*
1769 * Check what's mounted there before we start umounting stuff.
1770 */
1771 int rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
1772 if (rc == VINF_SUCCESS)
1773 { /* pszName is mounted there */ }
1774 else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
1775 return VINF_SUCCESS;
1776 else
1777 {
1778 Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
1779 return VERR_RESOURCE_BUSY;
1780 }
1781
1782 /*
1783 * Do host specific unmounting.
1784 */
1785#ifdef RT_OS_WINDOWS
1786 Assert(RT_C_IS_UPPER(pszMountPoint[0]) && pszMountPoint[1] == ':' && pszMountPoint[2] == '\0');
1787 RTUTF16 const wszDrive[4] = { pszMountPoint[0], ':', '\0', '\0' };
1788 DWORD dwErr = WNetCancelConnection2W(wszDrive, 0 /*dwFlags*/, FALSE /*fForce*/);
1789 if (dwErr == NO_ERROR)
1790 return VINF_SUCCESS;
1791 VGSvcVerbose(2, "vbsvcAutomounterUnmount: WNetCancelConnection2W returns %u for '%s' ('%s')\n", dwErr, pszMountPoint, pszName);
1792 if (dwErr == ERROR_NOT_CONNECTED)
1793 return VINF_SUCCESS;
1794
1795#elif defined(RT_OS_OS2)
1796 APIRET rcOs2 = DosFSAttach(pszMountPoint, "VBOXSF", NULL, 0, FS_DETACH);
1797 if (rcOs2 == NO_ERROR)
1798 return VINF_SUCCESS;
1799 VGSvcVerbose(2, "vbsvcAutomounterUnmount: DosFSAttach failed on '%s' ('%s'): %u\n", pszMountPoint, pszName, rcOs2);
1800 if (rcOs2 == ERROR_INVALID_FSD_NAME)
1801 return VERR_ACCESS_DENIED;
1802 if ( rcOs2 == ERROR_INVALID_DRIVE
1803 || rcOs2 == ERROR_INVALID_PATH)
1804 return VERR_TRY_AGAIN;
1805
1806#else
1807 int rc2 = umount(pszMountPoint);
1808 if (rc2 == 0)
1809 {
1810 /* Remove the mount directory if not directly under the root dir. */
1811 RTPATHPARSED Parsed = { 0 };
1812 RTPathParse(pszMountPoint, &Parsed, sizeof(Parsed), RTPATH_STR_F_STYLE_HOST);
1813 if (Parsed.cComps >= 3)
1814 RTDirRemove(pszMountPoint);
1815
1816 return VINF_SUCCESS;
1817 }
1818 rc2 = errno;
1819 VGSvcVerbose(2, "vbsvcAutomounterUnmount: umount failed on '%s' ('%s'): %d\n", pszMountPoint, pszName, rc2);
1820 if (rc2 != EBUSY && rc2 != EAGAIN)
1821 return VERR_ACCESS_DENIED;
1822#endif
1823
1824 /*
1825 * Check what's mounted there before we start delaying.
1826 */
1827 RTThreadSleep(8); /* fudge */
1828 rc = vbsvcAutomounterQueryMountPoint(pszMountPoint, pszName);
1829 if (rc == VINF_SUCCESS)
1830 { /* pszName is mounted there */ }
1831 else if (rc == VWRN_NOT_FOUND) /* nothing mounted there */
1832 return VINF_SUCCESS;
1833 else
1834 {
1835 Assert(rc == VERR_RESOURCE_BUSY || rc == VERR_ACCESS_DENIED);
1836 return VERR_RESOURCE_BUSY;
1837 }
1838
1839 if (iTry >= 5)
1840 return VERR_TRY_AGAIN;
1841 RTThreadSleep(1000);
1842 }
1843}
1844
1845
1846/**
1847 * Unmounts a mount table entry and evicts it from the table if successful.
1848 *
1849 * @returns The next iTable (same value on success, +1 on failure).
1850 * @param pTable The mount table.
1851 * @param iTable The table entry.
1852 * @param pszReason Why we're here.
1853 */
1854static uint32_t vbsvcAutomounterUnmountEntry(PVBSVCAUTOMOUNTERTABLE pTable, uint32_t iTable, const char *pszReason)
1855{
1856 Assert(iTable < pTable->cEntries);
1857 PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
1858 VGSvcVerbose(2, "vbsvcAutomounterUnmountEntry: #%u: '%s' at '%s' (reason: %s)\n",
1859 iTable, pEntry->pszName, pEntry->pszActualMountPoint, pszReason);
1860
1861 /*
1862 * Do we need to umount the entry? Return if unmount fails and we .
1863 */
1864 if (pEntry->pszActualMountPoint)
1865 {
1866 int rc = vbsvcAutomounterUnmount(pEntry->pszActualMountPoint, pEntry->pszName);
1867 if (rc == VERR_TRY_AGAIN)
1868 {
1869 VGSvcVerbose(1, "vbsvcAutomounterUnmountEntry: Keeping '%s' -> '%s' (VERR_TRY_AGAIN)\n",
1870 pEntry->pszActualMountPoint, pEntry->pszName);
1871 return iTable + 1;
1872 }
1873 }
1874
1875 /*
1876 * Remove the entry by shifting up the ones after it.
1877 */
1878 pTable->cEntries -= 1;
1879 uint32_t cAfter = pTable->cEntries - iTable;
1880 if (cAfter)
1881 memmove(&pTable->papEntries[iTable], &pTable->papEntries[iTable + 1], cAfter * sizeof(pTable->papEntries[0]));
1882 pTable->papEntries[pTable->cEntries] = NULL;
1883
1884 RTStrFree(pEntry->pszActualMountPoint);
1885 pEntry->pszActualMountPoint = NULL;
1886 RTStrFree(pEntry->pszMountPoint);
1887 pEntry->pszMountPoint = NULL;
1888 RTStrFree(pEntry->pszName);
1889 pEntry->pszName = NULL;
1890 RTMemFree(pEntry);
1891
1892 return iTable;
1893}
1894
1895
1896/**
1897 * @callback_method_impl{FNRTSORTCMP, For sorting the mappings by ID. }
1898 */
1899static DECLCALLBACK(int) vbsvcSharedFolderMappingCompare(void const *pvElement1, void const *pvElement2, void *pvUser)
1900{
1901 RT_NOREF_PV(pvUser);
1902 PVBGLR3SHAREDFOLDERMAPPING pMapping1 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement1;
1903 PVBGLR3SHAREDFOLDERMAPPING pMapping2 = (PVBGLR3SHAREDFOLDERMAPPING)pvElement2;
1904 return pMapping1->u32Root < pMapping2->u32Root ? -1 : pMapping1->u32Root != pMapping2->u32Root ? 1 : 0;
1905}
1906
1907
1908/**
1909 * Refreshes the mount table.
1910 *
1911 * @returns true if we've processed the current config, false if we failed to
1912 * query the mappings.
1913 * @param pTable The mount table to refresh.
1914 */
1915static bool vbsvcAutomounterRefreshTable(PVBSVCAUTOMOUNTERTABLE pTable)
1916{
1917 /*
1918 * Query the root IDs of all auto-mountable shared folder mappings.
1919 */
1920 uint32_t cMappings = 0;
1921 PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
1922 int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
1923 if (RT_FAILURE(rc))
1924 {
1925 VGSvcError("vbsvcAutomounterRefreshTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
1926 return false;
1927 }
1928
1929 /*
1930 * Walk the table and the mappings in parallel, so we have to make sure
1931 * they are both sorted by root ID.
1932 */
1933 if (cMappings > 1)
1934 RTSortShell(paMappings, cMappings, sizeof(paMappings[0]), vbsvcSharedFolderMappingCompare, NULL);
1935
1936 /*
1937 * Pass #1: Do all the umounting.
1938 *
1939 * By doing the umount pass separately from the mount pass, we can
1940 * better handle changing involving the same mount points (switching
1941 * mount points between two shares, new share on same mount point but
1942 * with lower root ID, ++).
1943 */
1944 uint32_t iTable = 0;
1945 for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
1946 {
1947 /*
1948 * Unmount table entries up to idRootSrc.
1949 */
1950 uint32_t const idRootSrc = paMappings[iSrc].u32Root;
1951 while ( iTable < pTable->cEntries
1952 && pTable->papEntries[iTable]->idRoot < idRootSrc)
1953 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped");
1954
1955 /*
1956 * If the paMappings entry and the mount table entry has the same
1957 * root ID, umount if anything has changed or if we cannot query
1958 * the mapping data.
1959 */
1960 if (iTable < pTable->cEntries)
1961 {
1962 PVBSVCAUTOMOUNTERENTRY pEntry = pTable->papEntries[iTable];
1963 if (pEntry->idRoot == idRootSrc)
1964 {
1965 uint32_t uRootIdVer = UINT32_MAX;
1966 uint64_t fFlags = 0;
1967 char *pszName = NULL;
1968 char *pszMntPt = NULL;
1969 rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
1970 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
1971 if (RT_FAILURE(rc))
1972 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "VbglR3SharedFolderQueryFolderInfo failed");
1973 else if (pEntry->uRootIdVersion != uRootIdVer)
1974 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "root ID version changed");
1975 else if (RTPathCompare(pEntry->pszMountPoint, pszMntPt) != 0)
1976 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "mount point changed");
1977 else if (RTStrICmp(pEntry->pszName, pszName) != 0)
1978 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "name changed");
1979 else
1980 {
1981 VGSvcVerbose(3, "vbsvcAutomounterRefreshTable: Unchanged: %s -> %s\n", pEntry->pszMountPoint, pEntry->pszName);
1982 iTable++;
1983 }
1984 if (RT_SUCCESS(rc))
1985 {
1986 RTStrFree(pszName);
1987 RTStrFree(pszMntPt);
1988 }
1989 }
1990 }
1991 }
1992
1993 while (iTable < pTable->cEntries)
1994 iTable = vbsvcAutomounterUnmountEntry(pTable, iTable, "dropped (tail)");
1995
1996 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u entries in mount table after pass #1.\n", pTable->cEntries);
1997
1998 /*
1999 * Pass #2: Try mount new folders that has mount points assigned.
2000 * Pass #3: Try mount new folders not mounted in pass #2.
2001 */
2002 for (uint32_t iPass = 2; iPass <= 3; iPass++)
2003 {
2004 iTable = 0;
2005 for (uint32_t iSrc = 0; iSrc < cMappings; iSrc++)
2006 {
2007 uint32_t const idRootSrc = paMappings[iSrc].u32Root;
2008
2009 /*
2010 * Skip tabel entries we couldn't umount in pass #1.
2011 */
2012 while ( iTable < pTable->cEntries
2013 && pTable->papEntries[iTable]->idRoot < idRootSrc)
2014 {
2015 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Skipping idRoot=%u %s\n",
2016 iPass, iSrc, iTable, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
2017 iTable++;
2018 }
2019
2020 /*
2021 * New share?
2022 */
2023 if ( iTable >= pTable->cEntries
2024 || pTable->papEntries[iTable]->idRoot != idRootSrc)
2025 {
2026 uint32_t uRootIdVer = UINT32_MAX;
2027 uint64_t fFlags = 0;
2028 char *pszName = NULL;
2029 char *pszMntPt = NULL;
2030 rc = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
2031 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
2032 if (RT_SUCCESS(rc))
2033 {
2034 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: Mounting idRoot=%u/%u %s\n", iPass, iSrc, iTable,
2035 idRootSrc, iTable >= pTable->cEntries ? UINT32_MAX : pTable->papEntries[iTable]->idRoot, pszName);
2036 iTable = vbsvcAutomounterMountNewEntry(pTable, iTable, pszName, pszMntPt, fFlags,
2037 idRootSrc, uRootIdVer, iPass == 3);
2038
2039 RTStrFree(pszName);
2040 RTStrFree(pszMntPt);
2041 }
2042 else
2043 VGSvcVerbose(1, "vbsvcAutomounterRefreshTable: VbglR3SharedFolderQueryFolderInfo failed: %Rrc\n", rc);
2044 }
2045 else
2046 VGSvcVerbose(4, "vbsvcAutomounterRefreshTable: %u/#%u/%#u: idRootSrc=%u vs idRoot=%u %s\n", iPass, iSrc,
2047 iTable, idRootSrc, pTable->papEntries[iTable]->idRoot, pTable->papEntries[iTable]->pszName);
2048 }
2049 }
2050
2051 VbglR3SharedFolderFreeMappings(paMappings);
2052 return true;
2053}
2054
2055
2056/**
2057 * @interface_method_impl{VBOXSERVICE,pfnWorker}
2058 */
2059static DECLCALLBACK(int) vbsvcAutomounterWorker(bool volatile *pfShutdown)
2060{
2061 /*
2062 * Tell the control thread that it can continue spawning services.
2063 */
2064 RTThreadUserSignal(RTThreadSelf());
2065
2066 /* Divert old hosts to original auto-mount code. */
2067 if (!g_fHostSupportsWaitAndInfoQuery)
2068 return vbsvcAutoMountWorkerOld(pfShutdown);
2069
2070 /*
2071 * Initialize the state in case we're restarted...
2072 */
2073 VBSVCAUTOMOUNTERTABLE MountTable = { 0, 0, NULL };
2074 int rc = vbsvcAutomounterPopulateTable(&MountTable);
2075 if (RT_FAILURE(rc))
2076 {
2077 VGSvcError("vbsvcAutomounterWorker: vbsvcAutomounterPopulateTable failed (%Rrc), quitting!\n", rc);
2078 return rc;
2079 }
2080
2081 /*
2082 * Work loop.
2083 */
2084 uint32_t uConfigVer = UINT32_MAX;
2085 uint32_t uNewVersion = 0;
2086 bool fForceRefresh = true;
2087 while (!*pfShutdown)
2088 {
2089 /*
2090 * Update the mounts.
2091 */
2092 if ( uConfigVer != uNewVersion
2093 || fForceRefresh)
2094 {
2095 fForceRefresh = !vbsvcAutomounterRefreshTable(&MountTable);
2096 uConfigVer = uNewVersion;
2097 }
2098
2099 /*
2100 * Wait for more to do.
2101 */
2102 if (!*pfShutdown)
2103 {
2104 uNewVersion = uConfigVer - 1;
2105 VGSvcVerbose(2, "vbsvcAutomounterWorker: Waiting with uConfigVer=%u\n", uConfigVer);
2106 rc = VbglR3SharedFolderWaitForMappingsChanges(g_idClientSharedFolders, uConfigVer, &uNewVersion);
2107 VGSvcVerbose(2, "vbsvcAutomounterWorker: Woke up with uNewVersion=%u and rc=%Rrc\n", uNewVersion, rc);
2108
2109 /* Delay a little before doing a table refresh so the GUI can finish
2110 all its updates. Delay a little longer on non-shutdown failure to
2111 avoid eating too many CPU cycles if something goes wrong here... */
2112 if (!*pfShutdown)
2113 RTSemEventMultiWait(g_hAutoMountEvent, RT_SUCCESS(rc) ? 256 : 1000);
2114 }
2115 }
2116
2117 /*
2118 * Destroy the mount table.
2119 */
2120 while (MountTable.cEntries-- > 0)
2121 RTMemFree(MountTable.papEntries[MountTable.cEntries]);
2122 MountTable.papEntries = NULL;
2123
2124 VGSvcVerbose(3, "vbsvcAutomounterWorker: Finished\n");
2125 return VINF_SUCCESS;
2126}
2127
2128
2129/**
2130 * @interface_method_impl{VBOXSERVICE,pfnStop}
2131 */
2132static DECLCALLBACK(void) vbsvcAutomounterStop(void)
2133{
2134 RTSemEventMultiSignal(g_hAutoMountEvent);
2135 if (g_fHostSupportsWaitAndInfoQuery)
2136 VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
2137}
2138
2139
2140/**
2141 * @interface_method_impl{VBOXSERVICE,pfnTerm}
2142 */
2143static DECLCALLBACK(void) vbsvcAutomounterTerm(void)
2144{
2145 VGSvcVerbose(3, "vbsvcAutoMountTerm\n");
2146
2147 if (g_fHostSupportsWaitAndInfoQuery)
2148 VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders);
2149
2150 VbglR3SharedFolderDisconnect(g_idClientSharedFolders);
2151 g_idClientSharedFolders = 0;
2152
2153 if (g_hAutoMountEvent != NIL_RTSEMEVENTMULTI)
2154 {
2155 RTSemEventMultiDestroy(g_hAutoMountEvent);
2156 g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
2157 }
2158}
2159
2160
2161/**
2162 * The 'automount' service description.
2163 */
2164VBOXSERVICE g_AutoMount =
2165{
2166 /* pszName. */
2167 "automount",
2168 /* pszDescription. */
2169 "Automounter for Shared Folders",
2170 /* pszUsage. */
2171 NULL,
2172 /* pszOptions. */
2173 NULL,
2174 /* methods */
2175 VGSvcDefaultPreInit,
2176 VGSvcDefaultOption,
2177 vbsvcAutomounterInit,
2178 vbsvcAutomounterWorker,
2179 vbsvcAutomounterStop,
2180 vbsvcAutomounterTerm
2181};
2182
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