Initialize the variable properly before passing to any function
[fuse.git] / lib / mount.c
blob0e585a43665e3733e6ac698a6a133ac1fd32fa89
1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7 */
9 #include "config.h"
10 #include "fuse_i.h"
11 #include "fuse_misc.h"
12 #include "fuse_opt.h"
13 #include "fuse_common_compat.h"
14 #include "mount_util.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <stddef.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <sys/poll.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <sys/wait.h>
27 #include <sys/mount.h>
29 #ifdef __NetBSD__
30 #include <perfuse.h>
32 #define MS_RDONLY MNT_RDONLY
33 #define MS_NOSUID MNT_NOSUID
34 #define MS_NODEV MNT_NODEV
35 #define MS_NOEXEC MNT_NOEXEC
36 #define MS_SYNCHRONOUS MNT_SYNCHRONOUS
37 #define MS_NOATIME MNT_NOATIME
39 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
40 #endif
42 #define FUSERMOUNT_PROG "fusermount"
43 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
45 #ifndef HAVE_FORK
46 #define fork() vfork()
47 #endif
49 #ifndef MS_DIRSYNC
50 #define MS_DIRSYNC 128
51 #endif
53 enum {
54 KEY_KERN_FLAG,
55 KEY_KERN_OPT,
56 KEY_FUSERMOUNT_OPT,
57 KEY_SUBTYPE_OPT,
58 KEY_MTAB_OPT,
59 KEY_ALLOW_ROOT,
60 KEY_RO,
61 KEY_HELP,
62 KEY_VERSION,
65 struct mount_opts {
66 int allow_other;
67 int allow_root;
68 int ishelp;
69 int flags;
70 int nonempty;
71 int blkdev;
72 char *fsname;
73 char *subtype;
74 char *subtype_opt;
75 char *mtab_opts;
76 char *fusermount_opts;
77 char *kernel_opts;
80 #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
82 static const struct fuse_opt fuse_mount_opts[] = {
83 FUSE_MOUNT_OPT("allow_other", allow_other),
84 FUSE_MOUNT_OPT("allow_root", allow_root),
85 FUSE_MOUNT_OPT("nonempty", nonempty),
86 FUSE_MOUNT_OPT("blkdev", blkdev),
87 FUSE_MOUNT_OPT("fsname=%s", fsname),
88 FUSE_MOUNT_OPT("subtype=%s", subtype),
89 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
90 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
91 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
92 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
93 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
95 FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
96 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
97 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
98 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
100 FUSE_OPT_KEY("user=", 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("-h", KEY_HELP),
116 FUSE_OPT_KEY("--help", KEY_HELP),
117 FUSE_OPT_KEY("-V", KEY_VERSION),
118 FUSE_OPT_KEY("--version", KEY_VERSION),
119 FUSE_OPT_END
122 static void mount_help(void)
124 fprintf(stderr,
125 " -o allow_other allow access to other users\n"
126 " -o allow_root allow access to root\n"
127 " -o nonempty allow mounts over non-empty file/dir\n"
128 " -o default_permissions enable permission checking by kernel\n"
129 " -o fsname=NAME set filesystem name\n"
130 " -o subtype=NAME set filesystem type\n"
131 " -o large_read issue large read requests (2.4 only)\n"
132 " -o max_read=N set maximum size of read requests\n"
133 "\n");
136 static void exec_fusermount(const char *argv[])
138 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
139 execvp(FUSERMOUNT_PROG, (char **) argv);
142 static void mount_version(void)
144 int pid = fork();
145 if (!pid) {
146 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
147 exec_fusermount(argv);
148 _exit(1);
149 } else if (pid != -1)
150 waitpid(pid, NULL, 0);
153 struct mount_flags {
154 const char *opt;
155 unsigned long flag;
156 int on;
159 static struct mount_flags mount_flags[] = {
160 {"rw", MS_RDONLY, 0},
161 {"ro", MS_RDONLY, 1},
162 {"suid", MS_NOSUID, 0},
163 {"nosuid", MS_NOSUID, 1},
164 {"dev", MS_NODEV, 0},
165 {"nodev", MS_NODEV, 1},
166 {"exec", MS_NOEXEC, 0},
167 {"noexec", MS_NOEXEC, 1},
168 {"async", MS_SYNCHRONOUS, 0},
169 {"sync", MS_SYNCHRONOUS, 1},
170 {"atime", MS_NOATIME, 0},
171 {"noatime", MS_NOATIME, 1},
172 #ifndef __NetBSD__
173 {"dirsync", MS_DIRSYNC, 1},
174 #endif
175 {NULL, 0, 0}
178 static void set_mount_flag(const char *s, int *flags)
180 int i;
182 for (i = 0; mount_flags[i].opt != NULL; i++) {
183 const char *opt = mount_flags[i].opt;
184 if (strcmp(opt, s) == 0) {
185 if (mount_flags[i].on)
186 *flags |= mount_flags[i].flag;
187 else
188 *flags &= ~mount_flags[i].flag;
189 return;
192 fprintf(stderr, "fuse: internal error, can't find mount flag\n");
193 abort();
196 static int fuse_mount_opt_proc(void *data, const char *arg, int key,
197 struct fuse_args *outargs)
199 struct mount_opts *mo = data;
201 switch (key) {
202 case KEY_ALLOW_ROOT:
203 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
204 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
205 return -1;
206 return 0;
208 case KEY_RO:
209 arg = "ro";
210 /* fall through */
211 case KEY_KERN_FLAG:
212 set_mount_flag(arg, &mo->flags);
213 return 0;
215 case KEY_KERN_OPT:
216 return fuse_opt_add_opt(&mo->kernel_opts, arg);
218 case KEY_FUSERMOUNT_OPT:
219 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
221 case KEY_SUBTYPE_OPT:
222 return fuse_opt_add_opt(&mo->subtype_opt, arg);
224 case KEY_MTAB_OPT:
225 return fuse_opt_add_opt(&mo->mtab_opts, arg);
227 case KEY_HELP:
228 mount_help();
229 mo->ishelp = 1;
230 break;
232 case KEY_VERSION:
233 mount_version();
234 mo->ishelp = 1;
235 break;
237 return 1;
240 /* return value:
241 * >= 0 => fd
242 * -1 => error
244 static int receive_fd(int fd)
246 struct msghdr msg;
247 struct iovec iov;
248 char buf[1];
249 int rv;
250 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
251 struct cmsghdr *cmsg;
253 iov.iov_base = buf;
254 iov.iov_len = 1;
256 memset(&msg, 0, sizeof(msg));
257 msg.msg_name = 0;
258 msg.msg_namelen = 0;
259 msg.msg_iov = &iov;
260 msg.msg_iovlen = 1;
261 /* old BSD implementations should use msg_accrights instead of
262 * msg_control; the interface is different. */
263 msg.msg_control = ccmsg;
264 msg.msg_controllen = sizeof(ccmsg);
266 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
267 if (rv == -1) {
268 perror("recvmsg");
269 return -1;
271 if(!rv) {
272 /* EOF */
273 return -1;
276 cmsg = CMSG_FIRSTHDR(&msg);
277 if (!cmsg->cmsg_type == SCM_RIGHTS) {
278 fprintf(stderr, "got control message of unknown type %d\n",
279 cmsg->cmsg_type);
280 return -1;
282 return *(int*)CMSG_DATA(cmsg);
285 void fuse_kern_unmount(const char *mountpoint, int fd)
287 int res;
288 int pid;
290 if (!mountpoint)
291 return;
293 if (fd != -1) {
294 struct pollfd pfd;
296 pfd.fd = fd;
297 pfd.events = 0;
298 res = poll(&pfd, 1, 0);
299 /* If file poll returns POLLERR on the device file descriptor,
300 then the filesystem is already unmounted */
301 if (res == 1 && (pfd.revents & POLLERR))
302 return;
304 /* Need to close file descriptor, otherwise synchronous umount
305 would recurse into filesystem, and deadlock */
306 close(fd);
309 if (geteuid() == 0) {
310 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
311 return;
314 res = umount2(mountpoint, 2);
315 if (res == 0)
316 return;
318 pid = fork();
319 if(pid == -1)
320 return;
322 if(pid == 0) {
323 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
324 "--", mountpoint, NULL };
326 exec_fusermount(argv);
327 _exit(1);
329 waitpid(pid, NULL, 0);
332 void fuse_unmount_compat22(const char *mountpoint)
334 fuse_kern_unmount(mountpoint, -1);
337 static int fuse_mount_fusermount(const char *mountpoint, const char *opts,
338 int quiet)
340 int fds[2], pid;
341 int res;
342 int rv;
344 if (!mountpoint) {
345 fprintf(stderr, "fuse: missing mountpoint parameter\n");
346 return -1;
349 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
350 if(res == -1) {
351 perror("fuse: socketpair() failed");
352 return -1;
355 pid = fork();
356 if(pid == -1) {
357 perror("fuse: fork() failed");
358 close(fds[0]);
359 close(fds[1]);
360 return -1;
363 if(pid == 0) {
364 char env[10];
365 const char *argv[32];
366 int a = 0;
368 if (quiet) {
369 int fd = open("/dev/null", O_RDONLY);
370 if (fd != -1) {
371 dup2(fd, 1);
372 dup2(fd, 2);
376 argv[a++] = FUSERMOUNT_PROG;
377 if (opts) {
378 argv[a++] = "-o";
379 argv[a++] = opts;
381 argv[a++] = "--";
382 argv[a++] = mountpoint;
383 argv[a++] = NULL;
385 close(fds[1]);
386 fcntl(fds[0], F_SETFD, 0);
387 snprintf(env, sizeof(env), "%i", fds[0]);
388 setenv(FUSE_COMMFD_ENV, env, 1);
389 exec_fusermount(argv);
390 perror("fuse: failed to exec fusermount");
391 _exit(1);
394 close(fds[0]);
395 rv = receive_fd(fds[1]);
396 close(fds[1]);
397 waitpid(pid, NULL, 0); /* bury zombie */
399 return rv;
402 int fuse_mount_compat22(const char *mountpoint, const char *opts)
404 return fuse_mount_fusermount(mountpoint, opts, 0);
407 static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
408 const char *mnt_opts)
410 char tmp[128];
411 const char *devname = "/dev/fuse";
412 char *source = NULL;
413 char *type = NULL;
414 struct stat stbuf;
415 int fd;
416 int res;
418 if (!mnt) {
419 fprintf(stderr, "fuse: missing mountpoint parameter\n");
420 return -1;
423 res = stat(mnt, &stbuf);
424 if (res == -1) {
425 fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
426 mnt, strerror(errno));
427 return -1;
430 if (!mo->nonempty) {
431 res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
432 stbuf.st_size);
433 if (res == -1)
434 return -1;
437 fd = open(devname, O_RDWR);
438 if (fd == -1) {
439 if (errno == ENODEV || errno == ENOENT)
440 fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
441 else
442 fprintf(stderr, "fuse: failed to open %s: %s\n",
443 devname, strerror(errno));
444 return -1;
447 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
448 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
450 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
451 if (res == -1)
452 goto out_close;
454 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
455 (mo->subtype ? strlen(mo->subtype) : 0) +
456 strlen(devname) + 32);
458 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
459 if (!type || !source) {
460 fprintf(stderr, "fuse: failed to allocate memory\n");
461 goto out_close;
464 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
465 if (mo->subtype) {
466 strcat(type, ".");
467 strcat(type, mo->subtype);
469 strcpy(source,
470 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
472 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
473 if (res == -1 && errno == ENODEV && mo->subtype) {
474 /* Probably missing subtype support */
475 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
476 if (mo->fsname) {
477 if (!mo->blkdev)
478 sprintf(source, "%s#%s", mo->subtype,
479 mo->fsname);
480 } else {
481 strcpy(source, type);
483 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
485 if (res == -1) {
487 * Maybe kernel doesn't support unprivileged mounts, in this
488 * case try falling back to fusermount
490 if (errno == EPERM) {
491 res = -2;
492 } else {
493 int errno_save = errno;
494 if (mo->blkdev && errno == ENODEV &&
495 !fuse_mnt_check_fuseblk())
496 fprintf(stderr,
497 "fuse: 'fuseblk' support missing\n");
498 else
499 fprintf(stderr, "fuse: mount failed: %s\n",
500 strerror(errno_save));
503 goto out_close;
506 #ifndef __NetBSD__
507 if (geteuid() == 0) {
508 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
509 res = -1;
510 if (!newmnt)
511 goto out_umount;
513 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
514 mnt_opts);
515 free(newmnt);
516 if (res == -1)
517 goto out_umount;
519 #endif /* __NetBSD__ */
520 free(type);
521 free(source);
523 return fd;
525 out_umount:
526 umount2(mnt, 2); /* lazy umount */
527 out_close:
528 free(type);
529 free(source);
530 close(fd);
531 return res;
534 static int get_mnt_flag_opts(char **mnt_optsp, int flags)
536 int i;
538 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
539 return -1;
541 for (i = 0; mount_flags[i].opt != NULL; i++) {
542 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
543 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
544 return -1;
546 return 0;
549 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
551 struct mount_opts mo;
552 int res = -1;
553 char *mnt_opts = NULL;
555 memset(&mo, 0, sizeof(mo));
556 mo.flags = MS_NOSUID | MS_NODEV;
558 if (args &&
559 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
560 return -1;
562 if (mo.allow_other && mo.allow_root) {
563 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
564 goto out;
566 res = 0;
567 if (mo.ishelp)
568 goto out;
570 res = -1;
571 if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
572 goto out;
573 if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
574 goto out;
575 if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
576 goto out;
578 res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
579 if (res == -2) {
580 if (mo.fusermount_opts &&
581 fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
582 goto out;
584 if (mo.subtype) {
585 char *tmp_opts = NULL;
587 res = -1;
588 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
589 fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
590 free(tmp_opts);
591 goto out;
594 res = fuse_mount_fusermount(mountpoint, tmp_opts, 1);
595 free(tmp_opts);
596 if (res == -1)
597 res = fuse_mount_fusermount(mountpoint,
598 mnt_opts, 0);
599 } else {
600 res = fuse_mount_fusermount(mountpoint, mnt_opts, 0);
603 out:
604 free(mnt_opts);
605 free(mo.fsname);
606 free(mo.subtype);
607 free(mo.fusermount_opts);
608 free(mo.subtype_opt);
609 free(mo.kernel_opts);
610 free(mo.mtab_opts);
611 return res;
614 FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
615 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");