Tizen 2.1 base
[framework/base/fuse.git] / util / fusermount.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 GPL.
6   See the file COPYING.
7 */
8 /* This program does the mounting and unmounting of FUSE filesystems */
9
10 #define _GNU_SOURCE /* for clone */
11 #include <config.h>
12
13 #include "mount_util.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <pwd.h>
23 #include <mntent.h>
24 #include <sys/wait.h>
25 #include <sys/stat.h>
26 #include <sys/mount.h>
27 #include <sys/fsuid.h>
28 #include <sys/socket.h>
29 #include <sys/utsname.h>
30 #include <sched.h>
31
32 #define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
33
34 #define FUSE_DEV_OLD "/proc/fs/fuse/dev"
35 #define FUSE_DEV_NEW "/dev/fuse"
36 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
37 #define FUSE_CONF "/etc/fuse.conf"
38
39 #ifndef MS_DIRSYNC
40 #define MS_DIRSYNC 128
41 #endif
42 #ifndef MS_REC
43 #define MS_REC 16384
44 #endif
45 #ifndef MS_PRIVATE
46 #define MS_PRIVATE (1<<18)
47 #endif
48
49 #ifndef UMOUNT_DETACH
50 #define UMOUNT_DETACH   0x00000002      /* Just detach from the tree */
51 #endif
52 #ifndef UMOUNT_NOFOLLOW
53 #define UMOUNT_NOFOLLOW 0x00000008      /* Don't follow symlink on umount */
54 #endif
55 #ifndef UMOUNT_UNUSED
56 #define UMOUNT_UNUSED   0x80000000      /* Flag guaranteed to be unused */
57 #endif
58
59 static const char *progname;
60
61 static int user_allow_other = 0;
62 static int mount_max = 1000;
63
64 static int auto_unmount = 0;
65
66 static const char *get_user_name(void)
67 {
68         struct passwd *pw = getpwuid(getuid());
69         if (pw != NULL && pw->pw_name != NULL)
70                 return pw->pw_name;
71         else {
72                 fprintf(stderr, "%s: could not determine username\n", progname);
73                 return NULL;
74         }
75 }
76
77 static uid_t oldfsuid;
78 static gid_t oldfsgid;
79
80 static void drop_privs(void)
81 {
82         if (getuid() != 0) {
83                 oldfsuid = setfsuid(getuid());
84                 oldfsgid = setfsgid(getgid());
85         }
86 }
87
88 static void restore_privs(void)
89 {
90         if (getuid() != 0) {
91                 setfsuid(oldfsuid);
92                 setfsgid(oldfsgid);
93         }
94 }
95
96 #ifndef IGNORE_MTAB
97 /*
98  * Make sure that /etc/mtab is checked and updated atomically
99  */
100 static int lock_umount(void)
101 {
102         const char *mtab_lock = _PATH_MOUNTED ".fuselock";
103         int mtablock;
104         int res;
105         struct stat mtab_stat;
106
107         /* /etc/mtab could be a symlink to /proc/mounts */
108         if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
109                 return -1;
110
111         mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
112         if (mtablock == -1) {
113                 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
114                         progname, strerror(errno));
115                 return -1;
116         }
117         res = lockf(mtablock, F_LOCK, 0);
118         if (res < 0) {
119                 fprintf(stderr, "%s: error getting lock: %s\n", progname,
120                         strerror(errno));
121                 close(mtablock);
122                 return -1;
123         }
124
125         return mtablock;
126 }
127
128 static void unlock_umount(int mtablock)
129 {
130         if (mtablock >= 0) {
131                 int res;
132
133                 res = lockf(mtablock, F_ULOCK, 0);
134                 if (res < 0) {
135                         fprintf(stderr, "%s: error releasing lock: %s\n",
136                                 progname, strerror(errno));
137                 }
138                 close(mtablock);
139         }
140 }
141
142 static int add_mount(const char *source, const char *mnt, const char *type,
143                      const char *opts)
144 {
145         return fuse_mnt_add_mount(progname, source, mnt, type, opts);
146 }
147
148 static int may_unmount(const char *mnt, int quiet)
149 {
150         struct mntent *entp;
151         FILE *fp;
152         const char *user = NULL;
153         char uidstr[32];
154         unsigned uidlen = 0;
155         int found;
156         const char *mtab = _PATH_MOUNTED;
157
158         user = get_user_name();
159         if (user == NULL)
160                 return -1;
161
162         fp = setmntent(mtab, "r");
163         if (fp == NULL) {
164                 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
165                         strerror(errno));
166                 return -1;
167         }
168
169         uidlen = sprintf(uidstr, "%u", getuid());
170
171         found = 0;
172         while ((entp = getmntent(fp)) != NULL) {
173                 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
174                     (strcmp(entp->mnt_type, "fuse") == 0 ||
175                      strcmp(entp->mnt_type, "fuseblk") == 0 ||
176                      strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
177                      strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
178                         char *p = strstr(entp->mnt_opts, "user=");
179                         if (p &&
180                             (p == entp->mnt_opts || *(p-1) == ',') &&
181                             strcmp(p + 5, user) == 0) {
182                                 found = 1;
183                                 break;
184                         }
185                         /* /etc/mtab is a link pointing to
186                            /proc/mounts: */
187                         else if ((p =
188                                   strstr(entp->mnt_opts, "user_id=")) &&
189                                  (p == entp->mnt_opts ||
190                                   *(p-1) == ',') &&
191                                  strncmp(p + 8, uidstr, uidlen) == 0 &&
192                                  (*(p+8+uidlen) == ',' ||
193                                   *(p+8+uidlen) == '\0')) {
194                                 found = 1;
195                                 break;
196                         }
197                 }
198         }
199         endmntent(fp);
200
201         if (!found) {
202                 if (!quiet)
203                         fprintf(stderr,
204                                 "%s: entry for %s not found in %s\n",
205                                 progname, mnt, mtab);
206                 return -1;
207         }
208
209         return 0;
210 }
211
212 /*
213  * Check whether the file specified in "fusermount -u" is really a
214  * mountpoint and not a symlink.  This is necessary otherwise the user
215  * could move the mountpoint away and replace it with a symlink
216  * pointing to an arbitrary mount, thereby tricking fusermount into
217  * unmounting that (umount(2) will follow symlinks).
218  *
219  * This is the child process running in a separate mount namespace, so
220  * we don't mess with the global namespace and if the process is
221  * killed for any reason, mounts are automatically cleaned up.
222  *
223  * First make sure nothing is propagated back into the parent
224  * namespace by marking all mounts "private".
225  *
226  * Then bind mount parent onto a stable base where the user can't move
227  * it around.
228  *
229  * Finally check /proc/mounts for an entry matching the requested
230  * mountpoint.  If it's found then we are OK, and the user can't move
231  * it around within the parent directory as rename() will return
232  * EBUSY.  Be careful to ignore any mounts that existed before the
233  * bind.
234  */
235 static int check_is_mount_child(void *p)
236 {
237         const char **a = p;
238         const char *last = a[0];
239         const char *mnt = a[1];
240         int res;
241         const char *procmounts = "/proc/mounts";
242         int found;
243         FILE *fp;
244         struct mntent *entp;
245         int count;
246
247         res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
248         if (res == -1) {
249                 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
250                         progname, strerror(errno));
251                 return 1;
252         }
253
254         fp = setmntent(procmounts, "r");
255         if (fp == NULL) {
256                 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
257                         procmounts, strerror(errno));
258                 return 1;
259         }
260
261         count = 0;
262         while (getmntent(fp) != NULL)
263                 count++;
264         endmntent(fp);
265
266         fp = setmntent(procmounts, "r");
267         if (fp == NULL) {
268                 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
269                         procmounts, strerror(errno));
270                 return 1;
271         }
272
273         res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
274         if (res == -1) {
275                 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
276                         progname, strerror(errno));
277                 return 1;
278         }
279
280         found = 0;
281         while ((entp = getmntent(fp)) != NULL) {
282                 if (count > 0) {
283                         count--;
284                         continue;
285                 }
286                 if (entp->mnt_dir[0] == '/' &&
287                     strcmp(entp->mnt_dir + 1, last) == 0) {
288                         found = 1;
289                         break;
290                 }
291         }
292         endmntent(fp);
293
294         if (!found) {
295                 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
296                 return 1;
297         }
298
299         return 0;
300 }
301
302 static pid_t clone_newns(void *a)
303 {
304         char buf[131072];
305         char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
306
307 #ifdef __ia64__
308         extern int __clone2(int (*fn)(void *),
309                             void *child_stack_base, size_t stack_size,
310                             int flags, void *arg, pid_t *ptid,
311                             void *tls, pid_t *ctid);
312
313         return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
314                         CLONE_NEWNS, a, NULL, NULL, NULL);
315 #else
316         return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
317 #endif
318 }
319
320 static int check_is_mount(const char *last, const char *mnt)
321 {
322         pid_t pid, p;
323         int status;
324         const char *a[2] = { last, mnt };
325
326         pid = clone_newns((void *) a);
327         if (pid == (pid_t) -1) {
328                 fprintf(stderr, "%s: failed to clone namespace: %s\n",
329                         progname, strerror(errno));
330                 return -1;
331         }
332         p = waitpid(pid, &status, __WCLONE);
333         if (p == (pid_t) -1) {
334                 fprintf(stderr, "%s: waitpid failed: %s\n",
335                         progname, strerror(errno));
336                 return -1;
337         }
338         if (!WIFEXITED(status)) {
339                 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
340                         progname, status);
341                 return -1;
342         }
343         if (WEXITSTATUS(status) != 0)
344                 return -1;
345
346         return 0;
347 }
348
349 static int chdir_to_parent(char *copy, const char **lastp)
350 {
351         char *tmp;
352         const char *parent;
353         char buf[65536];
354         int res;
355
356         tmp = strrchr(copy, '/');
357         if (tmp == NULL || tmp[1] == '\0') {
358                 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
359                         progname, copy);
360                 return -1;
361         }
362         if (tmp != copy) {
363                 *tmp = '\0';
364                 parent = copy;
365                 *lastp = tmp + 1;
366         } else if (tmp[1] != '\0') {
367                 *lastp = tmp + 1;
368                 parent = "/";
369         } else {
370                 *lastp = ".";
371                 parent = "/";
372         }
373
374         res = chdir(parent);
375         if (res == -1) {
376                 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
377                         progname, parent, strerror(errno));
378                 return -1;
379         }
380
381         if (getcwd(buf, sizeof(buf)) == NULL) {
382                 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
383                         progname, strerror(errno));
384                 return -1;
385         }
386         if (strcmp(buf, parent) != 0) {
387                 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
388                         parent, buf);
389                 return -1;
390
391         }
392
393         return 0;
394 }
395
396 /* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
397 static int umount_nofollow_support(void)
398 {
399         int res = umount2("", UMOUNT_UNUSED);
400         if (res != -1 || errno != EINVAL)
401                 return 0;
402
403         res = umount2("", UMOUNT_NOFOLLOW);
404         if (res != -1 || errno != ENOENT)
405                 return 0;
406
407         return 1;
408 }
409
410 static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
411 {
412         int res;
413         char *copy;
414         const char *last;
415         int umount_flags = lazy ? UMOUNT_DETACH : 0;
416
417         if (getuid() != 0) {
418                 res = may_unmount(mnt, quiet);
419                 if (res == -1)
420                         return -1;
421         }
422
423         copy = strdup(mnt);
424         if (copy == NULL) {
425                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
426                 return -1;
427         }
428
429         res = chdir_to_parent(copy, &last);
430         if (res == -1)
431                 goto out;
432
433         if (umount_nofollow_support()) {
434                 umount_flags |= UMOUNT_NOFOLLOW;
435         } else {
436                 res = check_is_mount(last, mnt);
437                 if (res == -1)
438                         goto out;
439         }
440
441         res = umount2(last, umount_flags);
442         if (res == -1 && !quiet) {
443                 fprintf(stderr, "%s: failed to unmount %s: %s\n",
444                         progname, mnt, strerror(errno));
445         }
446
447 out:
448         if (res == -1)
449                 return -1;
450
451         res = chdir("/");
452         if (res == -1) {
453                 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
454                 return -1;
455         }
456
457         return fuse_mnt_remove_mount(progname, mnt);
458 }
459
460 static int unmount_fuse(const char *mnt, int quiet, int lazy)
461 {
462         int res;
463         int mtablock = lock_umount();
464
465         res = unmount_fuse_locked(mnt, quiet, lazy);
466         unlock_umount(mtablock);
467
468         return res;
469 }
470
471 static int count_fuse_fs(void)
472 {
473         struct mntent *entp;
474         int count = 0;
475         const char *mtab = _PATH_MOUNTED;
476         FILE *fp = setmntent(mtab, "r");
477         if (fp == NULL) {
478                 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
479                         strerror(errno));
480                 return -1;
481         }
482         while ((entp = getmntent(fp)) != NULL) {
483                 if (strcmp(entp->mnt_type, "fuse") == 0 ||
484                     strncmp(entp->mnt_type, "fuse.", 5) == 0)
485                         count ++;
486         }
487         endmntent(fp);
488         return count;
489 }
490
491
492 #else /* IGNORE_MTAB */
493 static int count_fuse_fs()
494 {
495         return 0;
496 }
497
498 static int add_mount(const char *source, const char *mnt, const char *type,
499                      const char *opts)
500 {
501         (void) source;
502         (void) mnt;
503         (void) type;
504         (void) opts;
505         return 0;
506 }
507
508 static int unmount_fuse(const char *mnt, int quiet, int lazy)
509 {
510         return fuse_mnt_umount(progname, mnt, mnt, lazy);
511 }
512 #endif /* IGNORE_MTAB */
513
514 static void strip_line(char *line)
515 {
516         char *s = strchr(line, '#');
517         if (s != NULL)
518                 s[0] = '\0';
519         for (s = line + strlen(line) - 1;
520              s >= line && isspace((unsigned char) *s); s--);
521         s[1] = '\0';
522         for (s = line; isspace((unsigned char) *s); s++);
523         if (s != line)
524                 memmove(line, s, strlen(s)+1);
525 }
526
527 static void parse_line(char *line, int linenum)
528 {
529         int tmp;
530         if (strcmp(line, "user_allow_other") == 0)
531                 user_allow_other = 1;
532         else if (sscanf(line, "mount_max = %i", &tmp) == 1)
533                 mount_max = tmp;
534         else if(line[0])
535                 fprintf(stderr,
536                         "%s: unknown parameter in %s at line %i: '%s'\n",
537                         progname, FUSE_CONF, linenum, line);
538 }
539
540 static void read_conf(void)
541 {
542         FILE *fp = fopen(FUSE_CONF, "r");
543         if (fp != NULL) {
544                 int linenum = 1;
545                 char line[256];
546                 int isnewline = 1;
547                 while (fgets(line, sizeof(line), fp) != NULL) {
548                         if (isnewline) {
549                                 if (line[strlen(line)-1] == '\n') {
550                                         strip_line(line);
551                                         parse_line(line, linenum);
552                                 } else {
553                                         isnewline = 0;
554                                 }
555                         } else if(line[strlen(line)-1] == '\n') {
556                                 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
557
558                                 isnewline = 1;
559                         }
560                         if (isnewline)
561                                 linenum ++;
562                 }
563                 if (!isnewline) {
564                         fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
565
566                 }
567                 fclose(fp);
568         } else if (errno != ENOENT) {
569                 fprintf(stderr, "%s: failed to open %s: %s\n",
570                         progname, FUSE_CONF, strerror(errno));
571         }
572 }
573
574 static int begins_with(const char *s, const char *beg)
575 {
576         if (strncmp(s, beg, strlen(beg)) == 0)
577                 return 1;
578         else
579                 return 0;
580 }
581
582 struct mount_flags {
583         const char *opt;
584         unsigned long flag;
585         int on;
586         int safe;
587 };
588
589 static struct mount_flags mount_flags[] = {
590         {"rw",      MS_RDONLY,      0, 1},
591         {"ro",      MS_RDONLY,      1, 1},
592         {"suid",    MS_NOSUID,      0, 0},
593         {"nosuid",  MS_NOSUID,      1, 1},
594         {"dev",     MS_NODEV,       0, 0},
595         {"nodev",   MS_NODEV,       1, 1},
596         {"exec",    MS_NOEXEC,      0, 1},
597         {"noexec",  MS_NOEXEC,      1, 1},
598         {"async",   MS_SYNCHRONOUS, 0, 1},
599         {"sync",    MS_SYNCHRONOUS, 1, 1},
600         {"atime",   MS_NOATIME,     0, 1},
601         {"noatime", MS_NOATIME,     1, 1},
602         {"dirsync", MS_DIRSYNC,     1, 1},
603         {NULL,      0,              0, 0}
604 };
605
606 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
607 {
608         int i;
609
610         for (i = 0; mount_flags[i].opt != NULL; i++) {
611                 const char *opt = mount_flags[i].opt;
612                 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
613                         *on = mount_flags[i].on;
614                         *flag = mount_flags[i].flag;
615                         if (!mount_flags[i].safe && getuid() != 0) {
616                                 *flag = 0;
617                                 fprintf(stderr,
618                                         "%s: unsafe option %s ignored\n",
619                                         progname, opt);
620                         }
621                         return 1;
622                 }
623         }
624         return 0;
625 }
626
627 static int add_option(char **optsp, const char *opt, unsigned expand)
628 {
629         char *newopts;
630         if (*optsp == NULL)
631                 newopts = strdup(opt);
632         else {
633                 unsigned oldsize = strlen(*optsp);
634                 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
635                 newopts = (char *) realloc(*optsp, newsize);
636                 if (newopts)
637                         sprintf(newopts + oldsize, ",%s", opt);
638         }
639         if (newopts == NULL) {
640                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
641                 return -1;
642         }
643         *optsp = newopts;
644         return 0;
645 }
646
647 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
648 {
649         int i;
650         int l;
651
652         if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
653                 return -1;
654
655         for (i = 0; mount_flags[i].opt != NULL; i++) {
656                 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
657                     add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
658                         return -1;
659         }
660
661         if (add_option(mnt_optsp, opts, 0) == -1)
662                 return -1;
663         /* remove comma from end of opts*/
664         l = strlen(*mnt_optsp);
665         if ((*mnt_optsp)[l-1] == ',')
666                 (*mnt_optsp)[l-1] = '\0';
667         if (getuid() != 0) {
668                 const char *user = get_user_name();
669                 if (user == NULL)
670                         return -1;
671
672                 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
673                         return -1;
674                 strcat(*mnt_optsp, user);
675         }
676         return 0;
677 }
678
679 static int opt_eq(const char *s, unsigned len, const char *opt)
680 {
681         if(strlen(opt) == len && strncmp(s, opt, len) == 0)
682                 return 1;
683         else
684                 return 0;
685 }
686
687 static int get_string_opt(const char *s, unsigned len, const char *opt,
688                           char **val)
689 {
690         int i;
691         unsigned opt_len = strlen(opt);
692         char *d;
693
694         if (*val)
695                 free(*val);
696         *val = (char *) malloc(len - opt_len + 1);
697         if (!*val) {
698                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
699                 return 0;
700         }
701
702         d = *val;
703         s += opt_len;
704         len -= opt_len;
705         for (i = 0; i < len; i++) {
706                 if (s[i] == '\\' && i + 1 < len)
707                         i++;
708                 *d++ = s[i];
709         }
710         *d = '\0';
711         return 1;
712 }
713
714 static int do_mount(const char *mnt, char **typep, mode_t rootmode,
715                     int fd, const char *opts, const char *dev, char **sourcep,
716                     char **mnt_optsp, off_t rootsize)
717 {
718         int res;
719         int flags = MS_NOSUID | MS_NODEV;
720         char *optbuf;
721         char *mnt_opts = NULL;
722         const char *s;
723         char *d;
724         char *fsname = NULL;
725         char *subtype = NULL;
726         char *source = NULL;
727         char *type = NULL;
728         int check_empty = 1;
729         int blkdev = 0;
730
731         optbuf = (char *) malloc(strlen(opts) + 128);
732         if (!optbuf) {
733                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
734                 return -1;
735         }
736
737         for (s = opts, d = optbuf; *s;) {
738                 unsigned len;
739                 const char *fsname_str = "fsname=";
740                 const char *subtype_str = "subtype=";
741                 for (len = 0; s[len]; len++) {
742                         if (s[len] == '\\' && s[len + 1])
743                                 len++;
744                         else if (s[len] == ',')
745                                 break;
746                 }
747                 if (begins_with(s, fsname_str)) {
748                         if (!get_string_opt(s, len, fsname_str, &fsname))
749                                 goto err;
750                 } else if (begins_with(s, subtype_str)) {
751                         if (!get_string_opt(s, len, subtype_str, &subtype))
752                                 goto err;
753                 } else if (opt_eq(s, len, "blkdev")) {
754                         if (getuid() != 0) {
755                                 fprintf(stderr,
756                                         "%s: option blkdev is privileged\n",
757                                         progname);
758                                 goto err;
759                         }
760                         blkdev = 1;
761                 } else if (opt_eq(s, len, "nonempty")) {
762                         check_empty = 0;
763                 } else if (opt_eq(s, len, "auto_unmount")) {
764                         auto_unmount = 1;
765                 } else if (!begins_with(s, "fd=") &&
766                            !begins_with(s, "rootmode=") &&
767                            !begins_with(s, "user_id=") &&
768                            !begins_with(s, "group_id=")) {
769                         int on;
770                         int flag;
771                         int skip_option = 0;
772                         if (opt_eq(s, len, "large_read")) {
773                                 struct utsname utsname;
774                                 unsigned kmaj, kmin;
775                                 res = uname(&utsname);
776                                 if (res == 0 &&
777                                     sscanf(utsname.release, "%u.%u",
778                                            &kmaj, &kmin) == 2 &&
779                                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
780                                         fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
781                                         skip_option = 1;
782                                 }
783                         }
784                         if (getuid() != 0 && !user_allow_other &&
785                             (opt_eq(s, len, "allow_other") ||
786                              opt_eq(s, len, "allow_root"))) {
787                                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
788                                 goto err;
789                         }
790                         if (!skip_option) {
791                                 if (find_mount_flag(s, len, &on, &flag)) {
792                                         if (on)
793                                                 flags |= flag;
794                                         else
795                                                 flags  &= ~flag;
796                                 } else {
797                                         memcpy(d, s, len);
798                                         d += len;
799                                         *d++ = ',';
800                                 }
801                         }
802                 }
803                 s += len;
804                 if (*s)
805                         s++;
806         }
807         *d = '\0';
808         res = get_mnt_opts(flags, optbuf, &mnt_opts);
809         if (res == -1)
810                 goto err;
811
812         sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
813                 fd, rootmode, getuid(), getgid());
814
815         if (check_empty &&
816             fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
817                 goto err;
818
819         source = malloc((fsname ? strlen(fsname) : 0) +
820                         (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
821
822         type = malloc((subtype ? strlen(subtype) : 0) + 32);
823         if (!type || !source) {
824                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
825                 goto err;
826         }
827
828         if (subtype)
829                 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
830         else
831                 strcpy(type, blkdev ? "fuseblk" : "fuse");
832
833         if (fsname)
834                 strcpy(source, fsname);
835         else
836                 strcpy(source, subtype ? subtype : dev);
837
838         res = mount(source, mnt, type, flags, optbuf);
839         if (res == -1 && errno == ENODEV && subtype) {
840                 /* Probably missing subtype support */
841                 strcpy(type, blkdev ? "fuseblk" : "fuse");
842                 if (fsname) {
843                         if (!blkdev)
844                                 sprintf(source, "%s#%s", subtype, fsname);
845                 } else {
846                         strcpy(source, type);
847                 }
848
849                 res = mount(source, mnt, type, flags, optbuf);
850         }
851         if (res == -1 && errno == EINVAL) {
852                 /* It could be an old version not supporting group_id */
853                 sprintf(d, "fd=%i,rootmode=%o,user_id=%i",
854                         fd, rootmode, getuid());
855                 res = mount(source, mnt, type, flags, optbuf);
856         }
857         if (res == -1) {
858                 int errno_save = errno;
859                 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
860                         fprintf(stderr, "%s: 'fuseblk' support missing\n",
861                                 progname);
862                 else
863                         fprintf(stderr, "%s: mount failed: %s\n", progname,
864                                 strerror(errno_save));
865                 goto err;
866         }
867         *sourcep = source;
868         *typep = type;
869         *mnt_optsp = mnt_opts;
870         free(fsname);
871         free(optbuf);
872
873         return 0;
874
875 err:
876         free(fsname);
877         free(subtype);
878         free(source);
879         free(type);
880         free(mnt_opts);
881         free(optbuf);
882         return -1;
883 }
884
885 static int check_version(const char *dev)
886 {
887         int res;
888         int majorver;
889         int minorver;
890         const char *version_file;
891         FILE *vf;
892
893         if (strcmp(dev, FUSE_DEV_OLD) != 0)
894                 return 0;
895
896         version_file = FUSE_VERSION_FILE_OLD;
897         vf = fopen(version_file, "r");
898         if (vf == NULL) {
899                 fprintf(stderr, "%s: kernel interface too old\n", progname);
900                 return -1;
901         }
902         res = fscanf(vf, "%i.%i", &majorver, &minorver);
903         fclose(vf);
904         if (res != 2) {
905                 fprintf(stderr, "%s: error reading %s\n", progname,
906                         version_file);
907                 return -1;
908         }
909         if (majorver < 3) {
910                 fprintf(stderr, "%s: kernel interface too old\n", progname);
911                 return -1;
912         }
913         return 0;
914 }
915
916 static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
917 {
918         int res;
919         const char *mnt = *mntp;
920         const char *origmnt = mnt;
921
922         res = lstat(mnt, stbuf);
923         if (res == -1) {
924                 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
925                         progname, mnt, strerror(errno));
926                 return -1;
927         }
928
929         /* No permission checking is done for root */
930         if (getuid() == 0)
931                 return 0;
932
933         if (S_ISDIR(stbuf->st_mode)) {
934                 res = chdir(mnt);
935                 if (res == -1) {
936                         fprintf(stderr,
937                                 "%s: failed to chdir to mountpoint: %s\n",
938                                 progname, strerror(errno));
939                         return -1;
940                 }
941                 mnt = *mntp = ".";
942                 res = lstat(mnt, stbuf);
943                 if (res == -1) {
944                         fprintf(stderr,
945                                 "%s: failed to access mountpoint %s: %s\n",
946                                 progname, origmnt, strerror(errno));
947                         return -1;
948                 }
949
950                 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
951                         fprintf(stderr, "%s: mountpoint %s not owned by user\n",
952                                 progname, origmnt);
953                         return -1;
954                 }
955
956                 res = access(mnt, W_OK);
957                 if (res == -1) {
958                         fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
959                                 progname, origmnt);
960                         return -1;
961                 }
962         } else if (S_ISREG(stbuf->st_mode)) {
963                 static char procfile[256];
964                 *mountpoint_fd = open(mnt, O_WRONLY);
965                 if (*mountpoint_fd == -1) {
966                         fprintf(stderr, "%s: failed to open %s: %s\n",
967                                 progname, mnt, strerror(errno));
968                         return -1;
969                 }
970                 res = fstat(*mountpoint_fd, stbuf);
971                 if (res == -1) {
972                         fprintf(stderr,
973                                 "%s: failed to access mountpoint %s: %s\n",
974                                 progname, mnt, strerror(errno));
975                         return -1;
976                 }
977                 if (!S_ISREG(stbuf->st_mode)) {
978                         fprintf(stderr,
979                                 "%s: mountpoint %s is no longer a regular file\n",
980                                 progname, mnt);
981                         return -1;
982                 }
983
984                 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
985                 *mntp = procfile;
986         } else {
987                 fprintf(stderr,
988                         "%s: mountpoint %s is not a directory or a regular file\n",
989                         progname, mnt);
990                 return -1;
991         }
992
993
994         return 0;
995 }
996
997 static int try_open(const char *dev, char **devp, int silent)
998 {
999         int fd = open(dev, O_RDWR);
1000         if (fd != -1) {
1001                 *devp = strdup(dev);
1002                 if (*devp == NULL) {
1003                         fprintf(stderr, "%s: failed to allocate memory\n",
1004                                 progname);
1005                         close(fd);
1006                         fd = -1;
1007                 }
1008         } else if (errno == ENODEV ||
1009                    errno == ENOENT)/* check for ENOENT too, for the udev case */
1010                 return -2;
1011         else if (!silent) {
1012                 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
1013                         strerror(errno));
1014         }
1015         return fd;
1016 }
1017
1018 static int try_open_fuse_device(char **devp)
1019 {
1020         int fd;
1021         int err;
1022
1023         drop_privs();
1024         fd = try_open(FUSE_DEV_NEW, devp, 0);
1025         restore_privs();
1026         if (fd >= 0)
1027                 return fd;
1028
1029         err = fd;
1030         fd = try_open(FUSE_DEV_OLD, devp, 1);
1031         if (fd >= 0)
1032                 return fd;
1033
1034         return err;
1035 }
1036
1037 static int open_fuse_device(char **devp)
1038 {
1039         int fd = try_open_fuse_device(devp);
1040         if (fd >= -1)
1041                 return fd;
1042
1043         fprintf(stderr,
1044                 "%s: fuse device not found, try 'modprobe fuse' first\n",
1045                 progname);
1046
1047         return -1;
1048 }
1049
1050
1051 static int mount_fuse(const char *mnt, const char *opts)
1052 {
1053         int res;
1054         int fd;
1055         char *dev;
1056         struct stat stbuf;
1057         char *type = NULL;
1058         char *source = NULL;
1059         char *mnt_opts = NULL;
1060         const char *real_mnt = mnt;
1061         int mountpoint_fd = -1;
1062
1063         fd = open_fuse_device(&dev);
1064         if (fd == -1)
1065                 return -1;
1066
1067         drop_privs();
1068         read_conf();
1069
1070         if (getuid() != 0 && mount_max != -1) {
1071                 int mount_count = count_fuse_fs();
1072                 if (mount_count >= mount_max) {
1073                         fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
1074                         goto fail_close_fd;
1075                 }
1076         }
1077
1078         res = check_version(dev);
1079         if (res != -1) {
1080                 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1081                 restore_privs();
1082                 if (res != -1)
1083                         res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT,
1084                                        fd, opts, dev, &source, &mnt_opts,
1085                                        stbuf.st_size);
1086         } else
1087                 restore_privs();
1088
1089         if (mountpoint_fd != -1)
1090                 close(mountpoint_fd);
1091
1092         if (res == -1)
1093                 goto fail_close_fd;
1094
1095         res = chdir("/");
1096         if (res == -1) {
1097                 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1098                 goto fail_close_fd;
1099         }
1100
1101         if (geteuid() == 0) {
1102                 res = add_mount(source, mnt, type, mnt_opts);
1103                 if (res == -1) {
1104                         /* Can't clean up mount in a non-racy way */
1105                         goto fail_close_fd;
1106                 }
1107         }
1108
1109 out_free:
1110         free(source);
1111         free(type);
1112         free(mnt_opts);
1113         free(dev);
1114
1115         return fd;
1116
1117 fail_close_fd:
1118         close(fd);
1119         fd = -1;
1120         goto out_free;
1121 }
1122
1123 static int send_fd(int sock_fd, int fd)
1124 {
1125         int retval;
1126         struct msghdr msg;
1127         struct cmsghdr *p_cmsg;
1128         struct iovec vec;
1129         size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1130         int *p_fds;
1131         char sendchar = 0;
1132
1133         msg.msg_control = cmsgbuf;
1134         msg.msg_controllen = sizeof(cmsgbuf);
1135         p_cmsg = CMSG_FIRSTHDR(&msg);
1136         p_cmsg->cmsg_level = SOL_SOCKET;
1137         p_cmsg->cmsg_type = SCM_RIGHTS;
1138         p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1139         p_fds = (int *) CMSG_DATA(p_cmsg);
1140         *p_fds = fd;
1141         msg.msg_controllen = p_cmsg->cmsg_len;
1142         msg.msg_name = NULL;
1143         msg.msg_namelen = 0;
1144         msg.msg_iov = &vec;
1145         msg.msg_iovlen = 1;
1146         msg.msg_flags = 0;
1147         /* "To pass file descriptors or credentials you need to send/read at
1148          * least one byte" (man 7 unix) */
1149         vec.iov_base = &sendchar;
1150         vec.iov_len = sizeof(sendchar);
1151         while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1152         if (retval != 1) {
1153                 perror("sending file descriptor");
1154                 return -1;
1155         }
1156         return 0;
1157 }
1158
1159 static void usage(void)
1160 {
1161         fprintf(stderr,
1162                 "%s: [options] mountpoint\n"
1163                 "Options:\n"
1164                 " -h                print help\n"
1165                 " -V                print version\n"
1166                 " -o opt[,opt...]   mount options\n"
1167                 " -u                unmount\n"
1168                 " -q                quiet\n"
1169                 " -z                lazy unmount\n",
1170                 progname);
1171         exit(1);
1172 }
1173
1174 static void show_version(void)
1175 {
1176         printf("fusermount version: %s\n", PACKAGE_VERSION);
1177         exit(0);
1178 }
1179
1180 int main(int argc, char *argv[])
1181 {
1182         sigset_t sigset;
1183         int ch;
1184         int fd;
1185         int res;
1186         char *origmnt;
1187         char *mnt;
1188         static int unmount = 0;
1189         static int lazy = 0;
1190         static int quiet = 0;
1191         char *commfd;
1192         int cfd;
1193         const char *opts = "";
1194
1195         static const struct option long_opts[] = {
1196                 {"unmount", no_argument, NULL, 'u'},
1197                 {"lazy",    no_argument, NULL, 'z'},
1198                 {"quiet",   no_argument, NULL, 'q'},
1199                 {"help",    no_argument, NULL, 'h'},
1200                 {"version", no_argument, NULL, 'V'},
1201                 {0, 0, 0, 0}};
1202
1203         progname = strdup(argv[0]);
1204         if (progname == NULL) {
1205                 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1206                 exit(1);
1207         }
1208
1209         while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1210                                  NULL)) != -1) {
1211                 switch (ch) {
1212                 case 'h':
1213                         usage();
1214                         break;
1215
1216                 case 'V':
1217                         show_version();
1218                         break;
1219
1220                 case 'o':
1221                         opts = optarg;
1222                         break;
1223
1224                 case 'u':
1225                         unmount = 1;
1226                         break;
1227
1228                 case 'z':
1229                         lazy = 1;
1230                         break;
1231
1232                 case 'q':
1233                         quiet = 1;
1234                         break;
1235
1236                 default:
1237                         exit(1);
1238                 }
1239         }
1240
1241         if (lazy && !unmount) {
1242                 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1243                 exit(1);
1244         }
1245
1246         if (optind >= argc) {
1247                 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1248                 exit(1);
1249         } else if (argc > optind + 1) {
1250                 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1251                         progname);
1252                 exit(1);
1253         }
1254
1255         origmnt = argv[optind];
1256
1257         drop_privs();
1258         mnt = fuse_mnt_resolve_path(progname, origmnt);
1259         if (mnt != NULL) {
1260                 res = chdir("/");
1261                 if (res == -1) {
1262                         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1263                         exit(1);
1264                 }
1265         }
1266         restore_privs();
1267         if (mnt == NULL)
1268                 exit(1);
1269
1270         umask(033);
1271         if (unmount)
1272                 goto do_unmount;
1273
1274         commfd = getenv(FUSE_COMMFD_ENV);
1275         if (commfd == NULL) {
1276                 fprintf(stderr, "%s: old style mounting not supported\n",
1277                         progname);
1278                 exit(1);
1279         }
1280
1281         fd = mount_fuse(mnt, opts);
1282         if (fd == -1)
1283                 exit(1);
1284
1285         cfd = atoi(commfd);
1286         res = send_fd(cfd, fd);
1287         if (res == -1)
1288                 exit(1);
1289         close(fd);
1290
1291         if (!auto_unmount)
1292                 return 0;
1293
1294         /* Become a daemon and wait for the parent to exit or die.
1295            ie For the control socket to get closed. 
1296            btw We don't want to use daemon() function here because
1297            it forks and messes with the file descriptors. */
1298         setsid();
1299         res = chdir("/");
1300         if (res == -1) {
1301                 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1302                 exit(1);
1303         }
1304
1305         sigfillset(&sigset);
1306         sigprocmask(SIG_BLOCK, &sigset, NULL);
1307
1308         lazy  = 1;
1309         quiet = 1;
1310
1311         while (1) {
1312                 unsigned char buf[16];
1313                 int n = recv(cfd, buf, sizeof(buf), 0);
1314                 if (!n)
1315                         break;
1316
1317                 if (n < 0) {
1318                         if (errno == EINTR)
1319                                 continue;
1320                         break;
1321                 }
1322         }
1323
1324 do_unmount:
1325         if (geteuid() == 0)
1326                 res = unmount_fuse(mnt, quiet, lazy);
1327         else {
1328                 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1329                 if (res == -1 && !quiet)
1330                         fprintf(stderr,
1331                                 "%s: failed to unmount %s: %s\n",
1332                                 progname, mnt, strerror(errno));
1333         }
1334         if (res == -1)
1335                 exit(1);
1336         return 0;
1337 }