VirtualBox

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

Last change on this file since 79594 was 77955, checked in by vboxsync, 6 years ago

linux/vboxsf: Added more mount options: dcachettl=MILLISECONDS, inodettl=MILLISECONDS, dirbuf=BYTES and cache={default/strict|none|read|readwrite}. The readwrite cache mode isn't fully implemented yet (works same as 'read'). [build fix] bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 KB
Line 
1/* $Id: mount.vboxsf.c 77955 2019-03-29 17:20:01Z 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
85 || ( val > INT_MAX
86 && (base != 8 || val != UINT_MAX) ) /* hack for printf("%o", -1) - 037777777777 */
87 || endptr < s + size)
88 {
89 errno = ERANGE;
90 panic_err("could not convert %.*s to integer, result = %lld (%d)",
91 (int)size, s, val, (int)val);
92 }
93 return (int)val;
94}
95
96static unsigned
97safe_atoiu(const char *s, size_t size, int base)
98{
99 char *endptr;
100 long long int val = strtoll(s, &endptr, base);
101
102 if ( val < 0
103 || val > UINT_MAX
104 || endptr < s + size)
105 {
106 errno = ERANGE;
107 panic_err("could not convert %.*s to unsigned integer, result = %lld (%#llx)",
108 (int)size, s, val, val);
109 }
110 return (unsigned)val;
111}
112
113static void
114process_mount_opts(const char *s, struct vbsf_mount_opts *opts)
115{
116 const char *next = s;
117 size_t len;
118 typedef enum handler_opt
119 {
120 HO_RW,
121 HO_RO,
122 HO_UID,
123 HO_GID,
124 HO_TTL,
125 HO_DENTRY_TTL,
126 HO_INODE_TTL,
127 HO_MAX_IO_PAGES,
128 HO_DIR_BUF,
129 HO_CACHE,
130 HO_DMODE,
131 HO_FMODE,
132 HO_UMASK,
133 HO_DMASK,
134 HO_FMASK,
135 HO_IOCHARSET,
136 HO_CONVERTCP,
137 HO_NOEXEC,
138 HO_EXEC,
139 HO_NODEV,
140 HO_DEV,
141 HO_NOSUID,
142 HO_SUID,
143 HO_REMOUNT,
144 HO_NOAUTO,
145 HO_NIGNORE
146 } handler_opt;
147 struct
148 {
149 const char *name;
150 handler_opt opt;
151 int has_arg;
152 const char *desc;
153 } handlers[] =
154 {
155 {"rw", HO_RW, 0, "mount read write (default)"},
156 {"ro", HO_RO, 0, "mount read only"},
157 {"uid", HO_UID, 1, "default file owner user id"},
158 {"gid", HO_GID, 1, "default file owner group id"},
159 {"ttl", HO_TTL, 1, "time to live for dentries & inode info"},
160 {"dcachettl", HO_DENTRY_TTL, 1, "time to live for dentries"},
161 {"inodettl", HO_INODE_TTL, 1, "time to live for inode info"},
162 {"maxiopages", HO_MAX_IO_PAGES, 1, "max buffer size for I/O with host"},
163 {"dirbuf", HO_DIR_BUF, 1, "directory buffer size (0 for default)"},
164 {"cache", HO_CACHE, 1, "cache mode: none, strict (default), read, readwrite"},
165 {"iocharset", HO_IOCHARSET, 1, "i/o charset (default utf8)"},
166 {"convertcp", HO_CONVERTCP, 1, "convert share name from given charset to utf8"},
167 {"dmode", HO_DMODE, 1, "mode of all directories"},
168 {"fmode", HO_FMODE, 1, "mode of all regular files"},
169 {"umask", HO_UMASK, 1, "umask of directories and regular files"},
170 {"dmask", HO_DMASK, 1, "umask of directories"},
171 {"fmask", HO_FMASK, 1, "umask of regular files"},
172 {"noexec", HO_NOEXEC, 0, NULL}, /* don't document these options directly here */
173 {"exec", HO_EXEC, 0, NULL}, /* as they are well known and described in the */
174 {"nodev", HO_NODEV, 0, NULL}, /* usual manpages */
175 {"dev", HO_DEV, 0, NULL},
176 {"nosuid", HO_NOSUID, 0, NULL},
177 {"suid", HO_SUID, 0, NULL},
178 {"remount", HO_REMOUNT, 0, NULL},
179 {"noauto", HO_NOAUTO, 0, NULL},
180 {"_netdev", HO_NIGNORE, 0, NULL},
181 {"relatime", HO_NIGNORE, 0, NULL},
182 {NULL, 0, 0, NULL}
183 }, *handler;
184
185 while (next)
186 {
187 const char *val;
188 size_t key_len, val_len;
189
190 s = next;
191 next = strchr(s, ',');
192 if (!next)
193 {
194 len = strlen(s);
195 }
196 else
197 {
198 len = next - s;
199 next += 1;
200 if (!*next)
201 next = 0;
202 }
203
204 val = NULL;
205 val_len = 0;
206 for (key_len = 0; key_len < len; ++key_len)
207 {
208 if (s[key_len] == '=')
209 {
210 if (key_len + 1 < len)
211 {
212 val = s + key_len + 1;
213 val_len = len - key_len - 1;
214 }
215 break;
216 }
217 }
218
219 for (handler = handlers; handler->name; ++handler)
220 {
221 size_t j;
222 for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
223 ;
224
225 if (j == key_len && !handler->name[j])
226 {
227 if (handler->has_arg)
228 {
229 if (!(val && *val))
230 {
231 panic("%.*s requires an argument (i.e. %.*s=<arg>)\n",
232 (int)len, s, (int)len, s);
233 }
234 }
235
236 switch (handler->opt)
237 {
238 case HO_RW:
239 opts->ronly = 0;
240 break;
241 case HO_RO:
242 opts->ronly = 1;
243 break;
244 case HO_NOEXEC:
245 opts->noexec = 1;
246 break;
247 case HO_EXEC:
248 opts->noexec = 0;
249 break;
250 case HO_NODEV:
251 opts->nodev = 1;
252 break;
253 case HO_DEV:
254 opts->nodev = 0;
255 break;
256 case HO_NOSUID:
257 opts->nosuid = 1;
258 break;
259 case HO_SUID:
260 opts->nosuid = 0;
261 break;
262 case HO_REMOUNT:
263 opts->remount = 1;
264 break;
265 case HO_TTL:
266 opts->ttl = safe_atoi(val, val_len, 10);
267 break;
268 case HO_DENTRY_TTL:
269 opts->msDirCacheTTL = safe_atoi(val, val_len, 10);
270 break;
271 case HO_INODE_TTL:
272 opts->msInodeTTL = safe_atoi(val, val_len, 10);
273 break;
274 case HO_MAX_IO_PAGES:
275 opts->cMaxIoPages = safe_atoiu(val, val_len, 10);
276 break;
277 case HO_DIR_BUF:
278 opts->cbDirBuf = safe_atoiu(val, val_len, 10);
279 break;
280 case HO_CACHE:
281#define IS_EQUAL(a_sz) (val_len == sizeof(a_sz) - 1U && strncmp(val, a_sz, sizeof(a_sz) - 1U) == 0)
282 if (IS_EQUAL("default"))
283 opts->enmCacheMode = kVbsfCacheMode_Default;
284 else if (IS_EQUAL("none"))
285 opts->enmCacheMode = kVbsfCacheMode_None;
286 else if (IS_EQUAL("strict"))
287 opts->enmCacheMode = kVbsfCacheMode_Strict;
288 else if (IS_EQUAL("read"))
289 opts->enmCacheMode = kVbsfCacheMode_Read;
290 else if (IS_EQUAL("readwrite"))
291 opts->enmCacheMode = kVbsfCacheMode_ReadWrite;
292 else
293 panic("invalid cache mode '%.*s'\n"
294 "Valid cache modes are: default, none, strict, read, readwrite\n",
295 (int)val_len, val);
296 break;
297 case HO_UID:
298 /** @todo convert string to id. */
299 opts->uid = safe_atoi(val, val_len, 10);
300 break;
301 case HO_GID:
302 /** @todo convert string to id. */
303 opts->gid = safe_atoi(val, val_len, 10);
304 break;
305 case HO_DMODE:
306 opts->dmode = safe_atoi(val, val_len, 8);
307 break;
308 case HO_FMODE:
309 opts->fmode = safe_atoi(val, val_len, 8);
310 break;
311 case HO_UMASK:
312 opts->dmask = opts->fmask = safe_atoi(val, val_len, 8);
313 break;
314 case HO_DMASK:
315 opts->dmask = safe_atoi(val, val_len, 8);
316 break;
317 case HO_FMASK:
318 opts->fmask = safe_atoi(val, val_len, 8);
319 break;
320 case HO_IOCHARSET:
321 if (val_len + 1 > sizeof(opts->nls_name))
322 {
323 panic("iocharset name too long\n");
324 }
325 memcpy(opts->nls_name, val, val_len);
326 opts->nls_name[val_len] = 0;
327 break;
328 case HO_CONVERTCP:
329 opts->convertcp = malloc(val_len + 1);
330 if (!opts->convertcp)
331 {
332 panic_err("could not allocate memory");
333 }
334 memcpy(opts->convertcp, val, val_len);
335 opts->convertcp[val_len] = 0;
336 break;
337 case HO_NOAUTO:
338 case HO_NIGNORE:
339 break;
340 }
341 break;
342 }
343 continue;
344 }
345
346 if ( !handler->name
347 && !opts->sloppy)
348 {
349 fprintf(stderr, "unknown mount option `%.*s'\n", (int)len, s);
350 fprintf(stderr, "valid options:\n");
351
352 for (handler = handlers; handler->name; ++handler)
353 {
354 if (handler->desc)
355 fprintf(stderr, " %-10s%s %s\n", handler->name,
356 handler->has_arg ? "=<arg>" : "", handler->desc);
357 }
358 exit(EXIT_FAILURE);
359 }
360 }
361}
362
363static void
364convertcp(char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
365{
366 char *i = host_name;
367 char *o = info->name;
368 size_t ib = strlen(host_name);
369 size_t ob = sizeof(info->name) - 1;
370 iconv_t cd;
371
372 cd = iconv_open("UTF-8", in_codeset);
373 if (cd == (iconv_t) -1)
374 {
375 panic_err("could not convert share name, iconv_open `%s' failed",
376 in_codeset);
377 }
378
379 while (ib)
380 {
381 size_t c = iconv(cd, &i, &ib, &o, &ob);
382 if (c == (size_t) -1)
383 {
384 panic_err("could not convert share name(%s) at %d",
385 host_name, (int)(strlen (host_name) - ib));
386 }
387 }
388 *o = 0;
389}
390
391
392/**
393 * Print out a usage message and exit.
394 *
395 * @returns 1
396 * @param argv0 The name of the application
397 */
398static int usage(char *argv0)
399{
400 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
401 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
402 "\n"
403 " -w mount the shared folder writable (the default)\n"
404 " -r mount the shared folder read-only\n"
405 " -n do not create an mtab entry\n"
406 " -s sloppy parsing, ignore unrecognized mount options\n"
407 " -o OPTION[,OPTION...] use the mount options specified\n"
408 "\n", argv0);
409 printf("Available mount options are:\n"
410 " rw mount writable (the default)\n"
411 " ro mount read only\n"
412 " uid=UID set the default file owner user id to UID\n"
413 " gid=GID set the default file owner group id to GID\n");
414 printf(" ttl=MILLIESECSONDS set the \"time to live\" for both the directory cache\n"
415 " and inode info. -1 for kernel default, 0 disables it.\n"
416 " dcachettl=MILLIES set the \"time to live\" for the directory cache,\n"
417 " overriding the 'ttl' option. Ignored if negative.\n"
418 " inodettl=MILLIES set the \"time to live\" for the inode information,\n"
419 " overriding the 'ttl' option. Ignored if negative.\n");
420 printf(" maxiopages=PAGES set the max host I/O buffers size in pages. Uses\n"
421 " default if zero.\n"
422 " dirbuf=BYTES set the directory enumeration buffer size in bytes.\n"
423 " Uses default size if zero.\n");
424 printf(" cache=MODE set the caching mode for the mount. Allowed values:\n"
425 " default: use the kernel default (strict)\n"
426 " none: no caching; may experience guest side\n"
427 " coherence issues between mmap and read.\n");
428 printf(" strict: no caching, except for writably mapped\n"
429 " files (for guest side coherence)\n"
430 " read: read via the page cache; host changes\n"
431 " may be completely ignored\n");
432 printf(" readwrite: read and write via the page cache; host\n"
433 " changes may be completely ignored and\n"
434 " guest changes takes a while to reach the host\n");
435 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
436 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
437 " umask=UMASK set the umask to (octal) UMASK\n");
438 printf(" dmask=UMASK set the umask applied to directories only\n"
439 " fmask=UMASK set the umask applied to regular files only\n"
440 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
441 " (default set is utf8)\n"
442 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
443 "\n");
444 printf("Less common used options:\n"
445 " noexec,exec,nodev,dev,nosuid,suid\n");
446 return EXIT_FAILURE;
447}
448
449int
450main(int argc, char **argv)
451{
452 int c;
453 int err;
454 int saved_errno;
455 int nomtab = 0;
456 unsigned long flags = MS_NODEV;
457 char *host_name;
458 char *mount_point;
459 struct vbsf_mount_info_new mntinf;
460 struct vbsf_mount_opts opts =
461 {
462 -1, /* ttl */
463 -1, /* msDirCacheTTL */
464 -1, /* msInodeTTL */
465 0, /* cMaxIoPages */
466 0, /* cbDirBuf */
467 kVbsfCacheMode_Default,
468 0, /* uid */
469 0, /* gid */
470 ~0U, /* dmode */
471 ~0U, /* fmode*/
472 0, /* dmask */
473 0, /* fmask */
474 0, /* ronly */
475 0, /* sloppy */
476 0, /* noexec */
477 0, /* nodev */
478 0, /* nosuid */
479 0, /* remount */
480 "\0", /* nls_name */
481 NULL, /* convertcp */
482 };
483 AssertCompile(sizeof(uid_t) == sizeof(int));
484 AssertCompile(sizeof(gid_t) == sizeof(int));
485
486 memset(&mntinf, 0, sizeof(mntinf));
487 mntinf.nullchar = '\0';
488 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
489 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
490 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
491 mntinf.length = sizeof(mntinf);
492 mntinf.szTag[0] = '\0';
493
494 if (getuid())
495 panic("Only root can mount shared folders from the host.\n");
496
497 if (!argv[0])
498 argv[0] = "mount.vboxsf";
499
500 while ((c = getopt(argc, argv, "rwsno:h")) != -1)
501 {
502 switch (c)
503 {
504 default:
505 fprintf(stderr, "unknown option `%c:%#x'\n", c, c);
506 RT_FALL_THRU();
507 case '?':
508 case 'h':
509 return usage(argv[0]);
510
511 case 'r':
512 opts.ronly = 1;
513 break;
514
515 case 'w':
516 opts.ronly = 0;
517 break;
518
519 case 's':
520 opts.sloppy = 1;
521 break;
522
523 case 'o':
524 process_mount_opts(optarg, &opts);
525 break;
526
527 case 'n':
528 nomtab = 1;
529 break;
530 }
531 }
532
533 if (argc - optind < 2)
534 return usage(argv[0]);
535
536 host_name = argv[optind];
537 mount_point = argv[optind + 1];
538
539 if (opts.convertcp)
540 convertcp(opts.convertcp, host_name, &mntinf);
541 else
542 {
543 if (strlen(host_name) > MAX_HOST_NAME - 1)
544 panic("host name is too big\n");
545
546 strcpy(mntinf.name, host_name);
547 }
548
549 if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
550 panic("%s: the character set name for I/O is too long.\n", argv[0]);
551
552 strcpy(mntinf.nls_name, opts.nls_name);
553
554 if (opts.ronly)
555 flags |= MS_RDONLY;
556 if (opts.noexec)
557 flags |= MS_NOEXEC;
558 if (opts.nodev)
559 flags |= MS_NODEV;
560 if (opts.remount)
561 flags |= MS_REMOUNT;
562
563 mntinf.ttl = opts.ttl;
564 mntinf.msDirCacheTTL = opts.msDirCacheTTL;
565 mntinf.msInodeTTL = opts.msInodeTTL;
566 mntinf.cMaxIoPages = opts.cMaxIoPages;
567 mntinf.cbDirBuf = opts.cbDirBuf;
568 mntinf.enmCacheMode = opts.enmCacheMode;
569
570 mntinf.uid = opts.uid;
571 mntinf.gid = opts.gid;
572 mntinf.dmode = opts.dmode;
573 mntinf.fmode = opts.fmode;
574 mntinf.dmask = opts.dmask;
575 mntinf.fmask = opts.fmask;
576
577 /*
578 * Note: When adding and/or modifying parameters of the vboxsf mounting
579 * options you also would have to adjust VBoxServiceAutoMount.cpp
580 * to keep this code here slick without having VbglR3.
581 */
582 err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
583 saved_errno = errno;
584
585 /* Some versions of the mount utility (unknown which, if any) will turn the
586 shared folder name into an absolute path. So, we check if it starts with
587 the CWD and removes it. We must do this after failing, because there is
588 not actual restrictions on the shared folder name wrt to slashes and such. */
589 if (err == -1 && errno == ENXIO && host_name[0] == '/')
590 {
591 char szCWD[4096];
592 if (getcwd(szCWD, sizeof(szCWD)) != NULL)
593 {
594 size_t cchCWD = strlen(szCWD);
595 if (!strncmp(host_name, szCWD, cchCWD))
596 {
597 while (host_name[cchCWD] == '/')
598 ++cchCWD;
599 if (host_name[cchCWD])
600 {
601 /* We checked before that we have enough space. */
602 strcpy(mntinf.name, host_name + cchCWD);
603 err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
604 saved_errno = errno;
605 }
606 }
607 }
608 else
609 fprintf(stderr, "%s: failed to get the current working directory: %s", argv[0], strerror(errno));
610 errno = saved_errno;
611 }
612 if (err)
613 {
614 if (saved_errno == ENXIO)
615 panic("%s: shared folder '%s' was not found (check VM settings / spelling)\n", argv[0], host_name);
616 else
617 panic_err("%s: mounting failed with the error", argv[0]);
618 }
619
620 if (!nomtab)
621 {
622 err = vbsfmount_complete(host_name, mount_point, flags, &opts);
623 switch (err)
624 {
625 case 0: /* Success. */
626 break;
627
628 case 1:
629 panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
630 break;
631
632 case 2:
633 panic_err("%s: Could not open mount table for update.", argv[0]);
634 break;
635
636 case 3:
637 /* panic_err("%s: Could not add an entry to the mount table.", argv[0]); */
638 break;
639
640 default:
641 panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
642 break;
643 }
644 }
645
646 exit(EXIT_SUCCESS);
647}
648
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