11#include "fuse_config.h"
12#include "mount_util.h"
30#include "fuse_mount_compat.h"
33#include <sys/socket.h>
34#include <sys/utsname.h>
39#ifdef HAVE_CLOSE_RANGE
40#include <linux/close_range.h>
43#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
45#define FUSE_DEV "/dev/fuse"
47static const char *progname;
49static int user_allow_other = 0;
50static int mount_max = 1000;
52static int auto_unmount = 0;
54#ifdef GETMNTENT_NEEDS_UNESCAPING
59static void unescape(
char *buf) {
63 char *next_src = strchrnul(src,
'\\');
64 int offset = next_src - src;
65 memmove(dest, src, offset);
75 if(
'0' <= src[0] && src[0] <
'2' &&
76 '0' <= src[1] && src[1] <
'8' &&
77 '0' <= src[2] && src[2] <
'8') {
78 *dest++ = (src[0] -
'0') << 6
80 | (src[2] -
'0') << 0;
82 }
else if (src[0] ==
'\\') {
91static struct mntent *GETMNTENT(FILE *stream)
93 struct mntent *entp = getmntent(stream);
95 unescape(entp->mnt_fsname);
96 unescape(entp->mnt_dir);
97 unescape(entp->mnt_type);
98 unescape(entp->mnt_opts);
103#define GETMNTENT getmntent
109static int extract_x_options(
const char *original,
char **non_x_opts,
113 const char *opt, *opt_end;
115 orig_len = strlen(original) + 1;
117 *non_x_opts = calloc(1, orig_len);
118 *x_opts = calloc(1, orig_len);
120 size_t non_x_opts_len = orig_len;
121 size_t x_opts_len = orig_len;
123 if (*non_x_opts == NULL || *x_opts == NULL) {
124 fprintf(stderr,
"%s: Failed to allocate %zuB.\n",
129 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
132 opt_end = strchr(opt,
',');
134 opt_end = original + orig_len;
136 size_t opt_len = opt_end - opt;
137 size_t opt_len_left = orig_len - (opt - original);
141 if (strncmp(opt,
"x-", MIN(2, opt_len_left)) == 0) {
142 buf_len = x_opts_len;
146 buf_len = non_x_opts_len;
148 opt_buf = *non_x_opts;
151 if (buf_len < orig_len) {
152 strncat(opt_buf,
",", 2);
157 if ((ssize_t)(buf_len - opt_len) < 0) {
159 fprintf(stderr,
"%s: no buf space left in copy, orig='%s'\n",
164 strncat(opt_buf, opt, opt_end - opt);
168 x_opts_len = buf_len;
170 non_x_opts_len = buf_len;
176static const char *get_user_name(
void)
178 struct passwd *pw = getpwuid(getuid());
179 if (pw != NULL && pw->pw_name != NULL)
182 fprintf(stderr,
"%s: could not determine username\n", progname);
187static uid_t oldfsuid;
188static gid_t oldfsgid;
190static void drop_privs(
void)
193 oldfsuid = setfsuid(getuid());
194 oldfsgid = setfsgid(getgid());
198static void restore_privs(
void)
210static int lock_umount(
void)
212 const char *mtab_lock = _PATH_MOUNTED
".fuselock";
215 struct stat mtab_stat;
218 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
221 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
222 if (mtablock == -1) {
223 fprintf(stderr,
"%s: unable to open fuse lock file: %s\n",
224 progname, strerror(errno));
227 res = lockf(mtablock, F_LOCK, 0);
229 fprintf(stderr,
"%s: error getting lock: %s\n", progname,
238static void unlock_umount(
int mtablock)
243 res = lockf(mtablock, F_ULOCK, 0);
245 fprintf(stderr,
"%s: error releasing lock: %s\n",
246 progname, strerror(errno));
252static int add_mount(
const char *source,
const char *mnt,
const char *type,
255 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
258static int may_unmount(
const char *mnt,
int quiet)
262 const char *user = NULL;
266 const char *mtab = _PATH_MOUNTED;
268 user = get_user_name();
272 fp = setmntent(mtab,
"r");
274 fprintf(stderr,
"%s: failed to open %s: %s\n", progname, mtab,
279 uidlen = sprintf(uidstr,
"%u", getuid());
282 while ((entp = GETMNTENT(fp)) != NULL) {
283 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
284 (strcmp(entp->mnt_type,
"fuse") == 0 ||
285 strcmp(entp->mnt_type,
"fuseblk") == 0 ||
286 strncmp(entp->mnt_type,
"fuse.", 5) == 0 ||
287 strncmp(entp->mnt_type,
"fuseblk.", 8) == 0)) {
288 char *p = strstr(entp->mnt_opts,
"user=");
290 (p == entp->mnt_opts || *(p-1) ==
',') &&
291 strcmp(p + 5, user) == 0) {
298 strstr(entp->mnt_opts,
"user_id=")) &&
299 (p == entp->mnt_opts ||
301 strncmp(p + 8, uidstr, uidlen) == 0 &&
302 (*(p+8+uidlen) ==
',' ||
303 *(p+8+uidlen) ==
'\0')) {
314 "%s: entry for %s not found in %s\n",
315 progname, mnt, mtab);
346static int check_is_mount_child(
void *p)
349 const char *last = a[0];
350 const char *mnt = a[1];
351 const char *type = a[2];
353 const char *procmounts =
"/proc/mounts";
359 res = mount(
"",
"/",
"", MS_PRIVATE | MS_REC, NULL);
361 fprintf(stderr,
"%s: failed to mark mounts private: %s\n",
362 progname, strerror(errno));
366 fp = setmntent(procmounts,
"r");
368 fprintf(stderr,
"%s: failed to open %s: %s\n", progname,
369 procmounts, strerror(errno));
374 while (GETMNTENT(fp) != NULL)
378 fp = setmntent(procmounts,
"r");
380 fprintf(stderr,
"%s: failed to open %s: %s\n", progname,
381 procmounts, strerror(errno));
385 res = mount(
".",
"/",
"", MS_BIND | MS_REC, NULL);
387 fprintf(stderr,
"%s: failed to bind parent to /: %s\n",
388 progname, strerror(errno));
393 while ((entp = GETMNTENT(fp)) != NULL) {
398 if (entp->mnt_dir[0] ==
'/' &&
399 strcmp(entp->mnt_dir + 1, last) == 0 &&
400 (!type || strcmp(entp->mnt_type, type) == 0)) {
408 fprintf(stderr,
"%s: %s not mounted\n", progname, mnt);
415static pid_t clone_newns(
void *a)
418 char *stack = buf + (
sizeof(buf) / 2 - ((
size_t) buf & 15));
421 extern int __clone2(
int (*fn)(
void *),
422 void *child_stack_base,
size_t stack_size,
423 int flags,
void *arg, pid_t *ptid,
424 void *tls, pid_t *ctid);
426 return __clone2(check_is_mount_child, stack,
sizeof(buf) / 2,
427 CLONE_NEWNS, a, NULL, NULL, NULL);
429 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
433static int check_is_mount(
const char *last,
const char *mnt,
const char *type)
437 const char *a[3] = { last, mnt, type };
439 pid = clone_newns((
void *) a);
440 if (pid == (pid_t) -1) {
441 fprintf(stderr,
"%s: failed to clone namespace: %s\n",
442 progname, strerror(errno));
445 p = waitpid(pid, &status, __WCLONE);
446 if (p == (pid_t) -1) {
447 fprintf(stderr,
"%s: waitpid failed: %s\n",
448 progname, strerror(errno));
451 if (!WIFEXITED(status)) {
452 fprintf(stderr,
"%s: child terminated abnormally (status %i)\n",
456 if (WEXITSTATUS(status) != 0)
462static int chdir_to_parent(
char *copy,
const char **lastp)
469 tmp = strrchr(copy,
'/');
470 if (tmp == NULL || tmp[1] ==
'\0') {
471 fprintf(stderr,
"%s: internal error: invalid abs path: <%s>\n",
479 }
else if (tmp[1] !=
'\0') {
489 fprintf(stderr,
"%s: failed to chdir to %s: %s\n",
490 progname, parent, strerror(errno));
494 if (getcwd(buf,
sizeof(buf)) == NULL) {
495 fprintf(stderr,
"%s: failed to obtain current directory: %s\n",
496 progname, strerror(errno));
499 if (strcmp(buf, parent) != 0) {
500 fprintf(stderr,
"%s: mountpoint moved (%s -> %s)\n", progname,
510static int unmount_fuse_locked(
const char *mnt,
int quiet,
int lazy)
515 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
518 res = may_unmount(mnt, quiet);
525 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
530 res = chdir_to_parent(copy, &last);
536 res = umount2(last, umount_flags);
538 if (res == -1 && !quiet) {
539 fprintf(stderr,
"%s: failed to unmount %s: %s\n",
540 progname, mnt, strerror(errno));
550 fprintf(stderr,
"%s: failed to chdir to '/'\n", progname);
554 return fuse_mnt_remove_mount(progname, mnt);
557static int unmount_fuse(
const char *mnt,
int quiet,
int lazy)
560 int mtablock = lock_umount();
562 res = unmount_fuse_locked(mnt, quiet, lazy);
563 unlock_umount(mtablock);
568static int count_fuse_fs(
void)
572 const char *mtab = _PATH_MOUNTED;
573 FILE *fp = setmntent(mtab,
"r");
575 fprintf(stderr,
"%s: failed to open %s: %s\n", progname, mtab,
579 while ((entp = GETMNTENT(fp)) != NULL) {
580 if (strcmp(entp->mnt_type,
"fuse") == 0 ||
581 strncmp(entp->mnt_type,
"fuse.", 5) == 0)
590static int count_fuse_fs(
void)
595static int add_mount(
const char *source,
const char *mnt,
const char *type,
605static int unmount_fuse(
const char *mnt,
int quiet,
int lazy)
608 return fuse_mnt_umount(progname, mnt, mnt, lazy);
612static void strip_line(
char *line)
614 char *s = strchr(line,
'#');
617 for (s = line + strlen(line) - 1;
618 s >= line && isspace((
unsigned char) *s); s--);
620 for (s = line; isspace((
unsigned char) *s); s++);
622 memmove(line, s, strlen(s)+1);
625static void parse_line(
char *line,
int linenum)
628 if (strcmp(line,
"user_allow_other") == 0)
629 user_allow_other = 1;
630 else if (sscanf(line,
"mount_max = %i", &tmp) == 1)
634 "%s: unknown parameter in %s at line %i: '%s'\n",
635 progname, FUSE_CONF, linenum, line);
638static void read_conf(
void)
640 FILE *fp = fopen(FUSE_CONF,
"r");
645 while (fgets(line,
sizeof(line), fp) != NULL) {
647 if (line[strlen(line)-1] ==
'\n') {
649 parse_line(line, linenum);
653 }
else if(line[strlen(line)-1] ==
'\n') {
654 fprintf(stderr,
"%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
662 fprintf(stderr,
"%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
666 fprintf(stderr,
"%s: reading %s: read failed\n", progname, FUSE_CONF);
670 }
else if (errno != ENOENT) {
671 bool fatal = (errno != EACCES && errno != ELOOP &&
672 errno != ENAMETOOLONG && errno != ENOTDIR &&
674 fprintf(stderr,
"%s: failed to open %s: %s\n",
675 progname, FUSE_CONF, strerror(errno));
681static int begins_with(
const char *s,
const char *beg)
683 if (strncmp(s, beg, strlen(beg)) == 0)
696static struct mount_flags mount_flags[] = {
697 {
"rw", MS_RDONLY, 0, 1},
698 {
"ro", MS_RDONLY, 1, 1},
699 {
"suid", MS_NOSUID, 0, 0},
700 {
"nosuid", MS_NOSUID, 1, 1},
701 {
"dev", MS_NODEV, 0, 0},
702 {
"nodev", MS_NODEV, 1, 1},
703 {
"exec", MS_NOEXEC, 0, 1},
704 {
"noexec", MS_NOEXEC, 1, 1},
705 {
"async", MS_SYNCHRONOUS, 0, 1},
706 {
"sync", MS_SYNCHRONOUS, 1, 1},
707 {
"atime", MS_NOATIME, 0, 1},
708 {
"noatime", MS_NOATIME, 1, 1},
709 {
"diratime", MS_NODIRATIME, 0, 1},
710 {
"nodiratime", MS_NODIRATIME, 1, 1},
711 {
"lazytime", MS_LAZYTIME, 1, 1},
712 {
"nolazytime", MS_LAZYTIME, 0, 1},
713 {
"relatime", MS_RELATIME, 1, 1},
714 {
"norelatime", MS_RELATIME, 0, 1},
715 {
"strictatime", MS_STRICTATIME, 1, 1},
716 {
"nostrictatime", MS_STRICTATIME, 0, 1},
717 {
"dirsync", MS_DIRSYNC, 1, 1},
718 {
"symfollow", MS_NOSYMFOLLOW, 0, 1},
719 {
"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
723static int find_mount_flag(
const char *s,
unsigned len,
int *on,
int *flag)
727 for (i = 0; mount_flags[i].opt != NULL; i++) {
728 const char *opt = mount_flags[i].opt;
729 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
730 *on = mount_flags[i].on;
731 *flag = mount_flags[i].flag;
732 if (!mount_flags[i].safe && getuid() != 0) {
735 "%s: unsafe option %s ignored\n",
744static int add_option(
char **optsp,
const char *opt,
unsigned expand)
748 newopts = strdup(opt);
750 unsigned oldsize = strlen(*optsp);
751 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
752 newopts = (
char *) realloc(*optsp, newsize);
754 sprintf(newopts + oldsize,
",%s", opt);
756 if (newopts == NULL) {
757 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
764static int get_mnt_opts(
int flags,
char *opts,
char **mnt_optsp)
769 if (!(flags & MS_RDONLY) && add_option(mnt_optsp,
"rw", 0) == -1)
772 for (i = 0; mount_flags[i].opt != NULL; i++) {
773 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
774 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
778 if (add_option(mnt_optsp, opts, 0) == -1)
781 l = strlen(*mnt_optsp);
782 if ((*mnt_optsp)[l-1] ==
',')
783 (*mnt_optsp)[l-1] =
'\0';
785 const char *user = get_user_name();
789 if (add_option(mnt_optsp,
"user=", strlen(user)) == -1)
791 strcat(*mnt_optsp, user);
796static int opt_eq(
const char *s,
unsigned len,
const char *opt)
798 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
804static int get_string_opt(
const char *s,
unsigned len,
const char *opt,
808 unsigned opt_len = strlen(opt);
813 *val = (
char *) malloc(len - opt_len + 1);
815 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
822 for (i = 0; i < len; i++) {
823 if (s[i] ==
'\\' && i + 1 < len)
836static int mount_notrunc(
const char *source,
const char *target,
837 const char *filesystemtype,
unsigned long mountflags,
839 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
840 fprintf(stderr,
"%s: mount options too long\n", progname);
844 return mount(source, target, filesystemtype, mountflags, data);
848static int do_mount(
const char *mnt,
const char **typep, mode_t rootmode,
849 int fd,
const char *opts,
const char *dev,
char **sourcep,
853 int flags = MS_NOSUID | MS_NODEV;
855 char *mnt_opts = NULL;
859 char *subtype = NULL;
864 optbuf = (
char *) malloc(strlen(opts) + 128);
866 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
870 for (s = opts, d = optbuf; *s;) {
872 const char *fsname_str =
"fsname=";
873 const char *subtype_str =
"subtype=";
874 bool escape_ok = begins_with(s, fsname_str) ||
875 begins_with(s, subtype_str);
876 for (len = 0; s[len]; len++) {
877 if (escape_ok && s[len] ==
'\\' && s[len + 1])
879 else if (s[len] ==
',')
882 if (begins_with(s, fsname_str)) {
883 if (!get_string_opt(s, len, fsname_str, &fsname))
885 }
else if (begins_with(s, subtype_str)) {
886 if (!get_string_opt(s, len, subtype_str, &subtype))
888 }
else if (opt_eq(s, len,
"blkdev")) {
891 "%s: option blkdev is privileged\n",
896 }
else if (opt_eq(s, len,
"auto_unmount")) {
898 }
else if (!opt_eq(s, len,
"nonempty") &&
899 !begins_with(s,
"fd=") &&
900 !begins_with(s,
"rootmode=") &&
901 !begins_with(s,
"user_id=") &&
902 !begins_with(s,
"group_id=")) {
906 if (opt_eq(s, len,
"large_read")) {
907 struct utsname utsname;
909 res = uname(&utsname);
911 sscanf(utsname.release,
"%u.%u",
912 &kmaj, &kmin) == 2 &&
913 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
914 fprintf(stderr,
"%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
918 if (getuid() != 0 && !user_allow_other &&
919 (opt_eq(s, len,
"allow_other") ||
920 opt_eq(s, len,
"allow_root"))) {
921 fprintf(stderr,
"%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
925 if (find_mount_flag(s, len, &on, &flag)) {
930 }
else if (opt_eq(s, len,
"default_permissions") ||
931 opt_eq(s, len,
"allow_other") ||
932 begins_with(s,
"max_read=") ||
933 begins_with(s,
"blksize=")) {
938 fprintf(stderr,
"%s: unknown option '%.*s'\n", progname, len, s);
948 res = get_mnt_opts(flags, optbuf, &mnt_opts);
952 sprintf(d,
"fd=%i,rootmode=%o,user_id=%u,group_id=%u",
953 fd, rootmode, getuid(), getgid());
955 source = malloc((fsname ? strlen(fsname) : 0) +
956 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
958 type = malloc((subtype ? strlen(subtype) : 0) + 32);
959 if (!type || !source) {
960 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
965 sprintf(type,
"%s.%s", blkdev ?
"fuseblk" :
"fuse", subtype);
967 strcpy(type, blkdev ?
"fuseblk" :
"fuse");
970 strcpy(source, fsname);
972 strcpy(source, subtype ? subtype : dev);
974 res = mount_notrunc(source, mnt, type, flags, optbuf);
975 if (res == -1 && errno == ENODEV && subtype) {
977 strcpy(type, blkdev ?
"fuseblk" :
"fuse");
980 sprintf(source,
"%s#%s", subtype, fsname);
982 strcpy(source, type);
985 res = mount_notrunc(source, mnt, type, flags, optbuf);
987 if (res == -1 && errno == EINVAL) {
989 sprintf(d,
"fd=%i,rootmode=%o,user_id=%u",
990 fd, rootmode, getuid());
991 res = mount_notrunc(source, mnt, type, flags, optbuf);
994 int errno_save = errno;
995 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
996 fprintf(stderr,
"%s: 'fuseblk' support missing\n",
999 fprintf(stderr,
"%s: mount failed: %s\n", progname,
1000 strerror(errno_save));
1005 *mnt_optsp = mnt_opts;
1021static int check_perm(
const char **mntp,
struct stat *stbuf,
int *mountpoint_fd)
1024 const char *mnt = *mntp;
1025 const char *origmnt = mnt;
1026 struct statfs fs_buf;
1029 res = lstat(mnt, stbuf);
1031 fprintf(stderr,
"%s: failed to access mountpoint %s: %s\n",
1032 progname, mnt, strerror(errno));
1040 if (S_ISDIR(stbuf->st_mode)) {
1044 "%s: failed to chdir to mountpoint: %s\n",
1045 progname, strerror(errno));
1049 res = lstat(mnt, stbuf);
1052 "%s: failed to access mountpoint %s: %s\n",
1053 progname, origmnt, strerror(errno));
1057 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1058 fprintf(stderr,
"%s: mountpoint %s not owned by user\n",
1063 res = access(mnt, W_OK);
1065 fprintf(stderr,
"%s: user has no write access to mountpoint %s\n",
1069 }
else if (S_ISREG(stbuf->st_mode)) {
1070 static char procfile[256];
1071 *mountpoint_fd = open(mnt, O_WRONLY);
1072 if (*mountpoint_fd == -1) {
1073 fprintf(stderr,
"%s: failed to open %s: %s\n",
1074 progname, mnt, strerror(errno));
1077 res = fstat(*mountpoint_fd, stbuf);
1080 "%s: failed to access mountpoint %s: %s\n",
1081 progname, mnt, strerror(errno));
1084 if (!S_ISREG(stbuf->st_mode)) {
1086 "%s: mountpoint %s is no longer a regular file\n",
1091 sprintf(procfile,
"/proc/self/fd/%i", *mountpoint_fd);
1095 "%s: mountpoint %s is not a directory or a regular file\n",
1106 if (statfs(*mntp, &fs_buf)) {
1107 fprintf(stderr,
"%s: failed to access mountpoint %s: %s\n",
1108 progname, mnt, strerror(errno));
1117 typeof(fs_buf.f_type) f_type_whitelist[] = {
1149#if __SIZEOF_LONG__ > 4
1150 0x736675005346544e ,
1156 for (i = 0; i <
sizeof(f_type_whitelist)/
sizeof(f_type_whitelist[0]); i++) {
1157 if (f_type_whitelist[i] == fs_buf.f_type)
1161 fprintf(stderr,
"%s: mounting over filesystem type %#010lx is forbidden\n",
1162 progname, (
unsigned long)fs_buf.f_type);
1166static int try_open(
const char *dev,
char **devp,
int silent)
1168 int fd = open(dev, O_RDWR);
1170 *devp = strdup(dev);
1171 if (*devp == NULL) {
1172 fprintf(stderr,
"%s: failed to allocate memory\n",
1177 }
else if (errno == ENODEV ||
1181 fprintf(stderr,
"%s: failed to open %s: %s\n", progname, dev,
1187static int try_open_fuse_device(
char **devp)
1192 fd = try_open(FUSE_DEV, devp, 0);
1197static int open_fuse_device(
char **devp)
1199 int fd = try_open_fuse_device(devp);
1204 "%s: fuse device not found, try 'modprobe fuse' first\n",
1211static int mount_fuse(
const char *mnt,
const char *opts,
const char **type)
1217 char *source = NULL;
1218 char *mnt_opts = NULL;
1219 const char *real_mnt = mnt;
1220 int mountpoint_fd = -1;
1221 char *do_mount_opts = NULL;
1222 char *x_opts = NULL;
1224 fd = open_fuse_device(&dev);
1231 if (getuid() != 0 && mount_max != -1) {
1232 int mount_count = count_fuse_fs();
1233 if (mount_count >= mount_max) {
1234 fprintf(stderr,
"%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1240 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1244 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1247 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1248 fd, do_mount_opts, dev, &source, &mnt_opts);
1250 if (mountpoint_fd != -1)
1251 close(mountpoint_fd);
1258 fprintf(stderr,
"%s: failed to chdir to '/'\n", progname);
1262 if (geteuid() == 0) {
1263 if (x_opts && strlen(x_opts) > 0) {
1268 size_t mnt_opts_len = strlen(mnt_opts);
1269 size_t x_mnt_opts_len = mnt_opts_len+
1271 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1274 strcpy(x_mnt_opts, mnt_opts);
1275 strncat(x_mnt_opts,
",", 2);
1278 strncat(x_mnt_opts, x_opts,
1279 x_mnt_opts_len - mnt_opts_len - 2);
1282 mnt_opts = x_mnt_opts;
1285 res = add_mount(source, mnt, *type, mnt_opts);
1297 free(do_mount_opts);
1307static int send_fd(
int sock_fd,
int fd)
1311 struct cmsghdr *p_cmsg;
1313 size_t cmsgbuf[CMSG_SPACE(
sizeof(fd)) /
sizeof(size_t)];
1317 msg.msg_control = cmsgbuf;
1318 msg.msg_controllen =
sizeof(cmsgbuf);
1319 p_cmsg = CMSG_FIRSTHDR(&msg);
1320 p_cmsg->cmsg_level = SOL_SOCKET;
1321 p_cmsg->cmsg_type = SCM_RIGHTS;
1322 p_cmsg->cmsg_len = CMSG_LEN(
sizeof(fd));
1323 p_fds = (
int *) CMSG_DATA(p_cmsg);
1325 msg.msg_controllen = p_cmsg->cmsg_len;
1326 msg.msg_name = NULL;
1327 msg.msg_namelen = 0;
1333 vec.iov_base = &sendchar;
1334 vec.iov_len =
sizeof(sendchar);
1335 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1337 perror(
"sending file descriptor");
1349static int recheck_ENOTCONN_as_owner(
const char *mnt)
1353 perror(
"fuse: recheck_ENOTCONN_as_owner can't fork");
1354 _exit(EXIT_FAILURE);
1355 }
else if(pid == 0) {
1356 uid_t uid = getuid();
1357 gid_t gid = getgid();
1358 if(setresgid(gid, gid, gid) == -1) {
1359 perror(
"fuse: can't set resgid");
1360 _exit(EXIT_FAILURE);
1362 if(setresuid(uid, uid, uid) == -1) {
1363 perror(
"fuse: can't set resuid");
1364 _exit(EXIT_FAILURE);
1367 int fd = open(mnt, O_RDONLY);
1368 if(fd == -1 && errno == ENOTCONN)
1369 _exit(EXIT_SUCCESS);
1371 _exit(EXIT_FAILURE);
1374 int res = waitpid(pid, &status, 0);
1376 perror(
"fuse: waiting for child failed");
1377 _exit(EXIT_FAILURE);
1379 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1399static int should_auto_unmount(
const char *mnt,
const char *type)
1408 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
1412 if (chdir_to_parent(copy, &last) == -1)
1414 if (check_is_mount(last, mnt, type) == -1)
1417 fd = open(mnt, O_RDONLY);
1427 result = recheck_ENOTCONN_as_owner(mnt);
1439static void usage(
void)
1441 printf(
"%s: [options] mountpoint\n"
1444 " -V print version\n"
1445 " -o opt[,opt...] mount options\n"
1448 " -z lazy unmount\n",
1453static void show_version(
void)
1455 printf(
"fusermount3 version: %s\n", PACKAGE_VERSION);
1459static void close_range_loop(
int min_fd,
int max_fd,
int cfd)
1461 for (
int fd = min_fd; fd <= max_fd; fd++)
1471static int close_inherited_fds(
int cfd)
1477 if (cfd <= STDERR_FILENO)
1480#ifdef HAVE_CLOSE_RANGE
1481 if (cfd < STDERR_FILENO + 2) {
1482 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1484 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1490 rc = close_range(cfd + 1, ~0U, 0);
1497 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1499 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1502 nullfd = open(
"/dev/null", O_RDWR);
1504 perror(
"fusermount: cannot open /dev/null");
1509 dup2(nullfd, STDIN_FILENO);
1510 dup2(nullfd, STDOUT_FILENO);
1511 dup2(nullfd, STDERR_FILENO);
1512 if (nullfd > STDERR_FILENO)
1518int main(
int argc,
char *argv[])
1526 static int unmount = 0;
1527 static int lazy = 0;
1528 static int quiet = 0;
1529 char *commfd = NULL;
1531 const char *opts =
"";
1532 const char *type = NULL;
1533 int setup_auto_unmount_only = 0;
1535 static const struct option long_opts[] = {
1536 {
"unmount", no_argument, NULL,
'u'},
1537 {
"lazy", no_argument, NULL,
'z'},
1538 {
"quiet", no_argument, NULL,
'q'},
1539 {
"help", no_argument, NULL,
'h'},
1540 {
"version", no_argument, NULL,
'V'},
1541 {
"options", required_argument, NULL,
'o'},
1544 {
"auto-unmount", no_argument, NULL,
'U'},
1545 {
"comm-fd", required_argument, NULL,
'c'},
1548 progname = strdup(argc > 0 ? argv[0] :
"fusermount");
1549 if (progname == NULL) {
1550 fprintf(stderr,
"%s: failed to allocate memory\n", argv[0]);
1554 while ((ch = getopt_long(argc, argv,
"hVo:uzq", long_opts,
1575 setup_auto_unmount_only = 1;
1593 if (lazy && !unmount) {
1594 fprintf(stderr,
"%s: -z can only be used with -u\n", progname);
1598 if (optind >= argc) {
1599 fprintf(stderr,
"%s: missing mountpoint argument\n", progname);
1601 }
else if (argc > optind + 1) {
1602 fprintf(stderr,
"%s: extra arguments after the mountpoint\n",
1607 origmnt = argv[optind];
1610 mnt = fuse_mnt_resolve_path(progname, origmnt);
1614 fprintf(stderr,
"%s: failed to chdir to '/'\n", progname);
1623 if (!setup_auto_unmount_only && unmount)
1627 commfd = getenv(FUSE_COMMFD_ENV);
1628 if (commfd == NULL) {
1629 fprintf(stderr,
"%s: old style mounting not supported\n",
1634 res = libfuse_strtol(commfd, &cfd);
1637 "%s: invalid _FUSE_COMMFD: %s\n",
1644 struct stat statbuf;
1645 fstat(cfd, &statbuf);
1646 if(!S_ISSOCK(statbuf.st_mode)) {
1648 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1654 if (setup_auto_unmount_only)
1655 goto wait_for_auto_unmount;
1657 fd = mount_fuse(mnt, opts, &type);
1661 res = send_fd(cfd, fd);
1663 umount2(mnt, MNT_DETACH);
1668 if (!auto_unmount) {
1674wait_for_auto_unmount:
1680 res = close_inherited_fds(cfd);
1687 fprintf(stderr,
"%s: failed to chdir to '/'\n", progname);
1691 sigfillset(&sigset);
1692 sigprocmask(SIG_BLOCK, &sigset, NULL);
1698 unsigned char buf[16];
1699 int n = recv(cfd, buf,
sizeof(buf), 0);
1710 if (!should_auto_unmount(mnt, type)) {
1716 res = unmount_fuse(mnt, quiet, lazy);
1718 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1719 if (res == -1 && !quiet)
1721 "%s: failed to unmount %s: %s\n",
1722 progname, mnt, strerror(errno));