VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c@ 28934

Last change on this file since 28934 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.7 KB
Line 
1/** @file
2 * vboxvfs -- VirtualBox Guest Additions for Linux: mount(8) helper
3 *
4 * Parses options provided by mount (or user directly)
5 * Packs them into struct vbsfmount and passes to mount(2)
6 * Optionally adds entries to mtab
7 */
8
9/*
10 * Copyright (C) 2006-2007 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22#ifndef _GNU_SOURCE
23#define _GNU_SOURCE
24#endif
25
26/* #define DEBUG */
27#define DBG if (0)
28#include <errno.h>
29#include <fcntl.h>
30#include <ctype.h>
31#include <getopt.h>
32#include <mntent.h>
33#include <pwd.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/mount.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <unistd.h>
42#include <mntent.h>
43#include <limits.h>
44#include <iconv.h>
45
46#include "vbsfmount.h"
47
48/* Compile-time assertion. If a == 0, we get two identical switch cases, which is not
49 allowed. */
50#define CT_ASSERT(a) \
51 do { \
52 switch(0) { case 0: case (a): ; } \
53 } while (0)
54
55struct opts
56{
57 int uid;
58 int gid;
59 int ttl;
60 int dmode;
61 int fmode;
62 int dmask;
63 int fmask;
64 int ronly;
65 int noexec;
66 int nodev;
67 int nosuid;
68 int remount;
69 char nls_name[MAX_NLS_NAME];
70 char *convertcp;
71};
72
73#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
74
75static void PANIC_ATTR
76panic (const char *fmt, ...)
77{
78 va_list ap;
79
80 va_start (ap, fmt);
81 vfprintf (stderr, fmt, ap);
82 va_end (ap);
83 exit (EXIT_FAILURE);
84}
85
86static void PANIC_ATTR
87panic_err (const char *fmt, ...)
88{
89 va_list ap;
90 int errno_code = errno;
91
92 va_start (ap, fmt);
93 vfprintf (stderr, fmt, ap);
94 va_end (ap);
95 fprintf (stderr, ": %s\n", strerror (errno_code));
96 exit (EXIT_FAILURE);
97}
98
99static int
100safe_atoi (const char *s, size_t size, int base)
101{
102 char *endptr;
103 long long int val = strtoll (s, &endptr, base);
104
105 if (val < INT_MIN || val > INT_MAX || endptr < s + size)
106 {
107 errno = ERANGE;
108 panic_err ("could not convert %.*s to integer, result = %d",
109 (int)size, s, (int) val);
110 }
111 return (int) val;
112}
113
114static void
115process_mount_opts (const char *s, struct opts *opts)
116{
117 const char *next = s;
118 size_t len;
119 typedef enum handler_opt
120 {
121 HORW,
122 HORO,
123 HOUID,
124 HOGID,
125 HOTTL,
126 HODMODE,
127 HOFMODE,
128 HOUMASK,
129 HODMASK,
130 HOFMASK,
131 HOIOCHARSET,
132 HOCONVERTCP,
133 HONOEXEC,
134 HOEXEC,
135 HONODEV,
136 HODEV,
137 HONOSUID,
138 HOSUID,
139 HOREMOUNT,
140 HONOAUTO
141 } handler_opt;
142 struct
143 {
144 const char *name;
145 handler_opt opt;
146 int has_arg;
147 const char *desc;
148 } handlers[]
149 =
150 {
151 {"rw", HORW, 0, "mount read write (default)"},
152 {"ro", HORO, 0, "mount read only"},
153 {"uid", HOUID, 1, "default file owner user id"},
154 {"gid", HOGID, 1, "default file owner group id"},
155 {"ttl", HOTTL, 1, "time to live for dentry"},
156 {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
157 {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
158 {"dmode", HODMODE, 1, "mode of all directories"},
159 {"fmode", HOFMODE, 1, "mode of all regular files"},
160 {"umask", HOUMASK, 1, "umask of directories and regular files"},
161 {"dmask", HODMASK, 1, "umask of directories"},
162 {"fmask", HOFMASK, 1, "umask of regular files"},
163 {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
164 {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
165 {"nodev", HONODEV, 0, 0 }, /* usual manpages */
166 {"dev", HODEV, 0, 0 },
167 {"nosuid", HONOSUID, 0, 0 },
168 {"suid", HOSUID, 0, 0 },
169 {"remount", HOREMOUNT, 0, 0 },
170 {"noauto", HONOAUTO, 0, 0 },
171 {NULL, 0, 0, NULL}
172 }, *handler;
173
174 while (next)
175 {
176 const char *val;
177 size_t key_len, val_len;
178
179 s = next;
180 next = strchr (s, ',');
181 if (!next)
182 {
183 len = strlen (s);
184 }
185 else
186 {
187 len = next - s;
188 next += 1;
189 if (!*next)
190 next = 0;
191 }
192
193 val = NULL;
194 val_len = 0;
195 for (key_len = 0; key_len < len; ++key_len)
196 {
197 if (s[key_len] == '=')
198 {
199 if (key_len + 1 < len)
200 {
201 val = s + key_len + 1;
202 val_len = len - key_len - 1;
203 }
204 break;
205 }
206 }
207
208 for (handler = handlers; handler->name; ++handler)
209 {
210 size_t j;
211 for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
212 ;
213
214 if (j == key_len && !handler->name[j])
215 {
216 if (handler->has_arg)
217 {
218 if (!(val && *val))
219 {
220 panic ("%.*s requires an argument (i.e. %.*s=<arg>)\n",
221 (int)len, s, (int)len, s);
222 }
223 }
224
225 switch(handler->opt)
226 {
227 case HORW:
228 opts->ronly = 0;
229 break;
230 case HORO:
231 opts->ronly = 1;
232 break;
233 case HONOEXEC:
234 opts->noexec = 1;
235 break;
236 case HOEXEC:
237 opts->noexec = 0;
238 break;
239 case HONODEV:
240 opts->nodev = 1;
241 break;
242 case HODEV:
243 opts->nodev = 0;
244 break;
245 case HONOSUID:
246 opts->nosuid = 1;
247 break;
248 case HOSUID:
249 opts->nosuid = 0;
250 break;
251 case HOREMOUNT:
252 opts->remount = 1;
253 break;
254 case HOUID:
255 /** @todo convert string to id. */
256 opts->uid = safe_atoi (val, val_len, 10);
257 break;
258 case HOGID:
259 /** @todo convert string to id. */
260 opts->gid = safe_atoi (val, val_len, 10);
261 break;
262 case HOTTL:
263 opts->ttl = safe_atoi (val, val_len, 10);
264 break;
265 case HODMODE:
266 opts->dmode = safe_atoi (val, val_len, 8);
267 break;
268 case HOFMODE:
269 opts->fmode = safe_atoi (val, val_len, 8);
270 break;
271 case HOUMASK:
272 opts->dmask = opts->fmask = safe_atoi (val, val_len, 8);
273 break;
274 case HODMASK:
275 opts->dmask = safe_atoi (val, val_len, 8);
276 break;
277 case HOFMASK:
278 opts->fmask = safe_atoi (val, val_len, 8);
279 break;
280 case HOIOCHARSET:
281 if (val_len + 1 > sizeof (opts->nls_name))
282 {
283 panic ("iocharset name too long\n");
284 }
285 memcpy (opts->nls_name, val, val_len);
286 opts->nls_name[val_len] = 0;
287 break;
288 case HOCONVERTCP:
289 opts->convertcp = malloc (val_len + 1);
290 if (!opts->convertcp)
291 {
292 panic_err ("could not allocate memory");
293 }
294 memcpy (opts->convertcp, val, val_len);
295 opts->convertcp[val_len] = 0;
296 break;
297 case HONOAUTO:
298 break;
299 }
300 break;
301 }
302 continue;
303 }
304
305 if (!handler->name)
306 {
307 fprintf (stderr, "unknown mount option `%.*s'\n", (int)len, s);
308 fprintf (stderr, "valid options:\n");
309
310 for (handler = handlers; handler->name; ++handler)
311 {
312 if (handler->desc)
313 fprintf (stderr, " %-10s%s %s\n", handler->name,
314 handler->has_arg ? "=<arg>" : "", handler->desc);
315 }
316 exit (EXIT_FAILURE);
317 }
318 }
319}
320
321static void
322complete (char *host_name, char *mount_point,
323 unsigned long flags, struct opts *opts)
324{
325 FILE *f, *m;
326 char *buf;
327 size_t size;
328 struct mntent e;
329
330 m = open_memstream (&buf, &size);
331 if (!m)
332 panic_err ("could not update mount table (failed to create memstream)");
333
334 if (opts->uid)
335 fprintf (m, "uid=%d,", opts->uid);
336 if (opts->gid)
337 fprintf (m, "gid=%d,", opts->gid);
338 if (opts->ttl)
339 fprintf (m, "ttl=%d,", opts->ttl);
340 if (*opts->nls_name)
341 fprintf (m, "iocharset=%s,", opts->nls_name);
342 if (flags & MS_NOSUID)
343 fprintf (m, "%s,", MNTOPT_NOSUID);
344 if (flags & MS_RDONLY)
345 fprintf (m, "%s,", MNTOPT_RO);
346 else
347 fprintf (m, "%s,", MNTOPT_RW);
348
349 fclose (m);
350
351 if (size > 0)
352 buf[size - 1] = 0;
353 else
354 buf = "defaults";
355
356 f = setmntent (MOUNTED, "a+");
357 if (!f)
358 panic_err ("could not open mount table for update");
359
360 e.mnt_fsname = host_name;
361 e.mnt_dir = mount_point;
362 e.mnt_type = "vboxsf";
363 e.mnt_opts = buf;
364 e.mnt_freq = 0;
365 e.mnt_passno = 0;
366
367 if (addmntent (f, &e))
368 {
369 if (size > 0)
370 {
371 memset (buf, 0, size);
372 free (buf);
373 }
374 panic_err ("could not add an entry to the mount table");
375 }
376
377 endmntent (f);
378
379 if (size > 0)
380 {
381 memset (buf, 0, size);
382 free (buf);
383 }
384}
385
386static void
387convertcp (char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
388{
389 char *i = host_name;
390 char *o = info->name;
391 size_t ib = strlen (host_name);
392 size_t ob = sizeof (info->name) - 1;
393 iconv_t cd;
394
395 cd = iconv_open ("UTF-8", in_codeset);
396 if (cd == (iconv_t) -1)
397 {
398 panic_err ("could not convert share name, iconv_open `%s' failed",
399 in_codeset);
400 }
401
402 while (ib)
403 {
404 size_t c = iconv (cd, &i, &ib, &o, &ob);
405 if (c == (size_t) -1)
406 {
407 panic_err ("could not convert share name(%s) at %d",
408 host_name, (int)(strlen (host_name) - ib));
409 }
410 }
411 *o = 0;
412}
413
414
415/**
416 * Print out a usage message and exit.
417 *
418 * @param name The name of the application
419 */
420static void __attribute ((noreturn)) usage(char *name)
421{
422 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
423 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
424 "\n"
425 " -w mount the shared folder writably (the default)\n"
426 " -r mount the shared folder read-only\n"
427 " -n do not create an mtab entry\n"
428 " -o OPTION[,OPTION...] use the mount options specified\n"
429 "\n", name);
430 printf("Available mount options are:\n"
431 " rw mount writably (the default)\n"
432 " ro mount read only\n"
433 " uid=UID set the default file owner user id to UID\n"
434 " gid=GID set the default file owner group id to GID\n"
435 " ttl=TTL set the \"time to live\" to TID for the dentry\n");
436 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
437 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
438 " umask=UMASK set the umask to (octal) UMASK\n");
439 printf(" dmask=UMASK set the umask applied to directories only\n"
440 " fmask=UMASK set the umask applied to regular files only\n"
441 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
442 " (default set is utf8)\n"
443 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
444 "\n");
445 printf("Less common used options:\n"
446 " noexec,exec,nodev,dev,nosuid,suid\n");
447 exit(1);
448}
449
450int
451main (int argc, char **argv)
452{
453 int c;
454 int err;
455 int nomtab = 0;
456 unsigned long flags = MS_NODEV;
457 char *host_name;
458 char *mount_point;
459 struct vbsf_mount_info_new mntinf;
460 struct opts opts =
461 {
462 0, /* uid */
463 0, /* gid */
464 0, /* ttl */
465 ~0, /* dmode */
466 ~0, /* fmode*/
467 0, /* dmask */
468 0, /* fmask */
469 0, /* ronly */
470 0, /* noexec */
471 0, /* nodev */
472 0, /* nosuid */
473 0, /* remount */
474 "\0", /* nls_name */
475 NULL, /* convertcp */
476 };
477
478 mntinf.nullchar = '\0';
479 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
480 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
481 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
482 mntinf.length = sizeof(mntinf);
483
484 if (getuid ())
485 panic ("Only root can mount shared folders from the host.\n");
486
487 if (!argv[0])
488 argv[0] = "mount.vboxsf";
489
490 /* Compile-time assertions */
491 CT_ASSERT(sizeof(uid_t) == sizeof(int));
492 CT_ASSERT(sizeof(gid_t) == sizeof(int));
493
494 while ((c = getopt (argc, argv, "rwno:h")) != -1)
495 {
496 switch (c)
497 {
498 default:
499 fprintf (stderr, "unknown option `%c:%#x'\n", c, c);
500 case '?':
501 case 'h':
502 usage(argv[0]);
503
504 case 'r':
505 opts.ronly = 1;
506 break;
507
508 case 'w':
509 opts.ronly = 0;
510
511 case 'o':
512 process_mount_opts (optarg, &opts);
513 break;
514
515 case 'n':
516 nomtab = 1;
517 break;
518 }
519 }
520
521 if (argc - optind < 2)
522 usage(argv[0]);
523
524 host_name = argv[optind];
525 mount_point = argv[optind + 1];
526
527 if (opts.convertcp)
528 convertcp (opts.convertcp, host_name, &mntinf);
529 else
530 {
531 if (strlen (host_name) > MAX_HOST_NAME - 1)
532 panic ("host name is too big\n");
533
534 strcpy (mntinf.name, host_name);
535 }
536
537 if (strlen (opts.nls_name) > MAX_NLS_NAME - 1)
538 panic ("%s: the character set name for I/O is too long.\n", argv[0]);
539
540 strcpy (mntinf.nls_name, opts.nls_name);
541
542 if (opts.ronly)
543 flags |= MS_RDONLY;
544 if (opts.noexec)
545 flags |= MS_NOEXEC;
546 if (opts.nodev)
547 flags |= MS_NODEV;
548
549 mntinf.uid = opts.uid;
550 mntinf.gid = opts.gid;
551 mntinf.ttl = opts.ttl;
552 mntinf.dmode = opts.dmode;
553 mntinf.fmode = opts.fmode;
554 mntinf.dmask = opts.dmask;
555 mntinf.fmask = opts.fmask;
556
557 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
558 if (err == -1 && errno == EPROTO)
559 {
560 /* Sometimes the mount utility messes up the share name. Try to
561 * un-mangle it again. */
562 char szCWD[4096];
563 size_t cchCWD;
564 if (!getcwd(szCWD, sizeof(szCWD)))
565 panic_err("%s: failed to get the current working directory", argv[0]);
566 cchCWD = strlen(szCWD);
567 if (!strncmp(host_name, szCWD, cchCWD))
568 {
569 while (host_name[cchCWD] == '/')
570 ++cchCWD;
571 /* We checked before that we have enough space */
572 strcpy (mntinf.name, host_name + cchCWD);
573 }
574 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
575 }
576 if (err == -1 && errno == EPROTO)
577 {
578 /* New mount tool with old vboxsf module? Try again using the old
579 * vbsf_mount_info_old structure. */
580 struct vbsf_mount_info_old mntinf_old;
581 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
582 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
583 mntinf_old.uid = mntinf.uid;
584 mntinf_old.gid = mntinf.gid;
585 mntinf_old.ttl = mntinf.ttl;
586 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf_old);
587 }
588 if (err)
589 panic_err ("%s: mounting failed with the error", argv[0]);
590
591 if (!nomtab)
592 complete (host_name, mount_point, flags, &opts);
593
594 exit (EXIT_SUCCESS);
595}
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