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