summaryrefslogtreecommitdiffstats
path: root/xlators/mount/fuse/src/fuse-bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/mount/fuse/src/fuse-bridge.c')
-rw-r--r--xlators/mount/fuse/src/fuse-bridge.c335
1 files changed, 329 insertions, 6 deletions
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
index aea0f88b0c1..e37d2e0c8da 100644
--- a/xlators/mount/fuse/src/fuse-bridge.c
+++ b/xlators/mount/fuse/src/fuse-bridge.c
@@ -29,6 +29,7 @@
static int gf_fuse_conn_err_log;
static int gf_fuse_xattr_enotsup_log;
+static int gf_fuse_pre_stuck_log;
/*
@@ -536,7 +537,18 @@ fuse_fd_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
foo.open_flags = 0;
if (!IA_ISDIR (fd->inode->ia_type)) {
- if (priv->direct_io_mode)
+ if (priv->direct_io_mode &&
+ (
+#ifdef GF_LINUX_HOST_OS
+ (state->flags & O_ACCMODE) != O_RDONLY ||
+#endif
+#ifdef GF_DARWIN_HOST_OS
+ /* On Darwin machines, O_APPEND is not handled,
+ * which may corrupt the data
+ */
+ (state->flags & O_ACCMODE) == O_RDONLY &&
+#endif
+ priv->can_exec_directio))
foo.open_flags |= FOPEN_DIRECT_IO;
#ifdef GF_DARWIN_HOST_OS
/* In Linux: by default, buffer cache
@@ -1430,7 +1442,15 @@ fuse_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
if (op_ret >= 0) {
foo.fh = (uintptr_t) fd;
- if (priv->direct_io_mode)
+ if (priv->direct_io_mode &&
+ (
+#ifdef GF_LINUX_HOST_OS
+ (state->flags & O_ACCMODE) != O_RDONLY ||
+#endif
+#ifdef GF_DARWIN_HOST_OS
+ (state->flags & O_ACCMODE) == O_RDONLY &&
+#endif
+ priv->can_exec_directio))
foo.open_flags |= FOPEN_DIRECT_IO;
gf_log ("glusterfs-fuse", GF_LOG_TRACE,
@@ -2798,6 +2818,9 @@ fuse_setlk (xlator_t *this, fuse_in_header_t *finh, void *msg)
return;
}
+
+static void *fuse_pre_test_directio_exec (void *arg);
+
static void
fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg)
{
@@ -2806,6 +2829,9 @@ fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg)
struct fuse_init_out fino;
fuse_private_t *priv = NULL;
int ret;
+#ifndef GF_DARWIN_HOST_OS
+ pthread_t prethread = {0, };
+#endif
priv = this->private;
@@ -2867,6 +2893,20 @@ fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg)
out:
GF_FREE (finh);
+
+#ifdef GF_DARWIN_HOST_OS
+ /* MacFUSE applies a "mount comes after a succesful handshake"
+ * strategy, which was taken from early fuse4bsd. Linux (and recent
+ * fuse4bsd) is rather doing "mount, then handshake; if that fails,
+ * unmount" strategy. Therefore on OS X it would be too early to
+ * launch the pre-test thread here. To work this around, we launch
+ * it from the LOOKUP pre-test handler (that will come instantly,
+ * as OS X automatically checks for the presence of some files
+ * upon a new mount).
+ */
+#else
+ pthread_create (&prethread, NULL, &fuse_pre_test_directio_exec, this);
+#endif
}
@@ -3035,11 +3075,9 @@ fuse_thread_proc (void *data)
struct iovec iov_in[2];
void *msg = NULL;
const size_t msg0_size = sizeof (*finh) + 128;
- fuse_handler_t **fuse_ops = NULL;
this = data;
priv = this->private;
- fuse_ops = priv->fuse_ops;
THIS = this;
@@ -3154,7 +3192,7 @@ fuse_thread_proc (void *data)
fuse_enosys (this, finh, msg);
else
#endif
- fuse_ops[finh->opcode] (this, finh, msg);
+ priv->fuse_ops[finh->opcode] (this, finh, msg);
iobuf_unref (iobuf);
continue;
@@ -3419,6 +3457,287 @@ fuse_dumper (xlator_t *this, fuse_in_header_t *finh, void *msg)
}
+/*
+ * FUSE pre-test stuff
+ * -------------------
+ */
+
+#define FUSE_PRE_TESTFILE ".__fuse_pre_testfile"
+#define FUSE_PRE_TESTFILE1 ".__fuse_pre_testfile1"
+#define FUSE_PRE_TESTFILE_ID ((uint64_t)-1)
+#define FUSE_PRE_TESTFILE_FH ((uint64_t)1)
+
+#define FUSE_PRE_TEST_TEST do { \
+ if (finh->uid != getuid () || \
+ finh->nodeid != FUSE_PRE_TESTFILE_ID) \
+ return fuse_std_fallback (this, finh, msg); \
+} while (0)
+
+static fuse_handler_t *fuse_std_ops[];
+
+static void
+fuse_std_fallback (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ static int fuse_pre_cnt;
+
+ if (fuse_pre_cnt++ > 200)
+ GF_LOG_OCCASIONALLY (gf_fuse_pre_stuck_log,
+ "glusterfs-fuse",
+ GF_LOG_WARNING,
+ "fuse pre test does not complete");
+
+ return fuse_std_ops[finh->opcode] (this, finh, msg);
+}
+
+
+static void
+fuse_pre_lookup (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ char *name = msg;
+ fuse_private_t *priv = NULL;
+ struct fuse_entry_out feo = {0, };
+ unsigned pre_testfile[2] = {0, 0};
+#ifdef GF_DARWIN_HOST_OS
+ static int prethread_spawn;
+ pthread_t prethread = {0, };
+
+ if (!prethread_spawn) {
+ prethread_spawn = 1;
+ pthread_create (&prethread, NULL, &fuse_pre_test_directio_exec,
+ this);
+ }
+#endif
+
+ priv = this->private;
+
+ pre_testfile[0] = !strcmp (name, FUSE_PRE_TESTFILE);
+ pre_testfile[1] = !strcmp (name, FUSE_PRE_TESTFILE1);
+ if (finh->uid != getuid () ||
+ finh->nodeid != 1 ||
+ !(pre_testfile[0] || pre_testfile[1]))
+ return fuse_std_fallback (this, finh, msg);
+
+ if (priv->pre_test_stage == 0 && pre_testfile[1]) {
+ send_fuse_err (this, finh, ENOENT);
+
+ return;
+ }
+
+ memset (&feo, 0, sizeof (feo));
+ feo.nodeid = FUSE_PRE_TESTFILE_ID;
+ feo.attr.ino = FUSE_PRE_TESTFILE_ID;
+ feo.attr.mode = (priv->pre_test_stage == 2 ? S_IFDIR : S_IFREG) | 0755;
+ feo.attr.nlink = 1;
+
+#if FUSE_KERNEL_MINOR_VERSION >= 9
+ priv->proto_minor >= 9 ?
+ send_fuse_obj (this, finh, &feo) :
+ send_fuse_data (this, finh, &feo,
+ FUSE_COMPAT_ENTRY_OUT_SIZE);
+#else
+ send_fuse_obj (this, finh, &feo);
+#endif
+}
+
+
+static void
+fuse_pre_getattr (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ fuse_private_t *priv = NULL;
+ struct fuse_attr_out fao = {0, };
+
+ priv = this->private;
+
+ FUSE_PRE_TEST_TEST;
+
+ fao.attr.mode = (priv->pre_test_stage == 2 ? S_IFDIR : S_IFREG) | 0755;
+ fao.attr.nlink = 1;
+
+#if FUSE_KERNEL_MINOR_VERSION >= 9
+ priv->proto_minor >= 9 ?
+ send_fuse_obj (this, finh, &fao) :
+ send_fuse_data (this, finh, &fao,
+ FUSE_COMPAT_ATTR_OUT_SIZE);
+#else
+ send_fuse_obj (this, finh, &fao);
+#endif
+}
+
+
+static void
+fuse_pre_open (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ struct fuse_open_out foo = {0, };
+
+ FUSE_PRE_TEST_TEST;
+
+ foo.fh = FUSE_PRE_TESTFILE_FH;
+ foo.open_flags = FOPEN_DIRECT_IO;
+
+ send_fuse_obj (this, finh, &foo);
+}
+
+
+static void
+fuse_pre_deadpan (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ FUSE_PRE_TEST_TEST;
+
+ send_fuse_err (this, finh, 0);
+}
+
+
+static void
+fuse_pre_rename (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ struct fuse_rename_in *fri = msg;
+ char *oldname = (char *)(fri + 1);
+ char *newname = oldname + strlen (oldname) + 1;
+
+ fuse_private_t *priv = NULL;
+
+ if (finh->uid != getuid () ||
+ finh->nodeid != 1 ||
+ fri->newdir != 1 ||
+ strcmp (oldname, FUSE_PRE_TESTFILE) != 0 ||
+ strcmp (newname, FUSE_PRE_TESTFILE1) != 0)
+ return fuse_std_fallback (this, finh, msg);
+
+ priv = this->private;
+ priv->pre_test_stage = 1;
+
+ send_fuse_err (this, finh, 0);
+}
+
+
+static void
+fuse_pre_release (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ if (finh->nodeid != FUSE_PRE_TESTFILE_ID)
+ return fuse_std_fallback (this, finh, msg);
+
+ send_fuse_err (this, finh, 0);
+}
+
+
+static void
+fuse_pre_forget (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ fuse_private_t *priv = NULL;
+
+ if (finh->nodeid != FUSE_PRE_TESTFILE_ID)
+ return fuse_std_fallback (this, finh, msg);
+
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG,
+ "terminating pre-test, switching to standard ops");
+ priv = this->private;
+ *(priv->fuse_ops_flipped) = fuse_std_ops;
+}
+
+
+/* XXX Hope this will work out with xattr based ACL systems... */
+static void
+fuse_pre_xattr (xlator_t *this, fuse_in_header_t *finh, void *msg)
+{
+ FUSE_PRE_TEST_TEST;
+
+ send_fuse_err (this, finh, ENODATA);
+}
+
+
+static fuse_handler_t *fuse_pre_ops[FUSE_OP_HIGH] = {
+ [FUSE_LOOKUP] = fuse_pre_lookup,
+ [FUSE_FORGET] = fuse_pre_forget,
+ [FUSE_GETATTR] = fuse_pre_getattr,
+ [FUSE_ACCESS] = fuse_pre_deadpan,
+ [FUSE_RENAME] = fuse_pre_rename,
+ [FUSE_OPEN] = fuse_pre_open,
+ [FUSE_READ] = fuse_pre_deadpan,
+ [FUSE_FLUSH] = fuse_pre_deadpan,
+ [FUSE_RELEASE] = fuse_pre_release,
+ [FUSE_FSYNC] = fuse_pre_deadpan,
+ [FUSE_GETXATTR] = fuse_pre_xattr,
+ [FUSE_LISTXATTR] = fuse_pre_xattr,
+};
+
+
+enum {
+ FUSE_DIOEXEC_OK,
+ FUSE_DIOEXEC_FAIL,
+ FUSE_DIOEXEC_DUNNO
+};
+
+static void *
+fuse_pre_test_directio_exec (void *arg)
+{
+ fuse_private_t *priv = NULL;
+ pid_t pid = 0;
+ struct stat st = {0, };
+ char pre_testfile[PATH_MAX];
+ char pre_testfile1[PATH_MAX];
+ int ret = 0;
+ xlator_t *this = NULL;
+
+ this = arg;
+ priv = this->private;
+
+ if (fstat (priv->fd, &st) == -1)
+ return NULL;
+ if (strlen (priv->mount_point) +
+ max (strlen (FUSE_PRE_TESTFILE), strlen (FUSE_PRE_TESTFILE1)) + 2 >
+ PATH_MAX)
+ return NULL;
+ strcat (strcat (strcpy (pre_testfile, priv->mount_point), "/"),
+ FUSE_PRE_TESTFILE);
+ strcat (strcat (strcpy (pre_testfile1, priv->mount_point), "/"),
+ FUSE_PRE_TESTFILE1);
+
+ if ((pid = fork ()) == -1)
+ return NULL;
+ if (pid == 0) {
+ close (priv->fd);
+ execl (pre_testfile, FUSE_PRE_TESTFILE, NULL);
+ switch (errno) {
+ case ENOEXEC:
+ exit (FUSE_DIOEXEC_OK);
+ case EFAULT:
+ exit (FUSE_DIOEXEC_FAIL);
+ default:
+ fprintf (stderr,
+ "warning: fuse exec-on-diretio pre-test ended"
+ " unexpectedly (%s)\n", strerror (errno));
+ exit (FUSE_DIOEXEC_DUNNO);
+ }
+ }
+
+ if (waitpid (pid, &ret, 0) == pid &&
+ WIFEXITED (ret)) {
+ switch (WEXITSTATUS (ret)) {
+ case FUSE_DIOEXEC_OK:
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG,
+ "exec on directio: OK");
+ priv->can_exec_directio = 1;
+ break;
+ case FUSE_DIOEXEC_FAIL:
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG,
+ "exec on directio: FAIL");
+ break;
+ default:
+ gf_log ("glusterfs-fuse", GF_LOG_WARNING,
+ "exec on directio: uninterpretable result");
+ }
+ } else
+ gf_log ("glusterfs-fuse", GF_LOG_WARNING,
+ "exec on directio: abnormal termination");
+
+ ret = rename (pre_testfile, pre_testfile1);
+ priv->pre_test_stage = 2;
+ stat (ret ? pre_testfile : pre_testfile1, &st);
+
+ pthread_exit (0);
+}
+
+
int
init (xlator_t *this_xl)
{
@@ -3587,11 +3906,15 @@ init (xlator_t *this_xl)
fuse_std_ops[i] = fuse_enosys;
if (!fuse_dump_ops[i])
fuse_dump_ops[i] = fuse_dumper;
+ if (!fuse_pre_ops[i])
+ fuse_pre_ops[i] = fuse_std_fallback;
}
- priv->fuse_ops = fuse_std_ops;
+ priv->fuse_ops = fuse_pre_ops;
+ priv->fuse_ops_flipped = &priv->fuse_ops;
if (priv->fuse_dump_fd != -1) {
priv->fuse_ops0 = priv->fuse_ops;
priv->fuse_ops = fuse_dump_ops;
+ priv->fuse_ops_flipped = &priv->fuse_ops0;
}
return 0;