summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/fuse-include/fuse-mount.h11
-rw-r--r--contrib/fuse-lib/mount.c528
-rw-r--r--xlators/mount/fuse/src/Makefile.am3
-rw-r--r--xlators/mount/fuse/src/fuse-bridge.c59
4 files changed, 559 insertions, 42 deletions
diff --git a/contrib/fuse-include/fuse-mount.h b/contrib/fuse-include/fuse-mount.h
new file mode 100644
index 00000000000..d263a80390a
--- /dev/null
+++ b/contrib/fuse-include/fuse-mount.h
@@ -0,0 +1,11 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (c) 2009 Z RESEARCH, Inc. <http://www.zresearch.com>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+void gf_fuse_unmount (const char *mountpoint, int fd);
+int gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param);
diff --git a/contrib/fuse-lib/mount.c b/contrib/fuse-lib/mount.c
new file mode 100644
index 00000000000..c9e2035d467
--- /dev/null
+++ b/contrib/fuse-lib/mount.c
@@ -0,0 +1,528 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (c) 2009 Z RESEARCH, Inc. <http://www.zresearch.com>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <mntent.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "glusterfs.h"
+#include "logging.h"
+#include "common-utils.h"
+
+#define FUSERMOUNT_PROG "fusermount"
+#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
+
+#define GFFUSE_LOGERR(...) \
+ gf_log ("glusterfs-fuse", GF_LOG_ERROR, ## __VA_ARGS__)
+
+/*
+ * Functions below, until following note, were taken from libfuse
+ * (http://git.gluster.com/?p=users/csaba/fuse.git;a=commit;h=b988bbf9)
+ * almost verbatim. What has been changed:
+ * - style adopted to that of glusterfs
+ * - s/fprintf/gf_log/
+ * - s/free/FREE/, s/malloc/MALLOC/
+ * - there are some other minor things
+ */
+
+static int
+mtab_needs_update (const char *mnt)
+{
+ int res;
+ struct stat stbuf;
+
+ /* If mtab is within new mount, don't touch it */
+ if (strncmp (mnt, _PATH_MOUNTED, strlen (mnt)) == 0 &&
+ _PATH_MOUNTED[strlen (mnt)] == '/')
+ return 0;
+
+ /*
+ * Skip mtab update if /etc/mtab:
+ *
+ * - doesn't exist,
+ * - is a symlink,
+ * - is on a read-only filesystem.
+ */
+ res = lstat (_PATH_MOUNTED, &stbuf);
+ if (res == -1) {
+ if (errno == ENOENT)
+ return 0;
+ } else {
+ if (S_ISLNK (stbuf.st_mode))
+ return 0;
+
+ res = access (_PATH_MOUNTED, W_OK);
+ if (res == -1 && errno == EROFS)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+fuse_mnt_add_mount (const char *progname, const char *fsname,
+ const char *mnt, const char *type, const char *opts)
+{
+ int res;
+ int status;
+ sigset_t blockmask;
+ sigset_t oldmask;
+
+ if (!mtab_needs_update (mnt))
+ return 0;
+
+ sigemptyset (&blockmask);
+ sigaddset (&blockmask, SIGCHLD);
+ res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask);
+ if (res == -1) {
+ GFFUSE_LOGERR ("%s: sigprocmask: %s",
+ progname, strerror (errno));
+ return -1;
+ }
+
+ res = fork ();
+ if (res == -1) {
+ GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno));
+ goto out_restore;
+ }
+ if (res == 0) {
+ char templ[] = "/tmp/fusermountXXXXXX";
+ char *tmp;
+
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ setuid (geteuid ());
+
+ /*
+ * hide in a directory, where mount isn't able to resolve
+ * fsname as a valid path
+ */
+ tmp = mkdtemp (templ);
+ if (!tmp) {
+ GFFUSE_LOGERR ("%s: failed to create temporary directory",
+ progname);
+ exit (1);
+ }
+ if (chdir (tmp)) {
+ GFFUSE_LOGERR ("%s: failed to chdir to %s: %s",
+ progname, tmp, strerror (errno));
+ exit (1);
+ }
+ rmdir (tmp);
+ execl ("/bin/mount", "/bin/mount", "-i", "-f", "-t", type,
+ "-o", opts, fsname, mnt, NULL);
+ GFFUSE_LOGERR ("%s: failed to execute /bin/mount: %s",
+ progname, strerror (errno));
+ exit (1);
+ }
+ res = waitpid (res, &status, 0);
+ if (res == -1)
+ GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno));
+
+ if (status != 0)
+ res = -1;
+
+ out_restore:
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ return res;
+}
+
+static char
+*fuse_mnt_resolve_path (const char *progname, const char *orig)
+{
+ char buf[PATH_MAX];
+ char *copy;
+ char *dst;
+ char *end;
+ char *lastcomp;
+ const char *toresolv;
+
+ if (!orig[0]) {
+ GFFUSE_LOGERR ("%s: invalid mountpoint '%s'", progname, orig);
+ return NULL;
+ }
+
+ copy = strdup (orig);
+ if (copy == NULL) {
+ GFFUSE_LOGERR ("%s: failed to allocate memory", progname);
+ return NULL;
+ }
+
+ toresolv = copy;
+ lastcomp = NULL;
+ for (end = copy + strlen (copy) - 1; end > copy && *end == '/'; end --);
+ if (end[0] != '/') {
+ char *tmp;
+ end[1] = '\0';
+ tmp = strrchr (copy, '/');
+ if (tmp == NULL) {
+ lastcomp = copy;
+ toresolv = ".";
+ } else {
+ lastcomp = tmp + 1;
+ if (tmp == copy)
+ toresolv = "/";
+ }
+ if (strcmp (lastcomp, ".") == 0 || strcmp (lastcomp, "..") == 0) {
+ lastcomp = NULL;
+ toresolv = copy;
+ }
+ else if (tmp)
+ tmp[0] = '\0';
+ }
+ if (realpath (toresolv, buf) == NULL) {
+ GFFUSE_LOGERR ("%s: bad mount point %s: %s", progname, orig,
+ strerror (errno));
+ FREE (copy);
+ return NULL;
+ }
+ if (lastcomp == NULL)
+ dst = strdup (buf);
+ else {
+ dst = (char *) MALLOC (strlen (buf) + 1 + strlen (lastcomp) + 1);
+ if (dst) {
+ unsigned buflen = strlen (buf);
+ if (buflen && buf[buflen-1] == '/')
+ sprintf (dst, "%s%s", buf, lastcomp);
+ else
+ sprintf (dst, "%s/%s", buf, lastcomp);
+ }
+ }
+ FREE (copy);
+ if (dst == NULL)
+ GFFUSE_LOGERR ("%s: failed to allocate memory", progname);
+ return dst;
+}
+
+/* return value:
+ * >= 0 => fd
+ * -1 => error
+ */
+static int
+receive_fd (int fd)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf[1];
+ int rv;
+ size_t ccmsg[CMSG_SPACE (sizeof (int)) / sizeof (size_t)];
+ struct cmsghdr *cmsg;
+
+ iov.iov_base = buf;
+ iov.iov_len = 1;
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ /* old BSD implementations should use msg_accrights instead of
+ * msg_control; the interface is different. */
+ msg.msg_control = ccmsg;
+ msg.msg_controllen = sizeof (ccmsg);
+
+ while (((rv = recvmsg (fd, &msg, 0)) == -1) && errno == EINTR);
+ if (rv == -1) {
+ GFFUSE_LOGERR ("recvmsg failed: %s", strerror (errno));
+ return -1;
+ }
+ if (!rv) {
+ /* EOF */
+ return -1;
+ }
+
+ cmsg = CMSG_FIRSTHDR (&msg);
+ if (!cmsg->cmsg_type == SCM_RIGHTS) {
+ GFFUSE_LOGERR ("got control message of unknown type %d",
+ cmsg->cmsg_type);
+ return -1;
+ }
+ return *(int*)CMSG_DATA (cmsg);
+}
+
+static int
+fuse_mount_fusermount (const char *mountpoint, const char *opts)
+{
+ int fds[2], pid;
+ int res;
+ int rv;
+
+ res = socketpair (PF_UNIX, SOCK_STREAM, 0, fds);
+ if (res == -1) {
+ GFFUSE_LOGERR ("socketpair() failed: %s", strerror (errno));
+ return -1;
+ }
+
+ pid = fork ();
+ if (pid == -1) {
+ GFFUSE_LOGERR ("fork() failed: %s", strerror (errno));
+ close (fds[0]);
+ close (fds[1]);
+ return -1;
+ }
+
+ if (pid == 0) {
+ char env[10];
+ const char *argv[32];
+ int a = 0;
+
+ argv[a++] = FUSERMOUNT_PROG;
+ if (opts) {
+ argv[a++] = "-o";
+ argv[a++] = opts;
+ }
+ argv[a++] = "--";
+ argv[a++] = mountpoint;
+ argv[a++] = NULL;
+
+ close (fds[1]);
+ fcntl (fds[0], F_SETFD, 0);
+ snprintf (env, sizeof (env), "%i", fds[0]);
+ setenv (FUSE_COMMFD_ENV, env, 1);
+ execvp (FUSERMOUNT_PROG, (char **)argv);
+ GFFUSE_LOGERR ("failed to exec fusermount: %s",
+ strerror (errno));
+ _exit (1);
+ }
+
+ close (fds[0]);
+ rv = receive_fd (fds[1]);
+ close (fds[1]);
+ waitpid (pid, NULL, 0); /* bury zombie */
+
+ return rv;
+}
+
+static int
+fuse_mnt_umount (const char *progname, const char *mnt, int lazy)
+{
+ int res;
+ int status;
+ sigset_t blockmask;
+ sigset_t oldmask;
+
+ if (!mtab_needs_update (mnt)) {
+ res = umount2 (mnt, lazy ? 2 : 0);
+ if (res == -1)
+ GFFUSE_LOGERR ("%s: failed to unmount %s: %s",
+ progname, mnt, strerror (errno));
+ return res;
+ }
+
+ sigemptyset (&blockmask);
+ sigaddset (&blockmask, SIGCHLD);
+ res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask);
+ if (res == -1) {
+ GFFUSE_LOGERR ("%s: sigprocmask: %s", progname,
+ strerror (errno));
+ return -1;
+ }
+
+ res = fork ();
+ if (res == -1) {
+ GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno));
+ goto out_restore;
+ }
+ if (res == 0) {
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ setuid (geteuid ());
+ execl ("/bin/umount", "/bin/umount", "-i", mnt,
+ lazy ? "-l" : NULL, NULL);
+ GFFUSE_LOGERR ("%s: failed to execute /bin/umount: %s",
+ progname, strerror (errno));
+ exit (1);
+ }
+ res = waitpid (res, &status, 0);
+ if (res == -1)
+ GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno));
+
+ if (status != 0)
+ res = -1;
+
+ out_restore:
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ return res;
+}
+
+void
+gf_fuse_unmount (const char *mountpoint, int fd)
+{
+ int res;
+ int pid;
+
+ if (!mountpoint)
+ return;
+
+ if (fd != -1) {
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = 0;
+ res = poll (&pfd, 1, 0);
+ /* If file poll returns POLLERR on the device file descriptor,
+ then the filesystem is already unmounted */
+ if (res == 1 && (pfd.revents & POLLERR))
+ return;
+
+ /* Need to close file descriptor, otherwise synchronous umount
+ would recurse into filesystem, and deadlock */
+ close (fd);
+ }
+
+ if (geteuid () == 0) {
+ fuse_mnt_umount ("fuse", mountpoint, 1);
+ return;
+ }
+
+ res = umount2 (mountpoint, 2);
+ if (res == 0)
+ return;
+
+ pid = fork ();
+ if (pid == -1)
+ return;
+
+ if (pid == 0) {
+ const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
+ "--", mountpoint, NULL };
+
+ execvp (FUSERMOUNT_PROG, (char **)argv);
+ _exit (1);
+ }
+ waitpid (pid, NULL, 0);
+}
+
+/*
+ * Functions below are loosely modelled after similar functions of libfuse
+ */
+
+static int
+fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param)
+{
+ int fd = -1, ret = -1;
+ unsigned mounted = 0;
+ char *mnt_param_mnt = NULL;
+ char *fstype = "fuse.glusterfs";
+ char *source = fsname;
+
+ fd = open ("/dev/fuse", O_RDWR);
+ if (fd == -1) {
+ GFFUSE_LOGERR ("cannot open /dev/fuse (%s)", strerror (errno));
+
+ return -1;
+ }
+
+ ret = asprintf (&mnt_param_mnt,
+ "%s,fd=%i,rootmode=%o,user_id=%i,group_id=%i",
+ mnt_param, fd, S_IFDIR, getuid (), getgid ());
+ if (ret == -1) {
+ GFFUSE_LOGERR ("Out of memory");
+
+ goto out;
+ }
+ ret = mount (source, mountpoint, fstype, 0,
+ mnt_param_mnt);
+ if (ret == -1 && errno == ENODEV) {
+ /* fs subtype support was added by 79c0b2df aka
+ v2.6.21-3159-g79c0b2d. Probably we have an
+ older kernel ... */
+ fstype = "fuse";
+ ret = asprintf (&source, "glusterfs#%s", fsname);
+ if (ret == -1) {
+ GFFUSE_LOGERR ("Out of memory");
+
+ goto out;
+ }
+ ret = mount (source, mountpoint, fstype, 0,
+ mnt_param_mnt);
+ }
+ if (ret == -1)
+ goto out;
+ else
+ mounted = 1;
+
+ if (geteuid () == 0) {
+ char *newmnt = fuse_mnt_resolve_path ("fuse", mountpoint);
+
+ if (!newmnt) {
+ ret = -1;
+
+ goto out;
+ }
+
+ ret = fuse_mnt_add_mount ("fuse", source, newmnt, fstype,
+ mnt_param);
+ FREE (newmnt);
+ if (ret == -1) {
+ GFFUSE_LOGERR ("failed to add mtab entry");
+
+ goto out;
+ }
+ }
+
+ out:
+ if (ret == -1) {
+ if (mounted)
+ umount2 (mountpoint, 2); /* lazy umount */
+ close (fd);
+ fd = -1;
+ }
+ FREE (mnt_param_mnt);
+ if (source != fsname)
+ FREE (source);
+ return fd;
+}
+
+int
+gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param)
+{
+ int fd = -1, rv = -1;
+ char *fm_mnt_params = NULL, *p = NULL;
+
+ fd = fuse_mount_sys (mountpoint, fsname, mnt_param);
+ if (fd == -1) {
+ gf_log ("glusterfs-fuse", GF_LOG_NORMAL,
+ "direct mount failed (%s), "
+ "retry to mount via fusermount",
+ strerror (errno));
+
+ rv = asprintf (&fm_mnt_params,
+ "%s,fsname=%s,nonempty,subtype=glusterfs",
+ mnt_param, fsname);
+
+ if (rv == -1) {
+ GFFUSE_LOGERR ("Out of memory");
+
+ return -1;
+ }
+
+ fd = fuse_mount_fusermount (mountpoint, fm_mnt_params);
+ if (fd == -1) {
+ p = fm_mnt_params + strlen (fm_mnt_params);
+ while (*--p != ',');
+ *p = '\0';
+
+ fd = fuse_mount_fusermount (mountpoint, fm_mnt_params);
+ }
+
+ FREE (fm_mnt_params);
+
+ if (fd == -1)
+ GFFUSE_LOGERR ("mount failed");
+ }
+
+ return fd;
+}
diff --git a/xlators/mount/fuse/src/Makefile.am b/xlators/mount/fuse/src/Makefile.am
index c1e7755b5f8..0231e7be0b9 100644
--- a/xlators/mount/fuse/src/Makefile.am
+++ b/xlators/mount/fuse/src/Makefile.am
@@ -5,7 +5,8 @@ noinst_HEADERS = $(CONTRIBDIR)/fuse-include/fuse_kernel.h
xlator_LTLIBRARIES = fuse.la
xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/mount
-fuse_la_SOURCES = fuse-bridge.c $(CONTRIBDIR)/fuse-lib/misc.c
+fuse_la_SOURCES = fuse-bridge.c $(CONTRIBDIR)/fuse-lib/misc.c \
+ $(CONTRIBDIR)/fuse-lib/mount.c
fuse_la_LDFLAGS = -module -avoidversion -shared -nostartfiles
AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D$(GF_HOST_OS) -Wall \
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
index a86c15b0a0a..d2c08b57eb9 100644
--- a/xlators/mount/fuse/src/fuse-bridge.c
+++ b/xlators/mount/fuse/src/fuse-bridge.c
@@ -40,12 +40,12 @@
#include "glusterfs.h"
#include "logging.h"
#include "xlator.h"
-#include "glusterfs.h"
#include "defaults.h"
#include "common-utils.h"
#include "fuse_kernel.h"
#include "fuse-misc.h"
+#include "fuse-mount.h"
#include "list.h"
#include "dict.h"
@@ -2791,14 +2791,23 @@ fuse_thread_proc (void *data)
}
if (res == -1) {
+ if (errno == ENODEV || errno == EBADF) {
+ gf_log ("glusterfs-fuse", GF_LOG_NORMAL,
+ "terminating upon getting %s when "
+ "reading /dev/fuse",
+ errno == ENODEV ? "ENODEV" : "EBADF");
+
+ break;
+ }
if (errno != EINTR) {
gf_log ("glusterfs-fuse", GF_LOG_WARNING,
- "read from /dev/fuse returned -1 (%d)", errno);
+ "read from /dev/fuse returned -1 (%s)",
+ strerror (errno));
}
- if (errno == ENODEV || errno == EBADF)
- break;
+
iobuf_unref (iobuf);
FREE (iov_in[0].iov_base);
+
continue;
}
if (res < sizeof (finh)) {
@@ -2903,7 +2912,6 @@ init (xlator_t *this_xl)
dict_t *options = NULL;
char *value_string = NULL;
char *fsname = NULL;
- char *mount_param = NULL;
fuse_private_t *priv = NULL;
struct stat stbuf = {0,};
int i = 0;
@@ -3016,39 +3024,11 @@ init (xlator_t *this_xl)
goto cleanup_exit;
}
- priv->fd = open ("/dev/fuse", O_RDWR);
- if (priv->fd == -1) {
- gf_log ("glusterfs-fuse", GF_LOG_ERROR,
- "cannot open /dev/fuse (%s)", strerror (errno));
-
- goto cleanup_exit;
- }
- ret = asprintf (&mount_param,
- "allow_other,default_permissions,max_read=131072,"
- "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
- priv->fd, stbuf.st_mode & S_IFMT, getuid (), getgid ());
- if (ret == -1) {
- gf_log ("glusterfs-fuse", GF_LOG_ERROR,
- "Out of memory");
-
- goto cleanup_exit;
- }
- ret = mount (fsname, priv->mount_point, "fuse.glusterfs", 0,
- mount_param);
- if (ret == -1 && errno == ENODEV)
- /* fs subtype support was added by 79c0b2df aka
- v2.6.21-3159-g79c0b2d. Probably we have an
- older kernel ... */
- ret = mount (fsname, priv->mount_point, "fuse", 0,
- mount_param);
- if (ret == -1) {
- gf_log ("glusterfs-fuse", GF_LOG_ERROR,
- "mount failed (%s)", strerror (errno));
-
+ priv->fd = gf_fuse_mount (priv->mount_point, fsname,
+ "allow_other,default_permissions,"
+ "max_read=131072");
+ if (priv->fd == -1)
goto cleanup_exit;
- }
-
- FREE (mount_param);
this_xl->ctx->top = this_xl;
@@ -3096,7 +3076,6 @@ init (xlator_t *this_xl)
cleanup_exit:
if (xl_name_allocated)
FREE (this_xl->name);
- FREE (mount_param);
if (priv) {
FREE (priv->mount_point);
close (priv->fd);
@@ -3118,8 +3097,6 @@ fini (xlator_t *this_xl)
if ((priv = this_xl->private) == NULL)
return;
- close (priv->fd);
-
if (dict_get (this_xl->options, ZR_MOUNTPOINT_OPT))
mount_point = data_to_str (dict_get (this_xl->options,
ZR_MOUNTPOINT_OPT));
@@ -3128,7 +3105,7 @@ fini (xlator_t *this_xl)
"Unmounting '%s'.", mount_point);
dict_del (this_xl->options, ZR_MOUNTPOINT_OPT);
- umount (mount_point);
+ gf_fuse_unmount (mount_point, priv->fd);
}
}