VirtualBox

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

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

VBoxService,manual: Use /media on solaris too (11 has it, on 10 we create it).

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

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