VirtualBox

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

Last change on this file since 77390 was 77139, checked in by vboxsync, 6 years ago

linux/vboxsf: More read code tweaking, making the max read/write buffer size configurable (mount option maxiopages). Also added code for dealing with the host running out of heap. bugref:9172

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