VirtualBox

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

Last change on this file since 15217 was 14598, checked in by vboxsync, 16 years ago

Linux shared folders: missing initialization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/** @file
2 * vboxvfs -- 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-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
21 * Clara, CA 95054 USA or visit http://www.sun.com if you need
22 * additional information or have any questions.
23 */
24
25
26#ifndef _GNU_SOURCE
27#define _GNU_SOURCE
28#endif
29
30/* #define DEBUG */
31#define DBG if (0)
32#include <errno.h>
33#include <fcntl.h>
34#include <ctype.h>
35#include <getopt.h>
36#include <mntent.h>
37#include <pwd.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/mount.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45#include <unistd.h>
46#include <mntent.h>
47#include <limits.h>
48#include <iconv.h>
49
50#include "vbsfmount.h"
51
52/* Compile-time assertion. If a == 0, we get two identical switch cases, which is not
53 allowed. */
54#define CT_ASSERT(a) \
55 do { \
56 switch(0) { case 0: case (a): ; } \
57 } while (0)
58
59struct opts {
60 int uid;
61 int gid;
62 int ttl;
63 int dmode;
64 int fmode;
65 int dmask;
66 int fmask;
67 int ronly;
68 int noexec;
69 int nodev;
70 int nosuid;
71 int remount;
72 char nls_name[MAX_NLS_NAME];
73 char *convertcp;
74};
75
76#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
77
78static void PANIC_ATTR
79panic (const char *fmt, ...)
80{
81 va_list ap;
82
83 va_start (ap, fmt);
84 vfprintf (stderr, fmt, ap);
85 va_end (ap);
86 exit (EXIT_FAILURE);
87}
88
89static void PANIC_ATTR
90panic_err (const char *fmt, ...)
91{
92 va_list ap;
93 int errno_code = errno;
94
95 va_start (ap, fmt);
96 vfprintf (stderr, fmt, ap);
97 va_end (ap);
98 fprintf (stderr, ": %s\n", strerror (errno_code));
99 exit (EXIT_FAILURE);
100}
101
102static int
103safe_atoi (const char *s, size_t size, int base)
104{
105 char *endptr;
106 long long int val = strtoll (s, &endptr, base);
107
108 if (val < INT_MIN || val > INT_MAX || endptr < s + size) {
109 errno = ERANGE;
110 panic_err ("could not convert %.*s to integer, result = %d",
111 (int)size, s, (int) val);
112 }
113 return (int) val;
114}
115
116static void
117process_mount_opts (const char *s, struct opts *opts)
118{
119 const char *next = s;
120 size_t len;
121 typedef enum handler_opt
122 {
123 HORW,
124 HORO,
125 HOUID,
126 HOGID,
127 HOTTL,
128 HODMODE,
129 HOFMODE,
130 HOUMASK,
131 HODMASK,
132 HOFMASK,
133 HOIOCHARSET,
134 HOCONVERTCP,
135 HONOEXEC,
136 HOEXEC,
137 HONODEV,
138 HODEV,
139 HONOSUID,
140 HOSUID,
141 HOREMOUNT
142 } handler_opt;
143 struct {
144 const char *name;
145 handler_opt opt;
146 int has_arg;
147 const char *desc;
148 } handlers[] = {
149 {"rw", HORW, 0, "mount read write (default)"},
150 {"ro", HORO, 0, "mount read only"},
151 {"uid", HOUID, 1, "default file owner user id"},
152 {"gid", HOGID, 1, "default file owner group id"},
153 {"ttl", HOTTL, 1, "time to live for dentry"},
154 {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
155 {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
156 {"dmode", HODMODE, 1, "mode of all directories"},
157 {"fmode", HOFMODE, 1, "mode of all regular files"},
158 {"umask", HOUMASK, 1, "umask of directories and regular files"},
159 {"dmask", HODMASK, 1, "umask of directories"},
160 {"fmask", HOFMASK, 1, "umask of regular files"},
161 {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
162 {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
163 {"nodev", HONODEV, 0, 0 }, /* usual manpages */
164 {"dev", HODEV, 0, 0 },
165 {"nosuid", HONOSUID, 0, 0 },
166 {"suid", HOSUID, 0, 0 },
167 {"remount", HOREMOUNT, 0, 0 },
168 {NULL, 0, 0, NULL}
169 }, *handler;
170
171 while (next) {
172 const char *val;
173 size_t key_len, val_len;
174
175 s = next;
176 next = strchr (s, ',');
177 if (!next) {
178 len = strlen (s);
179 }
180 else {
181 len = next - s;
182 next += 1;
183 if (!*next) {
184 next = 0;
185 }
186 }
187
188 val = NULL;
189 val_len = 0;
190 for (key_len = 0; key_len < len; ++key_len) {
191 if (s[key_len] == '=') {
192 if (key_len + 1 < len) {
193 val = s + key_len + 1;
194 val_len = len - key_len - 1;
195 }
196 break;
197 }
198 }
199
200 for (handler = handlers; handler->name; ++handler) {
201 size_t j;
202 for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
203 ;
204
205 if (j == key_len && !handler->name[j]) {
206 if (handler->has_arg) {
207 if (!(val && *val)) {
208 panic ("%.*s requires an argument (i.e. %.*s=<arg>)\n",
209 (int)len, s, (int)len, s);
210 }
211 }
212
213 switch(handler->opt)
214 {
215 case HORW:
216 opts->ronly = 0;
217 break;
218 case HORO:
219 opts->ronly = 1;
220 break;
221 case HONOEXEC:
222 opts->noexec = 1;
223 break;
224 case HOEXEC:
225 opts->noexec = 0;
226 break;
227 case HONODEV:
228 opts->nodev = 1;
229 break;
230 case HODEV:
231 opts->nodev = 0;
232 break;
233 case HONOSUID:
234 opts->nosuid = 1;
235 break;
236 case HOSUID:
237 opts->nosuid = 0;
238 break;
239 case HOREMOUNT:
240 opts->remount = 1;
241 break;
242 case HOUID:
243 opts->uid = safe_atoi (val, val_len, 10);
244 break;
245 case HOGID:
246 opts->gid = safe_atoi (val, val_len, 10);
247 break;
248 case HOTTL:
249 opts->ttl = safe_atoi (val, val_len, 10);
250 break;
251 case HODMODE:
252 opts->dmode = safe_atoi (val, val_len, 8);
253 break;
254 case HOFMODE:
255 opts->fmode = safe_atoi (val, val_len, 8);
256 break;
257 case HOUMASK:
258 opts->dmask = opts->fmask = safe_atoi (val, val_len, 8);
259 break;
260 case HODMASK:
261 opts->dmask = safe_atoi (val, val_len, 8);
262 break;
263 case HOFMASK:
264 opts->fmask = safe_atoi (val, val_len, 8);
265 break;
266 case HOIOCHARSET:
267 if (val_len + 1 > sizeof (opts->nls_name)) {
268 panic ("iocharset name too long\n");
269 }
270 memcpy (opts->nls_name, val, val_len);
271 opts->nls_name[val_len] = 0;
272 break;
273 case HOCONVERTCP:
274 opts->convertcp = malloc (val_len + 1);
275 if (!opts->convertcp) {
276 panic_err ("could not allocate memory");
277 }
278 memcpy (opts->convertcp, val, val_len);
279 opts->convertcp[val_len] = 0;
280 break;
281 }
282 break;
283 }
284 continue;
285 }
286
287 if (!handler->name) {
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 if (handler->desc)
293 fprintf (stderr, " %-10s%s %s\n", handler->name,
294 handler->has_arg ? "=<arg>" : "", handler->desc);
295 }
296 exit (EXIT_FAILURE);
297 }
298 }
299}
300
301static void
302complete (char *host_name, char *mount_point,
303 unsigned long flags, struct opts *opts)
304{
305 FILE *f, *m;
306 char *buf;
307 size_t size;
308 struct mntent e;
309
310 m = open_memstream (&buf, &size);
311 if (!m)
312 panic_err ("could not update mount table (failed to create memstream)");
313
314 if (opts->uid)
315 fprintf (m, "uid=%d,", opts->uid);
316 if (opts->gid)
317 fprintf (m, "gid=%d,", opts->gid);
318 if (opts->ttl)
319 fprintf (m, "ttl=%d,", opts->ttl);
320 if (*opts->nls_name)
321 fprintf (m, "iocharset=%s,", opts->nls_name);
322 if (flags & MS_NOSUID)
323 fprintf (m, "%s,", MNTOPT_NOSUID);
324 if (flags & MS_RDONLY)
325 fprintf (m, "%s,", MNTOPT_RO);
326 else
327 fprintf (m, "%s,", MNTOPT_RW);
328
329 fclose (m);
330
331 if (size > 0)
332 buf[size - 1] = 0;
333 else
334 buf = "defaults";
335
336 f = setmntent (MOUNTED, "a+");
337 if (!f)
338 panic_err ("could not open mount table for update");
339
340 e.mnt_fsname = host_name;
341 e.mnt_dir = mount_point;
342 e.mnt_type = "vboxsf";
343 e.mnt_opts = buf;
344 e.mnt_freq = 0;
345 e.mnt_passno = 0;
346
347 if (addmntent (f, &e))
348 {
349 if (size > 0)
350 {
351 memset (buf, 0, size);
352 free (buf);
353 }
354 panic_err ("could not add an entry to the mount table");
355 }
356
357 endmntent (f);
358
359 if (size > 0)
360 {
361 memset (buf, 0, size);
362 free (buf);
363 }
364}
365
366static void
367convertcp (char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
368{
369 char *i = host_name;
370 char *o = info->name;
371 size_t ib = strlen (host_name);
372 size_t ob = sizeof (info->name) - 1;
373 iconv_t cd;
374
375 cd = iconv_open ("UTF-8", in_codeset);
376 if (cd == (iconv_t) -1) {
377 panic_err ("could not convert share name, iconv_open `%s' failed",
378 in_codeset);
379 }
380
381 while (ib) {
382 size_t c = iconv (cd, &i, &ib, &o, &ob);
383 if (c == (size_t) -1) {
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 * @param name The name of the application
396 */
397static void __attribute ((noreturn)) usage(char *name)
398{
399 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
400 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
401 "\n"
402 " -w mount the shared folder writably (the default)\n"
403 " -r mount the shared folder read-only\n"
404 " -n do not create an mtab entry\n"
405 " -o OPTION[,OPTION...] use the mount options specified\n"
406 "\n", name);
407 printf("Available mount options are:\n"
408 " rw mount writably (the default)\n"
409 " ro mount read only\n"
410 " uid=UID set the default file owner user id to UID\n"
411 " gid=GID set the default file owner group id to GID\n"
412 " ttl=TTL set the \"time to live\" to TID for the dentry\n");
413 printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
414 " fmode=MODE override the mode of all regular files to (octal) MODE\n"
415 " umask=UMASK set the umask to (octal) UMASK\n");
416 printf(" dmask=UMASK set the umask applied to directories only\n"
417 " fmask=UMASK set the umask applied to regular files only\n"
418 " iocharset CHARSET use the character set CHARSET for I/O operations\n"
419 " (default set is utf8)\n"
420 " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
421 "\n");
422 printf("Less common used options:\n"
423 " noexec,exec,nodev,dev,nosuid,suid\n");
424 exit(1);
425}
426
427int
428main (int argc, char **argv)
429{
430 int c;
431 int err;
432 int nomtab = 0;
433 unsigned long flags = MS_NODEV;
434 char *host_name;
435 char *mount_point;
436 struct vbsf_mount_info_new mntinf;
437 struct opts opts =
438 {
439 0, /* uid */
440 0, /* gid */
441 0, /* ttl */
442 ~0, /* dmode */
443 ~0, /* fmode*/
444 0, /* dmask */
445 0, /* fmask */
446 0, /* ronly */
447 0, /* noexec */
448 0, /* nodev */
449 0, /* nosuid */
450 0, /* remount */
451 "\0", /* nls_name */
452 NULL, /* convertcp */
453 };
454
455 mntinf.nullchar = '\0';
456 mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
457 mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
458 mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
459 mntinf.length = sizeof(mntinf);
460
461 if (getuid ())
462 panic ("Only root can mount shared folders from the host.\n");
463
464 if (!argv[0])
465 argv[0] = "mount.vboxsf";
466
467 /* Compile-time assertions */
468 CT_ASSERT(sizeof(uid_t) == sizeof(int));
469 CT_ASSERT(sizeof(gid_t) == sizeof(int));
470
471 while ((c = getopt (argc, argv, "rwno:h")) != -1) {
472 switch (c) {
473 default:
474 fprintf (stderr, "unknown option `%c:%#x'\n", c, c);
475 case '?':
476 case 'h':
477 usage(argv[0]);
478
479 case 'r':
480 opts.ronly = 1;
481 break;
482
483 case 'w':
484 opts.ronly = 0;
485
486 case 'o':
487 process_mount_opts (optarg, &opts);
488 break;
489
490 case 'n':
491 nomtab = 1;
492 break;
493 }
494 }
495
496 if (argc - optind < 2)
497 usage(argv[0]);
498
499 host_name = argv[optind];
500 mount_point = argv[optind + 1];
501
502 if (opts.convertcp)
503 convertcp (opts.convertcp, host_name, &mntinf);
504 else
505 {
506 if (strlen (host_name) > MAX_HOST_NAME - 1)
507 panic ("host name is too big\n");
508
509 strcpy (mntinf.name, host_name);
510 }
511
512 if (strlen (opts.nls_name) > MAX_NLS_NAME - 1)
513 panic ("%s: the character set name for I/O is too long.\n", argv[0]);
514
515 strcpy (mntinf.nls_name, opts.nls_name);
516
517 if (opts.ronly)
518 flags |= MS_RDONLY;
519 if (opts.noexec)
520 flags |= MS_NOEXEC;
521 if (opts.nodev)
522 flags |= MS_NODEV;
523
524 mntinf.uid = opts.uid;
525 mntinf.gid = opts.gid;
526 mntinf.ttl = opts.ttl;
527 mntinf.dmode = opts.dmode;
528 mntinf.fmode = opts.fmode;
529 mntinf.dmask = opts.dmask;
530 mntinf.fmask = opts.fmask;
531
532 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
533 if (err == -1 && errno == EPROTO)
534 {
535 /* New mount tool with old vboxsf module? Try again using the old
536 * vbsf_mount_info_old structure. */
537 struct vbsf_mount_info_old mntinf_old;
538 memcpy(&mntinf_old.name, &mntinf.name, MAX_HOST_NAME);
539 memcpy(&mntinf_old.nls_name, mntinf.nls_name, MAX_NLS_NAME);
540 mntinf_old.uid = mntinf.uid;
541 mntinf_old.gid = mntinf.gid;
542 mntinf_old.ttl = mntinf.ttl;
543 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf_old);
544 }
545 if (err)
546 panic_err ("%s: mounting failed with the error", argv[0]);
547
548 if (!nomtab)
549 complete (host_name, mount_point, flags, &opts);
550
551 exit (EXIT_SUCCESS);
552}
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