From 7d0397c2144810c8a396e00187a6617873c94002 Mon Sep 17 00:00:00 2001 From: Jeff Darcy Date: Mon, 23 Apr 2012 11:51:22 -0400 Subject: fuse: allow requests during mount (needed for SELinux labels) Change-Id: Ia1af402897e6a7290acf79617c34fdc804751729 BUG: 811217 Signed-off-by: Jeff Darcy Reviewed-on: http://review.gluster.com/3199 Tested-by: Gluster Build System Reviewed-by: Anand Avati --- contrib/fuse-include/fuse-mount.h | 2 +- contrib/fuse-lib/mount.c | 60 +++++++++++++--- xlators/mount/fuse/src/fuse-bridge.c | 105 ++++++++++++++++++++++++---- xlators/mount/fuse/src/fuse-bridge.h | 5 +- xlators/mount/fuse/utils/mount.glusterfs.in | 55 +++++++++++---- 5 files changed, 187 insertions(+), 40 deletions(-) diff --git a/contrib/fuse-include/fuse-mount.h b/contrib/fuse-include/fuse-mount.h index 9f83faf02..7a3756d92 100644 --- a/contrib/fuse-include/fuse-mount.h +++ b/contrib/fuse-include/fuse-mount.h @@ -9,4 +9,4 @@ void gf_fuse_unmount (const char *mountpoint, int fd); int gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param, - pid_t *mtab_pid); + pid_t *mtab_pid, int status_fd); diff --git a/contrib/fuse-lib/mount.c b/contrib/fuse-lib/mount.c index 800fd193e..8c5da3d61 100644 --- a/contrib/fuse-lib/mount.c +++ b/contrib/fuse-lib/mount.c @@ -544,19 +544,25 @@ gf_fuse_unmount (const char *mountpoint, int fd) #ifndef FUSE_UTIL static int -fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param, pid_t *mtab_pid) +fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param, pid_t *mtab_pid, int in_fd, int status_fd) { int fd = -1, ret = -1; unsigned mounted = 0; char *mnt_param_mnt = NULL; char *fstype = "fuse.glusterfs"; char *source = fsname; + pid_t mypid = -1; - fd = open ("/dev/fuse", O_RDWR); - if (fd == -1) { - GFFUSE_LOGERR ("cannot open /dev/fuse (%s)", strerror (errno)); - - return -1; + if (in_fd >= 0) { + fd = in_fd; + } + else { + 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, @@ -567,8 +573,14 @@ fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param, pid_t *mt goto out; } + ret = fork(); + if (ret != 0) { + goto parent_out; + } + GFFUSE_LOGERR("calling mount"); ret = mount (source, mountpoint, fstype, 0, mnt_param_mnt); + GFFUSE_LOGERR("mount returned %d",ret); if (ret == -1 && errno == ENODEV) { /* fs subtype support was added by 79c0b2df aka v2.6.21-3159-g79c0b2d. Probably we have an @@ -607,11 +619,31 @@ fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param, pid_t *mt } } - out: + ret = 0; +out: + if (status_fd >= 0) { + GFFUSE_LOGERR("writing status"); + (void)write(status_fd,&ret,sizeof(ret)); + mypid = getpid(); + /* + * This seems awkward, but the alternative would be to add + * or change return values for functions in multiple layers, + * just so they can store the value for later retrieval by + * the code already running in the right context at the other + * end of this pipe. That's a lot of disruption for nothing. + */ + (void)write(status_fd,&mypid,sizeof(mypid)); + } + GFFUSE_LOGERR("Mount child exiting"); + exit(0); + +parent_out: if (ret == -1) { if (mounted) umount2 (mountpoint, 2); /* lazy umount */ - close (fd); + if (fd != in_fd) { + close (fd); + } fd = -1; } FREE (mnt_param_mnt); @@ -651,13 +683,21 @@ escape (char *s) int gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param, - pid_t *mtab_pid) + pid_t *mtab_pid, int status_fd) { int fd = -1, rv = -1; char *fm_mnt_params = NULL, *p = NULL; char *efsname = NULL; - fd = fuse_mount_sys (mountpoint, fsname, mnt_param, mtab_pid); + fd = open ("/dev/fuse", O_RDWR); + if (fd == -1) { + GFFUSE_LOGERR ("cannot open /dev/fuse (%s)", + strerror (errno)); + return -1; + } + + fd = fuse_mount_sys (mountpoint, fsname, mnt_param, mtab_pid, fd, + status_fd); if (fd == -1) { gf_log ("glusterfs-fuse", GF_LOG_INFO, "direct mount failed (%s), " diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index c9f7c8940..c06da7eec 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -17,6 +17,7 @@ . */ +#include #include "fuse-bridge.h" static int gf_fuse_conn_err_log; @@ -3859,20 +3860,50 @@ unlock: return 0; } +int +fuse_get_mount_status (xlator_t *this) +{ + int kid_status = -1; + pid_t kid_pid = -1; + fuse_private_t *priv = this->private; + int our_status = -1; + + if (read(priv->status_pipe[0],&kid_status, sizeof(kid_status)) < 0) { + gf_log (this->name, GF_LOG_ERROR, "could not get mount status"); + goto out; + } + gf_log (this->name, GF_LOG_DEBUG, "mount status is %d", kid_status); + + if (read(priv->status_pipe[0],&kid_pid, sizeof(kid_pid)) < 0) { + gf_log (this->name, GF_LOG_ERROR, "could not get mount PID"); + goto out; + } + gf_log (this->name, GF_LOG_DEBUG, "mount PID is %d", kid_pid); + + (void)waitpid(kid_pid,NULL,0); + our_status = kid_status; + +out: + close(priv->status_pipe[0]); + close(priv->status_pipe[1]); + return our_status; +} static void * fuse_thread_proc (void *data) { - char *mount_point = NULL; - xlator_t *this = NULL; - fuse_private_t *priv = NULL; - ssize_t res = 0; - struct iobuf *iobuf = NULL; - fuse_in_header_t *finh; - struct iovec iov_in[2]; - void *msg = NULL; - const size_t msg0_size = sizeof (*finh) + 128; - fuse_handler_t **fuse_ops = NULL; + char *mount_point = NULL; + xlator_t *this = NULL; + fuse_private_t *priv = NULL; + ssize_t res = 0; + struct iobuf *iobuf = NULL; + fuse_in_header_t *finh; + struct iovec iov_in[2]; + void *msg = NULL; + const size_t msg0_size = sizeof (*finh) + 128; + fuse_handler_t **fuse_ops = NULL; + struct pollfd pfd[2] = {{0,}}; + gf_boolean_t mount_finished = _gf_false; this = data; priv = this->private; @@ -3889,6 +3920,41 @@ fuse_thread_proc (void *data) /* THIS has to be reset here */ THIS = this; + if (!mount_finished) { + memset(pfd,0,sizeof(pfd)); + pfd[0].fd = priv->status_pipe[0]; + pfd[0].events = POLLIN | POLLHUP | POLLERR; + pfd[1].fd = priv->fd; + pfd[1].events = POLLIN | POLLHUP | POLLERR; + if (poll(pfd,2,-1) < 0) { + gf_log (this->name, GF_LOG_ERROR, + "poll error %s", strerror(errno)); + break; + } + if (pfd[0].revents & POLLIN) { + if (fuse_get_mount_status(this) != 0) { + break; + } + mount_finished = _gf_true; + } + else if (pfd[0].revents) { + gf_log (this->name, GF_LOG_ERROR, + "mount pipe closed without status"); + break; + } + if (!pfd[1].revents) { + continue; + } + } + + /* + * We don't want to block on readv while we're still waiting + * for mount status. That means we only want to get here if + * mount_status is true (meaning that our wait completed + * already) or if we already called poll(2) on priv->fd to + * make sure it's ready. + */ + if (priv->init_recvd) fuse_graph_sync (this); @@ -4010,8 +4076,11 @@ fuse_thread_proc (void *data) GF_FREE (iov_in[0].iov_base); } - iobuf_unref (iobuf); - GF_FREE (iov_in[0].iov_base); + /* + * We could be in all sorts of states with respect to iobuf and iov_in + * by the time we get here, and it's just not worth untangling them if + * we're about to kill ourselves anyway. + */ if (dict_get (this->options, ZR_MOUNTPOINT_OPT)) mount_point = data_to_str (dict_get (this->options, @@ -4019,11 +4088,10 @@ fuse_thread_proc (void *data) if (mount_point) { gf_log (this->name, GF_LOG_INFO, "unmounting %s", mount_point); - dict_del (this->options, ZR_MOUNTPOINT_OPT); } + /* Kill the whole process, not just this thread. */ kill (getpid(), SIGTERM); - return NULL; } @@ -4492,8 +4560,15 @@ init (xlator_t *this_xl) if (!mnt_args) goto cleanup_exit; + if (pipe(priv->status_pipe) < 0) { + gf_log (this_xl->name, GF_LOG_ERROR, + "could not create pipe to separate mount process"); + goto cleanup_exit; + } + priv->fd = gf_fuse_mount (priv->mount_point, fsname, mnt_args, - sync_mtab ? &ctx->mtab_pid : NULL); + sync_mtab ? &ctx->mtab_pid : NULL, + priv->status_pipe[1]); if (priv->fd == -1) goto cleanup_exit; diff --git a/xlators/mount/fuse/src/fuse-bridge.h b/xlators/mount/fuse/src/fuse-bridge.h index 72aa6be48..d20413055 100644 --- a/xlators/mount/fuse/src/fuse-bridge.h +++ b/xlators/mount/fuse/src/fuse-bridge.h @@ -66,7 +66,7 @@ #define MAX_FUSE_PROC_DELAY 1 -#define DISABLE_SELINUX 1 +//#define DISABLE_SELINUX 1 typedef struct fuse_in_header fuse_in_header_t; typedef void (fuse_handler_t) (xlator_t *this, fuse_in_header_t *finh, @@ -116,6 +116,9 @@ struct fuse_private { int revchan_in; int revchan_out; gf_boolean_t reverse_fuse_thread_started; + + /* For communicating with separate mount thread. */ + int status_pipe[2]; }; typedef struct fuse_private fuse_private_t; diff --git a/xlators/mount/fuse/utils/mount.glusterfs.in b/xlators/mount/fuse/utils/mount.glusterfs.in index 073c3609a..aef0939db 100755 --- a/xlators/mount/fuse/utils/mount.glusterfs.in +++ b/xlators/mount/fuse/utils/mount.glusterfs.in @@ -34,6 +34,36 @@ _init () UPDATEDBCONF=/etc/updatedb.conf } +# Mount happens asynchronously, so the command status alone will never be +# sufficient. Instead, we have to wait for multiple events representing +# different possible outcomes. +wait_for () +{ + local daemon_pid=$1 + local mount_point=$2 + + waited=0 + while true; do + kill -s 0 $daemon_pid + if [ $? != 0 ]; then + echo "Gluster client daemon exited unexpectedly." + umount $mount_point &> /dev/null + exit 1 + fi + inode=$(stat -c %i $mount_point 2>/dev/null); + if [ "$inode" = "1" ]; then + break + fi + if [ $waited -ge 10 ]; then + break + fi + sleep 1 + waited=$((waited+1)) + done + + echo "$inode" +} + start_glusterfs () { if [ -n "$log_level_str" ]; then @@ -121,34 +151,33 @@ start_glusterfs () cmd_line=$(echo "$cmd_line --volfile=$volfile_loc"); fi - cmd_line=$(echo "$cmd_line $mount_point"); - err=0; - $cmd_line; - - - inode=$(stat -c %i $mount_point 2>/dev/null); + cmd_line=$(echo "$cmd_line $mount_point") + err=0 + $cmd_line + daemon_pid=$$ + # Wait for the inode to change *or* for the daemon to exit (indicating a + # problem with the mount). + inode=$(wait_for $daemon_pid $mount_point) # this is required if the stat returns error if [ -z "$inode" ]; then - inode="0"; + inode="0" fi # retry the failover - # if [ $? != "0" ]; then # <--- TODO: Once glusterfs returns proper error code, change it. if [ $inode -ne 1 ]; then - err=1; if [ -n "$cmd_line1" ]; then cmd_line1=$(echo "$cmd_line1 $mount_point"); - $cmd_line1; - err=0; + $cmd_line1 + daemon_pid=$$ - inode=$(stat -c %i $mount_point 2>/dev/null); + inode=$(wait_for $daemon_pid $mount_point) # this is required if the stat returns error if [ -z "$inode" ]; then inode="0"; fi if [ $inode -ne 1 ]; then - err=1; + err=1 fi fi fi -- cgit