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