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