Initial original import from: fuse-2.4.2-2.fc4
[captive.git] / src / client / fuse / fusermount.c
1 /*
2     FUSE: Filesystem in Userspace
3     Copyright (C) 2001-2005  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 /*
11  * NOTE: This program should be part of (or be called from) /bin/mount
12  *
13  * Unless that is done, operations on /etc/mtab are not under lock, and so
14  * data in this file may be lost. (I will _not_ reimplement that locking,
15  * and anyway that should be done in libc, if possible.  But probably it
16  * isn't).
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <pwd.h>
30 #include <mntent.h>
31 #include <dirent.h>
32 #include <sys/param.h>
33 #include <sys/wait.h>
34 #include <sys/stat.h>
35 #include <sys/mount.h>
36 #include <sys/fsuid.h>
37 #include <sys/socket.h>
38 #include <sys/un.h>
39 #include <sys/utsname.h>
40 #include <sys/sysmacros.h>
41
42 #define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
43
44 #define FUSE_DEV_OLD "/proc/fs/fuse/dev"
45 #define FUSE_DEV_NEW "/dev/fuse"
46 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
47 #define FUSE_CONF "/etc/fuse.conf"
48
49 static const char *progname;
50
51 static int user_allow_other = 0;
52 static int mount_max = 1000;
53
54 static const char *get_user_name(void)
55 {
56     struct passwd *pw = getpwuid(getuid());
57     if (pw != NULL && pw->pw_name != NULL)
58         return pw->pw_name;
59     else {
60         fprintf(stderr, "%s: could not determine username\n", progname);
61         return NULL;
62     }
63 }
64
65 static uid_t oldfsuid;
66 static gid_t oldfsgid;
67
68 static void drop_privs(void)
69 {
70     if (getuid() != 0) {
71         oldfsuid = setfsuid(getuid());
72         oldfsgid = setfsgid(getgid());
73     }
74 }
75
76 static void restore_privs(void)
77 {
78     if (getuid() != 0) {
79         setfsuid(oldfsuid);
80         setfsgid(oldfsgid);
81     }
82 }
83
84 static int do_unmount(const char *mnt, int quiet, int lazy)
85 {
86     int res = umount2(mnt, lazy ? 2 : 0);
87     if (res == -1) {
88         if (!quiet)
89             fprintf(stderr, "%s: failed to unmount %s: %s\n",
90                     progname, mnt, strerror(errno));
91     }
92     return res;
93 }
94
95 #ifndef IGNORE_MTAB
96 /* use a lock file so that multiple fusermount processes don't try and
97    modify the mtab file at once! */
98 static int lock_mtab(void)
99 {
100     const char *mtab_lock = _PATH_MOUNTED ".fuselock";
101     int mtablock;
102     int res;
103
104     mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
105     if (mtablock >= 0) {
106         res = lockf(mtablock, F_LOCK, 0);
107         if (res < 0)
108             fprintf(stderr, "%s: error getting lock", progname);
109     } else
110         fprintf(stderr, "%s: unable to open fuse lock file\n", progname);
111
112     return mtablock;
113 }
114
115 static void unlock_mtab(int mtablock)
116 {
117     if (mtablock >= 0) {
118         lockf(mtablock, F_ULOCK, 0);
119         close(mtablock);
120     }
121 }
122
123 /* Glibc addmntent() doesn't encode '\n', misencodes '\t' as '\n'
124    (version 2.3.2), and encodes '\\' differently as mount(8).  So
125    let's not allow those characters, they are not all that usual in
126    filenames. */
127 static int check_name(const char *name)
128 {
129     char *s;
130     for (s = "\n\t\\"; *s; s++) {
131         if (strchr(name, *s)) {
132             fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
133                     progname, *s);
134             return -1;
135         }
136     }
137     return 0;
138 }
139
140 static int add_mount(const char *fsname, const char *mnt, const char *type,
141                      const char *opts)
142 {
143     int res;
144     const char *mtab = _PATH_MOUNTED;
145     struct mntent ent;
146     FILE *fp;
147
148     if (check_name(fsname) == -1 || check_name(mnt) == -1 || 
149         check_name(type) == -1 || check_name(opts) == -1)
150         return -1;
151
152     fp = setmntent(mtab, "a");
153     if (fp == NULL) {
154         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
155                 strerror(errno));
156         return -1;
157     }
158
159     ent.mnt_fsname = (char *) fsname;
160     ent.mnt_dir = (char *) mnt;
161     ent.mnt_type = (char *) type;
162     ent.mnt_opts = (char *) opts;
163     ent.mnt_freq = 0;
164     ent.mnt_passno = 0;
165     res = addmntent(fp, &ent);
166     if (res != 0) {
167         fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
168                 mtab, strerror(errno));
169         return -1;
170     }
171
172     endmntent(fp);
173     return 0;
174 }
175
176 static int remove_mount(const char *mnt, int quiet, const char *mtab,
177                         const char *mtab_new)
178 {
179     int res;
180     struct mntent *entp;
181     FILE *fp;
182     FILE *newfp;
183     const char *user = NULL;
184     char uidstr[32];
185     unsigned uidlen = 0;
186     int found;
187
188     fp = setmntent(mtab, "r");
189     if (fp == NULL) {
190         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
191                 strerror(errno));
192         return -1;
193     }
194
195     newfp = setmntent(mtab_new, "w");
196     if (newfp == NULL) {
197         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab_new,
198                 strerror(errno));
199         return -1;
200     }
201
202     if (getuid() != 0) {
203         user = get_user_name();
204         if (user == NULL)
205             return -1;
206
207         uidlen = sprintf(uidstr, "%u", getuid());
208     }
209
210     found = 0;
211     while ((entp = getmntent(fp)) != NULL) {
212         int removed = 0;
213         if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
214            strcmp(entp->mnt_type, "fuse") == 0) {
215             if (user == NULL)
216                 removed = 1;
217             else {
218                 char *p = strstr(entp->mnt_opts, "user=");
219                 if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
220                     strcmp(p + 5, user) == 0)
221                     removed = 1;
222                 /* /etc/mtab is a link pointing to /proc/mounts: */
223                 else if ((p = strstr(entp->mnt_opts, "user_id=")) &&
224                          (p == entp->mnt_opts || *(p-1) == ',') &&
225                          strncmp(p + 8, uidstr, uidlen) == 0 &&
226                          (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0'))
227                     removed = 1;
228             }
229         }
230         if (removed)
231             found = 1;
232         else {
233             res = addmntent(newfp, entp);
234             if (res != 0) {
235                 fprintf(stderr, "%s: failed to add entry to %s: %s\n",
236                         progname, mtab_new, strerror(errno));
237             }
238         }
239     }
240
241     endmntent(fp);
242     endmntent(newfp);
243
244     if (!found) {
245         if (!quiet)
246             fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
247                     mnt, mtab);
248         unlink(mtab_new);
249         return -1;
250     }
251
252     return 0;
253 }
254
255 static int count_fuse_fs(void)
256 {
257     struct mntent *entp;
258     int count = 0;
259     const char *mtab = _PATH_MOUNTED;
260     FILE *fp = setmntent(mtab, "r");
261     if (fp == NULL) {
262         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
263                 strerror(errno));
264         return -1;
265     }
266     while ((entp = getmntent(fp)) != NULL) {
267         if (strcmp(entp->mnt_type, "fuse") == 0)
268             count ++;
269     }
270     endmntent(fp);
271     return count;
272 }
273
274 static int unmount_rename(const char *mnt, int quiet, int lazy,
275                           const char *mtab, const char *mtab_new)
276 {
277     int res;
278     struct stat sbuf;
279
280     drop_privs();
281     res = do_unmount(mnt, quiet, lazy);
282     restore_privs();
283     if (res == -1)
284         return -1;
285
286     if (stat(mtab, &sbuf) == 0)
287         chown(mtab_new, sbuf.st_uid, sbuf.st_gid);
288
289     res = rename(mtab_new, mtab);
290     if (res == -1) {
291         fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
292                 mtab_new, mtab, strerror(errno));
293         return -1;
294     }
295     return 0;
296 }
297
298 static int unmount_fuse(const char *mnt, int quiet, int lazy)
299 {
300     int res;
301     const char *mtab = _PATH_MOUNTED;
302     const char *mtab_new = _PATH_MOUNTED "~fuse~";
303
304     res = remove_mount(mnt, quiet, mtab, mtab_new);
305     if (res == -1)
306         return -1;
307
308     res = unmount_rename(mnt, quiet, lazy, mtab, mtab_new);
309     if (res == -1) {
310         unlink(mtab_new);
311         return -1;
312     }
313     return 0;
314 }
315 #else /* IGNORE_MTAB */
316 static int lock_mtab()
317 {
318     return 0;
319 }
320
321 static void unlock_mtab(int mtablock)
322 {
323     (void) mtablock;
324 }
325
326 static int count_fuse_fs()
327 {
328     return 0;
329 }
330
331 static int add_mount(const char *fsname, const char *mnt, const char *type,
332                      const char *opts)
333 {
334     (void) fsname;
335     (void) mnt;
336     (void) type;
337     (void) opts;
338     return 0;
339 }
340
341 static int unmount_fuse(const char *mnt, int quiet, int lazy)
342 {
343     return do_unmount(mnt, quiet, lazy);
344 }
345 #endif /* IGNORE_MTAB */
346
347 static void strip_line(char *line)
348 {
349     char *s = strchr(line, '#');
350     if (s != NULL)
351         s[0] = '\0';
352     for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
353     s[1] = '\0';
354     for (s = line; isspace((unsigned char) *s); s++);
355     if (s != line)
356         memmove(line, s, strlen(s)+1);
357 }
358
359 static void parse_line(char *line, int linenum)
360 {
361     int tmp;
362     if (strcmp(line, "user_allow_other") == 0)
363         user_allow_other = 1;
364     else if (sscanf(line, "mount_max = %i", &tmp) == 1)
365         mount_max = tmp;
366     else if(line[0])
367         fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
368                 progname, FUSE_CONF, linenum, line);
369 }
370
371 static void read_conf(void)
372 {
373     FILE *fp = fopen(FUSE_CONF, "r");
374     if (fp != NULL) {
375         int linenum = 1;
376         char line[256];
377         int isnewline = 1;
378         while (fgets(line, sizeof(line), fp) != NULL) {
379             if (isnewline) {
380                 if (line[strlen(line)-1] == '\n') {
381                     strip_line(line);
382                     parse_line(line, linenum);
383                 } else {
384                     fprintf(stderr, "%s: reading %s: line %i too long\n",
385                             progname, FUSE_CONF, linenum);
386                     isnewline = 0;
387                 }
388             } else if(line[strlen(line)-1] == '\n')
389                 isnewline = 1;
390             if (isnewline)
391                 linenum ++;
392         }
393         fclose(fp);
394     } else if (errno != ENOENT) {
395         fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
396                 strerror(errno));
397     }
398 }
399
400 static int begins_with(const char *s, const char *beg)
401 {
402     if (strncmp(s, beg, strlen(beg)) == 0)
403         return 1;
404     else
405         return 0;
406 }
407
408 struct mount_flags {
409     const char *opt;
410     unsigned long flag;
411     int on;
412     int safe;
413 };
414
415 static struct mount_flags mount_flags[] = {
416     {"rw",      MS_RDONLY,      0, 1},
417     {"ro",      MS_RDONLY,      1, 1},
418     {"suid",    MS_NOSUID,      0, 0},
419     {"nosuid",  MS_NOSUID,      1, 1},
420     {"dev",     MS_NODEV,       0, 0},
421     {"nodev",   MS_NODEV,       1, 1},
422     {"exec",    MS_NOEXEC,      0, 1},
423     {"noexec",  MS_NOEXEC,      1, 1},
424     {"async",   MS_SYNCHRONOUS, 0, 1},
425     {"sync",    MS_SYNCHRONOUS, 1, 1},
426     {"atime",   MS_NOATIME,     0, 1},
427     {"noatime", MS_NOATIME,     1, 1},
428     {NULL,      0,              0, 0}
429 };
430
431 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
432 {
433     int i;
434
435     for (i = 0; mount_flags[i].opt != NULL; i++) {
436         const char *opt = mount_flags[i].opt;
437         if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
438             *on = mount_flags[i].on;
439             *flag = mount_flags[i].flag;
440             if (!mount_flags[i].safe && getuid() != 0) {
441                 *flag = 0;
442                 fprintf(stderr, "%s: unsafe option %s ignored\n",
443                         progname, opt);
444             }
445             return 1;
446         }
447     }
448     return 0;
449 }
450
451 static int add_option(char **optsp, const char *opt, unsigned expand)
452 {
453     char *newopts;
454     if (*optsp == NULL)
455         newopts = strdup(opt);
456     else {
457         unsigned oldsize = strlen(*optsp);
458         unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
459         newopts = (char *) realloc(*optsp, newsize);
460         if (newopts)
461             sprintf(newopts + oldsize, ",%s", opt);
462     }
463     if (newopts == NULL) {
464         fprintf(stderr, "%s: failed to allocate memory\n", progname);
465         return -1;
466     }
467     *optsp = newopts;
468     return 0;
469 }
470
471 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
472 {
473     int i;
474     int l;
475
476     if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
477         return -1;
478
479     for (i = 0; mount_flags[i].opt != NULL; i++) {
480         if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
481             add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
482             return -1;
483     }
484
485     if (add_option(mnt_optsp, opts, 0) == -1)
486         return -1;
487     /* remove comma from end of opts*/
488     l = strlen(*mnt_optsp);
489     if ((*mnt_optsp)[l-1] == ',')
490         (*mnt_optsp)[l-1] = '\0';
491     if (getuid() != 0) {
492         const char *user = get_user_name();
493         if (user == NULL)
494             return -1;
495
496         if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
497             return -1;
498         strcat(*mnt_optsp, user);
499     }
500     return 0;
501 }
502
503 static int opt_eq(const char *s, unsigned len, const char *opt)
504 {
505     if(strlen(opt) == len && strncmp(s, opt, len) == 0)
506         return 1;
507     else
508         return 0;
509 }
510
511 static int check_mountpoint_empty(const char *mnt, mode_t rootmode,
512                                   off_t rootsize)
513 {
514     int isempty = 1;
515
516     if (S_ISDIR(rootmode)) {
517         struct dirent *ent;
518         DIR *dp = opendir(mnt);
519         if (dp == NULL) {
520             fprintf(stderr, "%s: failed to mountpoint for reading: %s\n",
521                     progname, strerror(errno));
522             return -1;
523         }
524         while ((ent = readdir(dp)) != NULL) {
525             if (strcmp(ent->d_name, ".") != 0 &&
526                 strcmp(ent->d_name, "..") != 0) {
527                 isempty = 0;
528                 break;
529             }
530         }
531         closedir(dp);
532     } else if (rootsize)
533         isempty = 0;
534
535     if (!isempty) {
536         fprintf(stderr, "%s: mountpoint is not empty\n", progname);
537         fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
538         return -1;
539     }
540     return 0;
541 }
542
543 static int do_mount(const char *mnt, const char *type, mode_t rootmode,
544                     int fd, const char *opts, const char *dev, char **fsnamep,
545                     char **mnt_optsp, off_t rootsize)
546 {
547     int res;
548     int flags = MS_NOSUID | MS_NODEV;
549     char *optbuf;
550     char *mnt_opts = NULL;
551     const char *s;
552     char *d;
553     char *fsname = NULL;
554     int check_empty = 1;
555
556     optbuf = (char *) malloc(strlen(opts) + 128);
557     if (!optbuf) {
558         fprintf(stderr, "%s: failed to allocate memory\n", progname);
559         return -1;
560     }
561
562     for (s = opts, d = optbuf; *s;) {
563         unsigned len;
564         const char *fsname_str = "fsname=";
565         for (len = 0; s[len] && s[len] != ','; len++);
566         if (begins_with(s, fsname_str)) {
567             unsigned fsname_str_len = strlen(fsname_str);
568             if (fsname)
569                 free(fsname);
570             fsname = (char *) malloc(len - fsname_str_len + 1);
571             if (!fsname) {
572                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
573                 goto err;
574             }
575             memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
576             fsname[len - fsname_str_len] = '\0';
577         } else if (opt_eq(s, len, "nonempty")) {
578             check_empty = 0;
579         } else if (!begins_with(s, "fd=") &&
580                    !begins_with(s, "rootmode=") &&
581                    !begins_with(s, "user_id=") &&
582                    !begins_with(s, "group_id=")) {
583             int on;
584             int flag;
585             int skip_option = 0;
586             if (opt_eq(s, len, "large_read")) {
587                 struct utsname utsname;
588                 unsigned kmaj, kmin;
589                 res = uname(&utsname);
590                 if (res == 0 &&
591                     sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
592                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
593                     fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
594                     skip_option = 1;
595                 }
596             }
597             if (getuid() != 0 && !user_allow_other &&
598                 (opt_eq(s, len, "allow_other") ||
599                  opt_eq(s, len, "allow_root"))) {
600                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
601                 goto err;
602             }
603             if (!skip_option) {
604                 if (find_mount_flag(s, len, &on, &flag)) {
605                     if (on)
606                         flags |= flag;
607                     else
608                         flags  &= ~flag;
609                 } else {
610                     memcpy(d, s, len);
611                     d += len;
612                     *d++ = ',';
613                 }
614             }
615         }
616         s += len;
617         if (*s)
618             s++;
619     }
620     *d = '\0';
621     res = get_mnt_opts(flags, optbuf, &mnt_opts);
622     if (res == -1)
623         goto err;
624
625     sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
626             fd, rootmode, getuid(), getgid());
627     if (fsname == NULL) {
628         fsname = strdup(dev);
629         if (!fsname) {
630             fprintf(stderr, "%s: failed to allocate memory\n", progname);
631             goto err;
632         }
633     }
634
635     if (check_empty && check_mountpoint_empty(mnt, rootmode, rootsize) == -1)
636         goto err;
637
638     res = mount(fsname, mnt, type, flags, optbuf);
639     if (res == -1 && errno == EINVAL) {
640         /* It could be an old version not supporting group_id */
641         sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
642         res = mount(fsname, mnt, type, flags, optbuf);
643     }
644     if (res == -1) {
645         fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
646         goto err;
647     } else {
648         *fsnamep = fsname;
649         *mnt_optsp = mnt_opts;
650     }
651     free(optbuf);
652
653     return res;
654
655  err:
656     free(fsname);
657     free(mnt_opts);
658     free(optbuf);
659     return -1;
660 }
661
662 static int check_version(const char *dev)
663 {
664     int res;
665     int majorver;
666     int minorver;
667     const char *version_file;
668     FILE *vf;
669
670     if (strcmp(dev, FUSE_DEV_OLD) != 0)
671         return 0;
672
673     version_file = FUSE_VERSION_FILE_OLD;
674     vf = fopen(version_file, "r");
675     if (vf == NULL) {
676         fprintf(stderr, "%s: kernel interface too old\n", progname);
677         return -1;
678     }
679     res = fscanf(vf, "%i.%i", &majorver, &minorver);
680     fclose(vf);
681     if (res != 2) {
682         fprintf(stderr, "%s: error reading %s\n", progname, version_file);
683         return -1;
684     }
685      if (majorver < 3) {
686         fprintf(stderr, "%s: kernel interface too old\n", progname);
687         return -1;
688     }
689     return 0;
690 }
691
692 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
693                       int *mountpoint_fd)
694 {
695     int res;
696     const char *mnt = *mntp;
697     const char *origmnt = mnt;
698
699     res = lstat(mnt, stbuf);
700     if (res == -1) {
701         fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
702                 progname, mnt, strerror(errno));
703         return -1;
704     }
705
706     /* No permission checking is done for root */
707     if (getuid() == 0)
708         return 0;
709
710     if (S_ISDIR(stbuf->st_mode)) {
711         *currdir_fd = open(".", O_RDONLY);
712         if (*currdir_fd == -1) {
713             fprintf(stderr, "%s: failed to open current directory: %s\n",
714                     progname, strerror(errno));
715             return -1;
716         }
717         res = chdir(mnt);
718         if (res == -1) {
719             fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
720                     progname, strerror(errno));
721             return -1;
722         }
723         mnt = *mntp = ".";
724         res = lstat(mnt, stbuf);
725         if (res == -1) {
726             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
727                     progname, origmnt, strerror(errno));
728             return -1;
729         }
730
731         if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
732             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
733                     progname, origmnt);
734             return -1;
735         }
736
737         res = access(mnt, W_OK);
738         if (res == -1) {
739             fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
740                     progname, origmnt);
741             return -1;
742         }
743     } else if (S_ISREG(stbuf->st_mode)) {
744         static char procfile[256];
745         *mountpoint_fd = open(mnt, O_WRONLY);
746         if (*mountpoint_fd == -1) {
747             fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
748                     strerror(errno));
749             return -1;
750         }
751         res = fstat(*mountpoint_fd, stbuf);
752         if (res == -1) {
753             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
754                     progname, mnt, strerror(errno));
755             return -1;
756         }
757         if (!S_ISREG(stbuf->st_mode)) {
758             fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
759                     progname, mnt);
760             return -1;
761         }
762
763         sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
764         *mntp = procfile;
765     } else {
766         fprintf(stderr,
767                 "%s: mountpoint %s is not a directory or a regular file\n",
768                 progname, mnt);
769         return -1;
770     }
771
772
773     return 0;
774 }
775
776 static int try_open(const char *dev, char **devp, int silent)
777 {
778     int fd = open(dev, O_RDWR);
779     if (fd != -1) {
780         *devp = strdup(dev);
781         if (*devp == NULL) {
782             fprintf(stderr, "%s: failed to allocate memory\n", progname);
783             close(fd);
784             fd = -1;
785         }
786     } else if (errno == ENODEV)
787         return -2;
788     else if (!silent) {
789         fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
790                 strerror(errno));
791     }
792     return fd;
793 }
794
795 static int try_open_fuse_device(char **devp)
796 {
797     int fd;
798     int err;
799
800     drop_privs();
801     fd = try_open(FUSE_DEV_NEW, devp, 0);
802     restore_privs();
803     if (fd >= 0)
804         return fd;
805
806     err = fd;
807     fd = try_open(FUSE_DEV_OLD, devp, 1);
808     if (fd >= 0)
809         return fd;
810
811     return err;
812 }
813
814 static int open_fuse_device(char **devp)
815 {
816     int fd = try_open_fuse_device(devp);
817     if (fd >= 0)
818         return fd;
819
820     if (fd == -2)
821         fprintf(stderr,
822                 "%s: fuse device not found, try 'modprobe fuse' first\n",
823                 progname);
824     return -1;
825 }
826
827
828 static int mount_fuse(const char *mnt, const char *opts)
829 {
830     int res;
831     int fd;
832     char *dev;
833     const char *type = "fuse";
834     struct stat stbuf;
835     char *fsname = NULL;
836     char *mnt_opts = NULL;
837     const char *real_mnt = mnt;
838     int currdir_fd = -1;
839     int mountpoint_fd = -1;
840     int mtablock = -1;
841
842     fd = open_fuse_device(&dev);
843     if (fd == -1)
844         return -1;
845
846     if (geteuid() == 0) {
847         mtablock = lock_mtab();
848         if (mtablock < 0) {
849             close(fd);
850             return -1;
851         }
852     }
853
854     drop_privs();
855     read_conf();
856
857     if (getuid() != 0 && mount_max != -1) {
858         int mount_count = count_fuse_fs();
859         if (mount_count >= mount_max) {
860             fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
861             close(fd);
862             unlock_mtab(mtablock);
863             return -1;
864         }
865     }
866
867     res = check_version(dev);
868     if (res != -1) {
869         res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
870         restore_privs();
871         if (res != -1)
872             res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
873                            dev, &fsname, &mnt_opts, stbuf.st_size);
874     } else
875         restore_privs();
876
877     if (currdir_fd != -1) {
878         fchdir(currdir_fd);
879         close(currdir_fd);
880     }
881     if (mountpoint_fd != -1)
882         close(mountpoint_fd);
883
884     if (res == -1) {
885         close(fd);
886         unlock_mtab(mtablock);
887         return -1;
888     }
889
890     if (geteuid() == 0) {
891         res = add_mount(fsname, mnt, type, mnt_opts);
892         unlock_mtab(mtablock);
893         if (res == -1) {
894             umount2(mnt, 2); /* lazy umount */
895             close(fd);
896             return -1;
897         }
898     }
899
900     free(fsname);
901     free(mnt_opts);
902     free(dev);
903
904     return fd;
905 }
906
907 static char *resolve_path(const char *orig)
908 {
909     char buf[PATH_MAX];
910     char *copy;
911     char *dst;
912     char *end;
913     char *lastcomp;
914     const char *toresolv;
915
916     if (!orig[0]) {
917         fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
918         return NULL;
919     }
920
921     copy = strdup(orig);
922     if (copy == NULL) {
923         fprintf(stderr, "%s: failed to allocate memory\n", progname);
924         return NULL;
925     }
926
927     toresolv = copy;
928     lastcomp = NULL;
929     for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
930     if (end[0] != '/') {
931         char *tmp;
932         end[1] = '\0';
933         tmp = strrchr(copy, '/');
934         if (tmp == NULL) {
935             lastcomp = copy;
936             toresolv = ".";
937         } else {
938             lastcomp = tmp + 1;
939             if (tmp == copy)
940                 toresolv = "/";
941         }
942         if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
943             lastcomp = NULL;
944             toresolv = copy;
945         }
946         else if (tmp)
947             tmp[0] = '\0';
948     }
949     if (realpath(toresolv, buf) == NULL) {
950         fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
951                 strerror(errno));
952         free(copy);
953         return NULL;
954     }
955     if (lastcomp == NULL)
956         dst = strdup(buf);
957     else {
958         dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
959         if (dst) {
960             unsigned buflen = strlen(buf);
961             if (buflen && buf[buflen-1] == '/')
962                 sprintf(dst, "%s%s", buf, lastcomp);
963             else
964                 sprintf(dst, "%s/%s", buf, lastcomp);
965         }
966     }
967     free(copy);
968     if (dst == NULL)
969         fprintf(stderr, "%s: failed to allocate memory\n", progname);
970     return dst;
971 }
972
973 static int send_fd(int sock_fd, int fd)
974 {
975     int retval;
976     struct msghdr msg;
977     struct cmsghdr *p_cmsg;
978     struct iovec vec;
979     char cmsgbuf[CMSG_SPACE(sizeof(fd))];
980     int *p_fds;
981     char sendchar = 0;
982
983     msg.msg_control = cmsgbuf;
984     msg.msg_controllen = sizeof(cmsgbuf);
985     p_cmsg = CMSG_FIRSTHDR(&msg);
986     p_cmsg->cmsg_level = SOL_SOCKET;
987     p_cmsg->cmsg_type = SCM_RIGHTS;
988     p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
989     p_fds = (int *) CMSG_DATA(p_cmsg);
990     *p_fds = fd;
991     msg.msg_controllen = p_cmsg->cmsg_len;
992     msg.msg_name = NULL;
993     msg.msg_namelen = 0;
994     msg.msg_iov = &vec;
995     msg.msg_iovlen = 1;
996     msg.msg_flags = 0;
997     /* "To pass file descriptors or credentials you need to send/read at
998      * least one byte" (man 7 unix) */
999     vec.iov_base = &sendchar;
1000     vec.iov_len = sizeof(sendchar);
1001     while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1002     if (retval != 1) {
1003         perror("sending file descriptor");
1004         return -1;
1005     }
1006     return 0;
1007 }
1008
1009 static void usage(void)
1010 {
1011     fprintf(stderr,
1012             "%s: [options] mountpoint\n"
1013             "Options:\n"
1014             " -h                print help\n"
1015             " -v                print version\n"
1016             " -o opt[,opt...]   mount options\n"
1017             " -u                unmount\n"
1018             " -q                quiet\n"
1019             " -z                lazy unmount\n",
1020             progname);
1021     exit(1);
1022 }
1023
1024 static void show_version(void)
1025 {
1026     printf("%s\n", PACKAGE_STRING);
1027     exit(0);
1028 }
1029
1030 int main(int argc, char *argv[])
1031 {
1032     int ch;
1033     int fd;
1034     int res;
1035     char *origmnt;
1036     char *mnt;
1037     static int unmount = 0;
1038     static int lazy = 0;
1039     static int quiet = 0;
1040     char *commfd;
1041     int cfd;
1042     const char *opts = "";
1043
1044     static const struct option long_opts[] = {
1045         {"unmount", no_argument, NULL, 'u'},
1046         {"lazy",    no_argument, NULL, 'z'},
1047         {"quiet",   no_argument, NULL, 'q'},
1048         {"help",    no_argument, NULL, 'h'},
1049         {"version", no_argument, NULL, 'v'},
1050         {0, 0, 0, 0}};
1051
1052     progname = strdup(argv[0]);
1053     if (progname == NULL) {
1054         fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1055         exit(1);
1056     }
1057
1058     while ((ch = getopt_long(argc, argv, "hvo:uzq", long_opts, NULL)) != -1) {
1059         switch (ch) {
1060         case 'h':
1061             usage();
1062             break;
1063
1064         case 'v':
1065             show_version();
1066             break;
1067
1068         case 'o':
1069             opts = optarg;
1070             break;
1071
1072         case 'u':
1073             unmount = 1;
1074             break;
1075
1076         case 'z':
1077             lazy = 1;
1078             break;
1079
1080         case 'q':
1081             quiet = 1;
1082             break;
1083
1084         default:
1085             exit(1);
1086         }
1087     }
1088
1089     if (lazy && !unmount) {
1090         fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1091         exit(1);
1092     }
1093
1094     if (optind >= argc) {
1095         fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1096         exit(1);
1097     }
1098
1099     origmnt = argv[optind];
1100
1101     drop_privs();
1102     mnt = resolve_path(origmnt);
1103     restore_privs();
1104     if (mnt == NULL)
1105         exit(1);
1106
1107     umask(033);
1108     if (unmount) {
1109         if (geteuid() == 0) {
1110             int mtablock = lock_mtab();
1111             res = unmount_fuse(mnt, quiet, lazy);
1112             unlock_mtab(mtablock);
1113         } else
1114             res = do_unmount(mnt, quiet, lazy);
1115         if (res == -1)
1116             exit(1);
1117         return 0;
1118     }
1119
1120     commfd = getenv(FUSE_COMMFD_ENV);
1121     if (commfd == NULL) {
1122         fprintf(stderr, "%s: old style mounting not supported\n", progname);
1123         exit(1);
1124     }
1125
1126     fd = mount_fuse(mnt, opts);
1127     if (fd == -1)
1128         exit(1);
1129
1130     cfd = atoi(commfd);
1131     res = send_fd(cfd, fd);
1132     if (res == -1)
1133         exit(1);
1134
1135     return 0;
1136 }