VirtualBox

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

Last change on this file since 3095 was 2981, checked in by vboxsync, 18 years ago

InnoTek -> innotek: all the headers and comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/** @file
2 *
3 * vboxvfs -- VirtualBox Guest Additions for Linux:
4 * mount(0) helper
5 *
6 * Parses options provided by mount (or user directly)
7 * Packs them into struct vbsfmount and passes to mount(2)
8 * Optionally adds entries to mtab
9 */
10
11/*
12 * Copyright (C) 2006-2007 innotek GmbH
13 *
14 * This file is part of VirtualBox Open Source Edition (OSE), as
15 * available from http://www.virtualbox.org. This file is free software;
16 * you can redistribute it and/or modify it under the terms of the GNU
17 * General Public License as published by the Free Software Foundation,
18 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
19 * distribution. VirtualBox OSE is distributed in the hope that it will
20 * be useful, but WITHOUT ANY WARRANTY of any kind.
21 *
22 * If you received this file as part of a commercial VirtualBox
23 * distribution, then only the terms of your commercial VirtualBox
24 * license agreement apply instead of the previous paragraph.
25 */
26
27
28#ifndef _GNU_SOURCE
29#define _GNU_SOURCE
30#endif
31
32/* #define DEBUG */
33#define DBG if (0)
34#include <errno.h>
35#include <fcntl.h>
36#include <ctype.h>
37#include <getopt.h>
38#include <mntent.h>
39#include <pwd.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <sys/mount.h>
45#include <sys/stat.h>
46#include <sys/types.h>
47#include <unistd.h>
48#include <mntent.h>
49#include <limits.h>
50#include <iconv.h>
51
52#include "vbsfmount.h"
53
54/* Compile-time assertion. If a == 0, we get two identical switch cases, which is not
55 allowed. */
56#define CT_ASSERT(a) \
57 do { \
58 switch(0) { case 0: case (a): ; } \
59 } while (0)
60
61struct opts {
62 int uid;
63 int gid;
64 int ttl;
65 int ronly;
66 int noexec;
67 int nodev;
68 int nosuid;
69 int remount;
70 char nls_name[MAX_NLS_NAME];
71 char *convertcp;
72};
73
74#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
75
76static void PANIC_ATTR
77panic (const char *fmt, ...)
78{
79 va_list ap;
80
81 va_start (ap, fmt);
82 vfprintf (stderr, fmt, ap);
83 va_end (ap);
84 exit (EXIT_FAILURE);
85}
86
87static void PANIC_ATTR
88panic_err (const char *fmt, ...)
89{
90 va_list ap;
91 int errno_code = errno;
92
93 va_start (ap, fmt);
94 vfprintf (stderr, fmt, ap);
95 va_end (ap);
96 fprintf (stderr, ": %s\n", strerror (errno_code));
97 exit (EXIT_FAILURE);
98}
99
100static int
101safe_atoi (const char *s, size_t size)
102{
103 char *endptr;
104 long long int val = strtoll (s, &endptr, 10);
105
106 if (val < INT_MIN || val > INT_MAX || endptr < s + size) {
107 errno = ERANGE;
108 panic_err ("could not convert %.*s to integer, result = %d",
109 size, s, (int) val);
110 }
111 return (int) val;
112}
113
114static void
115process_mount_opts (const char *s, struct opts *opts)
116{
117 const char *next = s;
118 size_t len;
119 typedef enum handler_opt
120 {
121 HORW,
122 HORO,
123 HOUID,
124 HOGID,
125 HOTTL,
126 HOIOCHARSET,
127 HOCONVERTCP,
128 HONOEXEC,
129 HOEXEC,
130 HONODEV,
131 HODEV,
132 HONOSUID,
133 HOSUID,
134 HOREMOUNT
135 } handler_opt;
136 struct {
137 const char *name;
138 handler_opt opt;
139 int has_arg;
140 const char *desc;
141 } handlers[] = {
142 {"rw", HORW, 0, "mount read write (default)"},
143 {"ro", HORO, 0, "mount read only"},
144 {"uid", HOUID, 1, "default file owner user id"},
145 {"gid", HOGID, 1, "default file owner group id"},
146 {"ttl", HOTTL, 1, "time to live for dentry"},
147 {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
148 {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
149 {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
150 {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
151 {"nodev", HONODEV, 0, 0 }, /* usual manpages */
152 {"dev", HODEV, 0, 0 },
153 {"nosuid", HONOSUID, 0, 0 },
154 {"suid", HOSUID, 0, 0 },
155 {"remount", HOREMOUNT, 0, 0 },
156 {NULL, 0, 0, NULL}
157 }, *handler;
158
159 while (next) {
160 const char *val;
161 size_t key_len, val_len;
162
163 s = next;
164 next = strchr (s, ',');
165 if (!next) {
166 len = strlen (s);
167 }
168 else {
169 len = next - s;
170 next += 1;
171 if (!*next) {
172 next = 0;
173 }
174 }
175
176 val = NULL;
177 val_len = 0;
178 for (key_len = 0; key_len < len; ++key_len) {
179 if (s[key_len] == '=') {
180 if (key_len + 1 < len) {
181 val = s + key_len + 1;
182 val_len = len - key_len - 1;
183 }
184 break;
185 }
186 }
187
188 for (handler = handlers; handler->name; ++handler) {
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 if (handler->has_arg) {
195 if (!(val && *val)) {
196 panic ("%.*s requires an argument (i.e. %.*s=<arg>)\n",
197 len, s, len, s);
198 }
199 }
200
201 switch(handler->opt)
202 {
203 case HORW:
204 opts->ronly = 0;
205 break;
206 case HORO:
207 opts->ronly = 1;
208 break;
209 case HONOEXEC:
210 opts->noexec = 1;
211 break;
212 case HOEXEC:
213 opts->noexec = 0;
214 break;
215 case HONODEV:
216 opts->nodev = 1;
217 break;
218 case HODEV:
219 opts->nodev = 0;
220 break;
221 case HONOSUID:
222 opts->nosuid = 1;
223 break;
224 case HOSUID:
225 opts->nosuid = 0;
226 break;
227 case HOREMOUNT:
228 opts->remount = 1;
229 break;
230 case HOUID:
231 opts->uid = safe_atoi (val, val_len);
232 break;
233 case HOGID:
234 opts->gid = safe_atoi (val, val_len);
235 break;
236 case HOTTL:
237 opts->ttl = safe_atoi (val, val_len);
238 break;
239 case HOIOCHARSET:
240 if (val_len + 1 > sizeof (opts->nls_name)) {
241 panic ("iocharset name too long\n");
242 }
243 memcpy (opts->nls_name, val, val_len);
244 opts->nls_name[val_len] = 0;
245 break;
246 case HOCONVERTCP:
247 opts->convertcp = malloc (val_len + 1);
248 if (!opts->convertcp) {
249 panic_err ("could not allocate memory");
250 }
251 memcpy (opts->convertcp, val, val_len);
252 opts->convertcp[val_len] = 0;
253 break;
254 }
255 break;
256 }
257 continue;
258 }
259
260 if (!handler->name) {
261 fprintf (stderr, "unknown mount option `%.*s'\n", len, s);
262 fprintf (stderr, "valid options:\n");
263
264 for (handler = handlers; handler->name; ++handler) {
265 if (handler->desc)
266 fprintf (stderr, " %-10s%s %s\n", handler->name,
267 handler->has_arg ? "=<arg>" : "", handler->desc);
268 }
269 exit (EXIT_FAILURE);
270 }
271 }
272}
273
274static void
275complete (char *host_name, char *mount_point,
276 unsigned long flags, struct opts *opts)
277{
278 FILE *f, *m;
279 char *buf;
280 size_t size;
281 struct mntent e;
282
283 m = open_memstream (&buf, &size);
284 if (!m)
285 panic_err ("could not update mount table (failed to create memstream)");
286
287 if (opts->uid)
288 fprintf (m, "uid=%d,", opts->uid);
289 if (opts->gid)
290 fprintf (m, "gid=%d,", opts->gid);
291 if (opts->ttl)
292 fprintf (m, "ttl=%d,", opts->ttl);
293 if (*opts->nls_name)
294 fprintf (m, "iocharset=%s,", opts->nls_name);
295 if (flags & MS_NOSUID)
296 fprintf (m, "%s,", MNTOPT_NOSUID);
297 if (flags & MS_RDONLY)
298 fprintf (m, "%s,", MNTOPT_RO);
299 else
300 fprintf (m, "%s,", MNTOPT_RW);
301
302 fclose (m);
303
304 if (size > 0)
305 buf[size - 1] = 0;
306 else
307 buf = "defaults";
308
309 f = setmntent (MOUNTED, "a+");
310 if (!f)
311 panic_err ("could not open mount table for update");
312
313 e.mnt_fsname = host_name;
314 e.mnt_dir = mount_point;
315 e.mnt_type = "vboxsf";
316 e.mnt_opts = buf;
317 e.mnt_freq = 0;
318 e.mnt_passno = 0;
319
320 if (addmntent (f, &e))
321 {
322 if (size > 0)
323 {
324 memset (buf, size, 0);
325 free (buf);
326 }
327 panic_err ("could not add an entry to the mount table");
328 }
329
330 endmntent (f);
331
332 if (size > 0)
333 {
334 memset (buf, size, 0);
335 free (buf);
336 }
337}
338
339static void
340convertcp (char *in_codeset, char *host_name, struct vbsf_mount_info *info)
341{
342 char *i = host_name;
343 char *o = info->name;
344 size_t ib = strlen (host_name);
345 size_t ob = sizeof (info->name) - 1;
346 iconv_t cd;
347
348 cd = iconv_open ("UTF-8", in_codeset);
349 if (cd == (iconv_t) -1) {
350 panic_err ("could not convert share name, iconv_open `%s' failed",
351 in_codeset);
352 }
353
354 while (ib) {
355 size_t c = iconv (cd, &i, &ib, &o, &ob);
356 if (c == (size_t) -1) {
357 panic_err ("could not convert share name(%s) at %d",
358 host_name, strlen (host_name) - ib);
359 }
360 }
361 *o = 0;
362}
363
364
365/**
366 * Print out a usage message and exit.
367 *
368 * @param name The name of the application
369 */
370static void __attribute ((noreturn)) usage(char *name)
371{
372 printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
373 "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
374 "\n"
375 " -w mount the shared folder writably (the default)\n"
376 " -r mount the shared folder read-only\n"
377 " -n do not add information about the folder to the mtab file\n"
378 " -o OPTION[,OPTION...] use the mount options specified\n"
379 "\n", name);
380 printf("Available mount options are:\n"
381 "\n"
382 " rw mount writably (the default)\n"
383 " ro mount read only\n"
384 " uid=UID set the default file owner user id to UID\n"
385 " gid=GID set the default file owner group id to GID\n"
386 " ttl=TTL set the \"time to live\" to TID for the dentry\n"
387 " iocharset CHARSET use the character set CHARSET for i/o operations (default utf8)\n"
388 " convertcp CHARSET convert the shared folder name from the character set CHARSET to utf8\n");
389 printf("Less common used options:\n"
390 " noexec,exec,nodev,dev,nosuid,suid\n");
391 exit(1);
392}
393
394int
395main (int argc, char **argv)
396{
397 int c;
398 int err;
399 int nomtab = 0;
400 unsigned long flags = MS_NODEV;
401 char *host_name;
402 char *mount_point;
403 struct vbsf_mount_info mntinf;
404 struct opts opts = {
405 0, /* uid */
406 0, /* gid */
407 0, /* ttl */
408 0, /* ronly */
409 0, /* noexec */
410 0, /* nodev */
411 0, /* nosuid */
412 0, /* remount */
413 "\0", /* nls_name */
414 NULL, /* convertcp */
415 };
416
417 if (getuid ())
418 panic ("Only root can mount shared folders from the host.\n");
419
420 if (!argv[0])
421 argv[0] = "mount.vboxsf";
422
423 /* Compile-time assertions */
424 CT_ASSERT(sizeof(uid_t) == sizeof(int));
425 CT_ASSERT(sizeof(gid_t) == sizeof(int));
426
427 while ((c = getopt (argc, argv, "rwno:h")) != -1) {
428 switch (c) {
429 default:
430 fprintf (stderr, "unknown option `%c:%#x'\n", c, c);
431 case '?':
432 case 'h':
433 usage(argv[0]);
434
435 case 'r':
436 opts.ronly = 1;
437 break;
438
439 case 'w':
440 opts.ronly = 0;
441
442 case 'o':
443 process_mount_opts (optarg, &opts);
444 break;
445
446 case 'n':
447 nomtab = 1;
448 break;
449 }
450 }
451
452 if (argc - optind < 2)
453 usage(argv[0]);
454
455 host_name = argv[optind];
456 mount_point = argv[optind + 1];
457
458 if (opts.convertcp)
459 convertcp (opts.convertcp, host_name, &mntinf);
460 else
461 {
462 if (strlen (host_name) > MAX_HOST_NAME - 1)
463 panic ("host name is too big\n");
464
465 strcpy (mntinf.name, host_name);
466 }
467
468 if (strlen (opts.nls_name) > MAX_NLS_NAME - 1)
469 panic ("%s: the character set name for I/O is too long.\n", argv[0]);
470
471 strcpy (mntinf.nls_name, opts.nls_name);
472
473 if (opts.ronly)
474 flags |= MS_RDONLY;
475 if (opts.noexec)
476 flags |= MS_NOEXEC;
477 if (opts.nodev)
478 flags |= MS_NODEV;
479
480 mntinf.uid = opts.uid;
481 mntinf.gid = opts.gid;
482 mntinf.ttl = opts.ttl;
483
484 err = mount (NULL, mount_point, "vboxsf", flags, &mntinf);
485 if (err)
486 panic_err ("%s: mounting failed with the error", argv[0]);
487
488 if (!nomtab)
489 complete (host_name, mount_point, flags, &opts);
490
491 exit (EXIT_SUCCESS);
492}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette