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