diff options
| author | Csaba Henk <csaba@gluster.com> | 2009-08-11 06:24:40 -0700 | 
|---|---|---|
| committer | Csaba Henk <csaba@gluster.com> | 2009-08-12 06:21:04 -0700 | 
| commit | fbb636390fe51dc6aa52ec7523a36b183434a28c (patch) | |
| tree | 7f3ff12017883fad198e65e225971d3d7b4b3611 | |
| parent | 472ac9944b2e99b8dd4a7e33f8dc4ae0f111b0cc (diff) | |
fuse: add proper mounting support, based on libfuse routines
| -rw-r--r-- | contrib/fuse-include/fuse-mount.h | 11 | ||||
| -rw-r--r-- | contrib/fuse-lib/mount.c | 528 | ||||
| -rw-r--r-- | xlators/mount/fuse/src/Makefile.am | 3 | ||||
| -rw-r--r-- | xlators/mount/fuse/src/fuse-bridge.c | 59 | 
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);          }  }  | 
