VirtualBox

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

Last change on this file since 62527 was 62527, checked in by vboxsync, 8 years ago

(C) 2016

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