VirtualBox

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

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

Shared Folders/VBoxService: Added Linux + Solaris support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.0 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-2010 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 {
292 fprintf(stderr, "unknown mount option `%.*s'\n", (int)len, s);
293 fprintf(stderr, "valid options:\n");
294
295 for (handler = handlers; handler->name; ++handler)
296 {
297 if (handler->desc)
298 fprintf(stderr, " %-10s%s %s\n", handler->name,
299 handler->has_arg ? "=<arg>" : "", handler->desc);
300 }
301 exit(EXIT_FAILURE);
302 }
303 }
304}
305
306static void
307convertcp(char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
308{
309 char *i = host_name;
310 char *o = info->name;
311 size_t ib = strlen(host_name);
312 size_t ob = sizeof(info->name) - 1;
313 iconv_t cd;
314
315 cd = iconv_open("UTF-8", in_codeset);
316 if (cd == (iconv_t) -1)
317 {
318 panic_err("could not convert share name, iconv_open `%s' failed",
319 in_codeset);
320 }
321
322 while (ib)
323 {
324 size_t c = iconv(cd, &i, &ib, &o, &ob);
325 if (c == (size_t) -1)
326 {
327 panic_err("could not convert share name(%s) at %d",
328 host_name, (int)(strlen (host_name) - ib));
329 }
330 }
331 *o = 0;
332}
333
334
335/**
336 * Print out a usage message and exit.
337 *
338 * @param name The name of the application
339 */
340static void __attribute ((noreturn)) usage(char *name)
341{
342 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
343 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
344 "\n"
345 " -w mount the shared folder writably (the default)\n"
346 " -r mount the shared folder read-only\n"
347 " -n do not create an mtab entry\n"
348 " -o OPTION[,OPTION...] use the mount options specified\n"
349 "\n", name);
350 printf("Available mount options are:\n"
351 " rw mount writably (the default)\n"
352 " ro mount read only\n"
353 " uid=UID set the default file owner user id to UID\n"
354 " gid=GID set the default file owner group id to GID\n"
355 " ttl=TTL set the \"time to live\" to TID for the dentry\n");
356 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
357 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
358 " umask=UMASK set the umask to (octal) UMASK\n");
359 printf(" dmask=UMASK set the umask applied to directories only\n"
360 " fmask=UMASK set the umask applied to regular files only\n"
361 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
362 " (default set is utf8)\n"
363 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
364 "\n");
365 printf("Less common used options:\n"
366 " noexec,exec,nodev,dev,nosuid,suid\n");
367 exit(1);
368}
369
370int
371main (int argc, char **argv)
372{
373 int c;
374 int err;
375 int nomtab = 0;
376 unsigned long flags = MS_NODEV;
377 char *host_name;
378 char *mount_point;
379 struct vbsf_mount_info_new mntinf;
380 struct vbsf_mount_opts opts =
381 {
382 0, /* uid */
383 0, /* gid */
384 0, /* ttl */
385 ~0, /* dmode */
386 ~0, /* fmode*/
387 0, /* dmask */
388 0, /* fmask */
389 0, /* ronly */
390 0, /* noexec */
391 0, /* nodev */
392 0, /* nosuid */
393 0, /* remount */
394 "\0", /* nls_name */
395 NULL, /* convertcp */
396 };
397
398 mntinf.nullchar = '\0';
399 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
400 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
401 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
402 mntinf.length = sizeof(mntinf);
403
404 if (getuid())
405 panic("Only root can mount shared folders from the host.\n");
406
407 if (!argv[0])
408 argv[0] = "mount.vboxsf";
409
410 /* Compile-time assertions */
411 CT_ASSERT(sizeof(uid_t) == sizeof(int));
412 CT_ASSERT(sizeof(gid_t) == sizeof(int));
413
414 while ((c = getopt(argc, argv, "rwno:h")) != -1)
415 {
416 switch (c)
417 {
418 default:
419 fprintf(stderr, "unknown option `%c:%#x'\n", c, c);
420 case '?':
421 case 'h':
422 usage(argv[0]);
423
424 case 'r':
425 opts.ronly = 1;
426 break;
427
428 case 'w':
429 opts.ronly = 0;
430
431 case 'o':
432 process_mount_opts(optarg, &opts);
433 break;
434
435 case 'n':
436 nomtab = 1;
437 break;
438 }
439 }
440
441 if (argc - optind < 2)
442 usage(argv[0]);
443
444 host_name = argv[optind];
445 mount_point = argv[optind + 1];
446
447 if (opts.convertcp)
448 convertcp(opts.convertcp, host_name, &mntinf);
449 else
450 {
451 if (strlen(host_name) > MAX_HOST_NAME - 1)
452 panic("host name is too big\n");
453
454 strcpy(mntinf.name, host_name);
455 }
456
457 if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
458 panic("%s: the character set name for I/O is too long.\n", argv[0]);
459
460 strcpy(mntinf.nls_name, opts.nls_name);
461
462 if (opts.ronly)
463 flags |= MS_RDONLY;
464 if (opts.noexec)
465 flags |= MS_NOEXEC;
466 if (opts.nodev)
467 flags |= MS_NODEV;
468
469 mntinf.uid = opts.uid;
470 mntinf.gid = opts.gid;
471 mntinf.ttl = opts.ttl;
472 mntinf.dmode = opts.dmode;
473 mntinf.fmode = opts.fmode;
474 mntinf.dmask = opts.dmask;
475 mntinf.fmask = opts.fmask;
476
477 /*
478 * Note: When adding and/or modifying parameters of the vboxsf mounting
479 * options you also would have to adjust VBoxServiceAutoMount.cpp
480 * to keep this code here slick without having VbglR3.
481 */
482 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf);
483 if (err == -1 && errno == EPROTO)
484 {
485 /* Sometimes the mount utility messes up the share name. Try to
486 * un-mangle it again. */
487 char szCWD[4096];
488 size_t cchCWD;
489 if (!getcwd(szCWD, sizeof(szCWD)))
490 panic_err("%s: failed to get the current working directory", argv[0]);
491 cchCWD = strlen(szCWD);
492 if (!strncmp(host_name, szCWD, cchCWD))
493 {
494 while (host_name[cchCWD] == '/')
495 ++cchCWD;
496 /* We checked before that we have enough space */
497 strcpy(mntinf.name, host_name + cchCWD);
498 }
499 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf);
500 }
501 if (err == -1 && errno == EPROTO)
502 {
503 /* New mount tool with old vboxsf module? Try again using the old
504 * vbsf_mount_info_old structure. */
505 struct vbsf_mount_info_old mntinf_old;
506 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
507 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
508 mntinf_old.uid = mntinf.uid;
509 mntinf_old.gid = mntinf.gid;
510 mntinf_old.ttl = mntinf.ttl;
511 err = mount(NULL, mount_point, "vboxsf", flags, &mntinf_old);
512 }
513 if (err)
514 panic_err("%s: mounting failed with the error", argv[0]);
515
516 if (!nomtab)
517 {
518 err = vbsfmount_complete(host_name, mount_point, flags, &opts);
519 switch (err)
520 {
521 case 0: /* Success. */
522 break;
523
524 case 1:
525 panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
526 break;
527
528 case 2:
529 panic_err("%s: Could not open mount table for update.", argv[0]);
530 break;
531
532 case 3:
533 panic_err("%s: Could not add an entry to the mount table.", argv[0]);
534 break;
535
536 default:
537 panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
538 break;
539 }
540 }
541
542 exit(EXIT_SUCCESS);
543}
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