14#include "fuse_config.h"
18#include "mount_util.h"
29#include <sys/socket.h>
33#include "fuse_mount_compat.h"
38#define MS_RDONLY MNT_RDONLY
39#define MS_NOSUID MNT_NOSUID
40#define MS_NODEV MNT_NODEV
41#define MS_NOEXEC MNT_NOEXEC
42#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
43#define MS_NOATIME MNT_NOATIME
44#define MS_NOSYMFOLLOW MNT_NOSYMFOLLOW
46#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
49#define FUSERMOUNT_PROG "fusermount3"
50#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
51#define FUSE_COMMFD2_ENV "_FUSE_COMMFD2"
76 char *fusermount_opts;
81#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83static const struct fuse_opt fuse_mount_opts[] = {
84 FUSE_MOUNT_OPT(
"allow_other", allow_other),
85 FUSE_MOUNT_OPT(
"blkdev", blkdev),
86 FUSE_MOUNT_OPT(
"auto_unmount", auto_unmount),
87 FUSE_MOUNT_OPT(
"fsname=%s", fsname),
88 FUSE_MOUNT_OPT(
"max_read=%u", max_read),
89 FUSE_MOUNT_OPT(
"subtype=%s", subtype),
129static int fusermount_posix_spawn(posix_spawn_file_actions_t *action,
130 char const *
const argv[], pid_t *out_pid)
132 const char *full_path = FUSERMOUNT_DIR
"/" FUSERMOUNT_PROG;
138 int status = posix_spawn(&pid, full_path, action, NULL,
139 (
char *
const *) argv, environ);
142 status = posix_spawnp(&pid, FUSERMOUNT_PROG, action, NULL,
143 (
char *
const *) argv, environ);
148 "On calling fusermount posix_spawn failed: %s\n",
156 waitpid(pid, NULL, 0);
161void fuse_mount_version(
void)
163 char const *
const argv[] = {FUSERMOUNT_PROG,
"--version", NULL};
164 int status = fusermount_posix_spawn(NULL, argv, NULL);
167 fuse_log(FUSE_LOG_ERR,
"Running '%s --version' failed",
177static const struct mount_flags mount_flags[] = {
178 {
"rw", MS_RDONLY, 0},
179 {
"ro", MS_RDONLY, 1},
180 {
"suid", MS_NOSUID, 0},
181 {
"nosuid", MS_NOSUID, 1},
182 {
"dev", MS_NODEV, 0},
183 {
"nodev", MS_NODEV, 1},
184 {
"exec", MS_NOEXEC, 0},
185 {
"noexec", MS_NOEXEC, 1},
186 {
"async", MS_SYNCHRONOUS, 0},
187 {
"sync", MS_SYNCHRONOUS, 1},
188 {
"noatime", MS_NOATIME, 1},
189 {
"nodiratime", MS_NODIRATIME, 1},
190 {
"norelatime", MS_RELATIME, 0},
191 {
"nostrictatime", MS_STRICTATIME, 0},
192 {
"symfollow", MS_NOSYMFOLLOW, 0},
193 {
"nosymfollow", MS_NOSYMFOLLOW, 1},
195 {
"dirsync", MS_DIRSYNC, 1},
200unsigned get_max_read(
struct mount_opts *o)
205static void set_mount_flag(
const char *s,
int *flags)
209 for (i = 0; mount_flags[i].opt != NULL; i++) {
210 const char *opt = mount_flags[i].opt;
211 if (strcmp(opt, s) == 0) {
212 if (mount_flags[i].on)
213 *flags |= mount_flags[i].flag;
215 *flags &= ~mount_flags[i].flag;
219 fuse_log(FUSE_LOG_ERR,
"fuse: internal error, can't find mount flag\n");
223static int fuse_mount_opt_proc(
void *data,
const char *arg,
int key,
227 struct mount_opts *mo = data;
234 set_mount_flag(arg, &mo->flags);
240 case KEY_FUSERMOUNT_OPT:
243 case KEY_SUBTYPE_OPT:
251 return (strncmp(
"x-", arg, 2) == 0) ?
264static int receive_fd(
int fd)
270 size_t ccmsg[CMSG_SPACE(
sizeof(
int)) /
sizeof(size_t)];
271 struct cmsghdr *cmsg;
276 memset(&msg, 0,
sizeof(msg));
283 msg.msg_control = ccmsg;
284 msg.msg_controllen =
sizeof(ccmsg);
286 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
288 fuse_log(FUSE_LOG_ERR,
"recvmsg failed: %s", strerror(errno));
296 cmsg = CMSG_FIRSTHDR(&msg);
297 if (cmsg->cmsg_type != SCM_RIGHTS) {
298 fuse_log(FUSE_LOG_ERR,
"got control message of unknown type %d\n",
302 return *(
int*)CMSG_DATA(cmsg);
305void fuse_kern_unmount(
const char *mountpoint,
int fd)
314 res = poll(&pfd, 1, 0);
326 if (res == 1 && (pfd.revents & POLLERR))
330 if (geteuid() == 0) {
331 fuse_mnt_umount(
"fuse", mountpoint, mountpoint, 1);
335 res = umount2(mountpoint, 2);
339 char const *
const argv[] =
340 { FUSERMOUNT_PROG,
"--unmount",
"--quiet",
"--lazy",
341 "--", mountpoint, NULL };
342 int status = fusermount_posix_spawn(NULL, argv, NULL);
344 fuse_log(FUSE_LOG_ERR,
"Spawning %s to unmount failed: %s",
345 FUSERMOUNT_PROG, strerror(-status));
350static int setup_auto_unmount(
const char *mountpoint,
int quiet)
357 fuse_log(FUSE_LOG_ERR,
"fuse: missing mountpoint parameter\n");
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
363 fuse_log(FUSE_LOG_ERR,
"Setting up auto-unmountsocketpair() failed",
368 char arg_fd_entry[30];
369 snprintf(arg_fd_entry,
sizeof(arg_fd_entry),
"%i", fds[0]);
370 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
377 snprintf(arg_fd_entry,
sizeof(arg_fd_entry),
"%i", fds[1]);
378 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
380 char const *
const argv[] = {
389 posix_spawn_file_actions_t action;
390 posix_spawn_file_actions_init(&action);
393 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO,
"/dev/null", O_WRONLY, 0);
394 posix_spawn_file_actions_addopen(&action, STDERR_FILENO,
"/dev/null", O_WRONLY, 0);
396 posix_spawn_file_actions_addclose(&action, fds[1]);
402 int status = fusermount_posix_spawn(&action, argv, &pid);
404 posix_spawn_file_actions_destroy(&action);
409 fuse_log(FUSE_LOG_ERR,
"fuse: Setting up auto-unmount failed (spawn): %s",
422static int fuse_mount_fusermount(
const char *mountpoint,
struct mount_opts *mo,
423 const char *opts,
int quiet)
430 fuse_log(FUSE_LOG_ERR,
"fuse: missing mountpoint parameter\n");
434 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
436 fuse_log(FUSE_LOG_ERR,
"Running %s: socketpair() failed: %s\n",
437 FUSERMOUNT_PROG, strerror(errno));
441 char arg_fd_entry[30];
442 snprintf(arg_fd_entry,
sizeof(arg_fd_entry),
"%i", fds[0]);
443 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
450 snprintf(arg_fd_entry,
sizeof(arg_fd_entry),
"%i", fds[1]);
451 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
453 char const *
const argv[] = {
455 "-o", opts ? opts :
"",
462 posix_spawn_file_actions_t action;
463 posix_spawn_file_actions_init(&action);
466 posix_spawn_file_actions_addopen(&action, STDOUT_FILENO,
"/dev/null", O_WRONLY, 0);
467 posix_spawn_file_actions_addopen(&action, STDERR_FILENO,
"/dev/null", O_WRONLY, 0);
469 posix_spawn_file_actions_addclose(&action, fds[1]);
471 int status = fusermount_posix_spawn(&action, argv, &pid);
473 posix_spawn_file_actions_destroy(&action);
478 fuse_log(FUSE_LOG_ERR,
"posix_spawn(p)() for %s failed: %s",
479 FUSERMOUNT_PROG, strerror(-status));
486 int fd = receive_fd(fds[1]);
488 if (!mo->auto_unmount) {
492 waitpid(pid, NULL, 0);
496 fcntl(fd, F_SETFD, FD_CLOEXEC);
505static int fuse_mount_sys(
const char *mnt,
struct mount_opts *mo,
506 const char *mnt_opts)
509 const char *devname =
"/dev/fuse";
517 fuse_log(FUSE_LOG_ERR,
"fuse: missing mountpoint parameter\n");
521 res = stat(mnt, &stbuf);
523 fuse_log(FUSE_LOG_ERR,
"fuse: failed to access mountpoint %s: %s\n",
524 mnt, strerror(errno));
528 fd = open(devname, O_RDWR | O_CLOEXEC);
530 if (errno == ENODEV || errno == ENOENT)
531 fuse_log(FUSE_LOG_ERR,
"fuse: device not found, try 'modprobe fuse' first\n");
533 fuse_log(FUSE_LOG_ERR,
"fuse: failed to open %s: %s\n",
534 devname, strerror(errno));
538 fcntl(fd, F_SETFD, FD_CLOEXEC);
540 snprintf(tmp,
sizeof(tmp),
"fd=%i,rootmode=%o,user_id=%u,group_id=%u",
541 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
547 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
548 (mo->subtype ? strlen(mo->subtype) : 0) +
549 strlen(devname) + 32);
551 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
552 if (!type || !source) {
553 fuse_log(FUSE_LOG_ERR,
"fuse: failed to allocate memory\n");
557 strcpy(type, mo->blkdev ?
"fuseblk" :
"fuse");
560 strcat(type, mo->subtype);
563 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
565 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
566 if (res == -1 && errno == ENODEV && mo->subtype) {
568 strcpy(type, mo->blkdev ?
"fuseblk" :
"fuse");
571 sprintf(source,
"%s#%s", mo->subtype,
574 strcpy(source, type);
576 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
583 if (errno == EPERM) {
586 int errno_save = errno;
587 if (mo->blkdev && errno == ENODEV &&
588 !fuse_mnt_check_fuseblk())
590 "fuse: 'fuseblk' support missing\n");
592 fuse_log(FUSE_LOG_ERR,
"fuse: mount failed: %s\n",
593 strerror(errno_save));
600 if (geteuid() == 0) {
601 char *newmnt = fuse_mnt_resolve_path(
"fuse", mnt);
606 res = fuse_mnt_add_mount(
"fuse", source, newmnt, type,
627static int get_mnt_flag_opts(
char **mnt_optsp,
int flags)
634 for (i = 0; mount_flags[i].opt != NULL; i++) {
635 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
642struct mount_opts *parse_mount_opts(
struct fuse_args *args)
644 struct mount_opts *mo;
646 mo = (
struct mount_opts*) malloc(
sizeof(
struct mount_opts));
650 memset(mo, 0,
sizeof(
struct mount_opts));
651 mo->flags = MS_NOSUID | MS_NODEV;
654 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
660 destroy_mount_opts(mo);
664void destroy_mount_opts(
struct mount_opts *mo)
668 free(mo->fusermount_opts);
669 free(mo->subtype_opt);
670 free(mo->kernel_opts);
676int fuse_kern_mount(
const char *mountpoint,
struct mount_opts *mo)
679 char *mnt_opts = NULL;
682 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
689 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
690 if (res >= 0 && mo->auto_unmount) {
691 if(0 > setup_auto_unmount(mountpoint, 0)) {
693 umount2(mountpoint, MNT_DETACH);
696 }
else if (res == -2) {
697 if (mo->fusermount_opts &&
702 char *tmp_opts = NULL;
711 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
714 res = fuse_mount_fusermount(mountpoint, mo,
717 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
void fuse_log(enum fuse_log_level level, const char *fmt,...)
#define FUSE_OPT_KEY(templ, key)
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
int fuse_opt_add_opt(char **opts, const char *opt)