Workarounded: warning: ignoring return value of ‘...’, declared with attribute warn_u...
[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         int trash0=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         int trash0=chown(mtab_new, sbuf.st_uid, sbuf.st_gid);
294                                 }
295
296     res = rename(mtab_new, mtab);
297     if (res == -1) {
298         fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
299                 mtab_new, mtab, strerror(errno));
300         return -1;
301     }
302     return 0;
303 }
304
305 static int unmount_fuse(const char *mnt, int quiet, int lazy)
306 {
307     int res;
308     const char *mtab = _PATH_MOUNTED;
309     const char *mtab_new = _PATH_MOUNTED "~fuse~";
310
311     res = remove_mount(mnt, quiet, mtab, mtab_new);
312     if (res == -1)
313         return -1;
314
315     res = unmount_rename(mnt, quiet, lazy, mtab, mtab_new);
316     if (res == -1) {
317         unlink(mtab_new);
318         return -1;
319     }
320     return 0;
321 }
322 #else /* IGNORE_MTAB */
323 static int lock_mtab()
324 {
325     return 0;
326 }
327
328 static void unlock_mtab(int mtablock)
329 {
330     (void) mtablock;
331 }
332
333 static int count_fuse_fs()
334 {
335     return 0;
336 }
337
338 static int add_mount(const char *fsname, const char *mnt, const char *type,
339                      const char *opts)
340 {
341     (void) fsname;
342     (void) mnt;
343     (void) type;
344     (void) opts;
345     return 0;
346 }
347
348 static int unmount_fuse(const char *mnt, int quiet, int lazy)
349 {
350     return do_unmount(mnt, quiet, lazy);
351 }
352 #endif /* IGNORE_MTAB */
353
354 static void strip_line(char *line)
355 {
356     char *s = strchr(line, '#');
357     if (s != NULL)
358         s[0] = '\0';
359     for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
360     s[1] = '\0';
361     for (s = line; isspace((unsigned char) *s); s++);
362     if (s != line)
363         memmove(line, s, strlen(s)+1);
364 }
365
366 static void parse_line(char *line, int linenum)
367 {
368     int tmp;
369     if (strcmp(line, "user_allow_other") == 0)
370         user_allow_other = 1;
371     else if (sscanf(line, "mount_max = %i", &tmp) == 1)
372         mount_max = tmp;
373     else if(line[0])
374         fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
375                 progname, FUSE_CONF, linenum, line);
376 }
377
378 static void read_conf(void)
379 {
380     FILE *fp = fopen(FUSE_CONF, "r");
381     if (fp != NULL) {
382         int linenum = 1;
383         char line[256];
384         int isnewline = 1;
385         while (fgets(line, sizeof(line), fp) != NULL) {
386             if (isnewline) {
387                 if (line[strlen(line)-1] == '\n') {
388                     strip_line(line);
389                     parse_line(line, linenum);
390                 } else {
391                     fprintf(stderr, "%s: reading %s: line %i too long\n",
392                             progname, FUSE_CONF, linenum);
393                     isnewline = 0;
394                 }
395             } else if(line[strlen(line)-1] == '\n')
396                 isnewline = 1;
397             if (isnewline)
398                 linenum ++;
399         }
400         fclose(fp);
401     } else if (errno != ENOENT) {
402         fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
403                 strerror(errno));
404     }
405 }
406
407 static int begins_with(const char *s, const char *beg)
408 {
409     if (strncmp(s, beg, strlen(beg)) == 0)
410         return 1;
411     else
412         return 0;
413 }
414
415 struct mount_flags {
416     const char *opt;
417     unsigned long flag;
418     int on;
419     int safe;
420 };
421
422 static struct mount_flags mount_flags[] = {
423     {"rw",      MS_RDONLY,      0, 1},
424     {"ro",      MS_RDONLY,      1, 1},
425     {"suid",    MS_NOSUID,      0, 0},
426     {"nosuid",  MS_NOSUID,      1, 1},
427     {"dev",     MS_NODEV,       0, 0},
428     {"nodev",   MS_NODEV,       1, 1},
429     {"exec",    MS_NOEXEC,      0, 1},
430     {"noexec",  MS_NOEXEC,      1, 1},
431     {"async",   MS_SYNCHRONOUS, 0, 1},
432     {"sync",    MS_SYNCHRONOUS, 1, 1},
433     {"atime",   MS_NOATIME,     0, 1},
434     {"noatime", MS_NOATIME,     1, 1},
435     {NULL,      0,              0, 0}
436 };
437
438 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
439 {
440     int i;
441
442     for (i = 0; mount_flags[i].opt != NULL; i++) {
443         const char *opt = mount_flags[i].opt;
444         if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
445             *on = mount_flags[i].on;
446             *flag = mount_flags[i].flag;
447             if (!mount_flags[i].safe && getuid() != 0) {
448                 *flag = 0;
449                 fprintf(stderr, "%s: unsafe option %s ignored\n",
450                         progname, opt);
451             }
452             return 1;
453         }
454     }
455     return 0;
456 }
457
458 static int add_option(char **optsp, const char *opt, unsigned expand)
459 {
460     char *newopts;
461     if (*optsp == NULL)
462         newopts = strdup(opt);
463     else {
464         unsigned oldsize = strlen(*optsp);
465         unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
466         newopts = (char *) realloc(*optsp, newsize);
467         if (newopts)
468             sprintf(newopts + oldsize, ",%s", opt);
469     }
470     if (newopts == NULL) {
471         fprintf(stderr, "%s: failed to allocate memory\n", progname);
472         return -1;
473     }
474     *optsp = newopts;
475     return 0;
476 }
477
478 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
479 {
480     int i;
481     int l;
482
483     if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
484         return -1;
485
486     for (i = 0; mount_flags[i].opt != NULL; i++) {
487         if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
488             add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
489             return -1;
490     }
491
492     if (add_option(mnt_optsp, opts, 0) == -1)
493         return -1;
494     /* remove comma from end of opts*/
495     l = strlen(*mnt_optsp);
496     if ((*mnt_optsp)[l-1] == ',')
497         (*mnt_optsp)[l-1] = '\0';
498     if (getuid() != 0) {
499         const char *user = get_user_name();
500         if (user == NULL)
501             return -1;
502
503         if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
504             return -1;
505         strcat(*mnt_optsp, user);
506     }
507     return 0;
508 }
509
510 static int opt_eq(const char *s, unsigned len, const char *opt)
511 {
512     if(strlen(opt) == len && strncmp(s, opt, len) == 0)
513         return 1;
514     else
515         return 0;
516 }
517
518 static int check_mountpoint_empty(const char *mnt, mode_t rootmode,
519                                   off_t rootsize)
520 {
521     int isempty = 1;
522
523     if (S_ISDIR(rootmode)) {
524         struct dirent *ent;
525         DIR *dp = opendir(mnt);
526         if (dp == NULL) {
527             fprintf(stderr, "%s: failed to mountpoint for reading: %s\n",
528                     progname, strerror(errno));
529             return -1;
530         }
531         while ((ent = readdir(dp)) != NULL) {
532             if (strcmp(ent->d_name, ".") != 0 &&
533                 strcmp(ent->d_name, "..") != 0) {
534                 isempty = 0;
535                 break;
536             }
537         }
538         closedir(dp);
539     } else if (rootsize)
540         isempty = 0;
541
542     if (!isempty) {
543         fprintf(stderr, "%s: mountpoint is not empty\n", progname);
544         fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
545         return -1;
546     }
547     return 0;
548 }
549
550 static int do_mount(const char *mnt, const char *type, mode_t rootmode,
551                     int fd, const char *opts, const char *dev, char **fsnamep,
552                     char **mnt_optsp, off_t rootsize)
553 {
554     int res;
555     int flags = MS_NOSUID | MS_NODEV;
556     char *optbuf;
557     char *mnt_opts = NULL;
558     const char *s;
559     char *d;
560     char *fsname = NULL;
561     int check_empty = 1;
562
563     optbuf = (char *) malloc(strlen(opts) + 128);
564     if (!optbuf) {
565         fprintf(stderr, "%s: failed to allocate memory\n", progname);
566         return -1;
567     }
568
569     for (s = opts, d = optbuf; *s;) {
570         unsigned len;
571         const char *fsname_str = "fsname=";
572         for (len = 0; s[len] && s[len] != ','; len++);
573         if (begins_with(s, fsname_str)) {
574             unsigned fsname_str_len = strlen(fsname_str);
575             if (fsname)
576                 free(fsname);
577             fsname = (char *) malloc(len - fsname_str_len + 1);
578             if (!fsname) {
579                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
580                 goto err;
581             }
582             memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
583             fsname[len - fsname_str_len] = '\0';
584         } else if (opt_eq(s, len, "nonempty")) {
585             check_empty = 0;
586         } else if (!begins_with(s, "fd=") &&
587                    !begins_with(s, "rootmode=") &&
588                    !begins_with(s, "user_id=") &&
589                    !begins_with(s, "group_id=")) {
590             int on;
591             int flag;
592             int skip_option = 0;
593             if (opt_eq(s, len, "large_read")) {
594                 struct utsname utsname;
595                 unsigned kmaj, kmin;
596                 res = uname(&utsname);
597                 if (res == 0 &&
598                     sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
599                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
600                     fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
601                     skip_option = 1;
602                 }
603             }
604             if (getuid() != 0 && !user_allow_other &&
605                 (opt_eq(s, len, "allow_other") ||
606                  opt_eq(s, len, "allow_root"))) {
607                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
608                 goto err;
609             }
610             if (!skip_option) {
611                 if (find_mount_flag(s, len, &on, &flag)) {
612                     if (on)
613                         flags |= flag;
614                     else
615                         flags  &= ~flag;
616                 } else {
617                     memcpy(d, s, len);
618                     d += len;
619                     *d++ = ',';
620                 }
621             }
622         }
623         s += len;
624         if (*s)
625             s++;
626     }
627     *d = '\0';
628     res = get_mnt_opts(flags, optbuf, &mnt_opts);
629     if (res == -1)
630         goto err;
631
632     sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
633             fd, rootmode, getuid(), getgid());
634     if (fsname == NULL) {
635         fsname = strdup(dev);
636         if (!fsname) {
637             fprintf(stderr, "%s: failed to allocate memory\n", progname);
638             goto err;
639         }
640     }
641
642     if (check_empty && check_mountpoint_empty(mnt, rootmode, rootsize) == -1)
643         goto err;
644
645     res = mount(fsname, mnt, type, flags, optbuf);
646     if (res == -1 && errno == EINVAL) {
647         /* It could be an old version not supporting group_id */
648         sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
649         res = mount(fsname, mnt, type, flags, optbuf);
650     }
651     if (res == -1) {
652         fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
653         goto err;
654     } else {
655         *fsnamep = fsname;
656         *mnt_optsp = mnt_opts;
657     }
658     free(optbuf);
659
660     return res;
661
662  err:
663     free(fsname);
664     free(mnt_opts);
665     free(optbuf);
666     return -1;
667 }
668
669 static int check_version(const char *dev)
670 {
671     int res;
672     int majorver;
673     int minorver;
674     const char *version_file;
675     FILE *vf;
676
677     if (strcmp(dev, FUSE_DEV_OLD) != 0)
678         return 0;
679
680     version_file = FUSE_VERSION_FILE_OLD;
681     vf = fopen(version_file, "r");
682     if (vf == NULL) {
683         fprintf(stderr, "%s: kernel interface too old\n", progname);
684         return -1;
685     }
686     res = fscanf(vf, "%i.%i", &majorver, &minorver);
687     fclose(vf);
688     if (res != 2) {
689         fprintf(stderr, "%s: error reading %s\n", progname, version_file);
690         return -1;
691     }
692      if (majorver < 3) {
693         fprintf(stderr, "%s: kernel interface too old\n", progname);
694         return -1;
695     }
696     return 0;
697 }
698
699 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
700                       int *mountpoint_fd)
701 {
702     int res;
703     const char *mnt = *mntp;
704     const char *origmnt = mnt;
705
706     res = lstat(mnt, stbuf);
707     if (res == -1) {
708         fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
709                 progname, mnt, strerror(errno));
710         return -1;
711     }
712
713     /* No permission checking is done for root */
714     if (getuid() == 0)
715         return 0;
716
717     if (S_ISDIR(stbuf->st_mode)) {
718         *currdir_fd = open(".", O_RDONLY);
719         if (*currdir_fd == -1) {
720             fprintf(stderr, "%s: failed to open current directory: %s\n",
721                     progname, strerror(errno));
722             return -1;
723         }
724         res = chdir(mnt);
725         if (res == -1) {
726             fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
727                     progname, strerror(errno));
728             return -1;
729         }
730         mnt = *mntp = ".";
731         res = lstat(mnt, stbuf);
732         if (res == -1) {
733             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
734                     progname, origmnt, strerror(errno));
735             return -1;
736         }
737
738         if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
739             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
740                     progname, origmnt);
741             return -1;
742         }
743
744         res = access(mnt, W_OK);
745         if (res == -1) {
746             fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
747                     progname, origmnt);
748             return -1;
749         }
750     } else if (S_ISREG(stbuf->st_mode)) {
751         static char procfile[256];
752         *mountpoint_fd = open(mnt, O_WRONLY);
753         if (*mountpoint_fd == -1) {
754             fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
755                     strerror(errno));
756             return -1;
757         }
758         res = fstat(*mountpoint_fd, stbuf);
759         if (res == -1) {
760             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
761                     progname, mnt, strerror(errno));
762             return -1;
763         }
764         if (!S_ISREG(stbuf->st_mode)) {
765             fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
766                     progname, mnt);
767             return -1;
768         }
769
770         sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
771         *mntp = procfile;
772     } else {
773         fprintf(stderr,
774                 "%s: mountpoint %s is not a directory or a regular file\n",
775                 progname, mnt);
776         return -1;
777     }
778
779
780     return 0;
781 }
782
783 static int try_open(const char *dev, char **devp, int silent)
784 {
785     int fd = open(dev, O_RDWR);
786     /* Captive */
787     if (fd == -1 && errno == ENOENT && !strcmp(dev, FUSE_DEV_NEW)
788         && !mknod(dev, 0660 | S_IFCHR, MKDEV(MISC_MAJOR, 229))) {
789         struct group *group;
790
791         fprintf(stderr, "%s: Notice: Created FUSE device: %s\n", progname, dev);
792         if ((group = getgrnam("fuse")) && !chown(dev, 0, group->gr_gid))
793             fd = open(dev, O_RDWR);
794     }
795     if (fd != -1) {
796         *devp = strdup(dev);
797         if (*devp == NULL) {
798             fprintf(stderr, "%s: failed to allocate memory\n", progname);
799             close(fd);
800             fd = -1;
801         }
802     } else if (errno == ENODEV)
803         return -2;
804     else if (!silent) {
805         fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
806                 strerror(errno));
807     }
808     return fd;
809 }
810
811 static int try_open_fuse_device(char **devp)
812 {
813     int fd;
814     int err;
815
816     drop_privs();
817     fd = try_open(FUSE_DEV_NEW, devp, 0);
818     restore_privs();
819     if (fd >= 0)
820         return fd;
821
822     err = fd;
823     fd = try_open(FUSE_DEV_OLD, devp, 1);
824     if (fd >= 0)
825         return fd;
826
827     return err;
828 }
829
830 static int open_fuse_device(char **devp)
831 {
832     int fd = try_open_fuse_device(devp);
833     if (fd >= 0)
834         return fd;
835
836     if (fd == -2)
837         fprintf(stderr,
838                 "%s: fuse device not found, try 'modprobe fuse' first\n",
839                 progname);
840     return -1;
841 }
842
843
844 static int mount_fuse(const char *mnt, const char *opts)
845 {
846     int res;
847     int fd;
848     char *dev;
849     const char *type = "fuse";
850     struct stat stbuf;
851     char *fsname = NULL;
852     char *mnt_opts = NULL;
853     const char *real_mnt = mnt;
854     int currdir_fd = -1;
855     int mountpoint_fd = -1;
856     int mtablock = -1;
857
858     fd = open_fuse_device(&dev);
859     if (fd == -1)
860         return -1;
861
862     if (geteuid() == 0) {
863         mtablock = lock_mtab();
864         if (mtablock < 0) {
865             close(fd);
866             return -1;
867         }
868     }
869
870     drop_privs();
871     read_conf();
872
873     if (getuid() != 0 && mount_max != -1) {
874         int mount_count = count_fuse_fs();
875         if (mount_count >= mount_max) {
876             fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
877             close(fd);
878             unlock_mtab(mtablock);
879             return -1;
880         }
881     }
882
883     res = check_version(dev);
884     if (res != -1) {
885         res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
886         restore_privs();
887         if (res != -1)
888             res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
889                            dev, &fsname, &mnt_opts, stbuf.st_size);
890     } else
891         restore_privs();
892
893     if (currdir_fd != -1) {
894         int trash0=fchdir(currdir_fd);
895         close(currdir_fd);
896     }
897     if (mountpoint_fd != -1)
898         close(mountpoint_fd);
899
900     if (res == -1) {
901         close(fd);
902         unlock_mtab(mtablock);
903         return -1;
904     }
905
906     if (geteuid() == 0) {
907         res = add_mount(fsname, mnt, type, mnt_opts);
908         unlock_mtab(mtablock);
909         if (res == -1) {
910             umount2(mnt, 2); /* lazy umount */
911             close(fd);
912             return -1;
913         }
914     }
915
916     free(fsname);
917     free(mnt_opts);
918     free(dev);
919
920     return fd;
921 }
922
923 static char *resolve_path(const char *orig)
924 {
925     char buf[PATH_MAX];
926     char *copy;
927     char *dst;
928     char *end;
929     char *lastcomp;
930     const char *toresolv;
931
932     if (!orig[0]) {
933         fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
934         return NULL;
935     }
936
937     copy = strdup(orig);
938     if (copy == NULL) {
939         fprintf(stderr, "%s: failed to allocate memory\n", progname);
940         return NULL;
941     }
942
943     toresolv = copy;
944     lastcomp = NULL;
945     for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
946     if (end[0] != '/') {
947         char *tmp;
948         end[1] = '\0';
949         tmp = strrchr(copy, '/');
950         if (tmp == NULL) {
951             lastcomp = copy;
952             toresolv = ".";
953         } else {
954             lastcomp = tmp + 1;
955             if (tmp == copy)
956                 toresolv = "/";
957         }
958         if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
959             lastcomp = NULL;
960             toresolv = copy;
961         }
962         else if (tmp)
963             tmp[0] = '\0';
964     }
965     if (realpath(toresolv, buf) == NULL) {
966         fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
967                 strerror(errno));
968         free(copy);
969         return NULL;
970     }
971     if (lastcomp == NULL)
972         dst = strdup(buf);
973     else {
974         dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
975         if (dst) {
976             unsigned buflen = strlen(buf);
977             if (buflen && buf[buflen-1] == '/')
978                 sprintf(dst, "%s%s", buf, lastcomp);
979             else
980                 sprintf(dst, "%s/%s", buf, lastcomp);
981         }
982     }
983     free(copy);
984     if (dst == NULL)
985         fprintf(stderr, "%s: failed to allocate memory\n", progname);
986     return dst;
987 }
988
989 static int send_fd(int sock_fd, int fd)
990 {
991     int retval;
992     struct msghdr msg;
993     struct cmsghdr *p_cmsg;
994     struct iovec vec;
995     char cmsgbuf[CMSG_SPACE(sizeof(fd))];
996     int *p_fds;
997     char sendchar = 0;
998
999     msg.msg_control = cmsgbuf;
1000     msg.msg_controllen = sizeof(cmsgbuf);
1001     p_cmsg = CMSG_FIRSTHDR(&msg);
1002     p_cmsg->cmsg_level = SOL_SOCKET;
1003     p_cmsg->cmsg_type = SCM_RIGHTS;
1004     p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1005     p_fds = (int *) CMSG_DATA(p_cmsg);
1006     *p_fds = fd;
1007     msg.msg_controllen = p_cmsg->cmsg_len;
1008     msg.msg_name = NULL;
1009     msg.msg_namelen = 0;
1010     msg.msg_iov = &vec;
1011     msg.msg_iovlen = 1;
1012     msg.msg_flags = 0;
1013     /* "To pass file descriptors or credentials you need to send/read at
1014      * least one byte" (man 7 unix) */
1015     vec.iov_base = &sendchar;
1016     vec.iov_len = sizeof(sendchar);
1017     while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1018     if (retval != 1) {
1019         perror("sending file descriptor");
1020         return -1;
1021     }
1022     return 0;
1023 }
1024
1025 static void usage(void)
1026 {
1027     fprintf(stderr,
1028             "%s: [options] mountpoint\n"
1029             "Options:\n"
1030             " -h                print help\n"
1031             " -v                print version\n"
1032             " -o opt[,opt...]   mount options\n"
1033             " -u                unmount\n"
1034             " -q                quiet\n"
1035             " -z                lazy unmount\n",
1036             progname);
1037     exit(1);
1038 }
1039
1040 static void show_version(void)
1041 {
1042     printf("%s\n", PACKAGE_STRING);
1043     exit(0);
1044 }
1045
1046 int main(int argc, char *argv[])
1047 {
1048     int ch;
1049     int fd;
1050     int res;
1051     char *origmnt;
1052     char *mnt;
1053     static int unmount = 0;
1054     static int lazy = 0;
1055     static int quiet = 0;
1056     char *commfd;
1057     int cfd;
1058     const char *opts = "";
1059
1060     static const struct option long_opts[] = {
1061         {"unmount", no_argument, NULL, 'u'},
1062         {"lazy",    no_argument, NULL, 'z'},
1063         {"quiet",   no_argument, NULL, 'q'},
1064         {"help",    no_argument, NULL, 'h'},
1065         {"version", no_argument, NULL, 'v'},
1066         {0, 0, 0, 0}};
1067
1068     /* Captive */
1069     captive_standalone_init();
1070
1071     progname = strdup(argv[0]);
1072     if (progname == NULL) {
1073         fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1074         exit(1);
1075     }
1076
1077     while ((ch = getopt_long(argc, argv, "hvo:uzq", long_opts, NULL)) != -1) {
1078         switch (ch) {
1079         case 'h':
1080             usage();
1081             break;
1082
1083         case 'v':
1084             show_version();
1085             break;
1086
1087         case 'o':
1088             opts = optarg;
1089             break;
1090
1091         case 'u':
1092             unmount = 1;
1093             break;
1094
1095         case 'z':
1096             lazy = 1;
1097             break;
1098
1099         case 'q':
1100             quiet = 1;
1101             break;
1102
1103         default:
1104             exit(1);
1105         }
1106     }
1107
1108     if (lazy && !unmount) {
1109         fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1110         exit(1);
1111     }
1112
1113     if (optind >= argc) {
1114         fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1115         exit(1);
1116     }
1117
1118     origmnt = argv[optind];
1119
1120     drop_privs();
1121     mnt = resolve_path(origmnt);
1122     restore_privs();
1123     if (mnt == NULL)
1124         exit(1);
1125
1126     umask(033);
1127     if (unmount) {
1128         if (geteuid() == 0) {
1129             int mtablock = lock_mtab();
1130             res = unmount_fuse(mnt, quiet, lazy);
1131             unlock_mtab(mtablock);
1132         } else
1133             res = do_unmount(mnt, quiet, lazy);
1134         if (res == -1)
1135             exit(1);
1136         return 0;
1137     }
1138
1139     commfd = getenv(FUSE_COMMFD_ENV);
1140     if (commfd == NULL) {
1141         fprintf(stderr, "%s: old style mounting not supported\n", progname);
1142         exit(1);
1143     }
1144
1145     fd = mount_fuse(mnt, opts);
1146     if (fd == -1)
1147         exit(1);
1148
1149     cfd = atoi(commfd);
1150     res = send_fd(cfd, fd);
1151     if (res == -1)
1152         exit(1);
1153
1154     return 0;
1155 }