summaryrefslogtreecommitdiffstats
path: root/contrib/fuse-lib/mount.c
diff options
context:
space:
mode:
authorCsaba Henk <csaba@gluster.com>2009-08-11 06:24:40 -0700
committerCsaba Henk <csaba@gluster.com>2009-08-12 06:21:04 -0700
commitfbb636390fe51dc6aa52ec7523a36b183434a28c (patch)
tree7f3ff12017883fad198e65e225971d3d7b4b3611 /contrib/fuse-lib/mount.c
parent472ac9944b2e99b8dd4a7e33f8dc4ae0f111b0cc (diff)
fuse: add proper mounting support, based on libfuse routines
Diffstat (limited to 'contrib/fuse-lib/mount.c')
-rw-r--r--contrib/fuse-lib/mount.c528
1 files changed, 528 insertions, 0 deletions
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;
+}