VirtualBox

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

Last change on this file since 24660 was 21560, checked in by vboxsync, 16 years ago

mount.vboxsf.c: todos.

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