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>
42 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
44 #define FUSE_DEV_OLD "/proc/fs/fuse/dev"
45 #define FUSE_DEV_NEW "/dev/fuse"
46 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
47 #define FUSE_CONF "/etc/fuse.conf"
49 static const char *progname;
51 static int user_allow_other = 0;
52 static int mount_max = 1000;
54 static const char *get_user_name(void)
56 struct passwd *pw = getpwuid(getuid());
57 if (pw != NULL && pw->pw_name != NULL)
60 fprintf(stderr, "%s: could not determine username\n", progname);
65 static uid_t oldfsuid;
66 static gid_t oldfsgid;
68 static void drop_privs(void)
71 oldfsuid = setfsuid(getuid());
72 oldfsgid = setfsgid(getgid());
76 static void restore_privs(void)
84 static int do_unmount(const char *mnt, int quiet, int lazy)
86 int res = umount2(mnt, lazy ? 2 : 0);
89 fprintf(stderr, "%s: failed to unmount %s: %s\n",
90 progname, mnt, strerror(errno));
96 /* use a lock file so that multiple fusermount processes don't try and
97 modify the mtab file at once! */
98 static int lock_mtab(void)
100 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
104 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
106 res = lockf(mtablock, F_LOCK, 0);
108 fprintf(stderr, "%s: error getting lock", progname);
110 fprintf(stderr, "%s: unable to open fuse lock file\n", progname);
115 static void unlock_mtab(int mtablock)
118 lockf(mtablock, F_ULOCK, 0);
123 /* Glibc addmntent() doesn't encode '\n', misencodes '\t' as '\n'
124 (version 2.3.2), and encodes '\\' differently as mount(8). So
125 let's not allow those characters, they are not all that usual in
127 static int check_name(const char *name)
130 for (s = "\n\t\\"; *s; s++) {
131 if (strchr(name, *s)) {
132 fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
140 static int add_mount(const char *fsname, const char *mnt, const char *type,
144 const char *mtab = _PATH_MOUNTED;
148 if (check_name(fsname) == -1 || check_name(mnt) == -1 ||
149 check_name(type) == -1 || check_name(opts) == -1)
152 fp = setmntent(mtab, "a");
154 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
159 ent.mnt_fsname = (char *) fsname;
160 ent.mnt_dir = (char *) mnt;
161 ent.mnt_type = (char *) type;
162 ent.mnt_opts = (char *) opts;
165 res = addmntent(fp, &ent);
167 fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
168 mtab, strerror(errno));
176 static int remove_mount(const char *mnt, int quiet, const char *mtab,
177 const char *mtab_new)
183 const char *user = NULL;
188 fp = setmntent(mtab, "r");
190 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
195 newfp = setmntent(mtab_new, "w");
197 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab_new,
203 user = get_user_name();
207 uidlen = sprintf(uidstr, "%u", getuid());
211 while ((entp = getmntent(fp)) != NULL) {
213 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
214 strcmp(entp->mnt_type, "fuse") == 0) {
218 char *p = strstr(entp->mnt_opts, "user=");
219 if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
220 strcmp(p + 5, user) == 0)
222 /* /etc/mtab is a link pointing to /proc/mounts: */
223 else if ((p = strstr(entp->mnt_opts, "user_id=")) &&
224 (p == entp->mnt_opts || *(p-1) == ',') &&
225 strncmp(p + 8, uidstr, uidlen) == 0 &&
226 (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0'))
233 res = addmntent(newfp, entp);
235 fprintf(stderr, "%s: failed to add entry to %s: %s\n",
236 progname, mtab_new, strerror(errno));
246 fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
255 static int count_fuse_fs(void)
259 const char *mtab = _PATH_MOUNTED;
260 FILE *fp = setmntent(mtab, "r");
262 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
266 while ((entp = getmntent(fp)) != NULL) {
267 if (strcmp(entp->mnt_type, "fuse") == 0)
274 static int unmount_rename(const char *mnt, int quiet, int lazy,
275 const char *mtab, const char *mtab_new)
281 res = do_unmount(mnt, quiet, lazy);
286 if (stat(mtab, &sbuf) == 0)
287 chown(mtab_new, sbuf.st_uid, sbuf.st_gid);
289 res = rename(mtab_new, mtab);
291 fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
292 mtab_new, mtab, strerror(errno));
298 static int unmount_fuse(const char *mnt, int quiet, int lazy)
301 const char *mtab = _PATH_MOUNTED;
302 const char *mtab_new = _PATH_MOUNTED "~fuse~";
304 res = remove_mount(mnt, quiet, mtab, mtab_new);
308 res = unmount_rename(mnt, quiet, lazy, mtab, mtab_new);
315 #else /* IGNORE_MTAB */
316 static int lock_mtab()
321 static void unlock_mtab(int mtablock)
326 static int count_fuse_fs()
331 static int add_mount(const char *fsname, const char *mnt, const char *type,
341 static int unmount_fuse(const char *mnt, int quiet, int lazy)
343 return do_unmount(mnt, quiet, lazy);
345 #endif /* IGNORE_MTAB */
347 static void strip_line(char *line)
349 char *s = strchr(line, '#');
352 for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
354 for (s = line; isspace((unsigned char) *s); s++);
356 memmove(line, s, strlen(s)+1);
359 static void parse_line(char *line, int linenum)
362 if (strcmp(line, "user_allow_other") == 0)
363 user_allow_other = 1;
364 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
367 fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
368 progname, FUSE_CONF, linenum, line);
371 static void read_conf(void)
373 FILE *fp = fopen(FUSE_CONF, "r");
378 while (fgets(line, sizeof(line), fp) != NULL) {
380 if (line[strlen(line)-1] == '\n') {
382 parse_line(line, linenum);
384 fprintf(stderr, "%s: reading %s: line %i too long\n",
385 progname, FUSE_CONF, linenum);
388 } else if(line[strlen(line)-1] == '\n')
394 } else if (errno != ENOENT) {
395 fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
400 static int begins_with(const char *s, const char *beg)
402 if (strncmp(s, beg, strlen(beg)) == 0)
415 static struct mount_flags mount_flags[] = {
416 {"rw", MS_RDONLY, 0, 1},
417 {"ro", MS_RDONLY, 1, 1},
418 {"suid", MS_NOSUID, 0, 0},
419 {"nosuid", MS_NOSUID, 1, 1},
420 {"dev", MS_NODEV, 0, 0},
421 {"nodev", MS_NODEV, 1, 1},
422 {"exec", MS_NOEXEC, 0, 1},
423 {"noexec", MS_NOEXEC, 1, 1},
424 {"async", MS_SYNCHRONOUS, 0, 1},
425 {"sync", MS_SYNCHRONOUS, 1, 1},
426 {"atime", MS_NOATIME, 0, 1},
427 {"noatime", MS_NOATIME, 1, 1},
431 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
435 for (i = 0; mount_flags[i].opt != NULL; i++) {
436 const char *opt = mount_flags[i].opt;
437 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
438 *on = mount_flags[i].on;
439 *flag = mount_flags[i].flag;
440 if (!mount_flags[i].safe && getuid() != 0) {
442 fprintf(stderr, "%s: unsafe option %s ignored\n",
451 static int add_option(char **optsp, const char *opt, unsigned expand)
455 newopts = strdup(opt);
457 unsigned oldsize = strlen(*optsp);
458 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
459 newopts = (char *) realloc(*optsp, newsize);
461 sprintf(newopts + oldsize, ",%s", opt);
463 if (newopts == NULL) {
464 fprintf(stderr, "%s: failed to allocate memory\n", progname);
471 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
476 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
479 for (i = 0; mount_flags[i].opt != NULL; i++) {
480 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
481 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
485 if (add_option(mnt_optsp, opts, 0) == -1)
487 /* remove comma from end of opts*/
488 l = strlen(*mnt_optsp);
489 if ((*mnt_optsp)[l-1] == ',')
490 (*mnt_optsp)[l-1] = '\0';
492 const char *user = get_user_name();
496 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
498 strcat(*mnt_optsp, user);
503 static int opt_eq(const char *s, unsigned len, const char *opt)
505 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
511 static int check_mountpoint_empty(const char *mnt, mode_t rootmode,
516 if (S_ISDIR(rootmode)) {
518 DIR *dp = opendir(mnt);
520 fprintf(stderr, "%s: failed to mountpoint for reading: %s\n",
521 progname, strerror(errno));
524 while ((ent = readdir(dp)) != NULL) {
525 if (strcmp(ent->d_name, ".") != 0 &&
526 strcmp(ent->d_name, "..") != 0) {
536 fprintf(stderr, "%s: mountpoint is not empty\n", progname);
537 fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
543 static int do_mount(const char *mnt, const char *type, mode_t rootmode,
544 int fd, const char *opts, const char *dev, char **fsnamep,
545 char **mnt_optsp, off_t rootsize)
548 int flags = MS_NOSUID | MS_NODEV;
550 char *mnt_opts = NULL;
556 optbuf = (char *) malloc(strlen(opts) + 128);
558 fprintf(stderr, "%s: failed to allocate memory\n", progname);
562 for (s = opts, d = optbuf; *s;) {
564 const char *fsname_str = "fsname=";
565 for (len = 0; s[len] && s[len] != ','; len++);
566 if (begins_with(s, fsname_str)) {
567 unsigned fsname_str_len = strlen(fsname_str);
570 fsname = (char *) malloc(len - fsname_str_len + 1);
572 fprintf(stderr, "%s: failed to allocate memory\n", progname);
575 memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
576 fsname[len - fsname_str_len] = '\0';
577 } else if (opt_eq(s, len, "nonempty")) {
579 } else if (!begins_with(s, "fd=") &&
580 !begins_with(s, "rootmode=") &&
581 !begins_with(s, "user_id=") &&
582 !begins_with(s, "group_id=")) {
586 if (opt_eq(s, len, "large_read")) {
587 struct utsname utsname;
589 res = uname(&utsname);
591 sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
592 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
593 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
597 if (getuid() != 0 && !user_allow_other &&
598 (opt_eq(s, len, "allow_other") ||
599 opt_eq(s, len, "allow_root"))) {
600 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
604 if (find_mount_flag(s, len, &on, &flag)) {
621 res = get_mnt_opts(flags, optbuf, &mnt_opts);
625 sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
626 fd, rootmode, getuid(), getgid());
627 if (fsname == NULL) {
628 fsname = strdup(dev);
630 fprintf(stderr, "%s: failed to allocate memory\n", progname);
635 if (check_empty && check_mountpoint_empty(mnt, rootmode, rootsize) == -1)
638 res = mount(fsname, mnt, type, flags, optbuf);
639 if (res == -1 && errno == EINVAL) {
640 /* It could be an old version not supporting group_id */
641 sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
642 res = mount(fsname, mnt, type, flags, optbuf);
645 fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
649 *mnt_optsp = mnt_opts;
662 static int check_version(const char *dev)
667 const char *version_file;
670 if (strcmp(dev, FUSE_DEV_OLD) != 0)
673 version_file = FUSE_VERSION_FILE_OLD;
674 vf = fopen(version_file, "r");
676 fprintf(stderr, "%s: kernel interface too old\n", progname);
679 res = fscanf(vf, "%i.%i", &majorver, &minorver);
682 fprintf(stderr, "%s: error reading %s\n", progname, version_file);
686 fprintf(stderr, "%s: kernel interface too old\n", progname);
692 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
696 const char *mnt = *mntp;
697 const char *origmnt = mnt;
699 res = lstat(mnt, stbuf);
701 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
702 progname, mnt, strerror(errno));
706 /* No permission checking is done for root */
710 if (S_ISDIR(stbuf->st_mode)) {
711 *currdir_fd = open(".", O_RDONLY);
712 if (*currdir_fd == -1) {
713 fprintf(stderr, "%s: failed to open current directory: %s\n",
714 progname, strerror(errno));
719 fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
720 progname, strerror(errno));
724 res = lstat(mnt, stbuf);
726 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
727 progname, origmnt, strerror(errno));
731 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
732 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
737 res = access(mnt, W_OK);
739 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
743 } else if (S_ISREG(stbuf->st_mode)) {
744 static char procfile[256];
745 *mountpoint_fd = open(mnt, O_WRONLY);
746 if (*mountpoint_fd == -1) {
747 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
751 res = fstat(*mountpoint_fd, stbuf);
753 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
754 progname, mnt, strerror(errno));
757 if (!S_ISREG(stbuf->st_mode)) {
758 fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
763 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
767 "%s: mountpoint %s is not a directory or a regular file\n",
776 static int try_open(const char *dev, char **devp, int silent)
778 int fd = open(dev, O_RDWR);
782 fprintf(stderr, "%s: failed to allocate memory\n", progname);
786 } else if (errno == ENODEV)
789 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
795 static int try_open_fuse_device(char **devp)
801 fd = try_open(FUSE_DEV_NEW, devp, 0);
807 fd = try_open(FUSE_DEV_OLD, devp, 1);
814 static int open_fuse_device(char **devp)
816 int fd = try_open_fuse_device(devp);
822 "%s: fuse device not found, try 'modprobe fuse' first\n",
828 static int mount_fuse(const char *mnt, const char *opts)
833 const char *type = "fuse";
836 char *mnt_opts = NULL;
837 const char *real_mnt = mnt;
839 int mountpoint_fd = -1;
842 fd = open_fuse_device(&dev);
846 if (geteuid() == 0) {
847 mtablock = lock_mtab();
857 if (getuid() != 0 && mount_max != -1) {
858 int mount_count = count_fuse_fs();
859 if (mount_count >= mount_max) {
860 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
862 unlock_mtab(mtablock);
867 res = check_version(dev);
869 res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
872 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
873 dev, &fsname, &mnt_opts, stbuf.st_size);
877 if (currdir_fd != -1) {
881 if (mountpoint_fd != -1)
882 close(mountpoint_fd);
886 unlock_mtab(mtablock);
890 if (geteuid() == 0) {
891 res = add_mount(fsname, mnt, type, mnt_opts);
892 unlock_mtab(mtablock);
894 umount2(mnt, 2); /* lazy umount */
907 static char *resolve_path(const char *orig)
914 const char *toresolv;
917 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
923 fprintf(stderr, "%s: failed to allocate memory\n", progname);
929 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
933 tmp = strrchr(copy, '/');
942 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
949 if (realpath(toresolv, buf) == NULL) {
950 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
955 if (lastcomp == NULL)
958 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
960 unsigned buflen = strlen(buf);
961 if (buflen && buf[buflen-1] == '/')
962 sprintf(dst, "%s%s", buf, lastcomp);
964 sprintf(dst, "%s/%s", buf, lastcomp);
969 fprintf(stderr, "%s: failed to allocate memory\n", progname);
973 static int send_fd(int sock_fd, int fd)
977 struct cmsghdr *p_cmsg;
979 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
983 msg.msg_control = cmsgbuf;
984 msg.msg_controllen = sizeof(cmsgbuf);
985 p_cmsg = CMSG_FIRSTHDR(&msg);
986 p_cmsg->cmsg_level = SOL_SOCKET;
987 p_cmsg->cmsg_type = SCM_RIGHTS;
988 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
989 p_fds = (int *) CMSG_DATA(p_cmsg);
991 msg.msg_controllen = p_cmsg->cmsg_len;
997 /* "To pass file descriptors or credentials you need to send/read at
998 * least one byte" (man 7 unix) */
999 vec.iov_base = &sendchar;
1000 vec.iov_len = sizeof(sendchar);
1001 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1003 perror("sending file descriptor");
1009 static void usage(void)
1012 "%s: [options] mountpoint\n"
1015 " -v print version\n"
1016 " -o opt[,opt...] mount options\n"
1019 " -z lazy unmount\n",
1024 static void show_version(void)
1026 printf("%s\n", PACKAGE_STRING);
1030 int main(int argc, char *argv[])
1037 static int unmount = 0;
1038 static int lazy = 0;
1039 static int quiet = 0;
1042 const char *opts = "";
1044 static const struct option long_opts[] = {
1045 {"unmount", no_argument, NULL, 'u'},
1046 {"lazy", no_argument, NULL, 'z'},
1047 {"quiet", no_argument, NULL, 'q'},
1048 {"help", no_argument, NULL, 'h'},
1049 {"version", no_argument, NULL, 'v'},
1052 progname = strdup(argv[0]);
1053 if (progname == NULL) {
1054 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1058 while ((ch = getopt_long(argc, argv, "hvo:uzq", long_opts, NULL)) != -1) {
1089 if (lazy && !unmount) {
1090 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1094 if (optind >= argc) {
1095 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1099 origmnt = argv[optind];
1102 mnt = resolve_path(origmnt);
1109 if (geteuid() == 0) {
1110 int mtablock = lock_mtab();
1111 res = unmount_fuse(mnt, quiet, lazy);
1112 unlock_mtab(mtablock);
1114 res = do_unmount(mnt, quiet, lazy);
1120 commfd = getenv(FUSE_COMMFD_ENV);
1121 if (commfd == NULL) {
1122 fprintf(stderr, "%s: old style mounting not supported\n", progname);
1126 fd = mount_fuse(mnt, opts);
1131 res = send_fd(cfd, fd);