2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU GPL.
8 /* This program does the mounting and unmounting of FUSE filesystems */
11 * NOTE: This program should be part of (or be called from) /bin/mount
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
32 #include <sys/param.h>
35 #include <sys/mount.h>
36 #include <sys/fsuid.h>
37 #include <sys/socket.h>
39 #include <sys/utsname.h>
40 #include <sys/sysmacros.h>
43 #include <captive/client.h>
44 #include <linux/kdev_t.h>
45 #include <linux/major.h>
48 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
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"
55 static const char *progname;
57 static int user_allow_other = 0;
58 static int mount_max = 1000;
60 static const char *get_user_name(void)
62 struct passwd *pw = getpwuid(getuid());
63 if (pw != NULL && pw->pw_name != NULL)
66 fprintf(stderr, "%s: could not determine username\n", progname);
71 static uid_t oldfsuid;
72 static gid_t oldfsgid;
74 static void drop_privs(void)
77 oldfsuid = setfsuid(getuid());
78 oldfsgid = setfsgid(getgid());
82 static void restore_privs(void)
90 static int do_unmount(const char *mnt, int quiet, int lazy)
92 int res = umount2(mnt, lazy ? 2 : 0);
95 fprintf(stderr, "%s: failed to unmount %s: %s\n",
96 progname, mnt, strerror(errno));
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)
106 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
110 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
112 res = lockf(mtablock, F_LOCK, 0);
114 fprintf(stderr, "%s: error getting lock", progname);
116 fprintf(stderr, "%s: unable to open fuse lock file\n", progname);
121 static void unlock_mtab(int mtablock)
124 int trash0=lockf(mtablock, F_ULOCK, 0);
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
133 static int check_name(const char *name)
136 for (s = "\n\t\\"; *s; s++) {
137 if (strchr(name, *s)) {
138 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
146 static int add_mount(const char *fsname, const char *mnt, const char *type,
150 const char *mtab = _PATH_MOUNTED;
154 if (check_name(fsname) == -1 || check_name(mnt) == -1 ||
155 check_name(type) == -1 || check_name(opts) == -1)
158 fp = setmntent(mtab, "a");
160 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
165 ent.mnt_fsname = (char *) fsname;
166 ent.mnt_dir = (char *) mnt;
167 ent.mnt_type = (char *) type;
168 ent.mnt_opts = (char *) opts;
171 res = addmntent(fp, &ent);
173 fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
174 mtab, strerror(errno));
182 static int remove_mount(const char *mnt, int quiet, const char *mtab,
183 const char *mtab_new)
189 const char *user = NULL;
194 fp = setmntent(mtab, "r");
196 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
201 newfp = setmntent(mtab_new, "w");
203 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab_new,
209 user = get_user_name();
213 uidlen = sprintf(uidstr, "%u", getuid());
217 while ((entp = getmntent(fp)) != NULL) {
219 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
220 strcmp(entp->mnt_type, "fuse") == 0) {
224 char *p = strstr(entp->mnt_opts, "user=");
225 if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
226 strcmp(p + 5, user) == 0)
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'))
239 res = addmntent(newfp, entp);
241 fprintf(stderr, "%s: failed to add entry to %s: %s\n",
242 progname, mtab_new, strerror(errno));
252 fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
261 static int count_fuse_fs(void)
265 const char *mtab = _PATH_MOUNTED;
266 FILE *fp = setmntent(mtab, "r");
268 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
272 while ((entp = getmntent(fp)) != NULL) {
273 if (strcmp(entp->mnt_type, "fuse") == 0)
280 static int unmount_rename(const char *mnt, int quiet, int lazy,
281 const char *mtab, const char *mtab_new)
287 res = do_unmount(mnt, quiet, lazy);
292 if (stat(mtab, &sbuf) == 0) {
293 int trash0=chown(mtab_new, sbuf.st_uid, sbuf.st_gid);
296 res = rename(mtab_new, mtab);
298 fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
299 mtab_new, mtab, strerror(errno));
305 static int unmount_fuse(const char *mnt, int quiet, int lazy)
308 const char *mtab = _PATH_MOUNTED;
309 const char *mtab_new = _PATH_MOUNTED "~fuse~";
311 res = remove_mount(mnt, quiet, mtab, mtab_new);
315 res = unmount_rename(mnt, quiet, lazy, mtab, mtab_new);
322 #else /* IGNORE_MTAB */
323 static int lock_mtab()
328 static void unlock_mtab(int mtablock)
333 static int count_fuse_fs()
338 static int add_mount(const char *fsname, const char *mnt, const char *type,
348 static int unmount_fuse(const char *mnt, int quiet, int lazy)
350 return do_unmount(mnt, quiet, lazy);
352 #endif /* IGNORE_MTAB */
354 static void strip_line(char *line)
356 char *s = strchr(line, '#');
359 for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
361 for (s = line; isspace((unsigned char) *s); s++);
363 memmove(line, s, strlen(s)+1);
366 static void parse_line(char *line, int linenum)
369 if (strcmp(line, "user_allow_other") == 0)
370 user_allow_other = 1;
371 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
374 fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
375 progname, FUSE_CONF, linenum, line);
378 static void read_conf(void)
380 FILE *fp = fopen(FUSE_CONF, "r");
385 while (fgets(line, sizeof(line), fp) != NULL) {
387 if (line[strlen(line)-1] == '\n') {
389 parse_line(line, linenum);
391 fprintf(stderr, "%s: reading %s: line %i too long\n",
392 progname, FUSE_CONF, linenum);
395 } else if(line[strlen(line)-1] == '\n')
401 } else if (errno != ENOENT) {
402 fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
407 static int begins_with(const char *s, const char *beg)
409 if (strncmp(s, beg, strlen(beg)) == 0)
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},
438 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
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) {
449 fprintf(stderr, "%s: unsafe option %s ignored\n",
458 static int add_option(char **optsp, const char *opt, unsigned expand)
462 newopts = strdup(opt);
464 unsigned oldsize = strlen(*optsp);
465 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
466 newopts = (char *) realloc(*optsp, newsize);
468 sprintf(newopts + oldsize, ",%s", opt);
470 if (newopts == NULL) {
471 fprintf(stderr, "%s: failed to allocate memory\n", progname);
478 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
483 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
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)
492 if (add_option(mnt_optsp, opts, 0) == -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';
499 const char *user = get_user_name();
503 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
505 strcat(*mnt_optsp, user);
510 static int opt_eq(const char *s, unsigned len, const char *opt)
512 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
518 static int check_mountpoint_empty(const char *mnt, mode_t rootmode,
523 if (S_ISDIR(rootmode)) {
525 DIR *dp = opendir(mnt);
527 fprintf(stderr, "%s: failed to mountpoint for reading: %s\n",
528 progname, strerror(errno));
531 while ((ent = readdir(dp)) != NULL) {
532 if (strcmp(ent->d_name, ".") != 0 &&
533 strcmp(ent->d_name, "..") != 0) {
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);
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)
555 int flags = MS_NOSUID | MS_NODEV;
557 char *mnt_opts = NULL;
563 optbuf = (char *) malloc(strlen(opts) + 128);
565 fprintf(stderr, "%s: failed to allocate memory\n", progname);
569 for (s = opts, d = optbuf; *s;) {
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);
577 fsname = (char *) malloc(len - fsname_str_len + 1);
579 fprintf(stderr, "%s: failed to allocate memory\n", progname);
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")) {
586 } else if (!begins_with(s, "fd=") &&
587 !begins_with(s, "rootmode=") &&
588 !begins_with(s, "user_id=") &&
589 !begins_with(s, "group_id=")) {
593 if (opt_eq(s, len, "large_read")) {
594 struct utsname utsname;
596 res = uname(&utsname);
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);
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);
611 if (find_mount_flag(s, len, &on, &flag)) {
628 res = get_mnt_opts(flags, optbuf, &mnt_opts);
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);
637 fprintf(stderr, "%s: failed to allocate memory\n", progname);
642 if (check_empty && check_mountpoint_empty(mnt, rootmode, rootsize) == -1)
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);
652 fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
656 *mnt_optsp = mnt_opts;
669 static int check_version(const char *dev)
674 const char *version_file;
677 if (strcmp(dev, FUSE_DEV_OLD) != 0)
680 version_file = FUSE_VERSION_FILE_OLD;
681 vf = fopen(version_file, "r");
683 fprintf(stderr, "%s: kernel interface too old\n", progname);
686 res = fscanf(vf, "%i.%i", &majorver, &minorver);
689 fprintf(stderr, "%s: error reading %s\n", progname, version_file);
693 fprintf(stderr, "%s: kernel interface too old\n", progname);
699 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
703 const char *mnt = *mntp;
704 const char *origmnt = mnt;
706 res = lstat(mnt, stbuf);
708 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
709 progname, mnt, strerror(errno));
713 /* No permission checking is done for root */
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));
726 fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
727 progname, strerror(errno));
731 res = lstat(mnt, stbuf);
733 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
734 progname, origmnt, strerror(errno));
738 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
739 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
744 res = access(mnt, W_OK);
746 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
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,
758 res = fstat(*mountpoint_fd, stbuf);
760 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
761 progname, mnt, strerror(errno));
764 if (!S_ISREG(stbuf->st_mode)) {
765 fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
770 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
774 "%s: mountpoint %s is not a directory or a regular file\n",
783 static int try_open(const char *dev, char **devp, int silent)
785 int fd = open(dev, O_RDWR);
787 if (fd == -1 && errno == ENOENT && !strcmp(dev, FUSE_DEV_NEW)
788 && !mknod(dev, 0660 | S_IFCHR, MKDEV(MISC_MAJOR, 229))) {
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);
795 if (fd == -1 && errno == ENODEV) {
796 const char *cmd = "/sbin/modprobe fuse";
799 fprintf(stderr, "%s: Notice: Loaded Linux kernel module FUSE: %s\n",
802 fprintf(stderr, "%s: Warning: \"%s\" cannot be opened and even failed: %s\n",
804 fd = open(dev, O_RDWR);
809 fprintf(stderr, "%s: failed to allocate memory\n", progname);
813 } else if (errno == ENODEV)
816 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
822 static int try_open_fuse_device(char **devp)
828 fd = try_open(FUSE_DEV_NEW, devp, 0);
834 fd = try_open(FUSE_DEV_OLD, devp, 1);
841 static int open_fuse_device(char **devp)
843 int fd = try_open_fuse_device(devp);
849 "%s: fuse device not found, try 'modprobe fuse' first\n",
855 static int mount_fuse(const char *mnt, const char *opts)
860 const char *type = "fuse";
863 char *mnt_opts = NULL;
864 const char *real_mnt = mnt;
866 int mountpoint_fd = -1;
869 fd = open_fuse_device(&dev);
873 if (geteuid() == 0) {
874 mtablock = lock_mtab();
884 if (getuid() != 0 && mount_max != -1) {
885 int mount_count = count_fuse_fs();
886 if (mount_count >= mount_max) {
887 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
889 unlock_mtab(mtablock);
894 res = check_version(dev);
896 res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
899 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
900 dev, &fsname, &mnt_opts, stbuf.st_size);
904 if (currdir_fd != -1) {
905 int trash0=fchdir(currdir_fd);
908 if (mountpoint_fd != -1)
909 close(mountpoint_fd);
913 unlock_mtab(mtablock);
917 if (geteuid() == 0) {
918 res = add_mount(fsname, mnt, type, mnt_opts);
919 unlock_mtab(mtablock);
921 umount2(mnt, 2); /* lazy umount */
934 static char *resolve_path(const char *orig)
941 const char *toresolv;
944 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
950 fprintf(stderr, "%s: failed to allocate memory\n", progname);
956 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
960 tmp = strrchr(copy, '/');
969 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
976 if (realpath(toresolv, buf) == NULL) {
977 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
982 if (lastcomp == NULL)
985 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
987 unsigned buflen = strlen(buf);
988 if (buflen && buf[buflen-1] == '/')
989 sprintf(dst, "%s%s", buf, lastcomp);
991 sprintf(dst, "%s/%s", buf, lastcomp);
996 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1000 static int send_fd(int sock_fd, int fd)
1004 struct cmsghdr *p_cmsg;
1006 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
1010 msg.msg_control = cmsgbuf;
1011 msg.msg_controllen = sizeof(cmsgbuf);
1012 p_cmsg = CMSG_FIRSTHDR(&msg);
1013 p_cmsg->cmsg_level = SOL_SOCKET;
1014 p_cmsg->cmsg_type = SCM_RIGHTS;
1015 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1016 p_fds = (int *) CMSG_DATA(p_cmsg);
1018 msg.msg_controllen = p_cmsg->cmsg_len;
1019 msg.msg_name = NULL;
1020 msg.msg_namelen = 0;
1024 /* "To pass file descriptors or credentials you need to send/read at
1025 * least one byte" (man 7 unix) */
1026 vec.iov_base = &sendchar;
1027 vec.iov_len = sizeof(sendchar);
1028 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1030 perror("sending file descriptor");
1036 static void usage(void)
1039 "%s: [options] mountpoint\n"
1042 " -v print version\n"
1043 " -o opt[,opt...] mount options\n"
1046 " -z lazy unmount\n",
1051 static void show_version(void)
1053 printf("%s\n", PACKAGE_STRING);
1057 int main(int argc, char *argv[])
1064 static int unmount = 0;
1065 static int lazy = 0;
1066 static int quiet = 0;
1069 const char *opts = "";
1071 static const struct option long_opts[] = {
1072 {"unmount", no_argument, NULL, 'u'},
1073 {"lazy", no_argument, NULL, 'z'},
1074 {"quiet", no_argument, NULL, 'q'},
1075 {"help", no_argument, NULL, 'h'},
1076 {"version", no_argument, NULL, 'v'},
1080 captive_standalone_init();
1082 progname = strdup(argv[0]);
1083 if (progname == NULL) {
1084 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1088 while ((ch = getopt_long(argc, argv, "hvo:uzq", long_opts, NULL)) != -1) {
1119 if (lazy && !unmount) {
1120 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1124 if (optind >= argc) {
1125 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1129 origmnt = argv[optind];
1132 mnt = resolve_path(origmnt);
1139 if (geteuid() == 0) {
1140 int mtablock = lock_mtab();
1141 res = unmount_fuse(mnt, quiet, lazy);
1142 unlock_mtab(mtablock);
1144 res = do_unmount(mnt, quiet, lazy);
1150 commfd = getenv(FUSE_COMMFD_ENV);
1151 if (commfd == NULL) {
1152 fprintf(stderr, "%s: old style mounting not supported\n", progname);
1156 fd = mount_fuse(mnt, opts);
1161 res = send_fd(cfd, fd);