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