VirtualBox

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

Last change on this file since 57971 was 56675, checked in by vboxsync, 10 years ago

Additions/linux: Implement '-s' sloppy option parsing for shared folders.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.3 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-2012 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
55#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
56
57static void PANIC_ATTR
58panic(const char *fmt, ...)
59{
60 va_list ap;
61
62 va_start(ap, fmt);
63 vfprintf(stderr, fmt, ap);
64 va_end(ap);
65 exit(EXIT_FAILURE);
66}
67
68static void PANIC_ATTR
69panic_err(const char *fmt, ...)
70{
71 va_list ap;
72 int errno_code = errno;
73
74 va_start(ap, fmt);
75 vfprintf(stderr, fmt, ap);
76 va_end(ap);
77 fprintf(stderr, ": %s\n", strerror(errno_code));
78 exit(EXIT_FAILURE);
79}
80
81static int
82safe_atoi(const char *s, size_t size, int base)
83{
84 char *endptr;
85 long long int val = strtoll(s, &endptr, base);
86
87 if (val < INT_MIN || val > INT_MAX || endptr < s + size)
88 {
89 errno = ERANGE;
90 panic_err("could not convert %.*s to integer, result = %d",
91 (int)size, s, (int) val);
92 }
93 return (int)val;
94}
95
96static void
97process_mount_opts(const char *s, struct vbsf_mount_opts *opts)
98{
99 const char *next = s;
100 size_t len;
101 typedef enum handler_opt
102 {
103 HORW,
104 HORO,
105 HOUID,
106 HOGID,
107 HOTTL,
108 HODMODE,
109 HOFMODE,
110 HOUMASK,
111 HODMASK,
112 HOFMASK,
113 HOIOCHARSET,
114 HOCONVERTCP,
115 HONOEXEC,
116 HOEXEC,
117 HONODEV,
118 HODEV,
119 HONOSUID,
120 HOSUID,
121 HOREMOUNT,
122 HONOAUTO,
123 HONIGNORE
124 } handler_opt;
125 struct
126 {
127 const char *name;
128 handler_opt opt;
129 int has_arg;
130 const char *desc;
131 } handlers[]
132 =
133 {
134 {"rw", HORW, 0, "mount read write (default)"},
135 {"ro", HORO, 0, "mount read only"},
136 {"uid", HOUID, 1, "default file owner user id"},
137 {"gid", HOGID, 1, "default file owner group id"},
138 {"ttl", HOTTL, 1, "time to live for dentry"},
139 {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
140 {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
141 {"dmode", HODMODE, 1, "mode of all directories"},
142 {"fmode", HOFMODE, 1, "mode of all regular files"},
143 {"umask", HOUMASK, 1, "umask of directories and regular files"},
144 {"dmask", HODMASK, 1, "umask of directories"},
145 {"fmask", HOFMASK, 1, "umask of regular files"},
146 {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
147 {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
148 {"nodev", HONODEV, 0, 0 }, /* usual manpages */
149 {"dev", HODEV, 0, 0 },
150 {"nosuid", HONOSUID, 0, 0 },
151 {"suid", HOSUID, 0, 0 },
152 {"remount", HOREMOUNT, 0, 0 },
153 {"noauto", HONOAUTO, 0, 0 },
154 {"_netdev", HONIGNORE, 0, 0 },
155 {NULL, 0, 0, NULL}
156 }, *handler;
157
158 while (next)
159 {
160 const char *val;
161 size_t key_len, val_len;
162
163 s = next;
164 next = strchr(s, ',');
165 if (!next)
166 {
167 len = strlen(s);
168 }
169 else
170 {
171 len = next - s;
172 next += 1;
173 if (!*next)
174 next = 0;
175 }
176
177 val = NULL;
178 val_len = 0;
179 for (key_len = 0; key_len < len; ++key_len)
180 {
181 if (s[key_len] == '=')
182 {
183 if (key_len + 1 < len)
184 {
185 val = s + key_len + 1;
186 val_len = len - key_len - 1;
187 }
188 break;
189 }
190 }
191
192 for (handler = handlers; handler->name; ++handler)
193 {
194 size_t j;
195 for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
196 ;
197
198 if (j == key_len && !handler->name[j])
199 {
200 if (handler->has_arg)
201 {
202 if (!(val && *val))
203 {
204 panic("%.*s requires an argument (i.e. %.*s=<arg>)\n",
205 (int)len, s, (int)len, s);
206 }
207 }
208
209 switch(handler->opt)
210 {
211 case HORW:
212 opts->ronly = 0;
213 break;
214 case HORO:
215 opts->ronly = 1;
216 break;
217 case HONOEXEC:
218 opts->noexec = 1;
219 break;
220 case HOEXEC:
221 opts->noexec = 0;
222 break;
223 case HONODEV:
224 opts->nodev = 1;
225 break;
226 case HODEV:
227 opts->nodev = 0;
228 break;
229 case HONOSUID:
230 opts->nosuid = 1;
231 break;
232 case HOSUID:
233 opts->nosuid = 0;
234 break;
235 case HOREMOUNT:
236 opts->remount = 1;
237 break;
238 case HOUID:
239 /** @todo convert string to id. */
240 opts->uid = safe_atoi(val, val_len, 10);
241 break;
242 case HOGID:
243 /** @todo convert string to id. */
244 opts->gid = safe_atoi(val, val_len, 10);
245 break;
246 case HOTTL:
247 opts->ttl = safe_atoi(val, val_len, 10);
248 break;
249 case HODMODE:
250 opts->dmode = safe_atoi(val, val_len, 8);
251 break;
252 case HOFMODE:
253 opts->fmode = safe_atoi(val, val_len, 8);
254 break;
255 case HOUMASK:
256 opts->dmask = opts->fmask = safe_atoi(val, val_len, 8);
257 break;
258 case HODMASK:
259 opts->dmask = safe_atoi(val, val_len, 8);
260 break;
261 case HOFMASK:
262 opts->fmask = safe_atoi(val, val_len, 8);
263 break;
264 case HOIOCHARSET:
265 if (val_len + 1 > sizeof(opts->nls_name))
266 {
267 panic("iocharset name too long\n");
268 }
269 memcpy(opts->nls_name, val, val_len);
270 opts->nls_name[val_len] = 0;
271 break;
272 case HOCONVERTCP:
273 opts->convertcp = malloc(val_len + 1);
274 if (!opts->convertcp)
275 {
276 panic_err("could not allocate memory");
277 }
278 memcpy(opts->convertcp, val, val_len);
279 opts->convertcp[val_len] = 0;
280 break;
281 case HONOAUTO:
282 case HONIGNORE:
283 break;
284 }
285 break;
286 }
287 continue;
288 }
289
290 if ( !handler->name
291 && !opts->sloppy)
292 {
293 fprintf(stderr, "unknown mount option `%.*s'\n", (int)len, s);
294 fprintf(stderr, "valid options:\n");
295
296 for (handler = handlers; handler->name; ++handler)
297 {
298 if (handler->desc)
299 fprintf(stderr, " %-10s%s %s\n", handler->name,
300 handler->has_arg ? "=<arg>" : "", handler->desc);
301 }
302 exit(EXIT_FAILURE);
303 }
304 }
305}
306
307static void
308convertcp(char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
309{
310 char *i = host_name;
311 char *o = info->name;
312 size_t ib = strlen(host_name);
313 size_t ob = sizeof(info->name) - 1;
314 iconv_t cd;
315
316 cd = iconv_open("UTF-8", in_codeset);
317 if (cd == (iconv_t) -1)
318 {
319 panic_err("could not convert share name, iconv_open `%s' failed",
320 in_codeset);
321 }
322
323 while (ib)
324 {
325 size_t c = iconv(cd, &i, &ib, &o, &ob);
326 if (c == (size_t) -1)
327 {
328 panic_err("could not convert share name(%s) at %d",
329 host_name, (int)(strlen (host_name) - ib));
330 }
331 }
332 *o = 0;
333}
334
335
336/**
337 * Print out a usage message and exit.
338 *
339 * @param name The name of the application
340 */
341static void __attribute ((noreturn)) usage(char *name)
342{
343 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
344 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
345 "\n"
346 " -w mount the shared folder writable (the default)\n"
347 " -r mount the shared folder read-only\n"
348 " -n do not create an mtab entry\n"
349 " -s sloppy parsing, ignore unrecognized mount options\n"
350 " -o OPTION[,OPTION...] use the mount options specified\n"
351 "\n", name);
352 printf("Available mount options are:\n"
353 " rw mount writable (the default)\n"
354 " ro mount read only\n"
355 " uid=UID set the default file owner user id to UID\n"
356 " gid=GID set the default file owner group id to GID\n"
357 " ttl=TTL set the \"time to live\" to TID for the dentry\n");
358 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
359 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
360 " umask=UMASK set the umask to (octal) UMASK\n");
361 printf(" dmask=UMASK set the umask applied to directories only\n"
362 " fmask=UMASK set the umask applied to regular files only\n"
363 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
364 " (default set is utf8)\n"
365 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
366 "\n");
367 printf("Less common used options:\n"
368 " noexec,exec,nodev,dev,nosuid,suid\n");
369 exit(1);
370}
371
372int
373main (int argc, char **argv)
374{
375 int c;
376 int err;
377 int nomtab = 0;
378 unsigned long flags = MS_NODEV;
379 char *host_name;
380 char *mount_point;
381 struct vbsf_mount_info_new mntinf;
382 struct vbsf_mount_opts opts =
383 {
384 0, /* uid */
385 0, /* gid */
386 0, /* ttl */
387 ~0U, /* dmode */
388 ~0U, /* fmode*/
389 0, /* dmask */
390 0, /* fmask */
391 0, /* ronly */
392 0, /* sloppy */
393 0, /* noexec */
394 0, /* nodev */
395 0, /* nosuid */
396 0, /* remount */
397 "\0", /* nls_name */
398 NULL, /* convertcp */
399 };
400
401 mntinf.nullchar = '\0';
402 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
403 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
404 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
405 mntinf.length = sizeof(mntinf);
406
407 if (getuid())
408 panic("Only root can mount shared folders from the host.\n");
409
410 if (!argv[0])
411 argv[0] = "mount.vboxsf";
412
413 /* Compile-time assertions */
414 CT_ASSERT(sizeof(uid_t) == sizeof(int));
415 CT_ASSERT(sizeof(gid_t) == sizeof(int));
416
417 while ((c = getopt(argc, argv, "rwsno:h")) != -1)
418 {
419 switch (c)
420 {
421 default:
422 fprintf(stderr, "unknown option `%c:%#x'\n", c, c);
423 case '?':
424 case 'h':
425 usage(argv[0]);
426
427 case 'r':
428 opts.ronly = 1;
429 break;
430
431 case 'w':
432 opts.ronly = 0;
433
434 case 's':
435 opts.sloppy = 1;
436 break;
437
438 case 'o':
439 process_mount_opts(optarg, &opts);
440 break;
441
442 case 'n':
443 nomtab = 1;
444 break;
445 }
446 }
447
448 if (argc - optind < 2)
449 usage(argv[0]);
450
451 host_name = argv[optind];
452 mount_point = argv[optind + 1];
453
454 if (opts.convertcp)
455 convertcp(opts.convertcp, host_name, &mntinf);
456 else
457 {
458 if (strlen(host_name) > MAX_HOST_NAME - 1)
459 panic("host name is too big\n");
460
461 strcpy(mntinf.name, host_name);
462 }
463
464 if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
465 panic("%s: the character set name for I/O is too long.\n", argv[0]);
466
467 strcpy(mntinf.nls_name, opts.nls_name);
468
469 if (opts.ronly)
470 flags |= MS_RDONLY;
471 if (opts.noexec)
472 flags |= MS_NOEXEC;
473 if (opts.nodev)
474 flags |= MS_NODEV;
475 if (opts.remount)
476 flags |= MS_REMOUNT;
477
478 mntinf.uid = opts.uid;
479 mntinf.gid = opts.gid;
480 mntinf.ttl = opts.ttl;
481 mntinf.dmode = opts.dmode;
482 mntinf.fmode = opts.fmode;
483 mntinf.dmask = opts.dmask;
484 mntinf.fmask = opts.fmask;
485
486 /*
487 * Note: When adding and/or modifying parameters of the vboxsf mounting
488 * options you also would have to adjust VBoxServiceAutoMount.cpp
489 * to keep this code here slick without having VbglR3.
490 */
491 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf);
492 if (err == -1 && errno == EPROTO)
493 {
494 /* Sometimes the mount utility messes up the share name. Try to
495 * un-mangle it again. */
496 char szCWD[4096];
497 size_t cchCWD;
498 if (!getcwd(szCWD, sizeof(szCWD)))
499 panic_err("%s: failed to get the current working directory", argv[0]);
500 cchCWD = strlen(szCWD);
501 if (!strncmp(host_name, szCWD, cchCWD))
502 {
503 while (host_name[cchCWD] == '/')
504 ++cchCWD;
505 /* We checked before that we have enough space */
506 strcpy(mntinf.name, host_name + cchCWD);
507 }
508 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf);
509 }
510 if (err == -1 && errno == EPROTO)
511 {
512 /* New mount tool with old vboxsf module? Try again using the old
513 * vbsf_mount_info_old structure. */
514 struct vbsf_mount_info_old mntinf_old;
515 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
516 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
517 mntinf_old.uid = mntinf.uid;
518 mntinf_old.gid = mntinf.gid;
519 mntinf_old.ttl = mntinf.ttl;
520 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf_old);
521 }
522 if (err)
523 panic_err("%s: mounting failed with the error", argv[0]);
524
525 if (!nomtab)
526 {
527 err = vbsfmount_complete(host_name, mount_point, flags, &opts);
528 switch (err)
529 {
530 case 0: /* Success. */
531 break;
532
533 case 1:
534 panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
535 break;
536
537 case 2:
538 panic_err("%s: Could not open mount table for update.", argv[0]);
539 break;
540
541 case 3:
542 /* panic_err("%s: Could not add an entry to the mount table.", argv[0]); */
543 break;
544
545 default:
546 panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
547 break;
548 }
549 }
550
551 exit(EXIT_SUCCESS);
552}
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