From fbb636390fe51dc6aa52ec7523a36b183434a28c Mon Sep 17 00:00:00 2001 From: Csaba Henk Date: Tue, 11 Aug 2009 06:24:40 -0700 Subject: fuse: add proper mounting support, based on libfuse routines --- contrib/fuse-lib/mount.c | 528 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 contrib/fuse-lib/mount.c (limited to 'contrib/fuse-lib') diff --git a/contrib/fuse-lib/mount.c b/contrib/fuse-lib/mount.c new file mode 100644 index 000000000..c9e2035d4 --- /dev/null +++ b/contrib/fuse-lib/mount.c @@ -0,0 +1,528 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + Copyright (c) 2009 Z RESEARCH, Inc. + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit