libfuse
mount.c
1 /*
2  FUSE: Filesystem in Userspace
3  Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 
5  Architecture specific file system mounting (Linux).
6 
7  This program can be distributed under the terms of the GNU LGPLv2.
8  See the file COPYING.LIB.
9 */
10 
11 #include "config.h"
12 #include "fuse_i.h"
13 #include "fuse_misc.h"
14 #include "fuse_opt.h"
15 #include "mount_util.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <poll.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/wait.h>
28 #include <sys/mount.h>
29 
30 #ifdef __NetBSD__
31 #include <perfuse.h>
32 
33 #define MS_RDONLY MNT_RDONLY
34 #define MS_NOSUID MNT_NOSUID
35 #define MS_NODEV MNT_NODEV
36 #define MS_NOEXEC MNT_NOEXEC
37 #define MS_SYNCHRONOUS MNT_SYNCHRONOUS
38 #define MS_NOATIME MNT_NOATIME
39 
40 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
41 #endif
42 
43 #define FUSERMOUNT_PROG "fusermount3"
44 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
45 
46 #ifndef HAVE_FORK
47 #define fork() vfork()
48 #endif
49 
50 #ifndef MS_DIRSYNC
51 #define MS_DIRSYNC 128
52 #endif
53 
54 enum {
55  KEY_KERN_FLAG,
56  KEY_KERN_OPT,
57  KEY_FUSERMOUNT_OPT,
58  KEY_SUBTYPE_OPT,
59  KEY_MTAB_OPT,
60  KEY_ALLOW_OTHER,
61  KEY_RO,
62 };
63 
64 struct mount_opts {
65  int allow_other;
66  int flags;
67  int auto_unmount;
68  int blkdev;
69  char *fsname;
70  char *subtype;
71  char *subtype_opt;
72  char *mtab_opts;
73  char *fusermount_opts;
74  char *kernel_opts;
75  unsigned max_read;
76 };
77 
78 #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
79 
80 static const struct fuse_opt fuse_mount_opts[] = {
81  FUSE_MOUNT_OPT("allow_other", allow_other),
82  FUSE_MOUNT_OPT("blkdev", blkdev),
83  FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
84  FUSE_MOUNT_OPT("fsname=%s", fsname),
85  FUSE_MOUNT_OPT("max_read=%u", max_read),
86  FUSE_MOUNT_OPT("subtype=%s", subtype),
87  FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
88  FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
89  FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
90  FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
91  FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
92  FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
93  FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
94  FUSE_OPT_KEY("context=", KEY_KERN_OPT),
95  FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
96  FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
97  FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
98  FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
99  FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
100  FUSE_OPT_KEY("-r", KEY_RO),
101  FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
102  FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
103  FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
104  FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
105  FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
106  FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
107  FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
108  FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
109  FUSE_OPT_KEY("async", KEY_KERN_FLAG),
110  FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
111  FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
112  FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
113  FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
115 };
116 
117 static void exec_fusermount(const char *argv[])
118 {
119  execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
120  execvp(FUSERMOUNT_PROG, (char **) argv);
121 }
122 
123 void fuse_mount_version(void)
124 {
125  int pid = fork();
126  if (!pid) {
127  const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
128  exec_fusermount(argv);
129  _exit(1);
130  } else if (pid != -1)
131  waitpid(pid, NULL, 0);
132 }
133 
134 struct mount_flags {
135  const char *opt;
136  unsigned long flag;
137  int on;
138 };
139 
140 static const struct mount_flags mount_flags[] = {
141  {"rw", MS_RDONLY, 0},
142  {"ro", MS_RDONLY, 1},
143  {"suid", MS_NOSUID, 0},
144  {"nosuid", MS_NOSUID, 1},
145  {"dev", MS_NODEV, 0},
146  {"nodev", MS_NODEV, 1},
147  {"exec", MS_NOEXEC, 0},
148  {"noexec", MS_NOEXEC, 1},
149  {"async", MS_SYNCHRONOUS, 0},
150  {"sync", MS_SYNCHRONOUS, 1},
151  {"atime", MS_NOATIME, 0},
152  {"noatime", MS_NOATIME, 1},
153 #ifndef __NetBSD__
154  {"dirsync", MS_DIRSYNC, 1},
155 #endif
156  {NULL, 0, 0}
157 };
158 
159 unsigned get_max_read(struct mount_opts *o)
160 {
161  return o->max_read;
162 }
163 
164 static void set_mount_flag(const char *s, int *flags)
165 {
166  int i;
167 
168  for (i = 0; mount_flags[i].opt != NULL; i++) {
169  const char *opt = mount_flags[i].opt;
170  if (strcmp(opt, s) == 0) {
171  if (mount_flags[i].on)
172  *flags |= mount_flags[i].flag;
173  else
174  *flags &= ~mount_flags[i].flag;
175  return;
176  }
177  }
178  fprintf(stderr, "fuse: internal error, can't find mount flag\n");
179  abort();
180 }
181 
182 static int fuse_mount_opt_proc(void *data, const char *arg, int key,
183  struct fuse_args *outargs)
184 {
185  (void) outargs;
186  struct mount_opts *mo = data;
187 
188  switch (key) {
189  case KEY_RO:
190  arg = "ro";
191  /* fall through */
192  case KEY_KERN_FLAG:
193  set_mount_flag(arg, &mo->flags);
194  return 0;
195 
196  case KEY_KERN_OPT:
197  return fuse_opt_add_opt(&mo->kernel_opts, arg);
198 
199  case KEY_FUSERMOUNT_OPT:
200  return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
201 
202  case KEY_SUBTYPE_OPT:
203  return fuse_opt_add_opt(&mo->subtype_opt, arg);
204 
205  case KEY_MTAB_OPT:
206  return fuse_opt_add_opt(&mo->mtab_opts, arg);
207  }
208 
209  /* Pass through unknown options */
210  return 1;
211 }
212 
213 /* return value:
214  * >= 0 => fd
215  * -1 => error
216  */
217 static int receive_fd(int fd)
218 {
219  struct msghdr msg;
220  struct iovec iov;
221  char buf[1];
222  int rv;
223  size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
224  struct cmsghdr *cmsg;
225 
226  iov.iov_base = buf;
227  iov.iov_len = 1;
228 
229  memset(&msg, 0, sizeof(msg));
230  msg.msg_name = 0;
231  msg.msg_namelen = 0;
232  msg.msg_iov = &iov;
233  msg.msg_iovlen = 1;
234  /* old BSD implementations should use msg_accrights instead of
235  * msg_control; the interface is different. */
236  msg.msg_control = ccmsg;
237  msg.msg_controllen = sizeof(ccmsg);
238 
239  while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
240  if (rv == -1) {
241  perror("recvmsg");
242  return -1;
243  }
244  if(!rv) {
245  /* EOF */
246  return -1;
247  }
248 
249  cmsg = CMSG_FIRSTHDR(&msg);
250  if (cmsg->cmsg_type != SCM_RIGHTS) {
251  fprintf(stderr, "got control message of unknown type %d\n",
252  cmsg->cmsg_type);
253  return -1;
254  }
255  return *(int*)CMSG_DATA(cmsg);
256 }
257 
258 void fuse_kern_unmount(const char *mountpoint, int fd)
259 {
260  int res;
261  int pid;
262 
263  if (fd != -1) {
264  struct pollfd pfd;
265 
266  pfd.fd = fd;
267  pfd.events = 0;
268  res = poll(&pfd, 1, 0);
269 
270  /* Need to close file descriptor, otherwise synchronous umount
271  would recurse into filesystem, and deadlock.
272 
273  Caller expects fuse_kern_unmount to close the fd, so close it
274  anyway. */
275  close(fd);
276 
277  /* If file poll returns POLLERR on the device file descriptor,
278  then the filesystem is already unmounted or the connection
279  was severed via /sys/fs/fuse/connections/NNN/abort */
280  if (res == 1 && (pfd.revents & POLLERR))
281  return;
282  }
283 
284  if (geteuid() == 0) {
285  fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
286  return;
287  }
288 
289  res = umount2(mountpoint, 2);
290  if (res == 0)
291  return;
292 
293  pid = fork();
294  if(pid == -1)
295  return;
296 
297  if(pid == 0) {
298  const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
299  "--", mountpoint, NULL };
300 
301  exec_fusermount(argv);
302  _exit(1);
303  }
304  waitpid(pid, NULL, 0);
305 }
306 
307 static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
308  const char *opts, int quiet)
309 {
310  int fds[2], pid;
311  int res;
312  int rv;
313 
314  if (!mountpoint) {
315  fprintf(stderr, "fuse: missing mountpoint parameter\n");
316  return -1;
317  }
318 
319  res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
320  if(res == -1) {
321  perror("fuse: socketpair() failed");
322  return -1;
323  }
324 
325  pid = fork();
326  if(pid == -1) {
327  perror("fuse: fork() failed");
328  close(fds[0]);
329  close(fds[1]);
330  return -1;
331  }
332 
333  if(pid == 0) {
334  char env[10];
335  const char *argv[32];
336  int a = 0;
337 
338  if (quiet) {
339  int fd = open("/dev/null", O_RDONLY);
340  if (fd != -1) {
341  dup2(fd, 1);
342  dup2(fd, 2);
343  }
344  }
345 
346  argv[a++] = FUSERMOUNT_PROG;
347  if (opts) {
348  argv[a++] = "-o";
349  argv[a++] = opts;
350  }
351  argv[a++] = "--";
352  argv[a++] = mountpoint;
353  argv[a++] = NULL;
354 
355  close(fds[1]);
356  fcntl(fds[0], F_SETFD, 0);
357  snprintf(env, sizeof(env), "%i", fds[0]);
358  setenv(FUSE_COMMFD_ENV, env, 1);
359  exec_fusermount(argv);
360  perror("fuse: failed to exec fusermount3");
361  _exit(1);
362  }
363 
364  close(fds[0]);
365  rv = receive_fd(fds[1]);
366 
367  if (!mo->auto_unmount) {
368  /* with auto_unmount option fusermount3 will not exit until
369  this socket is closed */
370  close(fds[1]);
371  waitpid(pid, NULL, 0); /* bury zombie */
372  }
373 
374  if (rv >= 0)
375  fcntl(rv, F_SETFD, FD_CLOEXEC);
376 
377  return rv;
378 }
379 
380 #ifndef O_CLOEXEC
381 #define O_CLOEXEC 0
382 #endif
383 
384 static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
385  const char *mnt_opts)
386 {
387  char tmp[128];
388  const char *devname = "/dev/fuse";
389  char *source = NULL;
390  char *type = NULL;
391  struct stat stbuf;
392  int fd;
393  int res;
394 
395  if (!mnt) {
396  fprintf(stderr, "fuse: missing mountpoint parameter\n");
397  return -1;
398  }
399 
400  res = stat(mnt, &stbuf);
401  if (res == -1) {
402  fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
403  mnt, strerror(errno));
404  return -1;
405  }
406 
407  if (mo->auto_unmount) {
408  /* Tell the caller to fallback to fusermount3 because
409  auto-unmount does not work otherwise. */
410  return -2;
411  }
412 
413  fd = open(devname, O_RDWR | O_CLOEXEC);
414  if (fd == -1) {
415  if (errno == ENODEV || errno == ENOENT)
416  fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
417  else
418  fprintf(stderr, "fuse: failed to open %s: %s\n",
419  devname, strerror(errno));
420  return -1;
421  }
422  if (!O_CLOEXEC)
423  fcntl(fd, F_SETFD, FD_CLOEXEC);
424 
425  snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
426  fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
427 
428  res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
429  if (res == -1)
430  goto out_close;
431 
432  source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
433  (mo->subtype ? strlen(mo->subtype) : 0) +
434  strlen(devname) + 32);
435 
436  type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
437  if (!type || !source) {
438  fprintf(stderr, "fuse: failed to allocate memory\n");
439  goto out_close;
440  }
441 
442  strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
443  if (mo->subtype) {
444  strcat(type, ".");
445  strcat(type, mo->subtype);
446  }
447  strcpy(source,
448  mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
449 
450  res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
451  if (res == -1 && errno == ENODEV && mo->subtype) {
452  /* Probably missing subtype support */
453  strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
454  if (mo->fsname) {
455  if (!mo->blkdev)
456  sprintf(source, "%s#%s", mo->subtype,
457  mo->fsname);
458  } else {
459  strcpy(source, type);
460  }
461  res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
462  }
463  if (res == -1) {
464  /*
465  * Maybe kernel doesn't support unprivileged mounts, in this
466  * case try falling back to fusermount3
467  */
468  if (errno == EPERM) {
469  res = -2;
470  } else {
471  int errno_save = errno;
472  if (mo->blkdev && errno == ENODEV &&
473  !fuse_mnt_check_fuseblk())
474  fprintf(stderr,
475  "fuse: 'fuseblk' support missing\n");
476  else
477  fprintf(stderr, "fuse: mount failed: %s\n",
478  strerror(errno_save));
479  }
480 
481  goto out_close;
482  }
483 
484 #ifndef IGNORE_MTAB
485  if (geteuid() == 0) {
486  char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
487  res = -1;
488  if (!newmnt)
489  goto out_umount;
490 
491  res = fuse_mnt_add_mount("fuse", source, newmnt, type,
492  mnt_opts);
493  free(newmnt);
494  if (res == -1)
495  goto out_umount;
496  }
497 #endif /* IGNORE_MTAB */
498  free(type);
499  free(source);
500 
501  return fd;
502 
503 out_umount:
504  umount2(mnt, 2); /* lazy umount */
505 out_close:
506  free(type);
507  free(source);
508  close(fd);
509  return res;
510 }
511 
512 static int get_mnt_flag_opts(char **mnt_optsp, int flags)
513 {
514  int i;
515 
516  if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
517  return -1;
518 
519  for (i = 0; mount_flags[i].opt != NULL; i++) {
520  if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
521  fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
522  return -1;
523  }
524  return 0;
525 }
526 
527 struct mount_opts *parse_mount_opts(struct fuse_args *args)
528 {
529  struct mount_opts *mo;
530 
531  mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
532  if (mo == NULL)
533  return NULL;
534 
535  memset(mo, 0, sizeof(struct mount_opts));
536  mo->flags = MS_NOSUID | MS_NODEV;
537 
538  if (args &&
539  fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
540  goto err_out;
541 
542  return mo;
543 
544 err_out:
545  destroy_mount_opts(mo);
546  return NULL;
547 }
548 
549 void destroy_mount_opts(struct mount_opts *mo)
550 {
551  free(mo->fsname);
552  free(mo->subtype);
553  free(mo->fusermount_opts);
554  free(mo->subtype_opt);
555  free(mo->kernel_opts);
556  free(mo->mtab_opts);
557  free(mo);
558 }
559 
560 
561 int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
562 {
563  int res = -1;
564  char *mnt_opts = NULL;
565 
566  res = -1;
567  if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
568  goto out;
569  if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
570  goto out;
571  if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
572  goto out;
573 
574  res = fuse_mount_sys(mountpoint, mo, mnt_opts);
575  if (res == -2) {
576  if (mo->fusermount_opts &&
577  fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
578  goto out;
579 
580  if (mo->subtype) {
581  char *tmp_opts = NULL;
582 
583  res = -1;
584  if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
585  fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
586  free(tmp_opts);
587  goto out;
588  }
589 
590  res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
591  free(tmp_opts);
592  if (res == -1)
593  res = fuse_mount_fusermount(mountpoint, mo,
594  mnt_opts, 0);
595  } else {
596  res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
597  }
598  }
599 out:
600  free(mnt_opts);
601  return res;
602 }
int fuse_opt_add_opt(char **opts, const char *opt)
Definition: fuse_opt.c:138
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition: fuse_opt.c:143
#define FUSE_OPT_END
Definition: fuse_opt.h:104
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397