2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 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 */
10 #define _GNU_SOURCE /* for clone */
13 #include "mount_util.h"
26 #include <sys/mount.h>
27 #include <sys/fsuid.h>
28 #include <sys/socket.h>
29 #include <sys/utsname.h>
32 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
34 #define FUSE_DEV_OLD "/proc/fs/fuse/dev"
35 #define FUSE_DEV_NEW "/dev/fuse"
36 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
37 #define FUSE_CONF "/etc/fuse.conf"
40 #define MS_DIRSYNC 128
46 #define MS_PRIVATE (1<<18)
50 #define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
52 #ifndef UMOUNT_NOFOLLOW
53 #define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
56 #define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
59 static const char *progname
;
61 static int user_allow_other
= 0;
62 static int mount_max
= 1000;
64 static int auto_unmount
= 0;
66 static const char *get_user_name(void)
68 struct passwd
*pw
= getpwuid(getuid());
69 if (pw
!= NULL
&& pw
->pw_name
!= NULL
)
72 fprintf(stderr
, "%s: could not determine username\n", progname
);
77 static uid_t oldfsuid
;
78 static gid_t oldfsgid
;
80 static void drop_privs(void)
83 oldfsuid
= setfsuid(getuid());
84 oldfsgid
= setfsgid(getgid());
88 static void restore_privs(void)
98 * Make sure that /etc/mtab is checked and updated atomically
100 static int lock_umount(void)
102 const char *mtab_lock
= _PATH_MOUNTED
".fuselock";
105 struct stat mtab_stat
;
107 /* /etc/mtab could be a symlink to /proc/mounts */
108 if (lstat(_PATH_MOUNTED
, &mtab_stat
) == 0 && S_ISLNK(mtab_stat
.st_mode
))
111 mtablock
= open(mtab_lock
, O_RDWR
| O_CREAT
, 0600);
112 if (mtablock
== -1) {
113 fprintf(stderr
, "%s: unable to open fuse lock file: %s\n",
114 progname
, strerror(errno
));
117 res
= lockf(mtablock
, F_LOCK
, 0);
119 fprintf(stderr
, "%s: error getting lock: %s\n", progname
,
128 static void unlock_umount(int mtablock
)
133 res
= lockf(mtablock
, F_ULOCK
, 0);
135 fprintf(stderr
, "%s: error releasing lock: %s\n",
136 progname
, strerror(errno
));
142 static int add_mount(const char *source
, const char *mnt
, const char *type
,
145 return fuse_mnt_add_mount(progname
, source
, mnt
, type
, opts
);
148 static int may_unmount(const char *mnt
, int quiet
)
152 const char *user
= NULL
;
156 const char *mtab
= _PATH_MOUNTED
;
158 user
= get_user_name();
162 fp
= setmntent(mtab
, "r");
164 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, mtab
,
169 uidlen
= sprintf(uidstr
, "%u", getuid());
172 while ((entp
= getmntent(fp
)) != NULL
) {
173 if (!found
&& strcmp(entp
->mnt_dir
, mnt
) == 0 &&
174 (strcmp(entp
->mnt_type
, "fuse") == 0 ||
175 strcmp(entp
->mnt_type
, "fuseblk") == 0 ||
176 strncmp(entp
->mnt_type
, "fuse.", 5) == 0 ||
177 strncmp(entp
->mnt_type
, "fuseblk.", 8) == 0)) {
178 char *p
= strstr(entp
->mnt_opts
, "user=");
180 (p
== entp
->mnt_opts
|| *(p
-1) == ',') &&
181 strcmp(p
+ 5, user
) == 0) {
185 /* /etc/mtab is a link pointing to
188 strstr(entp
->mnt_opts
, "user_id=")) &&
189 (p
== entp
->mnt_opts
||
191 strncmp(p
+ 8, uidstr
, uidlen
) == 0 &&
192 (*(p
+8+uidlen
) == ',' ||
193 *(p
+8+uidlen
) == '\0')) {
204 "%s: entry for %s not found in %s\n",
205 progname
, mnt
, mtab
);
213 * Check whether the file specified in "fusermount -u" is really a
214 * mountpoint and not a symlink. This is necessary otherwise the user
215 * could move the mountpoint away and replace it with a symlink
216 * pointing to an arbitrary mount, thereby tricking fusermount into
217 * unmounting that (umount(2) will follow symlinks).
219 * This is the child process running in a separate mount namespace, so
220 * we don't mess with the global namespace and if the process is
221 * killed for any reason, mounts are automatically cleaned up.
223 * First make sure nothing is propagated back into the parent
224 * namespace by marking all mounts "private".
226 * Then bind mount parent onto a stable base where the user can't move
229 * Finally check /proc/mounts for an entry matching the requested
230 * mountpoint. If it's found then we are OK, and the user can't move
231 * it around within the parent directory as rename() will return
232 * EBUSY. Be careful to ignore any mounts that existed before the
235 static int check_is_mount_child(void *p
)
238 const char *last
= a
[0];
239 const char *mnt
= a
[1];
241 const char *procmounts
= "/proc/mounts";
247 res
= mount("", "/", "", MS_PRIVATE
| MS_REC
, NULL
);
249 fprintf(stderr
, "%s: failed to mark mounts private: %s\n",
250 progname
, strerror(errno
));
254 fp
= setmntent(procmounts
, "r");
256 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
,
257 procmounts
, strerror(errno
));
262 while (getmntent(fp
) != NULL
)
266 fp
= setmntent(procmounts
, "r");
268 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
,
269 procmounts
, strerror(errno
));
273 res
= mount(".", "/", "", MS_BIND
| MS_REC
, NULL
);
275 fprintf(stderr
, "%s: failed to bind parent to /: %s\n",
276 progname
, strerror(errno
));
281 while ((entp
= getmntent(fp
)) != NULL
) {
286 if (entp
->mnt_dir
[0] == '/' &&
287 strcmp(entp
->mnt_dir
+ 1, last
) == 0) {
295 fprintf(stderr
, "%s: %s not mounted\n", progname
, mnt
);
302 static pid_t
clone_newns(void *a
)
305 char *stack
= buf
+ (sizeof(buf
) / 2 - ((size_t) buf
& 15));
308 extern int __clone2(int (*fn
)(void *),
309 void *child_stack_base
, size_t stack_size
,
310 int flags
, void *arg
, pid_t
*ptid
,
311 void *tls
, pid_t
*ctid
);
313 return __clone2(check_is_mount_child
, stack
, sizeof(buf
) / 2,
314 CLONE_NEWNS
, a
, NULL
, NULL
, NULL
);
316 return clone(check_is_mount_child
, stack
, CLONE_NEWNS
, a
);
320 static int check_is_mount(const char *last
, const char *mnt
)
324 const char *a
[2] = { last
, mnt
};
326 pid
= clone_newns((void *) a
);
327 if (pid
== (pid_t
) -1) {
328 fprintf(stderr
, "%s: failed to clone namespace: %s\n",
329 progname
, strerror(errno
));
332 p
= waitpid(pid
, &status
, __WCLONE
);
333 if (p
== (pid_t
) -1) {
334 fprintf(stderr
, "%s: waitpid failed: %s\n",
335 progname
, strerror(errno
));
338 if (!WIFEXITED(status
)) {
339 fprintf(stderr
, "%s: child terminated abnormally (status %i)\n",
343 if (WEXITSTATUS(status
) != 0)
349 static int chdir_to_parent(char *copy
, const char **lastp
)
356 tmp
= strrchr(copy
, '/');
357 if (tmp
== NULL
|| tmp
[1] == '\0') {
358 fprintf(stderr
, "%s: internal error: invalid abs path: <%s>\n",
366 } else if (tmp
[1] != '\0') {
376 fprintf(stderr
, "%s: failed to chdir to %s: %s\n",
377 progname
, parent
, strerror(errno
));
381 if (getcwd(buf
, sizeof(buf
)) == NULL
) {
382 fprintf(stderr
, "%s: failed to obtain current directory: %s\n",
383 progname
, strerror(errno
));
386 if (strcmp(buf
, parent
) != 0) {
387 fprintf(stderr
, "%s: mountpoint moved (%s -> %s)\n", progname
,
396 /* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
397 static int umount_nofollow_support(void)
399 int res
= umount2("", UMOUNT_UNUSED
);
400 if (res
!= -1 || errno
!= EINVAL
)
403 res
= umount2("", UMOUNT_NOFOLLOW
);
404 if (res
!= -1 || errno
!= ENOENT
)
410 static int unmount_fuse_locked(const char *mnt
, int quiet
, int lazy
)
415 int umount_flags
= lazy
? UMOUNT_DETACH
: 0;
418 res
= may_unmount(mnt
, quiet
);
425 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
429 res
= chdir_to_parent(copy
, &last
);
433 if (umount_nofollow_support()) {
434 umount_flags
|= UMOUNT_NOFOLLOW
;
436 res
= check_is_mount(last
, mnt
);
441 res
= umount2(last
, umount_flags
);
442 if (res
== -1 && !quiet
) {
443 fprintf(stderr
, "%s: failed to unmount %s: %s\n",
444 progname
, mnt
, strerror(errno
));
453 fprintf(stderr
, "%s: failed to chdir to '/'\n", progname
);
457 return fuse_mnt_remove_mount(progname
, mnt
);
460 static int unmount_fuse(const char *mnt
, int quiet
, int lazy
)
463 int mtablock
= lock_umount();
465 res
= unmount_fuse_locked(mnt
, quiet
, lazy
);
466 unlock_umount(mtablock
);
471 static int count_fuse_fs(void)
475 const char *mtab
= _PATH_MOUNTED
;
476 FILE *fp
= setmntent(mtab
, "r");
478 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, mtab
,
482 while ((entp
= getmntent(fp
)) != NULL
) {
483 if (strcmp(entp
->mnt_type
, "fuse") == 0 ||
484 strncmp(entp
->mnt_type
, "fuse.", 5) == 0)
492 #else /* IGNORE_MTAB */
493 static int count_fuse_fs()
498 static int add_mount(const char *source
, const char *mnt
, const char *type
,
508 static int unmount_fuse(const char *mnt
, int quiet
, int lazy
)
510 return fuse_mnt_umount(progname
, mnt
, mnt
, lazy
);
512 #endif /* IGNORE_MTAB */
514 static void strip_line(char *line
)
516 char *s
= strchr(line
, '#');
519 for (s
= line
+ strlen(line
) - 1;
520 s
>= line
&& isspace((unsigned char) *s
); s
--);
522 for (s
= line
; isspace((unsigned char) *s
); s
++);
524 memmove(line
, s
, strlen(s
)+1);
527 static void parse_line(char *line
, int linenum
)
530 if (strcmp(line
, "user_allow_other") == 0)
531 user_allow_other
= 1;
532 else if (sscanf(line
, "mount_max = %i", &tmp
) == 1)
536 "%s: unknown parameter in %s at line %i: '%s'\n",
537 progname
, FUSE_CONF
, linenum
, line
);
540 static void read_conf(void)
542 FILE *fp
= fopen(FUSE_CONF
, "r");
547 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
549 if (line
[strlen(line
)-1] == '\n') {
551 parse_line(line
, linenum
);
555 } else if(line
[strlen(line
)-1] == '\n') {
556 fprintf(stderr
, "%s: reading %s: line %i too long\n", progname
, FUSE_CONF
, linenum
);
564 fprintf(stderr
, "%s: reading %s: missing newline at end of file\n", progname
, FUSE_CONF
);
568 } else if (errno
!= ENOENT
) {
569 fprintf(stderr
, "%s: failed to open %s: %s\n",
570 progname
, FUSE_CONF
, strerror(errno
));
574 static int begins_with(const char *s
, const char *beg
)
576 if (strncmp(s
, beg
, strlen(beg
)) == 0)
589 static struct mount_flags mount_flags
[] = {
590 {"rw", MS_RDONLY
, 0, 1},
591 {"ro", MS_RDONLY
, 1, 1},
592 {"suid", MS_NOSUID
, 0, 0},
593 {"nosuid", MS_NOSUID
, 1, 1},
594 {"dev", MS_NODEV
, 0, 0},
595 {"nodev", MS_NODEV
, 1, 1},
596 {"exec", MS_NOEXEC
, 0, 1},
597 {"noexec", MS_NOEXEC
, 1, 1},
598 {"async", MS_SYNCHRONOUS
, 0, 1},
599 {"sync", MS_SYNCHRONOUS
, 1, 1},
600 {"atime", MS_NOATIME
, 0, 1},
601 {"noatime", MS_NOATIME
, 1, 1},
602 {"dirsync", MS_DIRSYNC
, 1, 1},
606 static int find_mount_flag(const char *s
, unsigned len
, int *on
, int *flag
)
610 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
611 const char *opt
= mount_flags
[i
].opt
;
612 if (strlen(opt
) == len
&& strncmp(opt
, s
, len
) == 0) {
613 *on
= mount_flags
[i
].on
;
614 *flag
= mount_flags
[i
].flag
;
615 if (!mount_flags
[i
].safe
&& getuid() != 0) {
618 "%s: unsafe option %s ignored\n",
627 static int add_option(char **optsp
, const char *opt
, unsigned expand
)
631 newopts
= strdup(opt
);
633 unsigned oldsize
= strlen(*optsp
);
634 unsigned newsize
= oldsize
+ 1 + strlen(opt
) + expand
+ 1;
635 newopts
= (char *) realloc(*optsp
, newsize
);
637 sprintf(newopts
+ oldsize
, ",%s", opt
);
639 if (newopts
== NULL
) {
640 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
647 static int get_mnt_opts(int flags
, char *opts
, char **mnt_optsp
)
652 if (!(flags
& MS_RDONLY
) && add_option(mnt_optsp
, "rw", 0) == -1)
655 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
656 if (mount_flags
[i
].on
&& (flags
& mount_flags
[i
].flag
) &&
657 add_option(mnt_optsp
, mount_flags
[i
].opt
, 0) == -1)
661 if (add_option(mnt_optsp
, opts
, 0) == -1)
663 /* remove comma from end of opts*/
664 l
= strlen(*mnt_optsp
);
665 if ((*mnt_optsp
)[l
-1] == ',')
666 (*mnt_optsp
)[l
-1] = '\0';
668 const char *user
= get_user_name();
672 if (add_option(mnt_optsp
, "user=", strlen(user
)) == -1)
674 strcat(*mnt_optsp
, user
);
679 static int opt_eq(const char *s
, unsigned len
, const char *opt
)
681 if(strlen(opt
) == len
&& strncmp(s
, opt
, len
) == 0)
687 static int get_string_opt(const char *s
, unsigned len
, const char *opt
,
691 unsigned opt_len
= strlen(opt
);
696 *val
= (char *) malloc(len
- opt_len
+ 1);
698 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
705 for (i
= 0; i
< len
; i
++) {
706 if (s
[i
] == '\\' && i
+ 1 < len
)
714 static int do_mount(const char *mnt
, char **typep
, mode_t rootmode
,
715 int fd
, const char *opts
, const char *dev
, char **sourcep
,
716 char **mnt_optsp
, off_t rootsize
)
719 int flags
= MS_NOSUID
| MS_NODEV
;
721 char *mnt_opts
= NULL
;
725 char *subtype
= NULL
;
731 optbuf
= (char *) malloc(strlen(opts
) + 128);
733 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
737 for (s
= opts
, d
= optbuf
; *s
;) {
739 const char *fsname_str
= "fsname=";
740 const char *subtype_str
= "subtype=";
741 for (len
= 0; s
[len
]; len
++) {
742 if (s
[len
] == '\\' && s
[len
+ 1])
744 else if (s
[len
] == ',')
747 if (begins_with(s
, fsname_str
)) {
748 if (!get_string_opt(s
, len
, fsname_str
, &fsname
))
750 } else if (begins_with(s
, subtype_str
)) {
751 if (!get_string_opt(s
, len
, subtype_str
, &subtype
))
753 } else if (opt_eq(s
, len
, "blkdev")) {
756 "%s: option blkdev is privileged\n",
761 } else if (opt_eq(s
, len
, "nonempty")) {
763 } else if (opt_eq(s
, len
, "auto_unmount")) {
765 } else if (!begins_with(s
, "fd=") &&
766 !begins_with(s
, "rootmode=") &&
767 !begins_with(s
, "user_id=") &&
768 !begins_with(s
, "group_id=")) {
772 if (opt_eq(s
, len
, "large_read")) {
773 struct utsname utsname
;
775 res
= uname(&utsname
);
777 sscanf(utsname
.release
, "%u.%u",
778 &kmaj
, &kmin
) == 2 &&
779 (kmaj
> 2 || (kmaj
== 2 && kmin
> 4))) {
780 fprintf(stderr
, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname
, kmaj
, kmin
);
784 if (getuid() != 0 && !user_allow_other
&&
785 (opt_eq(s
, len
, "allow_other") ||
786 opt_eq(s
, len
, "allow_root"))) {
787 fprintf(stderr
, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname
, len
, s
);
791 if (find_mount_flag(s
, len
, &on
, &flag
)) {
808 res
= get_mnt_opts(flags
, optbuf
, &mnt_opts
);
812 sprintf(d
, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
813 fd
, rootmode
, getuid(), getgid());
816 fuse_mnt_check_empty(progname
, mnt
, rootmode
, rootsize
) == -1)
819 source
= malloc((fsname
? strlen(fsname
) : 0) +
820 (subtype
? strlen(subtype
) : 0) + strlen(dev
) + 32);
822 type
= malloc((subtype
? strlen(subtype
) : 0) + 32);
823 if (!type
|| !source
) {
824 fprintf(stderr
, "%s: failed to allocate memory\n", progname
);
829 sprintf(type
, "%s.%s", blkdev
? "fuseblk" : "fuse", subtype
);
831 strcpy(type
, blkdev
? "fuseblk" : "fuse");
834 strcpy(source
, fsname
);
836 strcpy(source
, subtype
? subtype
: dev
);
838 res
= mount(source
, mnt
, type
, flags
, optbuf
);
839 if (res
== -1 && errno
== ENODEV
&& subtype
) {
840 /* Probably missing subtype support */
841 strcpy(type
, blkdev
? "fuseblk" : "fuse");
844 sprintf(source
, "%s#%s", subtype
, fsname
);
846 strcpy(source
, type
);
849 res
= mount(source
, mnt
, type
, flags
, optbuf
);
851 if (res
== -1 && errno
== EINVAL
) {
852 /* It could be an old version not supporting group_id */
853 sprintf(d
, "fd=%i,rootmode=%o,user_id=%i",
854 fd
, rootmode
, getuid());
855 res
= mount(source
, mnt
, type
, flags
, optbuf
);
858 int errno_save
= errno
;
859 if (blkdev
&& errno
== ENODEV
&& !fuse_mnt_check_fuseblk())
860 fprintf(stderr
, "%s: 'fuseblk' support missing\n",
863 fprintf(stderr
, "%s: mount failed: %s\n", progname
,
864 strerror(errno_save
));
869 *mnt_optsp
= mnt_opts
;
885 static int check_version(const char *dev
)
890 const char *version_file
;
893 if (strcmp(dev
, FUSE_DEV_OLD
) != 0)
896 version_file
= FUSE_VERSION_FILE_OLD
;
897 vf
= fopen(version_file
, "r");
899 fprintf(stderr
, "%s: kernel interface too old\n", progname
);
902 res
= fscanf(vf
, "%i.%i", &majorver
, &minorver
);
905 fprintf(stderr
, "%s: error reading %s\n", progname
,
910 fprintf(stderr
, "%s: kernel interface too old\n", progname
);
916 static int check_perm(const char **mntp
, struct stat
*stbuf
, int *mountpoint_fd
)
919 const char *mnt
= *mntp
;
920 const char *origmnt
= mnt
;
922 res
= lstat(mnt
, stbuf
);
924 fprintf(stderr
, "%s: failed to access mountpoint %s: %s\n",
925 progname
, mnt
, strerror(errno
));
929 /* No permission checking is done for root */
933 if (S_ISDIR(stbuf
->st_mode
)) {
937 "%s: failed to chdir to mountpoint: %s\n",
938 progname
, strerror(errno
));
942 res
= lstat(mnt
, stbuf
);
945 "%s: failed to access mountpoint %s: %s\n",
946 progname
, origmnt
, strerror(errno
));
950 if ((stbuf
->st_mode
& S_ISVTX
) && stbuf
->st_uid
!= getuid()) {
951 fprintf(stderr
, "%s: mountpoint %s not owned by user\n",
956 res
= access(mnt
, W_OK
);
958 fprintf(stderr
, "%s: user has no write access to mountpoint %s\n",
962 } else if (S_ISREG(stbuf
->st_mode
)) {
963 static char procfile
[256];
964 *mountpoint_fd
= open(mnt
, O_WRONLY
);
965 if (*mountpoint_fd
== -1) {
966 fprintf(stderr
, "%s: failed to open %s: %s\n",
967 progname
, mnt
, strerror(errno
));
970 res
= fstat(*mountpoint_fd
, stbuf
);
973 "%s: failed to access mountpoint %s: %s\n",
974 progname
, mnt
, strerror(errno
));
977 if (!S_ISREG(stbuf
->st_mode
)) {
979 "%s: mountpoint %s is no longer a regular file\n",
984 sprintf(procfile
, "/proc/self/fd/%i", *mountpoint_fd
);
988 "%s: mountpoint %s is not a directory or a regular file\n",
997 static int try_open(const char *dev
, char **devp
, int silent
)
999 int fd
= open(dev
, O_RDWR
);
1001 *devp
= strdup(dev
);
1002 if (*devp
== NULL
) {
1003 fprintf(stderr
, "%s: failed to allocate memory\n",
1008 } else if (errno
== ENODEV
||
1009 errno
== ENOENT
)/* check for ENOENT too, for the udev case */
1012 fprintf(stderr
, "%s: failed to open %s: %s\n", progname
, dev
,
1018 static int try_open_fuse_device(char **devp
)
1024 fd
= try_open(FUSE_DEV_NEW
, devp
, 0);
1030 fd
= try_open(FUSE_DEV_OLD
, devp
, 1);
1037 static int open_fuse_device(char **devp
)
1039 int fd
= try_open_fuse_device(devp
);
1044 "%s: fuse device not found, try 'modprobe fuse' first\n",
1051 static int mount_fuse(const char *mnt
, const char *opts
)
1058 char *source
= NULL
;
1059 char *mnt_opts
= NULL
;
1060 const char *real_mnt
= mnt
;
1061 int mountpoint_fd
= -1;
1063 fd
= open_fuse_device(&dev
);
1070 if (getuid() != 0 && mount_max
!= -1) {
1071 int mount_count
= count_fuse_fs();
1072 if (mount_count
>= mount_max
) {
1073 fprintf(stderr
, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname
);
1078 res
= check_version(dev
);
1080 res
= check_perm(&real_mnt
, &stbuf
, &mountpoint_fd
);
1083 res
= do_mount(real_mnt
, &type
, stbuf
.st_mode
& S_IFMT
,
1084 fd
, opts
, dev
, &source
, &mnt_opts
,
1089 if (mountpoint_fd
!= -1)
1090 close(mountpoint_fd
);
1097 fprintf(stderr
, "%s: failed to chdir to '/'\n", progname
);
1101 if (geteuid() == 0) {
1102 res
= add_mount(source
, mnt
, type
, mnt_opts
);
1104 /* Can't clean up mount in a non-racy way */
1123 static int send_fd(int sock_fd
, int fd
)
1127 struct cmsghdr
*p_cmsg
;
1129 size_t cmsgbuf
[CMSG_SPACE(sizeof(fd
)) / sizeof(size_t)];
1133 msg
.msg_control
= cmsgbuf
;
1134 msg
.msg_controllen
= sizeof(cmsgbuf
);
1135 p_cmsg
= CMSG_FIRSTHDR(&msg
);
1136 p_cmsg
->cmsg_level
= SOL_SOCKET
;
1137 p_cmsg
->cmsg_type
= SCM_RIGHTS
;
1138 p_cmsg
->cmsg_len
= CMSG_LEN(sizeof(fd
));
1139 p_fds
= (int *) CMSG_DATA(p_cmsg
);
1141 msg
.msg_controllen
= p_cmsg
->cmsg_len
;
1142 msg
.msg_name
= NULL
;
1143 msg
.msg_namelen
= 0;
1147 /* "To pass file descriptors or credentials you need to send/read at
1148 * least one byte" (man 7 unix) */
1149 vec
.iov_base
= &sendchar
;
1150 vec
.iov_len
= sizeof(sendchar
);
1151 while ((retval
= sendmsg(sock_fd
, &msg
, 0)) == -1 && errno
== EINTR
);
1153 perror("sending file descriptor");
1159 static void usage(void)
1162 "%s: [options] mountpoint\n"
1165 " -V print version\n"
1166 " -o opt[,opt...] mount options\n"
1169 " -z lazy unmount\n",
1174 static void show_version(void)
1176 printf("fusermount version: %s\n", PACKAGE_VERSION
);
1180 int main(int argc
, char *argv
[])
1188 static int unmount
= 0;
1189 static int lazy
= 0;
1190 static int quiet
= 0;
1193 const char *opts
= "";
1195 static const struct option long_opts
[] = {
1196 {"unmount", no_argument
, NULL
, 'u'},
1197 {"lazy", no_argument
, NULL
, 'z'},
1198 {"quiet", no_argument
, NULL
, 'q'},
1199 {"help", no_argument
, NULL
, 'h'},
1200 {"version", no_argument
, NULL
, 'V'},
1203 progname
= strdup(argv
[0]);
1204 if (progname
== NULL
) {
1205 fprintf(stderr
, "%s: failed to allocate memory\n", argv
[0]);
1209 while ((ch
= getopt_long(argc
, argv
, "hVo:uzq", long_opts
,
1241 if (lazy
&& !unmount
) {
1242 fprintf(stderr
, "%s: -z can only be used with -u\n", progname
);
1246 if (optind
>= argc
) {
1247 fprintf(stderr
, "%s: missing mountpoint argument\n", progname
);
1249 } else if (argc
> optind
+ 1) {
1250 fprintf(stderr
, "%s: extra arguments after the mountpoint\n",
1255 origmnt
= argv
[optind
];
1258 mnt
= fuse_mnt_resolve_path(progname
, origmnt
);
1262 fprintf(stderr
, "%s: failed to chdir to '/'\n", progname
);
1274 commfd
= getenv(FUSE_COMMFD_ENV
);
1275 if (commfd
== NULL
) {
1276 fprintf(stderr
, "%s: old style mounting not supported\n",
1281 fd
= mount_fuse(mnt
, opts
);
1286 res
= send_fd(cfd
, fd
);
1294 /* Become a daemon and wait for the parent to exit or die.
1295 ie For the control socket to get closed.
1296 btw We don't want to use daemon() function here because
1297 it forks and messes with the file descriptors. */
1301 fprintf(stderr
, "%s: failed to chdir to '/'\n", progname
);
1305 sigfillset(&sigset
);
1306 sigprocmask(SIG_BLOCK
, &sigset
, NULL
);
1312 unsigned char buf
[16];
1313 int n
= recv(cfd
, buf
, sizeof(buf
), 0);
1326 res
= unmount_fuse(mnt
, quiet
, lazy
);
1328 res
= umount2(mnt
, lazy
? UMOUNT_DETACH
: 0);
1329 if (res
== -1 && !quiet
)
1331 "%s: failed to unmount %s: %s\n",
1332 progname
, mnt
, strerror(errno
));