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