VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • 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 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxService - Auto-mounting for Shared Folders, only Linux & Solaris atm.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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/err.h>
48#include <VBox/VBoxGuestLib.h>
49#include <VBox/shflsvc.h>
50#include "VBoxServiceInternal.h"
51#include "VBoxServiceUtils.h"
52
53#ifdef RT_OS_WINDOWS
54#elif defined(RT_OS_OS2)
55# define INCL_DOSFILEMGR
56# define INCL_ERRORS
57# define OS2EMX_PLAIN_CHAR
58# include <os2emx.h>
59#else
60# include <errno.h>
61# include <grp.h>
62# include <sys/mount.h>
63# ifdef RT_OS_SOLARIS
64# include <sys/mntent.h>
65# include <sys/mnttab.h>
66# include <sys/vfs.h>
67RT_C_DECLS_BEGIN /* Only needed for old code.*/
68# include "../../linux/sharedfolders/vbsfmount.h"
69RT_C_DECLS_END
70# elif defined(RT_OS_LINUX)
71# include <mntent.h>
72# include <paths.h>
73RT_C_DECLS_BEGIN
74# include "../../linux/sharedfolders/vbsfmount.h"
75RT_C_DECLS_END
76# else
77# error "Port me!"
78# endif
79# include <unistd.h>
80#endif
81
82
83
84/*********************************************************************************************************************************
85* Defined Constants And Macros *
86*********************************************************************************************************************************/
87/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
88 * Default mount directory (unix only).
89 */
90#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR
91# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/media"
92#endif
93
94/** @def VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
95 * Default mount prefix (unix only).
96 */
97#ifndef VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX
98# define VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX "sf_"
99#endif
100
101#ifndef _PATH_MOUNTED
102# ifdef RT_OS_SOLARIS
103# define _PATH_MOUNTED "/etc/mnttab"
104# else
105# define _PATH_MOUNTED "/etc/mtab"
106# endif
107#endif
108
109/** @def VBOXSERVICE_AUTOMOUNT_MIQF
110 * The drive letter / path mount point flag. */
111#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
112# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_DRIVE_LETTER
113#else
114# define VBOXSERVICE_AUTOMOUNT_MIQF SHFL_MIQF_PATH
115#endif
116
117
118/*********************************************************************************************************************************
119* Structures and Typedefs *
120*********************************************************************************************************************************/
121/**
122 * Automounter mount table entry.
123 *
124 * This holds the information returned by SHFL_FN_QUERY_MAP_INFO and
125 * additional mount state info. We only keep entries for mounted mappings.
126 */
127typedef struct VBSVCAUTOMOUNTERENTRY
128{
129 /** The root ID. */
130 uint32_t idRoot;
131 /** The root ID version. */
132 uint32_t uRootIdVersion;
133 /** Map info flags, SHFL_MIF_XXX. */
134 uint64_t fFlags;
135 /** The shared folder (mapping) name. */
136 char *pszName;
137 /** The configured mount point, NULL if none. */
138 char *pszMountPoint;
139 /** The actual mount point, NULL if not mount. */
140 char *pszActualMountPoint;
141} VBSVCAUTOMOUNTERENTRY;
142/** Pointer to an automounter entry. */
143typedef VBSVCAUTOMOUNTERENTRY *PVBSVCAUTOMOUNTERENTRY;
144
145/** Automounter mount table. */
146typedef struct VBSVCAUTOMOUNTERTABLE
147{
148 /** Current number of entries in the array. */
149 uint32_t cEntries;
150 /** Max number of entries the array can hold w/o growing it. */
151 uint32_t cAllocated;
152 /** Pointer to an array of entry pointers. */
153 PVBSVCAUTOMOUNTERENTRY *papEntries;
154} VBSVCAUTOMOUNTERTABLE;
155/** Pointer to an automounter mount table. */
156typedef VBSVCAUTOMOUNTERTABLE *PVBSVCAUTOMOUNTERTABLE;
157
158
159/*********************************************************************************************************************************
160* Global Variables *
161*********************************************************************************************************************************/
162/** The semaphore we're blocking on. */
163static RTSEMEVENTMULTI g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
164/** The Shared Folders service client ID. */
165static uint32_t g_idClientSharedFolders = 0;
166/** Set if we can wait on changes to the mappings. */
167static bool g_fHostSupportsWaitAndInfoQuery = false;
168
169#ifdef RT_OS_OS2
170/** The attachment tag we use to identify attchments that belongs to us. */
171static char const g_szTag[] = "VBoxAutomounter";
172#elif defined(RT_OS_LINUX)
173/** Tag option value that lets us identify mounts that belongs to us. */
174static char const g_szTag[] = "VBoxAutomounter";
175#elif defined(RT_OS_SOLARIS)
176/** Dummy mount option that lets us identify mounts that belongs to us. */
177static char const g_szTag[] = "VBoxAutomounter";
178#endif
179
180
181
182/**
183 * @interface_method_impl{VBOXSERVICE,pfnInit}
184 */
185static DECLCALLBACK(int) vbsvcAutomounterInit(void)
186{
187 VGSvcVerbose(3, "vbsvcAutomounterInit\n");
188
189 int rc = RTSemEventMultiCreate(&g_hAutoMountEvent);
190 AssertRCReturn(rc, rc);
191
192 rc = VbglR3SharedFolderConnect(&g_idClientSharedFolders);
193 if (RT_SUCCESS(rc))
194 {
195 VGSvcVerbose(3, "vbsvcAutomounterInit: Service Client ID: %#x\n", g_idClientSharedFolders);
196 g_fHostSupportsWaitAndInfoQuery = RT_SUCCESS(VbglR3SharedFolderCancelMappingsChangesWaits(g_idClientSharedFolders));
197 }
198 else
199 {
200 /* If the service was not found, we disable this service without
201 causing VBoxService to fail. */
202 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
203 {
204 VGSvcVerbose(0, "vbsvcAutomounterInit: Shared Folders service is not available\n");
205 rc = VERR_SERVICE_DISABLED;
206 }
207 else
208 VGSvcError("Control: Failed to connect to the Shared Folders service! Error: %Rrc\n", rc);
209 RTSemEventMultiDestroy(g_hAutoMountEvent);
210 g_hAutoMountEvent = NIL_RTSEMEVENTMULTI;
211 }
212
213 return rc;
214}
215
216
217#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) /* The old code: */
218
219/**
220 * @todo Integrate into RTFsQueryMountpoint()?
221 */
222static bool vbsvcAutoMountShareIsMountedOld(const char *pszShare, char *pszMountPoint, size_t cbMountPoint)
223{
224 AssertPtrReturn(pszShare, false);
225 AssertPtrReturn(pszMountPoint, false);
226 AssertReturn(cbMountPoint, false);
227
228 bool fMounted = false;
229
230# if defined(RT_OS_SOLARIS)
231 /** @todo What to do if we have a relative path in mtab instead
232 * of an absolute one ("temp" vs. "/media/temp")?
233 * procfs contains the full path but not the actual share name ...
234 * FILE *pFh = setmntent("/proc/mounts", "r+t"); */
235 FILE *pFh = fopen(_PATH_MOUNTED, "r");
236 if (!pFh)
237 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
238 else
239 {
240 mnttab mntTab;
241 while ((getmntent(pFh, &mntTab)))
242 {
243 if (!RTStrICmp(mntTab.mnt_special, pszShare))
244 {
245 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp)
246 ? true : false;
247 break;
248 }
249 }
250 fclose(pFh);
251 }
252# elif defined(RT_OS_LINUX)
253 FILE *pFh = setmntent(_PATH_MOUNTED, "r+t"); /** @todo r=bird: why open it for writing? (the '+') */
254 if (pFh == NULL)
255 VGSvcError("vbsvcAutoMountShareIsMountedOld: Could not open mount tab '%s'!\n", _PATH_MOUNTED);
256 else
257 {
258 mntent *pMntEnt;
259 while ((pMntEnt = getmntent(pFh)))
260 {
261 if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare))
262 {
263 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir)
264 ? true : false;
265 break;
266 }
267 }
268 endmntent(pFh);
269 }
270# else
271# error "PORTME!"
272# endif
273
274 VGSvcVerbose(4, "vbsvcAutoMountShareIsMountedOld: Share '%s' at mount point '%s' = %s\n",
275 pszShare, fMounted ? pszMountPoint : "<None>", fMounted ? "Yes" : "No");
276 return fMounted;
277}
278
279
280/**
281 * Unmounts a shared folder.
282 *
283 * @returns VBox status code
284 * @param pszMountPoint The shared folder mount point.
285 */
286static int vbsvcAutoMountUnmountOld(const char *pszMountPoint)
287{
288 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
289
290 int rc = VINF_SUCCESS;
291 uint8_t uTries = 0;
292 int r;
293 while (uTries++ < 3)
294 {
295 r = umount(pszMountPoint);
296 if (r == 0)
297 break;
298/** @todo r=bird: Why do sleep 5 seconds after the final retry?
299 * May also be a good idea to check for EINVAL or other signs that someone
300 * else have already unmounted the share. */
301 RTThreadSleep(5000); /* Wait a while ... */
302 }
303 if (r == -1) /** @todo r=bird: RTThreadSleep set errno. */
304 rc = RTErrConvertFromErrno(errno);
305 return rc;
306}
307
308
309/**
310 * Prepares a mount point (create it, set group and mode).
311 *
312 * @returns VBox status code
313 * @param pszMountPoint The mount point.
314 * @param pszShareName Unused.
315 * @param pOpts For getting the group ID.
316 */
317static int vbsvcAutoMountPrepareMountPointOld(const char *pszMountPoint, const char *pszShareName, vbsf_mount_opts *pOpts)
318{
319 AssertPtrReturn(pOpts, VERR_INVALID_PARAMETER);
320 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
321 AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER);
322
323 RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */
324 int rc = RTDirCreateFullPath(pszMountPoint, fMode);
325 if (RT_SUCCESS(rc))
326 {
327 rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, pOpts->gid, RTPATH_F_ON_LINK);
328 if (RT_SUCCESS(rc))
329 {
330 rc = RTPathSetMode(pszMountPoint, fMode);
331 if (RT_FAILURE(rc))
332 {
333 if (rc == VERR_WRITE_PROTECT)
334 {
335 VGSvcVerbose(3, "vbsvcAutoMountPrepareMountPointOld: Mount directory '%s' already is used/mounted\n",
336 pszMountPoint);
337 rc = VINF_SUCCESS;
338 }
339 else
340 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set mode %RTfmode for mount directory '%s', rc = %Rrc\n",
341 fMode, pszMountPoint, rc);
342 }
343 }
344 else
345 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not set permissions for mount directory '%s', rc = %Rrc\n",
346 pszMountPoint, rc);
347 }
348 else
349 VGSvcError("vbsvcAutoMountPrepareMountPointOld: Could not create mount directory '%s' with mode %RTfmode, rc = %Rrc\n",
350 pszMountPoint, fMode, rc);
351 return rc;
352}
353
354
355/**
356 * Mounts a shared folder.
357 *
358 * @returns VBox status code reflecting unmount and mount point preparation
359 * results, but not actual mounting
360 *
361 * @param pszShareName The shared folder name.
362 * @param pszMountPoint The mount point.
363 */
364static int vbsvcAutoMountSharedFolderOld(const char *pszShareName, const char *pszMountPoint)
365{
366 /*
367 * Linux and solaris share the same mount structure.
368 */
369 struct group *grp_vboxsf = getgrnam("vboxsf");
370 if (!grp_vboxsf)
371 {
372 VGSvcError("vbsvcAutoMountWorker: Group 'vboxsf' does not exist\n");
373 return VINF_SUCCESS;
374 }
375
376 struct vbsf_mount_opts Opts =
377 {
378 0, /* uid */
379 (int)grp_vboxsf->gr_gid, /* gid */
380 0, /* ttl */
381 0770, /* dmode, owner and group "vboxsf" have full access */
382 0770, /* fmode, owner and group "vboxsf" have full access */
383 0, /* dmask */
384 0, /* fmask */
385 0, /* ronly */
386 0, /* sloppy */
387 0, /* noexec */
388 0, /* nodev */
389 0, /* nosuid */
390 0, /* remount */
391 "\0", /* nls_name */
392 NULL, /* convertcp */
393 };
394
395 int rc = vbsvcAutoMountPrepareMountPointOld(pszMountPoint, pszShareName, &Opts);
396 if (RT_SUCCESS(rc))
397 {
398# ifdef RT_OS_SOLARIS
399 int fFlags = 0;
400 if (Opts.ronly)
401 fFlags |= MS_RDONLY;
402 char szOptBuf[MAX_MNTOPT_STR] = { '\0', };
403 RTStrPrintf(szOptBuf, sizeof(szOptBuf), "uid=%d,gid=%d,dmode=%0o,fmode=%0o,dmask=%0o,fmask=%0o",
404 Opts.uid, Opts.gid, Opts.dmode, Opts.fmode, Opts.dmask, Opts.fmask);
405 int r = mount(pszShareName,
406 pszMountPoint,
407 fFlags | MS_OPTIONSTR,
408 "vboxfs",
409 NULL, /* char *dataptr */
410 0, /* int datalen */
411 szOptBuf,
412 sizeof(szOptBuf));
413 if (r == 0)
414 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
415 else if (errno != EBUSY) /* Share is already mounted? Then skip error msg. */
416 VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s', error = %s\n",
417 pszShareName, pszMountPoint, strerror(errno));
418
419# else /* RT_OS_LINUX */
420 unsigned long fFlags = MS_NODEV;
421
422 /*const char *szOptions = { "rw" }; - ??? */
423 struct vbsf_mount_info_new mntinf;
424 RT_ZERO(mntinf);
425
426 mntinf.nullchar = '\0';
427 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
428 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
429 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
430 mntinf.length = sizeof(mntinf);
431
432 mntinf.uid = Opts.uid;
433 mntinf.gid = Opts.gid;
434 mntinf.ttl = Opts.ttl;
435 mntinf.dmode = Opts.dmode;
436 mntinf.fmode = Opts.fmode;
437 mntinf.dmask = Opts.dmask;
438 mntinf.fmask = Opts.fmask;
439 mntinf.tag[0] = '\0';
440
441 strcpy(mntinf.name, pszShareName);
442 strcpy(mntinf.nls_name, "\0");
443
444 int r = mount(pszShareName,
445 pszMountPoint,
446 "vboxsf",
447 fFlags,
448 &mntinf);
449 if (r == 0)
450 {
451 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' was mounted to '%s'\n", pszShareName, pszMountPoint);
452
453 r = vbsfmount_complete(pszShareName, pszMountPoint, fFlags, &Opts);
454 switch (r)
455 {
456 case 0: /* Success. */
457 errno = 0; /* Clear all errors/warnings. */
458 break;
459
460 case 1:
461 VGSvcError("vbsvcAutoMountWorker: Could not update mount table (failed to create memstream): %s\n",
462 strerror(errno));
463 break;
464
465 case 2:
466 VGSvcError("vbsvcAutoMountWorker: Could not open mount table for update: %s\n", strerror(errno));
467 break;
468
469 case 3:
470 /* VGSvcError("vbsvcAutoMountWorker: Could not add an entry to the mount table: %s\n", strerror(errno)); */
471 errno = 0;
472 break;
473
474 default:
475 VGSvcError("vbsvcAutoMountWorker: Unknown error while completing mount operation: %d\n", r);
476 break;
477 }
478 }
479 else /* r == -1, we got some error in errno. */
480 {
481 if (errno == EPROTO)
482 {
483 VGSvcVerbose(3, "vbsvcAutoMountWorker: Messed up share name, re-trying ...\n");
484
485 /** @todo r=bird: What on earth is going on here????? Why can't you
486 * strcpy(mntinf.name, pszShareName) to fix it again? */
487
488 /* Sometimes the mount utility messes up the share name. Try to
489 * un-mangle it again. */
490 char szCWD[RTPATH_MAX];
491 size_t cchCWD;
492 if (!getcwd(szCWD, sizeof(szCWD)))
493 {
494 VGSvcError("vbsvcAutoMountWorker: Failed to get the current working directory\n");
495 szCWD[0] = '\0';
496 }
497 cchCWD = strlen(szCWD);
498 if (!strncmp(pszMountPoint, szCWD, cchCWD))
499 {
500 while (pszMountPoint[cchCWD] == '/')
501 ++cchCWD;
502 /* We checked before that we have enough space */
503 strcpy(mntinf.name, pszMountPoint + cchCWD);
504 }
505 r = mount(mntinf.name, pszMountPoint, "vboxsf", fFlags, &mntinf);
506 }
507 if (r == -1) /* Was there some error from one of the tries above? */
508 {
509 switch (errno)
510 {
511 /* If we get EINVAL here, the system already has mounted the Shared Folder to another
512 * mount point. */
513 case EINVAL:
514 VGSvcVerbose(0, "vbsvcAutoMountWorker: Shared folder '%s' already is mounted!\n", pszShareName);
515 /* Ignore this error! */
516 break;
517 case EBUSY:
518 /* Ignore these errors! */
519 break;
520
521 default:
522 VGSvcError("vbsvcAutoMountWorker: Could not mount shared folder '%s' to '%s': %s (%d)\n",
523 pszShareName, pszMountPoint, strerror(errno), errno);
524 rc = RTErrConvertFromErrno(errno);
525 break;
526 }
527 }
528 }
529# endif
530 }
531 VGSvcVerbose(3, "vbsvcAutoMountWorker: Mounting returned with rc=%Rrc\n", rc);
532 return rc;
533}
534
535
536/**
537 * Processes shared folder mappings retrieved from the host.
538 *
539 * @returns VBox status code.
540 * @param paMappings The mappings.
541 * @param cMappings The number of mappings.
542 * @param pszMountDir The mount directory.
543 * @param pszSharePrefix The share prefix.
544 * @param uClientID The shared folder service (HGCM) client ID.
545 */
546static int vbsvcAutoMountProcessMappingsOld(PCVBGLR3SHAREDFOLDERMAPPING paMappings, uint32_t cMappings,
547 const char *pszMountDir, const char *pszSharePrefix, uint32_t uClientID)
548{
549 if (cMappings == 0)
550 return VINF_SUCCESS;
551 AssertPtrReturn(paMappings, VERR_INVALID_PARAMETER);
552 AssertPtrReturn(pszMountDir, VERR_INVALID_PARAMETER);
553 AssertPtrReturn(pszSharePrefix, VERR_INVALID_PARAMETER);
554 AssertReturn(uClientID > 0, VERR_INVALID_PARAMETER);
555
556 /** @todo r=bird: Why is this loop schitzoid about status codes? It quits if
557 * RTPathJoin fails (i.e. if the user specifies a very long name), but happily
558 * continues if RTStrAPrintf failes (mem alloc).
559 *
560 * It also happily continues if the 'vboxsf' group is missing, which is a waste
561 * of effort... In fact, retrieving the group ID could probably be done up
562 * front, outside the loop. */
563 int rc = VINF_SUCCESS;
564 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
565 {
566 char *pszShareName = NULL;
567 rc = VbglR3SharedFolderGetName(uClientID, paMappings[i].u32Root, &pszShareName);
568 if ( RT_SUCCESS(rc)
569 && *pszShareName)
570 {
571 VGSvcVerbose(3, "vbsvcAutoMountWorker: Connecting share %u (%s) ...\n", i+1, pszShareName);
572
573 /** @todo r=bird: why do you copy things twice here and waste heap space?
574 * szMountPoint has a fixed size.
575 * @code
576 * char szMountPoint[RTPATH_MAX];
577 * rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, *pszSharePrefix ? pszSharePrefix : pszShareName);
578 * if (RT_SUCCESS(rc) && *pszSharePrefix)
579 * rc = RTStrCat(szMountPoint, sizeof(szMountPoint), pszShareName);
580 * @endcode */
581 char *pszShareNameFull = NULL;
582 if (RTStrAPrintf(&pszShareNameFull, "%s%s", pszSharePrefix, pszShareName) > 0)
583 {
584 char szMountPoint[RTPATH_MAX];
585 rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, pszShareNameFull);
586 if (RT_SUCCESS(rc))
587 {
588 VGSvcVerbose(4, "vbsvcAutoMountWorker: Processing mount point '%s'\n", szMountPoint);
589
590 /*
591 * Already mounted?
592 */
593 /** @todo r-bird: this does not take into account that a shared folder could
594 * be mounted twice... We're really just interested in whether the
595 * folder is mounted on 'szMountPoint', no where else... */
596 bool fSkip = false;
597 char szAlreadyMountedOn[RTPATH_MAX];
598 if (vbsvcAutoMountShareIsMountedOld(pszShareName, szAlreadyMountedOn, sizeof(szAlreadyMountedOn)))
599 {
600 /* Do if it not mounted to our desired mount point */
601 if (RTStrICmp(szMountPoint, szAlreadyMountedOn))
602 {
603 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', unmounting ...\n",
604 pszShareName, szAlreadyMountedOn);
605 rc = vbsvcAutoMountUnmountOld(szAlreadyMountedOn);
606 if (RT_SUCCESS(rc))
607 fSkip = false;
608 else
609 VGSvcError("vbsvcAutoMountWorker: Failed to unmount '%s', %s (%d)! (rc=%Rrc)\n",
610 szAlreadyMountedOn, strerror(errno), errno, rc); /** @todo errno isn't reliable at this point */
611 }
612 if (fSkip)
613 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder '%s' already mounted on '%s', skipping\n",
614 pszShareName, szAlreadyMountedOn);
615 }
616 if (!fSkip)
617 {
618 /*
619 * Mount it.
620 */
621 rc = vbsvcAutoMountSharedFolderOld(pszShareName, szMountPoint);
622 }
623 }
624 else
625 VGSvcError("vbsvcAutoMountWorker: Unable to join mount point/prefix/shrae, rc = %Rrc\n", rc);
626 RTStrFree(pszShareNameFull);
627 }
628 else
629 VGSvcError("vbsvcAutoMountWorker: Unable to allocate full share name\n");
630 RTStrFree(pszShareName);
631 }
632 else
633 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
634 paMappings[i].u32Root, rc);
635 } /* for cMappings. */
636 return rc;
637}
638
639#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) - the old code*/
640
641
642/**
643 * Service worker function for old host.
644 *
645 * This only mount stuff on startup.
646 *
647 * @returns VBox status code.
648 * @param pfShutdown Shutdown indicator.
649 */
650static int vbsvcAutoMountWorkerOld(bool volatile *pfShutdown)
651{
652#if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
653 /*
654 * We only do a single pass here.
655 */
656 uint32_t cMappings;
657 PVBGLR3SHAREDFOLDERMAPPING paMappings;
658 int rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /* Only process auto-mounted folders */,
659 &paMappings, &cMappings);
660 if ( RT_SUCCESS(rc)
661 && cMappings)
662 {
663 char *pszMountDir;
664 rc = VbglR3SharedFolderGetMountDir(&pszMountDir);
665 if (rc == VERR_NOT_FOUND)
666 rc = RTStrDupEx(&pszMountDir, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR);
667 if (RT_SUCCESS(rc))
668 {
669 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount dir set to '%s'\n", pszMountDir);
670
671 char *pszSharePrefix;
672 rc = VbglR3SharedFolderGetMountPrefix(&pszSharePrefix);
673 if (RT_SUCCESS(rc))
674 {
675 VGSvcVerbose(3, "vbsvcAutoMountWorker: Shared folder mount prefix set to '%s'\n", pszSharePrefix);
676# ifdef USE_VIRTUAL_SHARES
677 /* Check for a fixed/virtual auto-mount share. */
678 if (VbglR3SharedFolderExists(g_idClientSharedFolders, "vbsfAutoMount"))
679 VGSvcVerbose(3, "vbsvcAutoMountWorker: Host supports auto-mount root\n");
680 else
681 {
682# endif
683 VGSvcVerbose(3, "vbsvcAutoMountWorker: Got %u shared folder mappings\n", cMappings);
684 rc = vbsvcAutoMountProcessMappingsOld(paMappings, cMappings, pszMountDir, pszSharePrefix,
685 g_idClientSharedFolders);
686# ifdef USE_VIRTUAL_SHARES
687 }
688# endif
689 RTStrFree(pszSharePrefix);
690 } /* Mount share prefix. */
691 else
692 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mount prefix, rc = %Rrc\n", rc);
693 RTStrFree(pszMountDir);
694 }
695 else
696 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder directory, rc = %Rrc\n", rc);
697 VbglR3SharedFolderFreeMappings(paMappings);
698 }
699 else if (RT_FAILURE(rc))
700 VGSvcError("vbsvcAutoMountWorker: Error while getting the shared folder mappings, rc = %Rrc\n", rc);
701 else
702 VGSvcVerbose(3, "vbsvcAutoMountWorker: No shared folder mappings found\n");
703
704#else
705 int rc = VINF_SUCCESS;
706#endif /* defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) */
707
708
709 /*
710 * Wait on shutdown (this used to be a silly RTThreadSleep(500) loop).
711 */
712 while (!*pfShutdown)
713 {
714 rc = RTSemEventMultiWait(g_hAutoMountEvent, RT_MS_1MIN);
715 if (rc != VERR_TIMEOUT)
716 break;
717 }
718
719 VGSvcVerbose(3, "vbsvcAutoMountWorkerOld: Finished with rc=%Rrc\n", rc);
720 return rc;
721}
722
723#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
724/**
725 * Assembles the mount directory and prefix into @a pszDst.
726 *
727 * Will fall back on defaults if we have trouble with the configuration from the
728 * host. This ASSUMES that @a cbDst is rather large and won't cause trouble
729 * with the default.
730 *
731 * @returns IPRT status code.
732 * @param pszDst Where to return the prefix.
733 * @param cbDst The size of the prefix buffer.
734 */
735static int vbsvcAutomounterQueryMountDirAndPrefix(char *pszDst, size_t cbDst)
736{
737 /*
738 * Query the config first.
739 */
740 /* Mount directory: */
741 const char *pszDir = VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR;
742 char *pszCfgDir;
743 int rc = VbglR3SharedFolderGetMountDir(&pszCfgDir);
744 if (RT_SUCCESS(rc))
745 {
746 if (*pszCfgDir == '/')
747 pszDir = pszCfgDir;
748 }
749 else
750 pszCfgDir = NULL;
751
752 /* Prefix: */
753 const char *pszPrefix = VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX;
754 char *pszCfgPrefix;
755 rc = VbglR3SharedFolderGetMountPrefix(&pszCfgPrefix);
756 if (RT_SUCCESS(rc))
757 {
758 if ( strchr(pszCfgPrefix, '/') == NULL
759 && strchr(pszCfgPrefix, '\\') == NULL
760 && strcmp(pszCfgPrefix, "..") != 0)
761 pszPrefix = pszCfgPrefix;
762 }
763 else
764 pszCfgPrefix = NULL;
765
766 /*
767 * Try combine the two.
768 */
769 rc = RTPathAbs(pszDir, pszDst, cbDst);
770 if (RT_SUCCESS(rc))
771 {
772 if (*pszPrefix)
773 {
774 rc = RTPathAppend(pszDst, cbDst, pszPrefix);
775 if (RT_FAILURE(rc))
776 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAppend(%s,,%s) -> %Rrc\n", pszDst, pszPrefix, rc);
777 }
778 else
779 {
780 rc = RTPathEnsureTrailingSeparator(pszDst, cbDst);
781 if (RT_FAILURE(rc))
782 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathEnsureTrailingSeparator(%s) -> %Rrc\n", pszDst, rc);
783 }
784 }
785 else
786 VGSvcError("vbsvcAutomounterQueryMountDirAndPrefix: RTPathAbs(%s) -> %Rrc\n", pszDir, rc);
787
788
789 /*
790 * Return the default dir + prefix if the above failed.
791 */
792 if (RT_FAILURE(rc))
793 {
794 rc = RTStrCopy(pszDst, cbDst, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/" VBOXSERVICE_AUTOMOUNT_DEFAULT_PREFIX);
795 AssertRC(rc);
796 }
797
798 RTStrFree(pszCfgDir);
799 RTStrFree(pszCfgPrefix);
800 return rc;
801}
802#endif /* !RT_OS_WINDOW && !RT_OS_OS2 */
803
804
805/**
806 * @callback_method_impl{FNRTSORTCMP, For sorting mount table by root ID. }
807 */
808static DECLCALLBACK(int) vbsvcAutomounterCompareEntry(void const *pvElement1, void const *pvElement2, void *pvUser)
809{
810 RT_NOREF_PV(pvUser);
811 PVBSVCAUTOMOUNTERENTRY pEntry1 = (PVBSVCAUTOMOUNTERENTRY)pvElement1;
812 PVBSVCAUTOMOUNTERENTRY pEntry2 = (PVBSVCAUTOMOUNTERENTRY)pvElement2;
813 return pEntry1->idRoot < pEntry2->idRoot ? -1
814 : pEntry1->idRoot > pEntry2->idRoot ? 1 : 0;
815}
816
817
818/**
819 * Worker for vbsvcAutomounterPopulateTable for adding discovered entries.
820 *
821 * This is puts dummies in for missing values, depending on
822 * vbsvcAutomounterPopulateTable to query them later.
823 *
824 * @returns VINF_SUCCESS or VERR_NO_MEMORY;
825 * @param pMountTable The mount table to add an entry to.
826 * @param pszName The shared folder name.
827 * @param pszMountPoint The mount point.
828 */
829static int vbsvcAutomounterAddEntry(PVBSVCAUTOMOUNTERTABLE pMountTable, const char *pszName, const char *pszMountPoint)
830{
831 VGSvcVerbose(2, "vbsvcAutomounterAddEntry: %s -> %s\n", pszMountPoint, pszName);
832 PVBSVCAUTOMOUNTERENTRY pEntry = (PVBSVCAUTOMOUNTERENTRY)RTMemAlloc(sizeof(*pEntry));
833 pEntry->idRoot = UINT32_MAX;
834 pEntry->uRootIdVersion = UINT32_MAX;
835 pEntry->fFlags = UINT64_MAX;
836 pEntry->pszName = RTStrDup(pszName);
837 pEntry->pszMountPoint = NULL;
838 pEntry->pszActualMountPoint = RTStrDup(pszMountPoint);
839 if (pEntry->pszName && pEntry->pszActualMountPoint)
840 {
841 if (pMountTable->cEntries + 1 <= pMountTable->cAllocated)
842 {
843 pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
844 return VINF_SUCCESS;
845 }
846
847 void *pvNew = RTMemRealloc(pMountTable->papEntries, (pMountTable->cAllocated + 8) * sizeof(pMountTable->papEntries[0]));
848 if (pvNew)
849 {
850 pMountTable->cAllocated += 8;
851 pMountTable->papEntries = (PVBSVCAUTOMOUNTERENTRY *)pvNew;
852
853 pMountTable->papEntries[pMountTable->cEntries++] = pEntry;
854 return VINF_SUCCESS;
855 }
856 }
857 RTMemFree(pEntry->pszActualMountPoint);
858 RTMemFree(pEntry->pszName);
859 RTMemFree(pEntry);
860 return VERR_NO_MEMORY;
861}
862
863
864/**
865 * Populates the mount table as best we can with existing automount entries.
866 *
867 * @returns VINF_SUCCESS or VERR_NO_MEMORY;
868 * @param pMountTable The mount table (empty).
869 */
870static int vbsvcAutomounterPopulateTable(PVBSVCAUTOMOUNTERTABLE pMountTable)
871{
872 int rc;
873
874#ifdef RT_OS_WINDOWS
875 /*
876 * Loop thru the drive letters and check out each of them using QueryDosDeviceW.
877 */
878 static const char s_szDevicePath[] = "\\Device\\VBoxMiniRdr\\;";
879 for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
880 {
881 RTUTF16 const wszMountPoint[4] = { chDrive, ':', '\0', '\0' };
882 RTUTF16 wszTargetPath[RTPATH_MAX];
883 DWORD const cwcResult = QueryDosDeviceW(wszMountPoint, wszTargetPath, RT_ELEMENTS(wszTargetPath));
884 if ( cwcResult > sizeof(s_szDevicePath)
885 && RTUtf16NICmpAscii(wszTargetPath, RT_STR_TUPLE(s_szDevicePath)) == 0)
886 {
887 PCRTUTF16 pwsz = &wszTargetPath[RT_ELEMENTS(s_szDevicePath) - 1];
888 Assert(pwsz[-1] == ';');
889 if ( (pwsz[0] & ~(RTUTF16)0x20) == chDrive
890 && pwsz[1] == ':'
891 && pwsz[2] == '\\')
892 {
893 /* For now we'll just use the special capitalization of the
894 "server" name to identify it as our work. We could check
895 if the symlink is from \Global?? or \??, but that trick does
896 work for older OS versions (<= XP) or when running the
897 service manually for testing/wathever purposes. */
898 /** @todo Modify the windows shared folder driver to allow tagging drives.*/
899 if (RTUtf16NCmpAscii(&pwsz[3], RT_STR_TUPLE("VBoxSvr\\")) == 0)
900 {
901 pwsz += 3 + 8;
902 if (*pwsz != '\\' && *pwsz)
903 {
904 /* The shared folder name should follow immediately after the server prefix. */
905 char *pszMountedName = NULL;
906 rc = RTUtf16ToUtf8(pwsz, &pszMountedName);
907 if (RT_SUCCESS(rc))
908 {
909 char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
910 rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
911 RTStrFree(pszMountedName);
912 }
913 if (RT_FAILURE(rc))
914 return rc;
915 }
916 else
917 VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Malformed, not ours: %ls -> %ls\n",
918 wszMountPoint, wszTargetPath);
919 }
920 else
921 VGSvcVerbose(3, "vbsvcAutomounterPopulateTable: Not ours: %ls -> %ls\n", wszMountPoint, wszTargetPath);
922 }
923 }
924 }
925
926#elif defined(RT_OS_OS2)
927 /*
928 * Just loop thru the drive letters and check the attachment of each.
929 */
930 for (char chDrive = 'Z'; chDrive >= 'A'; chDrive--)
931 {
932 char const szMountPoint[4] = { chDrive, ':', '\0', '\0' };
933 union
934 {
935 FSQBUFFER2 FsQueryBuf;
936 char achPadding[1024];
937 } uBuf;
938 RT_ZERO(uBuf);
939 ULONG cbBuf = sizeof(uBuf) - 2;
940 APIRET rcOs2 = DosQueryFSAttach(szMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
941 if (rcOs2 == NO_ERROR)
942 {
943 const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
944 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
945 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
946 {
947 const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
948 const char *pszTag = pszMountedName + strlen(pszMountedName) + 1; /* (Safe. Always two trailing zero bytes, see above.) */
949 if (strcmp(pszTag, g_szTag) == 0)
950 {
951 rc = vbsvcAutomounterAddEntry(pMountTable, pszMountedName, szMountPoint);
952 if (RT_FAILURE(rc))
953 return rc;
954 }
955 }
956 }
957 }
958
959#elif defined(RT_OS_LINUX)
960 /*
961 * Scan the mount table file for the mount point and then match file system
962 * and device/share. We identify our mounts by mount path + prefix for now,
963 * but later we may use the same approach as on solaris.
964 */
965 FILE *pFile = setmntent("/proc/mounts", "r");
966 int iErrMounts = errno;
967 if (!pFile)
968 pFile = setmntent("/etc/mtab", "r");
969 if (pFile)
970 {
971 rc = VWRN_NOT_FOUND;
972 struct mntent *pEntry;
973 while ((pEntry = getmntent(pFile)) != NULL)
974 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
975 if (strstr(pEntry->mnt_opts, g_szTag) != NULL)
976 {
977 rc = vbsvcAutomounterAddEntry(pMountTable, pEntry->mnt_fsname, pEntry->mnt_dir);
978 if (RT_FAILURE(rc))
979 {
980 endmntent(pFile);
981 return rc;
982 }
983 }
984 endmntent(pFile);
985 }
986 else
987 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d) or '/proc/mounts' (errno=%d)\n",
988 _PATH_MOUNTED, errno, iErrMounts);
989
990#elif defined(RT_OS_SOLARIS)
991 /*
992 * Look thru the system mount table and inspect the vboxsf mounts.
993 */
994 FILE *pFile = fopen(_PATH_MOUNTED, "r");
995 if (pFile)
996 {
997 rc = VINF_SUCCESS;
998 struct mnttab Entry;
999 while (getmntent(pFile, &Entry) == 0)
1000 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
1001 {
1002 /* Look for the dummy automounter option. */
1003 if ( Entry.mnt_mntopts != NULL
1004 && strstr(Entry.mnt_mntopts, g_szTag) != NULL)
1005 {
1006 rc = vbsvcAutomounterAddEntry(pMountTable, Entry.mnt_special, Entry.mnt_mountp);
1007 if (RT_FAILURE(rc))
1008 {
1009 fclose(pFile);
1010 return rc;
1011 }
1012 }
1013 }
1014 fclose(pFile);
1015 }
1016 else
1017 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
1018
1019#else
1020# error "PORTME!"
1021#endif
1022
1023 /*
1024 * Try reconcile the detected folders with data from the host.
1025 */
1026 uint32_t cMappings = 0;
1027 PVBGLR3SHAREDFOLDERMAPPING paMappings = NULL;
1028 rc = VbglR3SharedFolderGetMappings(g_idClientSharedFolders, true /*fAutoMountOnly*/, &paMappings, &cMappings);
1029 if (RT_SUCCESS(rc))
1030 {
1031 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
1032 {
1033 uint32_t const idRootSrc = paMappings[i].u32Root;
1034
1035 uint32_t uRootIdVer = UINT32_MAX;
1036 uint64_t fFlags = 0;
1037 char *pszName = NULL;
1038 char *pszMntPt = NULL;
1039 int rc2 = VbglR3SharedFolderQueryFolderInfo(g_idClientSharedFolders, idRootSrc, VBOXSERVICE_AUTOMOUNT_MIQF,
1040 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
1041 if (RT_SUCCESS(rc2))
1042 {
1043 uint32_t iPrevHit = UINT32_MAX;
1044 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
1045 {
1046 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
1047 if (RTStrICmp(pEntry->pszName, pszName) == 0)
1048 {
1049 VGSvcVerbose(2, "vbsvcAutomounterPopulateTable: Identified %s -> %s: idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
1050 pEntry->pszActualMountPoint, pEntry->pszName, idRootSrc, uRootIdVer, fFlags, pszMntPt);
1051 pEntry->fFlags = fFlags;
1052 pEntry->idRoot = idRootSrc;
1053 pEntry->uRootIdVersion = uRootIdVer;
1054 RTStrFree(pEntry->pszMountPoint);
1055 pEntry->pszMountPoint = RTStrDup(pszMntPt);
1056 if (!pEntry->pszMountPoint)
1057 {
1058 rc = VERR_NO_MEMORY;
1059 break;
1060 }
1061
1062 /* If multiple mappings of the same folder, pick the first or the one
1063 with matching mount point. */
1064 if (iPrevHit == UINT32_MAX)
1065 iPrevHit = iTable;
1066 else if (RTPathCompare(pszMntPt, pEntry->pszActualMountPoint) == 0)
1067 {
1068 if (iPrevHit != UINT32_MAX)
1069 pMountTable->papEntries[iPrevHit]->uRootIdVersion -= 1;
1070 iPrevHit = iTable;
1071 }
1072 else
1073 pEntry->uRootIdVersion -= 1;
1074 }
1075 }
1076
1077 RTStrFree(pszName);
1078 RTStrFree(pszMntPt);
1079 }
1080 else
1081 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderQueryFolderInfo(%u) failed: %Rrc\n", idRootSrc, rc2);
1082 }
1083
1084 VbglR3SharedFolderFreeMappings(paMappings);
1085
1086 /*
1087 * Sort the table by root ID.
1088 */
1089 if (pMountTable->cEntries > 1)
1090 RTSortApvShell((void **)pMountTable->papEntries, pMountTable->cEntries, vbsvcAutomounterCompareEntry, NULL);
1091
1092 for (uint32_t iTable = 0; iTable < pMountTable->cEntries; iTable++)
1093 {
1094 PVBSVCAUTOMOUNTERENTRY pEntry = pMountTable->papEntries[iTable];
1095 if (pMountTable->papEntries[iTable]->idRoot != UINT32_MAX)
1096 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s idRoot=%u ver=%u fFlags=%#x AutoMntPt=%s\n",
1097 iTable, pEntry->pszActualMountPoint, pEntry->pszName, pEntry->idRoot, pEntry->uRootIdVersion,
1098 pEntry->fFlags, pEntry->pszMountPoint);
1099 else
1100 VGSvcVerbose(1, "vbsvcAutomounterPopulateTable: #%u: %s -> %s - not identified!\n",
1101 iTable, pEntry->pszActualMountPoint, pEntry->pszName);
1102 }
1103 }
1104 else
1105 VGSvcError("vbsvcAutomounterPopulateTable: VbglR3SharedFolderGetMappings failed: %Rrc\n", rc);
1106 return rc;
1107}
1108
1109
1110/**
1111 * Checks whether the shared folder @a pszName is mounted on @a pszMountPoint.
1112 *
1113 * @returns Exactly one of the following IPRT status codes;
1114 * @retval VINF_SUCCESS if mounted
1115 * @retval VWRN_NOT_FOUND if nothing is mounted at @a pszMountPoint.
1116 * @retval VERR_RESOURCE_BUSY if a different shared folder is mounted there.
1117 * @retval VERR_ACCESS_DENIED if a non-shared folder file system is mounted
1118 * there.
1119 *
1120 * @param pszMountPoint The mount point to check.
1121 * @param pszName The name of the shared folder (mapping).
1122 */
1123static int vbsvcAutomounterQueryMountPoint(const char *pszMountPoint, const char *pszName)
1124{
1125 VGSvcVerbose(4, "vbsvcAutomounterQueryMountPoint: pszMountPoint=%s pszName=%s\n", pszMountPoint, pszName);
1126
1127#ifdef RT_OS_WINDOWS
1128 /*
1129 * We could've used RTFsQueryType here but would then have to
1130 * calling RTFsQueryLabel for the share name hint, ending up
1131 * doing the same work twice. We could also use QueryDosDeviceW,
1132 * but output is less clear...
1133 */
1134 PRTUTF16 pwszMountPoint = NULL;
1135 int rc = RTStrToUtf16(pszMountPoint, &pwszMountPoint);
1136 if (RT_SUCCESS(rc))
1137 {
1138 DWORD uSerial = 0;
1139 DWORD cchCompMax = 0;
1140 DWORD fFlags = 0;
1141 RTUTF16 wszLabel[512];
1142 RTUTF16 wszFileSystem[256];
1143 RT_ZERO(wszLabel);
1144 RT_ZERO(wszFileSystem);
1145 if (GetVolumeInformationW(pwszMountPoint, wszLabel, RT_ELEMENTS(wszLabel) - 1, &uSerial, &cchCompMax, &fFlags,
1146 wszFileSystem, RT_ELEMENTS(wszFileSystem) - 1))
1147 {
1148 if (RTUtf16ICmpAscii(wszFileSystem, "VBoxSharedFolderFS") == 0)
1149 {
1150 char *pszLabel = NULL;
1151 rc = RTUtf16ToUtf8(wszLabel, &pszLabel);
1152 if (RT_SUCCESS(rc))
1153 {
1154 const char *pszMountedName = pszLabel;
1155 if (RTStrStartsWith(pszMountedName, "VBOX_"))
1156 pszMountedName += sizeof("VBOX_") - 1;
1157 if (RTStrICmp(pszMountedName, pszName) == 0)
1158 {
1159 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1160 pszName, pszMountPoint);
1161 rc = VINF_SUCCESS;
1162 }
1163 else
1164 {
1165 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1166 pszMountedName, pszMountPoint, pszName);
1167 rc = VERR_RESOURCE_BUSY;
1168 }
1169 RTStrFree(pszLabel);
1170 }
1171 else
1172 {
1173 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: RTUtf16ToUtf8(%ls,) failed: %Rrc\n", wszLabel, rc);
1174 rc = VERR_RESOURCE_BUSY;
1175 }
1176 }
1177 else
1178 {
1179 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%ls' with label '%ls' mount at '%s', not '%s'...\n",
1180 wszFileSystem, wszLabel, pszMountPoint, pszName);
1181 rc = VERR_ACCESS_DENIED;
1182 }
1183 }
1184 else
1185 {
1186 rc = GetLastError();
1187 if (rc != ERROR_PATH_NOT_FOUND || g_cVerbosity >= 4)
1188 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: GetVolumeInformationW('%ls',,,,) failed: %u\n", pwszMountPoint, rc);
1189 rc = VWRN_NOT_FOUND;
1190 }
1191 RTUtf16Free(pwszMountPoint);
1192 }
1193 else
1194 {
1195 VGSvcError("vbsvcAutomounterQueryMountPoint: RTStrToUtf16(%s,) -> %Rrc\n", pszMountPoint, rc);
1196 rc = VWRN_NOT_FOUND;
1197 }
1198 return rc;
1199
1200#elif defined(RT_OS_OS2)
1201 /*
1202 * Query file system attachment info for the given drive letter.
1203 */
1204 union
1205 {
1206 FSQBUFFER2 FsQueryBuf;
1207 char achPadding[512];
1208 } uBuf;
1209 RT_ZERO(uBuf);
1210
1211 ULONG cbBuf = sizeof(uBuf);
1212 APIRET rcOs2 = DosQueryFSAttach(pszMountPoint, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
1213 int rc;
1214 if (rcOs2 == NO_ERROR)
1215 {
1216 const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
1217 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
1218 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
1219 {
1220 const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
1221 if (RTStrICmp(pszMountedName, pszName) == 0)
1222 {
1223 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1224 pszName, pszMountPoint);
1225 rc = VINF_SUCCESS;
1226 }
1227 else
1228 {
1229 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1230 pszMountedName, pszMountPoint, pszName);
1231 rc = VERR_RESOURCE_BUSY;
1232 }
1233 }
1234 else
1235 {
1236 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' type %u mount at '%s', not '%s'...\n",
1237 pszFsdName, uBuf.FsQueryBuf.iType, pszMountPoint, pszName);
1238 rc = VERR_ACCESS_DENIED;
1239 }
1240 }
1241 else
1242 {
1243 rc = VWRN_NOT_FOUND;
1244 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: DosQueryFSAttach(%s) -> %u\n", pszMountPoint, rcOs2);
1245 AssertMsgStmt(rcOs2 != ERROR_BUFFER_OVERFLOW && rcOs2 != ERROR_INVALID_PARAMETER,
1246 ("%s -> %u\n", pszMountPoint, rcOs2), rc = VERR_ACCESS_DENIED);
1247 }
1248 return rc;
1249
1250#elif defined(RT_OS_LINUX)
1251 /*
1252 * Scan one of the mount table file for the mount point and then
1253 * match file system and device/share.
1254 */
1255 FILE *pFile = setmntent("/proc/mounts", "r");
1256 int rc = errno;
1257 if (!pFile)
1258 pFile = setmntent(_PATH_MOUNTED, "r");
1259 if (pFile)
1260 {
1261 rc = VWRN_NOT_FOUND;
1262 struct mntent *pEntry;
1263 while ((pEntry = getmntent(pFile)) != NULL)
1264 if (RTPathCompare(pEntry->mnt_dir, pszMountPoint) == 0)
1265 {
1266 if (strcmp(pEntry->mnt_type, "vboxsf") == 0)
1267 {
1268 if (RTStrICmp(pEntry->mnt_fsname, pszName) == 0)
1269 {
1270 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1271 pszName, pszMountPoint);
1272 rc = VINF_SUCCESS;
1273 }
1274 else
1275 {
1276 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1277 pEntry->mnt_fsname, pszMountPoint, pszName);
1278 rc = VERR_RESOURCE_BUSY;
1279 }
1280 }
1281 else
1282 {
1283 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1284 pEntry->mnt_type, pEntry->mnt_fsname, pszMountPoint, pszName);
1285 rc = VERR_ACCESS_DENIED;
1286 }
1287 /* We continue searching in case of stacked mounts, we want the last one. */
1288 }
1289 endmntent(pFile);
1290 }
1291 else
1292 {
1293 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '/proc/mounts' (errno=%d) or '%s' (errno=%d)\n",
1294 rc, _PATH_MOUNTED, errno);
1295 rc = VERR_ACCESS_DENIED;
1296 }
1297 return rc;
1298
1299#elif defined(RT_OS_SOLARIS)
1300 /*
1301 * Similar to linux.
1302 */
1303 int rc;
1304 FILE *pFile = fopen(_PATH_MOUNTED, "r");
1305 if (pFile)
1306 {
1307 rc = VWRN_NOT_FOUND;
1308 struct mnttab Entry;
1309 while (getmntent(pFile, &Entry) == 0)
1310 if (RTPathCompare(Entry.mnt_mountp, pszMountPoint) == 0)
1311 {
1312 if (strcmp(Entry.mnt_fstype, "vboxfs") == 0)
1313 {
1314 if (RTStrICmp(Entry.mnt_special, pszName) == 0)
1315 {
1316 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s'.\n",
1317 pszName, pszMountPoint);
1318 rc = VINF_SUCCESS;
1319 }
1320 else
1321 {
1322 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found shared folder '%s' at '%s', not '%s'...\n",
1323 Entry.mnt_special, pszMountPoint, pszName);
1324 rc = VERR_RESOURCE_BUSY;
1325 }
1326 }
1327 else
1328 {
1329 VGSvcVerbose(3, "vbsvcAutomounterQueryMountPoint: Found a '%s' mount of '%s' at '%s', not '%s'...\n",
1330 Entry.mnt_fstype, Entry.mnt_special, pszMountPoint, pszName);
1331 rc = VERR_ACCESS_DENIED;
1332 }
1333 /* We continue searching in case of stacked mounts, we want the last one. */
1334 }
1335 fclose(pFile);
1336 }
1337 else
1338 {
1339 VGSvcError("vbsvcAutomounterQueryMountPoint: Could not open mount tab '%s' (errno=%d)\n", _PATH_MOUNTED, errno);
1340 rc = VERR_ACCESS_DENIED;
1341 }
1342 return rc;
1343#else
1344# error "PORTME"
1345#endif
1346}
1347
1348
1349/**
1350 * Worker for vbsvcAutomounterMountNewEntry that does the OS mounting.
1351 *
1352 * @returns IPRT status code.
1353 * @param pEntry The entry to try mount.
1354 */
1355static int vbsvcAutomounterMountIt(PVBSVCAUTOMOUNTERENTRY pEntry)
1356{
1357 VGSvcVerbose(3, "vbsvcAutomounterMountIt: Trying to mount '%s' (idRoot=%#x) on '%s'...\n",
1358 pEntry->pszName, pEntry->idRoot, pEntry->pszActualMountPoint);
1359#ifdef RT_OS_WINDOWS
1360 /*
1361 * Attach the shared folder using WNetAddConnection2W.
1362 *
1363 * According to google we should get a drive symlink in \\GLOBAL?? when
1364 * we are running under the system account. Otherwise it will a session
1365 * local link (\\??).
1366 */
1367 Assert(RT_C_IS_UPPER(pEntry->pszActualMountPoint[0]) && pEntry->pszActualMountPoint[1] == ':' && pEntry->pszActualMountPoint[2] == '\0');
1368 RTUTF16 wszDrive[4] = { pEntry->pszActualMountPoint[0], ':', '\0', '\0' };
1369
1370 RTUTF16 wszPrefixedName[RTPATH_MAX];
1371 int rc = RTUtf16CopyAscii(wszPrefixedName, RT_ELEMENTS(wszPrefixedName), "\\\\VBoxSvr\\");
1372 AssertRC(rc);
1373
1374 size_t const offName = RTUtf16Len(wszPrefixedName);
1375 PRTUTF16 pwszName = &wszPrefixedName[offName];
1376 rc = RTStrToUtf16Ex(pEntry->pszName, RTSTR_MAX, &pwszName, sizeof(wszPrefixedName) - offName, 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.

© 2023 Oracle
ContactPrivacy policyTerms of Use