VirtualBox

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

Last change on this file since 29485 was 28998, checked in by vboxsync, 15 years ago

Linux Additions: rename the vboxvfs module to vboxsf to make it load by demand of the Linux kernel

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.8 KB
Line 
1/** @file
2 * vboxsf -- 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 HONIGNORE
142 } handler_opt;
143 struct
144 {
145 const char *name;
146 handler_opt opt;
147 int has_arg;
148 const char *desc;
149 } handlers[]
150 =
151 {
152 {"rw", HORW, 0, "mount read write (default)"},
153 {"ro", HORO, 0, "mount read only"},
154 {"uid", HOUID, 1, "default file owner user id"},
155 {"gid", HOGID, 1, "default file owner group id"},
156 {"ttl", HOTTL, 1, "time to live for dentry"},
157 {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
158 {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
159 {"dmode", HODMODE, 1, "mode of all directories"},
160 {"fmode", HOFMODE, 1, "mode of all regular files"},
161 {"umask", HOUMASK, 1, "umask of directories and regular files"},
162 {"dmask", HODMASK, 1, "umask of directories"},
163 {"fmask", HOFMASK, 1, "umask of regular files"},
164 {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
165 {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
166 {"nodev", HONODEV, 0, 0 }, /* usual manpages */
167 {"dev", HODEV, 0, 0 },
168 {"nosuid", HONOSUID, 0, 0 },
169 {"suid", HOSUID, 0, 0 },
170 {"remount", HOREMOUNT, 0, 0 },
171 {"noauto", HONOAUTO, 0, 0 },
172 {"_netdev", HONIGNORE, 0, 0 },
173 {NULL, 0, 0, NULL}
174 }, *handler;
175
176 while (next)
177 {
178 const char *val;
179 size_t key_len, val_len;
180
181 s = next;
182 next = strchr (s, ',');
183 if (!next)
184 {
185 len = strlen (s);
186 }
187 else
188 {
189 len = next - s;
190 next += 1;
191 if (!*next)
192 next = 0;
193 }
194
195 val = NULL;
196 val_len = 0;
197 for (key_len = 0; key_len < len; ++key_len)
198 {
199 if (s[key_len] == '=')
200 {
201 if (key_len + 1 < len)
202 {
203 val = s + key_len + 1;
204 val_len = len - key_len - 1;
205 }
206 break;
207 }
208 }
209
210 for (handler = handlers; handler->name; ++handler)
211 {
212 size_t j;
213 for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
214 ;
215
216 if (j == key_len && !handler->name[j])
217 {
218 if (handler->has_arg)
219 {
220 if (!(val && *val))
221 {
222 panic ("%.*s requires an argument (i.e. %.*s=<arg>)\n",
223 (int)len, s, (int)len, s);
224 }
225 }
226
227 switch(handler->opt)
228 {
229 case HORW:
230 opts->ronly = 0;
231 break;
232 case HORO:
233 opts->ronly = 1;
234 break;
235 case HONOEXEC:
236 opts->noexec = 1;
237 break;
238 case HOEXEC:
239 opts->noexec = 0;
240 break;
241 case HONODEV:
242 opts->nodev = 1;
243 break;
244 case HODEV:
245 opts->nodev = 0;
246 break;
247 case HONOSUID:
248 opts->nosuid = 1;
249 break;
250 case HOSUID:
251 opts->nosuid = 0;
252 break;
253 case HOREMOUNT:
254 opts->remount = 1;
255 break;
256 case HOUID:
257 /** @todo convert string to id. */
258 opts->uid = safe_atoi (val, val_len, 10);
259 break;
260 case HOGID:
261 /** @todo convert string to id. */
262 opts->gid = safe_atoi (val, val_len, 10);
263 break;
264 case HOTTL:
265 opts->ttl = safe_atoi (val, val_len, 10);
266 break;
267 case HODMODE:
268 opts->dmode = safe_atoi (val, val_len, 8);
269 break;
270 case HOFMODE:
271 opts->fmode = safe_atoi (val, val_len, 8);
272 break;
273 case HOUMASK:
274 opts->dmask = opts->fmask = safe_atoi (val, val_len, 8);
275 break;
276 case HODMASK:
277 opts->dmask = safe_atoi (val, val_len, 8);
278 break;
279 case HOFMASK:
280 opts->fmask = safe_atoi (val, val_len, 8);
281 break;
282 case HOIOCHARSET:
283 if (val_len + 1 > sizeof (opts->nls_name))
284 {
285 panic ("iocharset name too long\n");
286 }
287 memcpy (opts->nls_name, val, val_len);
288 opts->nls_name[val_len] = 0;
289 break;
290 case HOCONVERTCP:
291 opts->convertcp = malloc (val_len + 1);
292 if (!opts->convertcp)
293 {
294 panic_err ("could not allocate memory");
295 }
296 memcpy (opts->convertcp, val, val_len);
297 opts->convertcp[val_len] = 0;
298 break;
299 case HONOAUTO:
300 case HONIGNORE:
301 break;
302 }
303 break;
304 }
305 continue;
306 }
307
308 if (!handler->name)
309 {
310 fprintf (stderr, "unknown mount option `%.*s'\n", (int)len, s);
311 fprintf (stderr, "valid options:\n");
312
313 for (handler = handlers; handler->name; ++handler)
314 {
315 if (handler->desc)
316 fprintf (stderr, " %-10s%s %s\n", handler->name,
317 handler->has_arg ? "=<arg>" : "", handler->desc);
318 }
319 exit (EXIT_FAILURE);
320 }
321 }
322}
323
324static void
325complete (char *host_name, char *mount_point,
326 unsigned long flags, struct opts *opts)
327{
328 FILE *f, *m;
329 char *buf;
330 size_t size;
331 struct mntent e;
332
333 m = open_memstream (&buf, &size);
334 if (!m)
335 panic_err ("could not update mount table (failed to create memstream)");
336
337 if (opts->uid)
338 fprintf (m, "uid=%d,", opts->uid);
339 if (opts->gid)
340 fprintf (m, "gid=%d,", opts->gid);
341 if (opts->ttl)
342 fprintf (m, "ttl=%d,", opts->ttl);
343 if (*opts->nls_name)
344 fprintf (m, "iocharset=%s,", opts->nls_name);
345 if (flags & MS_NOSUID)
346 fprintf (m, "%s,", MNTOPT_NOSUID);
347 if (flags & MS_RDONLY)
348 fprintf (m, "%s,", MNTOPT_RO);
349 else
350 fprintf (m, "%s,", MNTOPT_RW);
351
352 fclose (m);
353
354 if (size > 0)
355 buf[size - 1] = 0;
356 else
357 buf = "defaults";
358
359 f = setmntent (MOUNTED, "a+");
360 if (!f)
361 panic_err ("could not open mount table for update");
362
363 e.mnt_fsname = host_name;
364 e.mnt_dir = mount_point;
365 e.mnt_type = "vboxsf";
366 e.mnt_opts = buf;
367 e.mnt_freq = 0;
368 e.mnt_passno = 0;
369
370 if (addmntent (f, &e))
371 {
372 if (size > 0)
373 {
374 memset (buf, 0, size);
375 free (buf);
376 }
377 panic_err ("could not add an entry to the mount table");
378 }
379
380 endmntent (f);
381
382 if (size > 0)
383 {
384 memset (buf, 0, size);
385 free (buf);
386 }
387}
388
389static void
390convertcp (char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
391{
392 char *i = host_name;
393 char *o = info->name;
394 size_t ib = strlen (host_name);
395 size_t ob = sizeof (info->name) - 1;
396 iconv_t cd;
397
398 cd = iconv_open ("UTF-8", in_codeset);
399 if (cd == (iconv_t) -1)
400 {
401 panic_err ("could not convert share name, iconv_open `%s' failed",
402 in_codeset);
403 }
404
405 while (ib)
406 {
407 size_t c = iconv (cd, &i, &ib, &o, &ob);
408 if (c == (size_t) -1)
409 {
410 panic_err ("could not convert share name(%s) at %d",
411 host_name, (int)(strlen (host_name) - ib));
412 }
413 }
414 *o = 0;
415}
416
417
418/**
419 * Print out a usage message and exit.
420 *
421 * @param name The name of the application
422 */
423static void __attribute ((noreturn)) usage(char *name)
424{
425 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
426 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
427 "\n"
428 " -w mount the shared folder writably (the default)\n"
429 " -r mount the shared folder read-only\n"
430 " -n do not create an mtab entry\n"
431 " -o OPTION[,OPTION...] use the mount options specified\n"
432 "\n", name);
433 printf("Available mount options are:\n"
434 " rw mount writably (the default)\n"
435 " ro mount read only\n"
436 " uid=UID set the default file owner user id to UID\n"
437 " gid=GID set the default file owner group id to GID\n"
438 " ttl=TTL set the \"time to live\" to TID for the dentry\n");
439 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
440 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
441 " umask=UMASK set the umask to (octal) UMASK\n");
442 printf(" dmask=UMASK set the umask applied to directories only\n"
443 " fmask=UMASK set the umask applied to regular files only\n"
444 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
445 " (default set is utf8)\n"
446 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
447 "\n");
448 printf("Less common used options:\n"
449 " noexec,exec,nodev,dev,nosuid,suid\n");
450 exit(1);
451}
452
453int
454main (int argc, char **argv)
455{
456 int c;
457 int err;
458 int nomtab = 0;
459 unsigned long flags = MS_NODEV;
460 char *host_name;
461 char *mount_point;
462 struct vbsf_mount_info_new mntinf;
463 struct opts opts =
464 {
465 0, /* uid */
466 0, /* gid */
467 0, /* ttl */
468 ~0, /* dmode */
469 ~0, /* fmode*/
470 0, /* dmask */
471 0, /* fmask */
472 0, /* ronly */
473 0, /* noexec */
474 0, /* nodev */
475 0, /* nosuid */
476 0, /* remount */
477 "\0", /* nls_name */
478 NULL, /* convertcp */
479 };
480
481 mntinf.nullchar = '\0';
482 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
483 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
484 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
485 mntinf.length = sizeof(mntinf);
486
487 if (getuid ())
488 panic ("Only root can mount shared folders from the host.\n");
489
490 if (!argv[0])
491 argv[0] = "mount.vboxsf";
492
493 /* Compile-time assertions */
494 CT_ASSERT(sizeof(uid_t) == sizeof(int));
495 CT_ASSERT(sizeof(gid_t) == sizeof(int));
496
497 while ((c = getopt (argc, argv, "rwno:h")) != -1)
498 {
499 switch (c)
500 {
501 default:
502 fprintf (stderr, "unknown option `%c:%#x'\n", c, c);
503 case '?':
504 case 'h':
505 usage(argv[0]);
506
507 case 'r':
508 opts.ronly = 1;
509 break;
510
511 case 'w':
512 opts.ronly = 0;
513
514 case 'o':
515 process_mount_opts (optarg, &opts);
516 break;
517
518 case 'n':
519 nomtab = 1;
520 break;
521 }
522 }
523
524 if (argc - optind < 2)
525 usage(argv[0]);
526
527 host_name = argv[optind];
528 mount_point = argv[optind + 1];
529
530 if (opts.convertcp)
531 convertcp (opts.convertcp, host_name, &mntinf);
532 else
533 {
534 if (strlen (host_name) > MAX_HOST_NAME - 1)
535 panic ("host name is too big\n");
536
537 strcpy (mntinf.name, host_name);
538 }
539
540 if (strlen (opts.nls_name) > MAX_NLS_NAME - 1)
541 panic ("%s: the character set name for I/O is too long.\n", argv[0]);
542
543 strcpy (mntinf.nls_name, opts.nls_name);
544
545 if (opts.ronly)
546 flags |= MS_RDONLY;
547 if (opts.noexec)
548 flags |= MS_NOEXEC;
549 if (opts.nodev)
550 flags |= MS_NODEV;
551
552 mntinf.uid = opts.uid;
553 mntinf.gid = opts.gid;
554 mntinf.ttl = opts.ttl;
555 mntinf.dmode = opts.dmode;
556 mntinf.fmode = opts.fmode;
557 mntinf.dmask = opts.dmask;
558 mntinf.fmask = opts.fmask;
559
560 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
561 if (err == -1 && errno == EPROTO)
562 {
563 /* Sometimes the mount utility messes up the share name. Try to
564 * un-mangle it again. */
565 char szCWD[4096];
566 size_t cchCWD;
567 if (!getcwd(szCWD, sizeof(szCWD)))
568 panic_err("%s: failed to get the current working directory", argv[0]);
569 cchCWD = strlen(szCWD);
570 if (!strncmp(host_name, szCWD, cchCWD))
571 {
572 while (host_name[cchCWD] == '/')
573 ++cchCWD;
574 /* We checked before that we have enough space */
575 strcpy (mntinf.name, host_name + cchCWD);
576 }
577 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
578 }
579 if (err == -1 && errno == EPROTO)
580 {
581 /* New mount tool with old vboxsf module? Try again using the old
582 * vbsf_mount_info_old structure. */
583 struct vbsf_mount_info_old mntinf_old;
584 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
585 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
586 mntinf_old.uid = mntinf.uid;
587 mntinf_old.gid = mntinf.gid;
588 mntinf_old.ttl = mntinf.ttl;
589 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf_old);
590 }
591 if (err)
592 panic_err ("%s: mounting failed with the error", argv[0]);
593
594 if (!nomtab)
595 complete (host_name, mount_point, flags, &opts);
596
597 exit (EXIT_SUCCESS);
598}
Note: See TracBrowser for help on using the repository browser.

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