VirtualBox

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

Last change on this file since 72627 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/* $Id: mount.vboxsf.c 69500 2017-10-28 15:14:05Z 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-2017 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 RT_FALL_THRU();
418 case '?':
419 case 'h':
420 return usage(argv[0]);
421
422 case 'r':
423 opts.ronly = 1;
424 break;
425
426 case 'w':
427 opts.ronly = 0;
428 break;
429
430 case 's':
431 opts.sloppy = 1;
432 break;
433
434 case 'o':
435 process_mount_opts(optarg, &opts);
436 break;
437
438 case 'n':
439 nomtab = 1;
440 break;
441 }
442 }
443
444 if (argc - optind < 2)
445 return usage(argv[0]);
446
447 host_name = argv[optind];
448 mount_point = argv[optind + 1];
449
450 if (opts.convertcp)
451 convertcp(opts.convertcp, host_name, &mntinf);
452 else
453 {
454 if (strlen(host_name) > MAX_HOST_NAME - 1)
455 panic("host name is too big\n");
456
457 strcpy(mntinf.name, host_name);
458 }
459
460 if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
461 panic("%s: the character set name for I/O is too long.\n", argv[0]);
462
463 strcpy(mntinf.nls_name, opts.nls_name);
464
465 if (opts.ronly)
466 flags |= MS_RDONLY;
467 if (opts.noexec)
468 flags |= MS_NOEXEC;
469 if (opts.nodev)
470 flags |= MS_NODEV;
471 if (opts.remount)
472 flags |= MS_REMOUNT;
473
474 mntinf.uid = opts.uid;
475 mntinf.gid = opts.gid;
476 mntinf.ttl = opts.ttl;
477 mntinf.dmode = opts.dmode;
478 mntinf.fmode = opts.fmode;
479 mntinf.dmask = opts.dmask;
480 mntinf.fmask = opts.fmask;
481
482 /*
483 * Note: When adding and/or modifying parameters of the vboxsf mounting
484 * options you also would have to adjust VBoxServiceAutoMount.cpp
485 * to keep this code here slick without having VbglR3.
486 */
487 err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
488 if (err == -1 && errno == EPROTO)
489 {
490 /* Sometimes the mount utility messes up the share name. Try to
491 * un-mangle it again. */
492 char szCWD[4096];
493 size_t cchCWD;
494 if (!getcwd(szCWD, sizeof(szCWD)))
495 panic_err("%s: failed to get the current working directory", argv[0]);
496 cchCWD = strlen(szCWD);
497 if (!strncmp(host_name, szCWD, cchCWD))
498 {
499 while (host_name[cchCWD] == '/')
500 ++cchCWD;
501 /* We checked before that we have enough space */
502 strcpy(mntinf.name, host_name + cchCWD);
503 }
504 err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
505 }
506 if (err)
507 panic_err("%s: mounting failed with the error", argv[0]);
508
509 if (!nomtab)
510 {
511 err = vbsfmount_complete(host_name, mount_point, flags, &opts);
512 switch (err)
513 {
514 case 0: /* Success. */
515 break;
516
517 case 1:
518 panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
519 break;
520
521 case 2:
522 panic_err("%s: Could not open mount table for update.", argv[0]);
523 break;
524
525 case 3:
526 /* panic_err("%s: Could not add an entry to the mount table.", argv[0]); */
527 break;
528
529 default:
530 panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
531 break;
532 }
533 }
534
535 exit(EXIT_SUCCESS);
536}
537
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