VirtualBox

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

Last change on this file since 35016 was 35016, checked in by vboxsync, 14 years ago

VBoxService/VbglR3: Added ability to optionally specify auto-mount directory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.7 KB
Line 
1/* $Id: VBoxServiceAutoMount.cpp 35016 2010-12-13 14:40:09Z vboxsync $ */
2/** @file
3 * VBoxService - Auto-mounting for Shared Folders.
4 */
5
6/*
7 * Copyright (C) 2010 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/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/dir.h>
24#include <iprt/mem.h>
25#include <iprt/path.h>
26#include <iprt/string.h>
27#include <iprt/semaphore.h>
28#include <VBox/VBoxGuestLib.h>
29#include "VBoxServiceInternal.h"
30#include "VBoxServiceUtils.h"
31
32#include <errno.h>
33#include <grp.h>
34#include <sys/mount.h>
35#ifdef RT_OS_SOLARIS
36# include <sys/mntent.h>
37# include <sys/mnttab.h>
38# include <sys/vfs.h>
39#else
40# include <mntent.h>
41# include <paths.h>
42#endif
43#include <unistd.h>
44
45RT_C_DECLS_BEGIN
46#include "../../linux/sharedfolders/vbsfmount.h"
47RT_C_DECLS_END
48
49#ifdef RT_OS_SOLARIS
50# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/mnt"
51#else
52# define VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR "/media"
53#endif
54
55#ifndef _PATH_MOUNTED
56 #ifdef RT_OS_SOLARIS
57 #define _PATH_MOUNTED "/etc/mnttab"
58 #else
59 #define _PATH_MOUNTED "/etc/mtab"
60 #endif
61#endif
62
63/*******************************************************************************
64* Global Variables *
65*******************************************************************************/
66/** The semaphore we're blocking on. */
67static RTSEMEVENTMULTI g_AutoMountEvent = NIL_RTSEMEVENTMULTI;
68
69
70/** @copydoc VBOXSERVICE::pfnPreInit */
71static DECLCALLBACK(int) VBoxServiceAutoMountPreInit(void)
72{
73 return VINF_SUCCESS;
74}
75
76
77/** @copydoc VBOXSERVICE::pfnOption */
78static DECLCALLBACK(int) VBoxServiceAutoMountOption(const char **ppszShort, int argc, char **argv, int *pi)
79{
80 NOREF(ppszShort);
81 NOREF(argc);
82 NOREF(argv);
83 NOREF(pi);
84 return VINF_SUCCESS;
85}
86
87
88/** @copydoc VBOXSERVICE::pfnInit */
89static DECLCALLBACK(int) VBoxServiceAutoMountInit(void)
90{
91 VBoxServiceVerbose(3, "VBoxServiceAutoMountInit\n");
92
93 int rc = RTSemEventMultiCreate(&g_AutoMountEvent);
94 AssertRCReturn(rc, rc);
95
96 return rc;
97}
98
99
100/** @todo Integrate into RTFsQueryMountpoint(). */
101static bool VBoxServiceAutoMountShareIsMounted(const char *pszShare,
102 char *pszMountPoint, size_t cbMountPoint)
103{
104 AssertPtrReturn(pszShare, VERR_INVALID_PARAMETER);
105 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
106 AssertReturn(cbMountPoint, VERR_INVALID_PARAMETER);
107
108 bool fMounted = false;
109 /* @todo What to do if we have a relative path in mtab instead
110 * of an absolute one ("temp" vs. "/media/temp")?
111 * procfs contains the full path but not the actual share name ...
112 * FILE *pFh = setmntent("/proc/mounts", "r+t"); */
113#ifdef RT_OS_SOLARIS
114 FILE *pFh = fopen(_PATH_MOUNTED, "r");
115 if (!pFh)
116 VBoxServiceError("VBoxServiceAutoMountShareIsMounted: Could not open mount tab \"%s\"!\n",
117 _PATH_MOUNTED);
118 else
119 {
120 mnttab mntTab;
121 while ((getmntent(pFh, &mntTab)))
122 {
123 if (!RTStrICmp(mntTab.mnt_special, pszShare))
124 {
125 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", mntTab.mnt_mountp)
126 ? true : false;
127 break;
128 }
129 }
130 fclose(pFh);
131 }
132#else
133 FILE *pFh = setmntent(_PATH_MOUNTED, "r+t");
134 if (pFh == NULL)
135 VBoxServiceError("VBoxServiceAutoMountShareIsMounted: Could not open mount tab \"%s\"!\n",
136 _PATH_MOUNTED);
137 else
138 {
139 mntent *pMntEnt;
140 while ((pMntEnt = getmntent(pFh)))
141 {
142 if (!RTStrICmp(pMntEnt->mnt_fsname, pszShare))
143 {
144 fMounted = RTStrPrintf(pszMountPoint, cbMountPoint, "%s", pMntEnt->mnt_dir)
145 ? true : false;
146 break;
147 }
148 }
149 endmntent(pFh);
150 }
151#endif
152 return fMounted;
153}
154
155
156static int VBoxServiceAutoMountUnmount(const char *pszMountPoint)
157{
158 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
159
160 int rc = VINF_SUCCESS;
161 uint8_t uTries = 0;
162 int r;
163 while (uTries++ < 3)
164 {
165 r = umount(pszMountPoint);
166 if (r == 0)
167 break;
168 RTThreadSleep(5000); /* Wait a while ... */
169 }
170 if (r == -1)
171 rc = RTErrConvertFromErrno(errno);
172 return rc;
173}
174
175
176static int VBoxServiceAutoMountPrepareMountPoint(const char *pszMountPoint, const char *pszShareName,
177 vbsf_mount_opts *pOpts)
178{
179 AssertPtrReturn(pOpts, VERR_INVALID_PARAMETER);
180 AssertPtrReturn(pszMountPoint, VERR_INVALID_PARAMETER);
181 AssertPtrReturn(pszShareName, VERR_INVALID_PARAMETER);
182
183 RTFMODE fMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG; /* Owner (=root) and the group (=vboxsf) have full access. */
184 int rc = RTDirCreateFullPath(pszMountPoint, fMode);
185 if (RT_SUCCESS(rc))
186 {
187 rc = RTPathSetOwnerEx(pszMountPoint, NIL_RTUID /* Owner, unchanged */, pOpts->gid, RTPATH_F_ON_LINK);
188 if (RT_SUCCESS(rc))
189 {
190 rc = RTPathSetMode(pszMountPoint, fMode);
191 if (RT_FAILURE(rc))
192 VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not set mode %RTfmode for mount directory \"%s\", rc = %Rrc\n",
193 fMode, pszMountPoint, rc);
194 }
195 else
196 VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not set permissions for mount directory \"%s\", rc = %Rrc\n",
197 pszMountPoint, rc);
198 }
199 else
200 VBoxServiceError("VBoxServiceAutoMountPrepareMountPoint: Could not create mount directory \"%s\" with mode %RTfmode, rc = %Rrc\n",
201 pszMountPoint, fMode, rc);
202 return rc;
203}
204
205
206static int VBoxServiceAutoMountSharedFolder(const char *pszShareName, const char *pszMountPoint,
207 vbsf_mount_opts *pOpts)
208{
209 AssertPtr(pOpts);
210
211 int rc = VINF_SUCCESS;
212 char szAlreadyMountedTo[RTPATH_MAX];
213 /* If a Shared Folder already is mounted but not to our desired mount point,
214 * do an unmount first! */
215 if ( VBoxServiceAutoMountShareIsMounted(pszShareName, szAlreadyMountedTo, sizeof(szAlreadyMountedTo))
216 && RTStrICmp(pszMountPoint, szAlreadyMountedTo))
217 {
218 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder \"%s\" already mounted to \"%s\", unmounting ...\n",
219 pszShareName, szAlreadyMountedTo);
220 rc = VBoxServiceAutoMountUnmount(szAlreadyMountedTo);
221 if (RT_FAILURE(rc))
222 VBoxServiceError("VBoxServiceAutoMountWorker: Failed to unmount \"%s\", %s (%d)!\n",
223 szAlreadyMountedTo, strerror(errno), errno);
224 }
225
226 if (RT_SUCCESS(rc))
227 rc = VBoxServiceAutoMountPrepareMountPoint(pszMountPoint, pszShareName, pOpts);
228 if (RT_SUCCESS(rc))
229 {
230#ifdef RT_OS_SOLARIS
231 int flags = 0; /* No flags used yet. */
232 int r = mount(pszShareName,
233 pszMountPoint,
234 flags,
235 "vboxsf",
236 NULL, /* char *dataptr */
237 0, /* int datalen */
238 NULL, /* char *optptr */
239 0); /* int optlen */
240 if (r == 0)
241 {
242 VBoxServiceVerbose(0, "VBoxServiceAutoMountWorker: Shared folder \"%s\" was mounted to \"%s\"\n", pszShareName, pszMountPoint);
243 }
244 else
245 {
246 if (errno != EBUSY) /* Share is already mounted? Then skip error msg. */
247 VBoxServiceError("VBoxServiceAutoMountWorker: Could not mount shared folder \"%s\" to \"%s\", error = %s\n",
248 pszShareName, pszMountPoint, strerror(errno));
249 }
250#else /* !RT_OS_SOLARIS */
251 unsigned long flags = MS_NODEV;
252
253 const char *szOptions = { "rw" };
254 struct vbsf_mount_info_new mntinf;
255
256 mntinf.nullchar = '\0';
257 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
258 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
259 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
260 mntinf.length = sizeof(mntinf);
261
262 mntinf.uid = pOpts->uid;
263 mntinf.gid = pOpts->gid;
264 mntinf.ttl = pOpts->ttl;
265 mntinf.dmode = pOpts->dmode;
266 mntinf.fmode = pOpts->fmode;
267 mntinf.dmask = pOpts->dmask;
268 mntinf.fmask = pOpts->fmask;
269
270 strcpy(mntinf.name, pszShareName);
271 strcpy(mntinf.nls_name, "\0");
272
273 int r = mount(NULL,
274 pszMountPoint,
275 "vboxsf",
276 flags,
277 &mntinf);
278 if (r == 0)
279 {
280 VBoxServiceVerbose(0, "VBoxServiceAutoMountWorker: Shared folder \"%s\" was mounted to \"%s\"\n", pszShareName, pszMountPoint);
281
282 r = vbsfmount_complete(pszShareName, pszMountPoint, flags, pOpts);
283 switch (r)
284 {
285 case 0: /* Success. */
286 errno = 0; /* Clear all errors/warnings. */
287 break;
288
289 case 1:
290 VBoxServiceError("VBoxServiceAutoMountWorker: Could not update mount table (failed to create memstream): %s\n", strerror(errno));
291 break;
292
293 case 2:
294 VBoxServiceError("VBoxServiceAutoMountWorker: Could not open mount table for update: %s\n", strerror(errno));
295 break;
296
297 case 3:
298 VBoxServiceError("VBoxServiceAutoMountWorker: Could not add an entry to the mount table: %s\n", strerror(errno));
299 break;
300
301 default:
302 VBoxServiceError("VBoxServiceAutoMountWorker: Unknown error while completing mount operation: %d\n", r);
303 break;
304 }
305 }
306 else /* r == -1, we got some error in errno. */
307 {
308 if (errno == EPROTO)
309 {
310 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Messed up share name, re-trying ...\n");
311
312 /* Sometimes the mount utility messes up the share name. Try to
313 * un-mangle it again. */
314 char szCWD[4096];
315 size_t cchCWD;
316 if (!getcwd(szCWD, sizeof(szCWD)))
317 VBoxServiceError("VBoxServiceAutoMountWorker: Failed to get the current working directory\n");
318 cchCWD = strlen(szCWD);
319 if (!strncmp(pszMountPoint, szCWD, cchCWD))
320 {
321 while (pszMountPoint[cchCWD] == '/')
322 ++cchCWD;
323 /* We checked before that we have enough space */
324 strcpy(mntinf.name, pszMountPoint + cchCWD);
325 }
326 r = mount(NULL, pszMountPoint, "vboxsf", flags, &mntinf);
327 }
328 if (errno == EPROTO)
329 {
330 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Re-trying with old mounting structure ...\n");
331
332 /* New mount tool with old vboxsf module? Try again using the old
333 * vbsf_mount_info_old structure. */
334 struct vbsf_mount_info_old mntinf_old;
335 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
336 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
337 mntinf_old.uid = mntinf.uid;
338 mntinf_old.gid = mntinf.gid;
339 mntinf_old.ttl = mntinf.ttl;
340 r = mount(NULL, pszMountPoint, "vboxsf", flags, &mntinf_old);
341 }
342 if (r == -1) /* Was there some error from one of the tries above? */
343 {
344 switch (errno)
345 {
346 /* If we get EINVAL here, the system already has mounted the Shared Folder to another
347 * mount point. */
348 case EINVAL:
349 VBoxServiceVerbose(0, "VBoxServiceAutoMountWorker: Shared folder \"%s\" already is mounted!\n", pszShareName);
350 /* Ignore this error! */
351 break;
352 case EBUSY:
353 /* Ignore these errors! */
354 break;
355
356 default:
357 VBoxServiceError("VBoxServiceAutoMountWorker: Could not mount shared folder \"%s\" to \"%s\": %s (%d)\n",
358 pszShareName, pszMountPoint, strerror(errno), errno);
359 rc = RTErrConvertFromErrno(errno);
360 break;
361 }
362 }
363 }
364#endif /* !RT_OS_SOLARIS */
365 }
366 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Mounting returned with rc=%Rrc\n", rc);
367 return rc;
368}
369
370static int VBoxServiceAutoMountProcessMappings(PVBGLR3SHAREDFOLDERMAPPING paMappings, uint32_t cMappings,
371 const char *pszMountDir, const char *pszSharePrefix, uint32_t uClientID)
372{
373 if (cMappings == 0)
374 return VINF_SUCCESS;
375 AssertPtrReturn(paMappings, VERR_INVALID_PARAMETER);
376 AssertPtrReturn(pszMountDir, VERR_INVALID_PARAMETER);
377 AssertPtrReturn(pszSharePrefix, VERR_INVALID_PARAMETER);
378 AssertPtrReturn(*pszSharePrefix, VERR_INVALID_PARAMETER);
379 AssertReturn(uClientID > 0, VERR_INVALID_PARAMETER);
380
381 int rc = VINF_SUCCESS;
382 for (uint32_t i = 0; i < cMappings && RT_SUCCESS(rc); i++)
383 {
384 char *pszShareName = NULL;
385 rc = VbglR3SharedFolderGetName(uClientID, paMappings[i].u32Root, &pszShareName);
386 if ( RT_SUCCESS(rc)
387 && *pszShareName)
388 {
389 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Connecting share %u (%s) ...\n", i+1, pszShareName);
390
391 char *pszShareNameFull = NULL;
392 if (RTStrAPrintf(&pszShareNameFull, "%s%s", pszSharePrefix, pszShareName) > 0)
393 {
394 char szMountPoint[RTPATH_MAX];
395 rc = RTPathJoin(szMountPoint, sizeof(szMountPoint), pszMountDir, pszShareNameFull);
396 if (RT_SUCCESS(rc))
397 {
398 VBoxServiceVerbose(4, "VBoxServiceAutoMountWorker: Processing mount point \"%s\"\n", szMountPoint);
399
400 struct group *grp_vboxsf = getgrnam("vboxsf");
401 if (grp_vboxsf)
402 {
403 struct vbsf_mount_opts mount_opts =
404 {
405 0, /* uid */
406 grp_vboxsf->gr_gid, /* gid */
407 0, /* ttl */
408 0770, /* dmode, owner and group "vboxsf" have full access */
409 0770, /* fmode, owner and group "vboxsf" have full access */
410 0, /* dmask */
411 0, /* fmask */
412 0, /* ronly */
413 0, /* noexec */
414 0, /* nodev */
415 0, /* nosuid */
416 0, /* remount */
417 "\0", /* nls_name */
418 NULL, /* convertcp */
419 };
420
421 /* We always use "/media" as our root mounting directory. */
422 /** @todo Detect the correct "media/mnt" directory, based on the current guest (?). */
423 rc = VBoxServiceAutoMountSharedFolder(pszShareName, szMountPoint, &mount_opts);
424 }
425 else
426 VBoxServiceError("VBoxServiceAutoMountWorker: Group \"vboxsf\" does not exist\n");
427 }
428 else
429 VBoxServiceError("VBoxServiceAutoMountWorker: Unable to join mount point/prefix/shrae, rc = %Rrc\n", rc);
430 RTStrFree(pszShareNameFull);
431 }
432 else
433 VBoxServiceError("VBoxServiceAutoMountWorker: Unable to allocate full share name\n");
434 RTStrFree(pszShareName);
435 }
436 else
437 VBoxServiceError("VBoxServiceAutoMountWorker: Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
438 paMappings[i].u32Root, rc);
439 } /* for cMappings. */
440 return rc;
441}
442
443
444/** @copydoc VBOXSERVICE::pfnWorker */
445DECLCALLBACK(int) VBoxServiceAutoMountWorker(bool volatile *pfShutdown)
446{
447 /*
448 * Tell the control thread that it can continue
449 * spawning services.
450 */
451 RTThreadUserSignal(RTThreadSelf());
452
453 uint32_t u32ClientId;
454 int rc = VbglR3SharedFolderConnect(&u32ClientId);
455 if (!RT_SUCCESS(rc))
456 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Failed to connect to the shared folder service, error %Rrc\n", rc);
457 else
458 {
459 uint32_t cMappings;
460 PVBGLR3SHAREDFOLDERMAPPING paMappings;
461
462 rc = VbglR3SharedFolderGetMappings(u32ClientId, true /* Only process auto-mounted folders */,
463 &paMappings, &cMappings);
464 if (RT_SUCCESS(rc))
465 {
466 char *pszMountDir;
467 rc = VbglR3SharedFolderGetMountDir(&pszMountDir);
468 if (rc == VERR_NOT_FOUND)
469 rc = RTStrDupEx(&pszMountDir, VBOXSERVICE_AUTOMOUNT_DEFAULT_DIR);
470 if (RT_SUCCESS(rc))
471 {
472 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder mount dir set to \"%s\"\n", pszMountDir);
473
474 char *pszSharePrefix;
475 rc = VbglR3SharedFolderGetMountPrefix(&pszSharePrefix);
476
477 {
478 }
479 if (RT_SUCCESS(rc))
480 {
481 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder mount prefix set to \"%s\"\n", pszSharePrefix);
482 #if 0
483 /* Check for a fixed/virtual auto-mount share. */
484 if (VbglR3SharedFolderExists(u32ClientId, "vbsfAutoMount"))
485 {
486 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Host supports auto-mount root\n");
487 }
488 else
489 {
490 #endif
491 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Got %u shared folder mappings\n", cMappings);
492 rc = VBoxServiceAutoMountProcessMappings(paMappings, cMappings, pszMountDir, pszSharePrefix, u32ClientId);
493 #if 0
494 }
495 #endif
496 RTStrFree(pszSharePrefix);
497 } /* Mount share prefix. */
498 else
499 VBoxServiceError("VBoxServiceAutoMountWorker: Error while getting the shared folder mount prefix, rc = %Rrc\n", rc);
500 RTStrFree(pszMountDir);
501 }
502 else
503 VBoxServiceError("VBoxServiceAutoMountWorker: Error while getting the shared folder directory, rc = %Rrc\n", rc);
504 RTMemFree(paMappings);
505 }
506 else
507 VBoxServiceError("VBoxServiceAutoMountWorker: Error while getting the shared folder mappings, rc = %Rrc\n", rc);
508 VbglR3SharedFolderDisconnect(u32ClientId);
509 }
510
511 RTSemEventMultiDestroy(g_AutoMountEvent);
512 g_AutoMountEvent = NIL_RTSEMEVENTMULTI;
513
514 VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Finished\n");
515 return 0;
516}
517
518/** @copydoc VBOXSERVICE::pfnTerm */
519static DECLCALLBACK(void) VBoxServiceAutoMountTerm(void)
520{
521 VBoxServiceVerbose(3, "VBoxServiceAutoMountTerm\n");
522 return;
523}
524
525
526/** @copydoc VBOXSERVICE::pfnStop */
527static DECLCALLBACK(void) VBoxServiceAutoMountStop(void)
528{
529 RTSemEventMultiSignal(g_AutoMountEvent);
530}
531
532
533/**
534 * The 'automount' service description.
535 */
536VBOXSERVICE g_AutoMount =
537{
538 /* pszName. */
539 "automount",
540 /* pszDescription. */
541 "Auto-mount for Shared Folders",
542 /* pszUsage. */
543 NULL,
544 /* pszOptions. */
545 NULL,
546 /* methods */
547 VBoxServiceAutoMountPreInit,
548 VBoxServiceAutoMountOption,
549 VBoxServiceAutoMountInit,
550 VBoxServiceAutoMountWorker,
551 VBoxServiceAutoMountStop,
552 VBoxServiceAutoMountTerm
553};
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