From 77adf4cd648dce41f89469dd185deec6b6b53a0b Mon Sep 17 00:00:00 2001 From: Vikas Gorur Date: Wed, 18 Feb 2009 17:36:07 +0530 Subject: Added all files --- glusterfs-guts/Makefile.am | 1 + glusterfs-guts/src/Makefile.am | 17 + glusterfs-guts/src/fuse-bridge.c | 2724 +++++++++++++++++++++++++++++++++++ glusterfs-guts/src/fuse-extra.c | 137 ++ glusterfs-guts/src/fuse-extra.h | 38 + glusterfs-guts/src/fuse_kernel.h | 380 +++++ glusterfs-guts/src/glusterfs-fuse.h | 58 + glusterfs-guts/src/glusterfs-guts.c | 400 +++++ glusterfs-guts/src/glusterfs-guts.h | 62 + glusterfs-guts/src/guts-extra.c | 18 + glusterfs-guts/src/guts-lowlevel.h | 86 ++ glusterfs-guts/src/guts-parse.c | 217 +++ glusterfs-guts/src/guts-parse.h | 140 ++ glusterfs-guts/src/guts-replay.c | 834 +++++++++++ glusterfs-guts/src/guts-replay.h | 33 + glusterfs-guts/src/guts-tables.c | 248 ++++ glusterfs-guts/src/guts-tables.h | 80 + glusterfs-guts/src/guts-trace.c | 650 +++++++++ glusterfs-guts/src/guts-trace.h | 54 + 19 files changed, 6177 insertions(+) create mode 100644 glusterfs-guts/Makefile.am create mode 100644 glusterfs-guts/src/Makefile.am create mode 100644 glusterfs-guts/src/fuse-bridge.c create mode 100644 glusterfs-guts/src/fuse-extra.c create mode 100644 glusterfs-guts/src/fuse-extra.h create mode 100644 glusterfs-guts/src/fuse_kernel.h create mode 100644 glusterfs-guts/src/glusterfs-fuse.h create mode 100644 glusterfs-guts/src/glusterfs-guts.c create mode 100644 glusterfs-guts/src/glusterfs-guts.h create mode 100644 glusterfs-guts/src/guts-extra.c create mode 100644 glusterfs-guts/src/guts-lowlevel.h create mode 100644 glusterfs-guts/src/guts-parse.c create mode 100644 glusterfs-guts/src/guts-parse.h create mode 100644 glusterfs-guts/src/guts-replay.c create mode 100644 glusterfs-guts/src/guts-replay.h create mode 100644 glusterfs-guts/src/guts-tables.c create mode 100644 glusterfs-guts/src/guts-tables.h create mode 100644 glusterfs-guts/src/guts-trace.c create mode 100644 glusterfs-guts/src/guts-trace.h (limited to 'glusterfs-guts') diff --git a/glusterfs-guts/Makefile.am b/glusterfs-guts/Makefile.am new file mode 100644 index 000000000..f963effea --- /dev/null +++ b/glusterfs-guts/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src \ No newline at end of file diff --git a/glusterfs-guts/src/Makefile.am b/glusterfs-guts/src/Makefile.am new file mode 100644 index 000000000..bb8c7b176 --- /dev/null +++ b/glusterfs-guts/src/Makefile.am @@ -0,0 +1,17 @@ +sbin_PROGRAMS = glusterfs-guts + +glusterfs_guts_SOURCES = glusterfs-guts.c fuse-bridge.c guts-replay.c guts-trace.c \ + fuse-extra.c guts-extra.c guts-parse.c guts-tables.c + +noinst_HEADERS = fuse_kernel.h fuse-extra.h glusterfs-guts.h glusterfs-fuse.h guts-lowlevel.h \ + guts-parse.h guts-replay.h guts-tables.h guts-trace.h + +glusterfs_guts_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la -lfuse + +AM_CFLAGS = -fPIC -Wall -pthread + +AM_CPPFLAGS = -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -DFUSE_USE_VERSION=26 \ + -I$(top_srcdir)/libglusterfs/src -DDATADIR=\"$(localstatedir)\" \ + -DCONFDIR=\"$(sysconfdir)/glusterfs\" + +CLEANFILES = diff --git a/glusterfs-guts/src/fuse-bridge.c b/glusterfs-guts/src/fuse-bridge.c new file mode 100644 index 000000000..0972563c6 --- /dev/null +++ b/glusterfs-guts/src/fuse-bridge.c @@ -0,0 +1,2724 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + + +#include +#include +#include + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#include "glusterfs.h" +#include "logging.h" +#include "xlator.h" +#include "glusterfs.h" +#include "transport.h" +#include "defaults.h" +#include "common-utils.h" + +#include + +#include "fuse-extra.h" +#include "list.h" + +#include "guts-lowlevel.h" + +#define BIG_FUSE_CHANNEL_SIZE 1048576 + +struct fuse_private { + int fd; + struct fuse *fuse; + struct fuse_session *se; + struct fuse_chan *ch; + char *mountpoint; +}; + +char glusterfs_fuse_direct_io_mode = 1; +float glusterfs_fuse_entry_timeout = 1.0; +float glusterfs_fuse_attr_timeout = 1.0; + +#define FI_TO_FD(fi) ((fd_t *)((long)fi->fh)) + +#define FUSE_FOP(state, ret, op, args ...) \ +do { \ + call_frame_t *frame = get_call_frame_for_req (state, 1); \ + xlator_t *xl = frame->this->children ? \ + frame->this->children->xlator : NULL; \ + dict_t *refs = frame->root->req_refs; \ + frame->root->state = state; \ + STACK_WIND (frame, ret, xl, xl->fops->op, args); \ + dict_unref (refs); \ +} while (0) + +#define FUSE_FOP_NOREPLY(state, op, args ...) \ +do { \ + call_frame_t *_frame = get_call_frame_for_req (state, 0); \ + xlator_t *xl = _frame->this->children->xlator; \ + _frame->root->req_refs = NULL; \ + STACK_WIND (_frame, fuse_nop_cbk, xl, xl->fops->op, args); \ +} while (0) + +typedef struct { + loc_t loc; + inode_t *parent; + inode_t *inode; + char *name; +} fuse_loc_t; + +typedef struct { + void *pool; + xlator_t *this; + inode_table_t *itable; + fuse_loc_t fuse_loc; + fuse_loc_t fuse_loc2; + fuse_req_t req; + + int32_t flags; + off_t off; + size_t size; + unsigned long nlookup; + fd_t *fd; + dict_t *dict; + char *name; + char is_revalidate; +} fuse_state_t; + + +static void +loc_wipe (loc_t *loc) +{ + if (loc->inode) { + inode_unref (loc->inode); + loc->inode = NULL; + } + if (loc->path) { + FREE (loc->path); + loc->path = NULL; + } +} + + +static inode_t * +dummy_inode (inode_table_t *table) +{ + inode_t *dummy; + + dummy = CALLOC (1, sizeof (*dummy)); + ERR_ABORT (dummy); + + dummy->table = table; + + INIT_LIST_HEAD (&dummy->list); + INIT_LIST_HEAD (&dummy->inode_hash); + INIT_LIST_HEAD (&dummy->fds); + INIT_LIST_HEAD (&dummy->dentry.name_hash); + INIT_LIST_HEAD (&dummy->dentry.inode_list); + + dummy->ref = 1; + dummy->ctx = get_new_dict (); + + LOCK_INIT (&dummy->lock); + return dummy; +} + +static void +fuse_loc_wipe (fuse_loc_t *fuse_loc) +{ + loc_wipe (&fuse_loc->loc); + if (fuse_loc->name) { + FREE (fuse_loc->name); + fuse_loc->name = NULL; + } + if (fuse_loc->inode) { + inode_unref (fuse_loc->inode); + fuse_loc->inode = NULL; + } + if (fuse_loc->parent) { + inode_unref (fuse_loc->parent); + fuse_loc->parent = NULL; + } +} + + +static void +free_state (fuse_state_t *state) +{ + fuse_loc_wipe (&state->fuse_loc); + + fuse_loc_wipe (&state->fuse_loc2); + + if (state->dict) { + dict_unref (state->dict); + state->dict = (void *)0xaaaaeeee; + } + if (state->name) { + FREE (state->name); + state->name = NULL; + } +#ifdef DEBUG + memset (state, 0x90, sizeof (*state)); +#endif + FREE (state); + state = NULL; +} + + +static int32_t +fuse_nop_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno) +{ + if (frame->root->state) + free_state (frame->root->state); + + frame->root->state = EEEEKS; + STACK_DESTROY (frame->root); + return 0; +} + +fuse_state_t * +state_from_req (fuse_req_t req) +{ + fuse_state_t *state; + transport_t *trans = fuse_req_userdata (req); + + state = (void *)calloc (1, sizeof (*state)); + ERR_ABORT (state); + state->pool = trans->xl->ctx->pool; + state->itable = trans->xl->itable; + state->req = req; + state->this = trans->xl; + + return state; +} + + +static call_frame_t * +get_call_frame_for_req (fuse_state_t *state, char d) +{ + call_pool_t *pool = state->pool; + fuse_req_t req = state->req; + const struct fuse_ctx *ctx = NULL; + call_ctx_t *cctx = NULL; + transport_t *trans = NULL; + + cctx = CALLOC (1, sizeof (*cctx)); + ERR_ABORT (cctx); + cctx->frames.root = cctx; + + if (req) { + ctx = fuse_req_ctx(req); + + cctx->uid = ctx->uid; + cctx->gid = ctx->gid; + cctx->pid = ctx->pid; + cctx->unique = req_callid (req); + } + + if (req) { + trans = fuse_req_userdata (req); + cctx->frames.this = trans->xl; + cctx->trans = trans; + } else { + cctx->frames.this = state->this; + } + + if (d) { + cctx->req_refs = dict_ref (get_new_dict ()); + dict_set (cctx->req_refs, NULL, trans->buf); + cctx->req_refs->is_locked = 1; + } + + cctx->pool = pool; + LOCK (&pool->lock); + list_add (&cctx->all_frames, &pool->all_frames); + UNLOCK (&pool->lock); + + return &cctx->frames; +} + + +static void +fuse_loc_fill (fuse_loc_t *fuse_loc, + fuse_state_t *state, + ino_t ino, + const char *name) +{ + size_t n; + inode_t *inode, *parent = NULL; + + /* resistance against multiple invocation of loc_fill not to get + reference leaks via inode_search() */ + inode = fuse_loc->inode; + if (!inode) { + inode = inode_search (state->itable, ino, name); + } + fuse_loc->inode = inode; + + if (name) { + if (!fuse_loc->name) + fuse_loc->name = strdup (name); + + parent = fuse_loc->parent; + if (!parent) { + if (inode) + parent = inode_parent (inode, ino); + else + parent = inode_search (state->itable, ino, NULL); + } + } + fuse_loc->parent = parent; + + if (inode) { + fuse_loc->loc.inode = inode_ref (inode); + fuse_loc->loc.ino = inode->ino; + } + + if (parent) { + n = inode_path (parent, name, NULL, 0) + 1; + fuse_loc->loc.path = CALLOC (1, n); + ERR_ABORT (fuse_loc->loc.path); + inode_path (parent, name, (char *)fuse_loc->loc.path, n); + } else if (inode) { + n = inode_path (inode, NULL, NULL, 0) + 1; + fuse_loc->loc.path = CALLOC (1, n); + ERR_ABORT (fuse_loc->loc.path); + inode_path (inode, NULL, (char *)fuse_loc->loc.path, n); + } +} + +static int32_t +fuse_lookup_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + inode_t *inode, + struct stat *stat, + dict_t *dict); + +static int32_t +fuse_entry_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + inode_t *inode, + struct stat *buf) +{ + fuse_state_t *state; + fuse_req_t req; + struct fuse_entry_param e = {0, }; + + state = frame->root->state; + req = state->req; + + if (!op_ret) { + if (inode->ino == 1) + buf->st_ino = 1; + } + + if (!op_ret && inode && inode->ino && buf && inode->ino != buf->st_ino) { + /* temporary workaround to handle AFR returning differnt inode number */ + gf_log ("glusterfs-fuse", GF_LOG_WARNING, + "%"PRId64": %s => inode number changed %"PRId64" -> %"PRId64, + frame->root->unique, state->fuse_loc.loc.path, + inode->ino, buf->st_ino); + inode_unref (state->fuse_loc.loc.inode); + state->fuse_loc.loc.inode = dummy_inode (state->itable); + state->is_revalidate = 2; + + STACK_WIND (frame, fuse_lookup_cbk, + FIRST_CHILD (this), FIRST_CHILD (this)->fops->lookup, + &state->fuse_loc.loc, + 0); + + return 0; + } + + if (op_ret == 0) { + ino_t ino = buf->st_ino; + inode_t *fuse_inode; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %"PRId64, frame->root->unique, + state->fuse_loc.loc.path, ino); + + try_again: + fuse_inode = inode_update (state->itable, state->fuse_loc.parent, + state->fuse_loc.name, buf); + + if (fuse_inode->ctx) { + /* if the inode was already in the hash, checks to flush out + old name hashes */ + if ((fuse_inode->st_mode ^ buf->st_mode) & S_IFMT) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, + "%"PRId64": %s => %"PRId64" Rehashing %x/%x", + frame->root->unique, + state->fuse_loc.loc.path, ino, (S_IFMT & buf->st_ino), + (S_IFMT & fuse_inode->st_mode)); + + fuse_inode->st_mode = buf->st_mode; + inode_unhash_name (state->itable, fuse_inode); + inode_unref (fuse_inode); + goto try_again; + } + if (buf->st_nlink == 1) { + /* no other name hashes should exist */ + if (!list_empty (&fuse_inode->dentry.inode_list)) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, + "%"PRId64": %s => %"PRId64" Rehashing because st_nlink less than dentry maps", + frame->root->unique, + state->fuse_loc.loc.path, ino); + inode_unhash_name (state->itable, fuse_inode); + inode_unref (fuse_inode); + goto try_again; + } + if ((state->fuse_loc.parent != fuse_inode->dentry.parent) || + strcmp (state->fuse_loc.name, fuse_inode->dentry.name)) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, + "%"PRId64": %s => %"PRId64" Rehashing because single st_nlink does not match dentry map", + frame->root->unique, + state->fuse_loc.loc.path, ino); + inode_unhash_name (state->itable, fuse_inode); + inode_unref (fuse_inode); + goto try_again; + } + } + } + + if ((fuse_inode->ctx != inode->ctx) && + list_empty (&fuse_inode->fds)) { + dict_t *swap = inode->ctx; + inode->ctx = fuse_inode->ctx; + fuse_inode->ctx = swap; + fuse_inode->generation = inode->generation; + fuse_inode->st_mode = buf->st_mode; + } + + inode_lookup (fuse_inode); + + inode_unref (fuse_inode); + + /* TODO: make these timeouts configurable (via meta?) */ + e.ino = fuse_inode->ino; + e.generation = buf->st_ctime; + e.entry_timeout = glusterfs_fuse_entry_timeout; + e.attr_timeout = glusterfs_fuse_attr_timeout; + e.attr = *buf; + e.attr.st_blksize = BIG_FUSE_CHANNEL_SIZE; + if (state->fuse_loc.parent) + fuse_reply_entry (req, &e); + else + fuse_reply_attr (req, buf, glusterfs_fuse_attr_timeout); + } else { + if (state->is_revalidate == -1 && op_errno == ENOENT) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + } + + if (state->is_revalidate == 1) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "unlinking stale dentry for `%s'", + state->fuse_loc.loc.path); + + if (state->fuse_loc.parent) + inode_unlink (state->itable, state->fuse_loc.parent, + state->fuse_loc.name); + + inode_unref (state->fuse_loc.loc.inode); + state->fuse_loc.loc.inode = dummy_inode (state->itable); + state->is_revalidate = 2; + + STACK_WIND (frame, fuse_lookup_cbk, + FIRST_CHILD (this), FIRST_CHILD (this)->fops->lookup, + &state->fuse_loc.loc, 0); + + return 0; + } + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + return 0; +} + + +static int32_t +fuse_lookup_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + inode_t *inode, + struct stat *stat, + dict_t *dict) +{ + fuse_entry_cbk (frame, cookie, this, op_ret, op_errno, inode, stat); + return 0; +} + + +static void +fuse_lookup (fuse_req_t req, + fuse_ino_t par, + const char *name) +{ + fuse_state_t *state; + + state = state_from_req (req); + + fuse_loc_fill (&state->fuse_loc, state, par, name); + + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": LOOKUP %s", req_callid (req), + state->fuse_loc.loc.path); + + state->fuse_loc.loc.inode = dummy_inode (state->itable); + /* to differntiate in entry_cbk what kind of call it is */ + state->is_revalidate = -1; + } else { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": LOOKUP %s(%"PRId64")", req_callid (req), + state->fuse_loc.loc.path, state->fuse_loc.loc.inode->ino); + state->is_revalidate = 1; + } + + FUSE_FOP (state, fuse_lookup_cbk, lookup, + &state->fuse_loc.loc, 0); +} + + +static void +fuse_forget (fuse_req_t req, + fuse_ino_t ino, + unsigned long nlookup) +{ + inode_t *fuse_inode; + fuse_state_t *state; + + if (ino == 1) { + fuse_reply_none (req); + return; + } + + state = state_from_req (req); + fuse_inode = inode_search (state->itable, ino, NULL); + inode_forget (fuse_inode, nlookup); + inode_unref (fuse_inode); + + free_state (state); + fuse_reply_none (req); +} + + +static int32_t +fuse_attr_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct stat *buf) +{ + fuse_state_t *state; + fuse_req_t req; + + state = frame->root->state; + req = state->req; + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %"PRId64, frame->root->unique, + state->fuse_loc.loc.path ? state->fuse_loc.loc.path : "ERR", + buf->st_ino); + /* TODO: make these timeouts configurable via meta */ + /* TODO: what if the inode number has changed by now */ + buf->st_blksize = BIG_FUSE_CHANNEL_SIZE; + fuse_reply_attr (req, buf, glusterfs_fuse_attr_timeout); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64"; %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path ? state->fuse_loc.loc.path : "ERR", + op_errno); + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + return 0; +} + + +static void +fuse_getattr (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + + if (ino == 1) { + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (state->fuse_loc.loc.inode) + state->is_revalidate = 1; + else + state->is_revalidate = -1; + FUSE_FOP (state, + fuse_lookup_cbk, lookup, &state->fuse_loc.loc, 0); + return; + } + + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": GETATTR %"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + if (list_empty (&state->fuse_loc.loc.inode->fds) || + S_ISDIR (state->fuse_loc.loc.inode->st_mode)) { + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": GETATTR %"PRId64" (%s)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_attr_cbk, + stat, + &state->fuse_loc.loc); + } else { + fd_t *fd = list_entry (state->fuse_loc.loc.inode->fds.next, + fd_t, inode_list); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FGETATTR %"PRId64" (%s/%p)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path, fd); + + FUSE_FOP (state, + fuse_attr_cbk, + fstat, fd); + } +} + + +static int32_t +fuse_fd_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + fd_t *fd) +{ + fuse_state_t *state; + fuse_req_t req; + + state = frame->root->state; + req = state->req; + fd = state->fd; + + if (op_ret >= 0) { + struct fuse_file_info fi = {0, }; + + LOCK (&fd->inode->lock); + list_add (&fd->inode_list, &fd->inode->fds); + UNLOCK (&fd->inode->lock); + + fi.fh = (unsigned long) fd; + fi.flags = state->flags; + + if (!S_ISDIR (fd->inode->st_mode)) { + if ((fi.flags & 3) && glusterfs_fuse_direct_io_mode) + fi.direct_io = 1; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %p", frame->root->unique, + state->fuse_loc.loc.path, fd); + + if (fuse_reply_open (req, &fi) == -ENOENT) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, "open() got EINTR"); + state->req = 0; + + if (S_ISDIR (fd->inode->st_mode)) + FUSE_FOP_NOREPLY (state, closedir, fd); + else + FUSE_FOP_NOREPLY (state, close, fd); + } + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + fuse_reply_err (req, op_errno); + fd_destroy (fd); + } + + free_state (state); + STACK_DESTROY (frame->root); + return 0; +} + + + +static void +do_chmod (fuse_req_t req, + fuse_ino_t ino, + struct stat *attr, + struct fuse_file_info *fi) +{ + fuse_state_t *state = state_from_req (req); + + if (fi) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FCHMOD %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, + fuse_attr_cbk, + fchmod, + FI_TO_FD (fi), + attr->st_mode); + } else { + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": CHMOD %"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": CHMOD %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_attr_cbk, + chmod, + &state->fuse_loc.loc, + attr->st_mode); + } +} + +static void +do_chown (fuse_req_t req, + fuse_ino_t ino, + struct stat *attr, + int valid, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; + gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; + + state = state_from_req (req); + + if (fi) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FCHOWN %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, + fuse_attr_cbk, + fchown, + FI_TO_FD (fi), + uid, + gid); + } else { + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": CHOWN %"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": CHOWN %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_attr_cbk, + chown, + &state->fuse_loc.loc, + uid, + gid); + } +} + +static void +do_truncate (fuse_req_t req, + fuse_ino_t ino, + struct stat *attr, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + + if (fi) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FTRUNCATE %p/%"PRId64, req_callid (req), + FI_TO_FD (fi), attr->st_size); + + FUSE_FOP (state, + fuse_attr_cbk, + ftruncate, + FI_TO_FD (fi), + attr->st_size); + } else { + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": TRUNCATE %s/%"PRId64" (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path, attr->st_size); + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": TRUNCATE %s/%"PRId64, req_callid (req), + state->fuse_loc.loc.path, attr->st_size); + + FUSE_FOP (state, + fuse_attr_cbk, + truncate, + &state->fuse_loc.loc, + attr->st_size); + } + + return; +} + +static void +do_utimes (fuse_req_t req, + fuse_ino_t ino, + struct stat *attr) +{ + fuse_state_t *state; + + struct timespec tv[2]; +#ifdef FUSE_STAT_HAS_NANOSEC + tv[0] = ST_ATIM(attr); + tv[1] = ST_MTIM(attr); +#else + tv[0].tv_sec = attr->st_atime; + tv[0].tv_nsec = 0; + tv[1].tv_sec = attr->st_mtime; + tv[1].tv_nsec = 0; +#endif + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": UTIMENS %s (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": UTIMENS %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_attr_cbk, + utimens, + &state->fuse_loc.loc, + tv); +} + +static void +fuse_setattr (fuse_req_t req, + fuse_ino_t ino, + struct stat *attr, + int valid, + struct fuse_file_info *fi) +{ + + if (valid & FUSE_SET_ATTR_MODE) + do_chmod (req, ino, attr, fi); + else if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) + do_chown (req, ino, attr, valid, fi); + else if (valid & FUSE_SET_ATTR_SIZE) + do_truncate (req, ino, attr, fi); + else if ((valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) + do_utimes (req, ino, attr); + + if (!valid) + fuse_getattr (req, ino, fi); +} + + +static int32_t +fuse_err_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => 0", frame->root->unique, + state->fuse_loc.loc.path ? state->fuse_loc.loc.path : "ERR"); + fuse_reply_err (req, 0); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path ? state->fuse_loc.loc.path : "ERR", + op_errno); + fuse_reply_err (req, op_errno); + } + + if (state->fd) + fd_destroy (state->fd); + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + + +static int32_t +fuse_unlink_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret == 0) + inode_unlink (state->itable, state->fuse_loc.parent, state->fuse_loc.name); + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => 0", frame->root->unique, + state->fuse_loc.loc.path); + + fuse_reply_err (req, 0); + } else { + gf_log ("glusterfs-fuse", (op_errno == ENOTEMPTY) ? GF_LOG_DEBUG : GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + +static void +fuse_access (fuse_req_t req, + fuse_ino_t ino, + int mask) +{ + fuse_state_t *state; + + state = state_from_req (req); + + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": ACCESS %"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), (int64_t)ino, state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + FUSE_FOP (state, + fuse_err_cbk, + access, + &state->fuse_loc.loc, + mask); + + return; +} + + + +static int32_t +fuse_readlink_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + const char *linkname) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret > 0) { + ((char *)linkname)[op_ret] = '\0'; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %s", frame->root->unique, + state->fuse_loc.loc.path, linkname); + + fuse_reply_readlink(req, linkname); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + fuse_reply_err(req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + +static void +fuse_readlink (fuse_req_t req, + fuse_ino_t ino) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64" READLINK %s/%"PRId64" (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path, state->fuse_loc.loc.inode->ino); + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64" READLINK %s/%"PRId64, req_callid (req), + state->fuse_loc.loc.path, state->fuse_loc.loc.inode->ino); + + FUSE_FOP (state, + fuse_readlink_cbk, + readlink, + &state->fuse_loc.loc, + 4096); + + return; +} + + +static void +fuse_mknod (fuse_req_t req, + fuse_ino_t par, + const char *name, + mode_t mode, + dev_t rdev) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, par, name); + + state->fuse_loc.loc.inode = dummy_inode (state->itable); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": MKNOD %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_entry_cbk, + mknod, + &state->fuse_loc.loc, + mode, + rdev); + + return; +} + + +static void +fuse_mkdir (fuse_req_t req, + fuse_ino_t par, + const char *name, + mode_t mode) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, par, name); + + state->fuse_loc.loc.inode = dummy_inode (state->itable); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": MKDIR %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_entry_cbk, + mkdir, + &state->fuse_loc.loc, + mode); + + return; +} + + +static void +fuse_unlink (fuse_req_t req, + fuse_ino_t par, + const char *name) +{ + fuse_state_t *state; + + state = state_from_req (req); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": UNLINK %s", req_callid (req), + state->fuse_loc.loc.path); + + fuse_loc_fill (&state->fuse_loc, state, par, name); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": UNLINK %s (fuse_loc_fill() returned NULL inode)", req_callid (req), + state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + FUSE_FOP (state, + fuse_unlink_cbk, + unlink, + &state->fuse_loc.loc); + + return; +} + + +static void +fuse_rmdir (fuse_req_t req, + fuse_ino_t par, + const char *name) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, par, name); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": RMDIR %s (fuse_loc_fill() returned NULL inode)", req_callid (req), + state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": RMDIR %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_unlink_cbk, + rmdir, + &state->fuse_loc.loc); + + return; +} + + +static void +fuse_symlink (fuse_req_t req, + const char *linkname, + fuse_ino_t par, + const char *name) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, par, name); + + state->fuse_loc.loc.inode = dummy_inode (state->itable); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": SYMLINK %s -> %s", req_callid (req), + state->fuse_loc.loc.path, linkname); + + FUSE_FOP (state, + fuse_entry_cbk, + symlink, + linkname, + &state->fuse_loc.loc); + return; +} + + +int32_t +fuse_rename_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct stat *buf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s -> %s => 0", frame->root->unique, + state->fuse_loc.loc.path, + state->fuse_loc2.loc.path); + + inode_t *inode; + { + /* ugly ugly - to stay blind to situation where + rename happens on a new inode + */ + buf->st_ino = state->fuse_loc.loc.ino; + } + inode = inode_rename (state->itable, + state->fuse_loc.parent, + state->fuse_loc.name, + state->fuse_loc2.parent, + state->fuse_loc2.name, + buf); + + inode_unref (inode); + fuse_reply_err (req, 0); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s -> %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, + state->fuse_loc2.loc.path, op_errno); + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + return 0; +} + + +static void +fuse_rename (fuse_req_t req, + fuse_ino_t oldpar, + const char *oldname, + fuse_ino_t newpar, + const char *newname) +{ + fuse_state_t *state; + + state = state_from_req (req); + + fuse_loc_fill (&state->fuse_loc, state, oldpar, oldname); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "for %s %"PRId64": RENAME `%s' -> `%s' (fuse_loc_fill() returned NULL inode)", + state->fuse_loc.loc.path, req_callid (req), state->fuse_loc.loc.path, + state->fuse_loc2.loc.path); + + fuse_reply_err (req, EINVAL); + return; + } + + fuse_loc_fill (&state->fuse_loc2, state, newpar, newname); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": RENAME `%s' -> `%s'", + req_callid (req), state->fuse_loc.loc.path, + state->fuse_loc2.loc.path); + + FUSE_FOP (state, + fuse_rename_cbk, + rename, + &state->fuse_loc.loc, + &state->fuse_loc2.loc); + + return; +} + + +static void +fuse_link (fuse_req_t req, + fuse_ino_t ino, + fuse_ino_t par, + const char *name) +{ + fuse_state_t *state; + + state = state_from_req (req); + + fuse_loc_fill (&state->fuse_loc, state, par, name); + fuse_loc_fill (&state->fuse_loc2, state, ino, NULL); + if (!state->fuse_loc2.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "fuse_loc_fill() returned NULL inode for %s %"PRId64": LINK %s %s", + state->fuse_loc2.loc.path, req_callid (req), + state->fuse_loc2.loc.path, state->fuse_loc.loc.path); + fuse_reply_err (req, EINVAL); + return; + } + + state->fuse_loc.loc.inode = inode_ref (state->fuse_loc2.loc.inode); + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": LINK %s %s", req_callid (req), + state->fuse_loc2.loc.path, state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_entry_cbk, + link, + &state->fuse_loc2.loc, + state->fuse_loc.loc.path); + + return; +} + + +static int32_t +fuse_create_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + fd_t *fd, + inode_t *inode, + struct stat *buf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + struct fuse_file_info fi = {0, }; + struct fuse_entry_param e = {0, }; + + fd = state->fd; + + fi.flags = state->flags; + if (op_ret >= 0) { + inode_t *fuse_inode; + fi.fh = (unsigned long) fd; + + if ((fi.flags & 3) && glusterfs_fuse_direct_io_mode) + fi.direct_io = 1; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %p", frame->root->unique, + state->fuse_loc.loc.path, fd); + + fuse_inode = inode_update (state->itable, + state->fuse_loc.parent, + state->fuse_loc.name, + buf); + if (fuse_inode->ctx) { + inode_unhash_name (state->itable, fuse_inode); + inode_unref (fuse_inode); + + fuse_inode = inode_update (state->itable, + state->fuse_loc.parent, + state->fuse_loc.name, + buf); + } + + + { + if (fuse_inode->ctx != inode->ctx) { + dict_t *swap = inode->ctx; + inode->ctx = fuse_inode->ctx; + fuse_inode->ctx = swap; + fuse_inode->generation = inode->generation; + fuse_inode->st_mode = buf->st_mode; + } + + inode_lookup (fuse_inode); + + /* list_del (&fd->inode_list); */ + + LOCK (&fuse_inode->lock); + list_add (&fd->inode_list, &fuse_inode->fds); + inode_unref (fd->inode); + fd->inode = inode_ref (fuse_inode); + UNLOCK (&fuse_inode->lock); + + // inode_destroy (inode); + } + + inode_unref (fuse_inode); + + e.ino = fuse_inode->ino; + e.generation = buf->st_ctime; + e.entry_timeout = glusterfs_fuse_entry_timeout; + e.attr_timeout = glusterfs_fuse_attr_timeout; + e.attr = *buf; + e.attr.st_blksize = BIG_FUSE_CHANNEL_SIZE; + + fi.keep_cache = 0; + + // if (fi.flags & 1) + // fi.direct_io = 1; + + if (fuse_reply_create (req, &e, &fi) == -ENOENT) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, "create() got EINTR"); + /* TODO: forget this node too */ + state->req = 0; + FUSE_FOP_NOREPLY (state, close, fd); + } + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", req_callid (req), + state->fuse_loc.loc.path, op_errno); + fuse_reply_err (req, op_errno); + fd_destroy (fd); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + +static void +fuse_create (fuse_req_t req, + fuse_ino_t par, + const char *name, + mode_t mode, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + fd_t *fd; + + state = state_from_req (req); + state->flags = fi->flags; + + fuse_loc_fill (&state->fuse_loc, state, par, name); + state->fuse_loc.loc.inode = dummy_inode (state->itable); + + fd = fd_create (state->fuse_loc.loc.inode); + state->fd = fd; + + + LOCK (&fd->inode->lock); + list_del_init (&fd->inode_list); + UNLOCK (&fd->inode->lock); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": CREATE %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_create_cbk, + create, + &state->fuse_loc.loc, + state->flags, + mode, fd); + + return; +} + + +static void +fuse_open (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + fd_t *fd; + + state = state_from_req (req); + state->flags = fi->flags; + + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": OPEN %s (fuse_loc_fill() returned NULL inode)", req_callid (req), + state->fuse_loc.loc.path); + + fuse_reply_err (req, EINVAL); + return; + } + + + fd = fd_create (state->fuse_loc.loc.inode); + state->fd = fd; + + LOCK (&fd->inode->lock); + list_del_init (&fd->inode_list); + UNLOCK (&fd->inode->lock); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": OPEN %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_fd_cbk, + open, + &state->fuse_loc.loc, + fi->flags, fd); + + return; +} + + +static int32_t +fuse_readv_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct iovec *vector, + int32_t count, + struct stat *stbuf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret >= 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": READ => %d/%d,%"PRId64"/%"PRId64, frame->root->unique, + op_ret, state->size, state->off, stbuf->st_size); + + fuse_reply_vec (req, vector, count); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": READ => -1 (%d)", frame->root->unique, op_errno); + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + +static void +fuse_readv (fuse_req_t req, + fuse_ino_t ino, + size_t size, + off_t off, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->size = size; + state->off = off; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": READ (%p, size=%d, offset=%"PRId64")", + req_callid (req), FI_TO_FD (fi), size, off); + + FUSE_FOP (state, + fuse_readv_cbk, + readv, + FI_TO_FD (fi), + size, + off); + +} + + +static int32_t +fuse_writev_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct stat *stbuf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret >= 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": WRITE => %d/%d,%"PRId64"/%"PRId64, frame->root->unique, + op_ret, state->size, state->off, stbuf->st_size); + + fuse_reply_write (req, op_ret); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": WRITE => -1 (%d)", frame->root->unique, op_errno); + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + +static void +fuse_write (fuse_req_t req, + fuse_ino_t ino, + const char *buf, + size_t size, + off_t off, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + struct iovec vector; + + state = state_from_req (req); + state->size = size; + state->off = off; + + vector.iov_base = (void *)buf; + vector.iov_len = size; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": WRITE (%p, size=%d, offset=%"PRId64")", + req_callid (req), FI_TO_FD (fi), size, off); + + FUSE_FOP (state, + fuse_writev_cbk, + writev, + FI_TO_FD (fi), + &vector, + 1, + off); + return; +} + + +static void +fuse_flush (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FLUSH %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, + fuse_err_cbk, + flush, + FI_TO_FD (fi)); + + return; +} + + +static void +fuse_release (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->fd = FI_TO_FD (fi); + + LOCK (&state->fd->inode->lock); + list_del_init (&state->fd->inode_list); + UNLOCK (&state->fd->inode->lock); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": CLOSE %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, fuse_err_cbk, close, state->fd); + return; +} + + +static void +fuse_fsync (fuse_req_t req, + fuse_ino_t ino, + int datasync, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": FSYNC %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, + fuse_err_cbk, + fsync, + FI_TO_FD (fi), + datasync); + + return; +} + +static void +fuse_opendir (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + fd_t *fd; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": OPEN %s (fuse_loc_fill() returned NULL inode)", req_callid (req), + state->fuse_loc.loc.path); + + fuse_reply_err (req, EINVAL); + return; + } + + + fd = fd_create (state->fuse_loc.loc.inode); + state->fd = fd; + + LOCK (&fd->inode->lock); + list_del_init (&fd->inode_list); + UNLOCK (&fd->inode->lock); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": OPEN %s", req_callid (req), + state->fuse_loc.loc.path); + + FUSE_FOP (state, + fuse_fd_cbk, + opendir, + &state->fuse_loc.loc, fd); +} + +#if 0 + +void +fuse_dir_reply (fuse_req_t req, + size_t size, + off_t off, + fd_t *fd) +{ + char *buf; + size_t size_limited; + data_t *buf_data; + + buf_data = dict_get (fd->ctx, "__fuse__getdents__internal__@@!!"); + buf = buf_data->data; + size_limited = size; + + if (size_limited > (buf_data->len - off)) + size_limited = (buf_data->len - off); + + if (off > buf_data->len) { + size_limited = 0; + off = 0; + } + + fuse_reply_buf (req, buf + off, size_limited); +} + + +static int32_t +fuse_getdents_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + dir_entry_t *entries, + int32_t count) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret < 0) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": READDIR => -1 (%d)", + frame->root->unique, op_errno); + + fuse_reply_err (state->req, op_errno); + } else { + dir_entry_t *trav; + size_t size = 0; + char *buf; + data_t *buf_data; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": READDIR => %d entries", + frame->root->unique, count); + + for (trav = entries->next; trav; trav = trav->next) { + size += fuse_add_direntry (req, NULL, 0, trav->name, NULL, 0); + } + + buf = CALLOC (1, size); + ERR_ABORT (buf); + buf_data = data_from_dynptr (buf, size); + size = 0; + + for (trav = entries->next; trav; trav = trav->next) { + size_t entry_size; + entry_size = fuse_add_direntry (req, NULL, 0, trav->name, NULL, 0); + fuse_add_direntry (req, buf + size, entry_size, trav->name, + &trav->buf, entry_size + size); + size += entry_size; + } + + dict_set (state->fd->ctx, + "__fuse__getdents__internal__@@!!", + buf_data); + + fuse_dir_reply (state->req, state->size, state->off, state->fd); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + +static void +fuse_getdents (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi, + size_t size, + off_t off, + int32_t flag) +{ + fuse_state_t *state; + fd_t *fd = FI_TO_FD (fi); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": GETDENTS %p", req_callid (req), FI_TO_FD (fi)); + + if (!off) + dict_del (fd->ctx, "__fuse__getdents__internal__@@!!"); + + if (dict_get (fd->ctx, "__fuse__getdents__internal__@@!!")) { + fuse_dir_reply (req, size, off, fd); + return; + } + + state = state_from_req (req); + + state->size = size; + state->off = off; + state->fd = fd; + + FUSE_FOP (state, + fuse_getdents_cbk, + getdents, + fd, + size, + off, + 0); +} + +#endif + +static int32_t +fuse_readdir_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + gf_dirent_t *buf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (op_ret >= 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": READDIR => %d/%d,%"PRId64, frame->root->unique, + op_ret, state->size, state->off); + + fuse_reply_buf (req, (void *)buf, op_ret); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": READDIR => -1 (%d)", frame->root->unique, op_errno); + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; + +} + +static void +fuse_readdir (fuse_req_t req, + fuse_ino_t ino, + size_t size, + off_t off, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->size = size; + state->off = off; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": READDIR (%p, size=%d, offset=%"PRId64")", + req_callid (req), FI_TO_FD (fi), size, off); + + FUSE_FOP (state, + fuse_readdir_cbk, + readdir, + FI_TO_FD (fi), + size, + off); +} + + +static void +fuse_releasedir (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->fd = FI_TO_FD (fi); + + LOCK (&state->fd->inode->lock); + list_del_init (&state->fd->inode_list); + UNLOCK (&state->fd->inode->lock); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": CLOSEDIR %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, fuse_err_cbk, closedir, state->fd); +} + + +static void +fuse_fsyncdir (fuse_req_t req, + fuse_ino_t ino, + int datasync, + struct fuse_file_info *fi) +{ + fuse_state_t *state; + + state = state_from_req (req); + + FUSE_FOP (state, + fuse_err_cbk, + fsyncdir, + FI_TO_FD (fi), + datasync); + + return; +} + + +static int32_t +fuse_statfs_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct statvfs *buf) +{ + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + /* + Filesystems (like ZFS on solaris) reports + different ->f_frsize and ->f_bsize. Old coreutils + df tools use statfs() and do not see ->f_frsize. + the ->f_blocks, ->f_bavail and ->f_bfree are + w.r.t ->f_frsize and not ->f_bsize which makes the + df tools report wrong values. + + Scale the block counts to match ->f_bsize. + */ + /* TODO: with old coreutils, f_bsize is taken from stat()'s st_blksize + * so the df with old coreutils this wont work :( + */ + + if (op_ret == 0) { + + buf->f_blocks *= buf->f_frsize; + buf->f_blocks /= BIG_FUSE_CHANNEL_SIZE; + + buf->f_bavail *= buf->f_frsize; + buf->f_bavail /= BIG_FUSE_CHANNEL_SIZE; + + buf->f_bfree *= buf->f_frsize; + buf->f_bfree /= BIG_FUSE_CHANNEL_SIZE; + + buf->f_frsize = buf->f_bsize = BIG_FUSE_CHANNEL_SIZE; + + fuse_reply_statfs (req, buf); + + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": ERR => -1 (%d)", frame->root->unique, op_errno); + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + +static void +fuse_statfs (fuse_req_t req, + fuse_ino_t ino) +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, 1, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": STATFS (fuse_loc_fill() returned NULL inode)", req_callid (req)); + + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": STATFS", req_callid (req)); + + FUSE_FOP (state, + fuse_statfs_cbk, + statfs, + &state->fuse_loc.loc); +} + +static void +fuse_setxattr (fuse_req_t req, + fuse_ino_t ino, + const char *name, + const char *value, + size_t size, + int flags) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->size = size; + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": SETXATTR %s/%"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), + state->fuse_loc.loc.path, (int64_t)ino, name); + + fuse_reply_err (req, EINVAL); + return; + } + + state->dict = get_new_dict (); + + dict_set (state->dict, (char *)name, + bin_to_data ((void *)value, size)); + dict_ref (state->dict); + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": SETXATTR %s/%"PRId64" (%s)", req_callid (req), + state->fuse_loc.loc.path, (int64_t)ino, name); + + FUSE_FOP (state, + fuse_err_cbk, + setxattr, + &state->fuse_loc.loc, + state->dict, + flags); + + return; +} + + +static int32_t +fuse_xattr_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + dict_t *dict) +{ + int32_t ret = op_ret; + char *value = ""; + fuse_state_t *state = frame->root->state; + fuse_req_t req = state->req; + + if (ret >= 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => %d", frame->root->unique, + state->fuse_loc.loc.path, op_ret); + + /* if successful */ + if (state->name) { + /* if callback for getxattr */ + data_t *value_data = dict_get (dict, state->name); + if (value_data) { + ret = value_data->len; /* Don't return the value for '\0' */ + value = value_data->data; + + if (state->size) { + /* if callback for getxattr and asks for value */ + fuse_reply_buf (req, value, ret); + } else { + /* if callback for getxattr and asks for value length only */ + fuse_reply_xattr (req, ret); + } + } else { + fuse_reply_err (req, ENODATA); + } + } else { + /* if callback for listxattr */ + int32_t len = 0; + data_pair_t *trav = dict->members_list; + while (trav) { + len += strlen (trav->key) + 1; + trav = trav->next; + } + value = alloca (len + 1); + ERR_ABORT (value); + len = 0; + trav = dict->members_list; + while (trav) { + strcpy (value + len, trav->key); + value[len + strlen(trav->key)] = '\0'; + len += strlen (trav->key) + 1; + trav = trav->next; + } + if (state->size) { + /* if callback for listxattr and asks for list of keys */ + fuse_reply_buf (req, value, len); + } else { + /* if callback for listxattr and asks for length of keys only */ + fuse_reply_xattr (req, len); + } + } + } else { + /* if failure - no need to check if listxattr or getxattr */ + if (op_errno != ENODATA) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + } else { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": %s => -1 (%d)", frame->root->unique, + state->fuse_loc.loc.path, op_errno); + } + + fuse_reply_err (req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + + +static void +fuse_getxattr (fuse_req_t req, + fuse_ino_t ino, + const char *name, + size_t size) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->size = size; + state->name = strdup (name); + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": GETXATTR %s/%"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path, (int64_t)ino, name); + + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": GETXATTR %s/%"PRId64" (%s)", req_callid (req), + state->fuse_loc.loc.path, (int64_t)ino, name); + + FUSE_FOP (state, + fuse_xattr_cbk, + getxattr, + &state->fuse_loc.loc); + + return; +} + + +static void +fuse_listxattr (fuse_req_t req, + fuse_ino_t ino, + size_t size) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->size = size; + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": LISTXATTR %s/%"PRId64" (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path, (int64_t)ino); + + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": LISTXATTR %s/%"PRId64, req_callid (req), + state->fuse_loc.loc.path, (int64_t)ino); + + FUSE_FOP (state, + fuse_xattr_cbk, + getxattr, + &state->fuse_loc.loc); + + return; +} + + +static void +fuse_removexattr (fuse_req_t req, + fuse_ino_t ino, + const char *name) + +{ + fuse_state_t *state; + + state = state_from_req (req); + fuse_loc_fill (&state->fuse_loc, state, ino, NULL); + if (!state->fuse_loc.loc.inode) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": REMOVEXATTR %s/%"PRId64" (%s) (fuse_loc_fill() returned NULL inode)", + req_callid (req), state->fuse_loc.loc.path, (int64_t)ino, name); + + fuse_reply_err (req, EINVAL); + return; + } + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": REMOVEXATTR %s/%"PRId64" (%s)", req_callid (req), + state->fuse_loc.loc.path, (int64_t)ino, name); + + FUSE_FOP (state, + fuse_err_cbk, + removexattr, + &state->fuse_loc.loc, + name); + + return; +} + +static int32_t +fuse_getlk_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct flock *lock) +{ + fuse_state_t *state = frame->root->state; + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": ERR => 0", frame->root->unique); + fuse_reply_lock (state->req, lock); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": ERR => -1 (%d)", frame->root->unique, op_errno); + fuse_reply_err (state->req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + +static void +fuse_getlk (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi, + struct flock *lock) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->req = req; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": GETLK %p", req_callid (req), FI_TO_FD (fi)); + + FUSE_FOP (state, + fuse_getlk_cbk, + lk, + FI_TO_FD (fi), + F_GETLK, + lock); + + return; +} + +static int32_t +fuse_setlk_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + struct flock *lock) +{ + fuse_state_t *state = frame->root->state; + + if (op_ret == 0) { + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": ERR => 0", frame->root->unique); + fuse_reply_err (state->req, 0); + } else { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "%"PRId64": ERR => -1 (%d)", frame->root->unique, op_errno); + fuse_reply_err (state->req, op_errno); + } + + free_state (state); + STACK_DESTROY (frame->root); + + return 0; +} + +static void +fuse_setlk (fuse_req_t req, + fuse_ino_t ino, + struct fuse_file_info *fi, + struct flock *lock, + int sleep) +{ + fuse_state_t *state; + + state = state_from_req (req); + state->req = req; + + gf_log ("glusterfs-fuse", GF_LOG_DEBUG, + "%"PRId64": SETLK %p (sleep=%d)", req_callid (req), FI_TO_FD (fi), + sleep); + + FUSE_FOP (state, + fuse_setlk_cbk, + lk, + FI_TO_FD(fi), + (sleep ? F_SETLKW : F_SETLK), + lock); + + return; +} + + +int32_t +fuse_forget_notify (call_frame_t *frame, xlator_t *this, + inode_t *inode) +{ + return 0; +} + +struct xlator_fops fuse_xl_fops = { + .forget = fuse_forget_notify +}; + +static void +fuse_init (void *data, struct fuse_conn_info *conn) +{ + transport_t *trans = data; + struct fuse_private *priv = trans->private; + xlator_t *xl = trans->xl; + int32_t ret; + + xl->name = "fuse"; + xl->fops = &fuse_xl_fops; + xl->itable = inode_table_new (0, xl); + xl->notify = default_notify; + ret = xlator_tree_init (xl); + if (ret == 0) { + + } else { + fuse_unmount (priv->mountpoint, priv->ch); + exit (1); + } +} + + +static void +fuse_destroy (void *data) +{ + +} + +struct fuse_lowlevel_ops fuse_ops = { + .init = fuse_init, + .destroy = fuse_destroy, + .lookup = fuse_lookup, + .forget = fuse_forget, + .getattr = fuse_getattr, + .setattr = fuse_setattr, + .opendir = fuse_opendir, + .readdir = fuse_readdir, + .releasedir = fuse_releasedir, + .access = fuse_access, + .readlink = fuse_readlink, + .mknod = fuse_mknod, + .mkdir = fuse_mkdir, + .unlink = fuse_unlink, + .rmdir = fuse_rmdir, + .symlink = fuse_symlink, + .rename = fuse_rename, + .link = fuse_link, + .create = fuse_create, + .open = fuse_open, + .read = fuse_readv, + .write = fuse_write, + .flush = fuse_flush, + .release = fuse_release, + .fsync = fuse_fsync, + .fsyncdir = fuse_fsyncdir, + .statfs = fuse_statfs, + .setxattr = fuse_setxattr, + .getxattr = fuse_getxattr, + .listxattr = fuse_listxattr, + .removexattr = fuse_removexattr, + .getlk = fuse_getlk, + .setlk = fuse_setlk +}; + + +static int32_t +fuse_transport_disconnect (transport_t *this) +{ + struct fuse_private *priv = this->private; + + gf_log ("glusterfs-fuse", + GF_LOG_DEBUG, + "cleaning up fuse transport in disconnect handler"); + + fuse_session_remove_chan (priv->ch); + fuse_session_destroy (priv->se); + fuse_unmount (priv->mountpoint, priv->ch); + + FREE (priv); + priv = NULL; + this->private = NULL; + + /* TODO: need graceful exit. every xlator should be ->fini()'ed + and come out of main poll loop cleanly + */ + exit (0); + + return -1; +} + + +static int32_t +fuse_transport_init (transport_t *this, + dict_t *options, + event_notify_fn_t notify) +{ + char *mountpoint = strdup (data_to_str (dict_get (options, + "mountpoint"))); + char *source; + asprintf (&source, "fsname=glusterfs"); + char *argv[] = { "glusterfs", + +#ifndef GF_DARWIN_HOST_OS + "-o", "nonempty", +#endif + "-o", "allow_other", + "-o", "default_permissions", + "-o", source, + "-o", "max_readahead=1048576", + "-o", "max_read=1048576", + "-o", "max_write=1048576", + NULL }; +#ifdef GF_DARWIN_HOST_OS + int argc = 13; +#else + int argc = 15; +#endif + + struct fuse_args args = FUSE_ARGS_INIT(argc, + argv); + struct fuse_private *priv = NULL; + int32_t res; + + priv = CALLOC (1, sizeof (*priv)); + ERR_ABORT (priv); + + + this->notify = notify; + this->private = (void *)priv; + + priv->ch = fuse_mount (mountpoint, &args); + if (!priv->ch) { + gf_log ("glusterfs-fuse", + GF_LOG_ERROR, "fuse_mount failed (%s)\n", strerror (errno)); + fuse_opt_free_args(&args); + goto err_free; + } + + priv->se = fuse_lowlevel_new (&args, &fuse_ops, sizeof (fuse_ops), this); + fuse_opt_free_args(&args); + + res = fuse_set_signal_handlers (priv->se); + if (res == -1) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, "fuse_set_signal_handlers failed"); + goto err; + } + + fuse_session_add_chan (priv->se, priv->ch); + + priv->fd = fuse_chan_fd (priv->ch); + this->buf = data_ref (data_from_dynptr (NULL, 0)); + this->buf->is_locked = 1; + + priv->mountpoint = mountpoint; + + transport_ref (this); + //poll_register (this->xl_private, priv->fd, this); + + return 0; + + err: + fuse_unmount (mountpoint, priv->ch); + err_free: + FREE (mountpoint); + mountpoint = NULL; + return -1; +} + +void +guts_log_req (void *, int32_t); + +static void * +fuse_thread_proc (void *data) +{ + transport_t *trans = data; + struct fuse_private *priv = trans->private; + int32_t res = 0; + data_t *buf = trans->buf; + int32_t ref = 0; + size_t chan_size = fuse_chan_bufsize (priv->ch); + char *recvbuf = CALLOC (1, chan_size); + ERR_ABORT (recvbuf); + + while (!fuse_session_exited (priv->se)) { + int32_t fuse_chan_receive (struct fuse_chan * ch, + char *buf, + int32_t size); + + + res = fuse_chan_receive (priv->ch, + recvbuf, + chan_size); + + if (res == -1) { + transport_disconnect (trans); + } + + buf = trans->buf; + + if (res && res != -1) { + if (buf->len < (res)) { + if (buf->data) { + FREE (buf->data); + buf->data = NULL; + } + buf->data = CALLOC (1, res); + ERR_ABORT (buf->data); + buf->len = res; + } + memcpy (buf->data, recvbuf, res); // evil evil + guts_log_req (buf->data, res); + fuse_session_process (priv->se, + buf->data, + res, + priv->ch); + } + + LOCK (&buf->lock); + ref = buf->refcount; + UNLOCK (&buf->lock); + if (1) { + data_unref (buf); + + trans->buf = data_ref (data_from_dynptr (NULL, 0)); + trans->buf->is_locked = 1; + } + } + + exit (0); + + return NULL; +} + + +static int32_t +fuse_transport_notify (xlator_t *xl, + int32_t event, + void *data, + ...) +{ + transport_t *trans = data; + struct fuse_private *priv = trans->private; + int32_t res = 0; + data_t *buf; + int32_t ref = 0; + + if (event == GF_EVENT_POLLERR) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, "got GF_EVENT_POLLERR"); + transport_disconnect (trans); + return -1; + } + + if (event != GF_EVENT_POLLIN) { + gf_log ("glusterfs-fuse", GF_LOG_WARNING, "Ignoring notify event %d", + event); + return 0; + } + + if (!fuse_session_exited(priv->se)) { + static size_t chan_size = 0; + + int32_t fuse_chan_receive (struct fuse_chan * ch, + char *buf, + int32_t size); + if (!chan_size) + chan_size = fuse_chan_bufsize (priv->ch) ; + + buf = trans->buf; + + if (!buf->data) { + buf->data = MALLOC (chan_size); + ERR_ABORT (buf->data); + buf->len = chan_size; + } + + res = fuse_chan_receive (priv->ch, + buf->data, + chan_size); + /* if (res == -1) { + transport_destroy (trans); + */ + if (res && res != -1) { + /* trace the request and log it to tio file */ + guts_log_req (buf->data, res); + fuse_session_process (priv->se, + buf->data, + res, + priv->ch); + } + + LOCK (&buf->lock); + ref = buf->refcount; + UNLOCK (&buf->lock); + /* TODO do the check with a lock */ + if (ref > 1) { + data_unref (buf); + + // trans->buf = data_ref (data_from_dynptr (malloc (fuse_chan_bufsize (priv->ch)), + trans->buf = data_ref (data_from_dynptr (NULL, 0)); + trans->buf->data = MALLOC (chan_size); + ERR_ABORT (trans->buf->data); + trans->buf->len = chan_size; + trans->buf->is_locked = 1; + } + } else { + transport_disconnect (trans); + } + + /* + if (fuse_session_exited (priv->se)) { + transport_destroy (trans); + res = -1; + }*/ + + return res >= 0 ? 0 : res; +} + +static void +fuse_transport_fini (transport_t *this) +{ + +} + +static struct transport_ops fuse_transport_ops = { + .disconnect = fuse_transport_disconnect, +}; + +static transport_t fuse_transport = { + .ops = &fuse_transport_ops, + .private = NULL, + .xl = NULL, + .init = fuse_transport_init, + .fini = fuse_transport_fini, + .notify = fuse_transport_notify +}; + + +transport_t * +glusterfs_mount (glusterfs_ctx_t *ctx, + const char *mount_point) +{ + dict_t *options = get_new_dict (); + transport_t *new_fuse = CALLOC (1, sizeof (*new_fuse)); + ERR_ABORT (new_fuse); + + memcpy (new_fuse, &fuse_transport, sizeof (*new_fuse)); + new_fuse->ops = &fuse_transport_ops; + new_fuse->xl_private = ctx; + + dict_set (options, + "mountpoint", + str_to_data ((char *)mount_point)); + + return (new_fuse->init (new_fuse, + options, + fuse_transport_notify) == 0 ? new_fuse : NULL); +} + +int32_t +fuse_thread (pthread_t *thread, void *data) +{ + return pthread_create (thread, NULL, fuse_thread_proc, data); +} + + diff --git a/glusterfs-guts/src/fuse-extra.c b/glusterfs-guts/src/fuse-extra.c new file mode 100644 index 000000000..93574d174 --- /dev/null +++ b/glusterfs-guts/src/fuse-extra.c @@ -0,0 +1,137 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#include "fuse-extra.h" +#include "common-utils.h" +#include +#include +#include +#include +#include "common-utils.h" + +struct fuse_req; +struct fuse_ll; + +struct fuse_req { + struct fuse_ll *f; + uint64_t unique; + int ctr; + pthread_mutex_t lock; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; + +struct fuse_ll { + int debug; + int allow_root; + struct fuse_lowlevel_ops op; + int got_init; + void *userdata; + uid_t owner; + struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; + int got_destroy; +}; + +struct fuse_out_header { + uint32_t len; + int32_t error; + uint64_t unique; +}; + +uint64_t req_callid (fuse_req_t req) +{ + return req->unique; +} + +static void destroy_req(fuse_req_t req) +{ + pthread_mutex_destroy (&req->lock); + FREE (req); +} + +static void list_del_req(struct fuse_req *req) +{ + struct fuse_req *prev = req->prev; + struct fuse_req *next = req->next; + prev->next = next; + next->prev = prev; +} + +static void +free_req (fuse_req_t req) +{ + int ctr; + struct fuse_ll *f = req->f; + + pthread_mutex_lock(&req->lock); + req->u.ni.func = NULL; + req->u.ni.data = NULL; + pthread_mutex_unlock(&req->lock); + + pthread_mutex_lock(&f->lock); + list_del_req(req); + ctr = --req->ctr; + pthread_mutex_unlock(&f->lock); + if (!ctr) + destroy_req(req); +} + +int32_t +fuse_reply_vec (fuse_req_t req, + struct iovec *vector, + int32_t count) +{ + int32_t error = 0; + struct fuse_out_header out; + struct iovec *iov; + int res; + + iov = alloca ((count + 1) * sizeof (*vector)); + out.unique = req->unique; + out.error = error; + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + memcpy (&iov[1], vector, count * sizeof (*vector)); + count++; + out.len = iov_length(iov, count); + res = fuse_chan_send(req->ch, iov, count); + free_req(req); + + return res; +} diff --git a/glusterfs-guts/src/fuse-extra.h b/glusterfs-guts/src/fuse-extra.h new file mode 100644 index 000000000..c7d2877c0 --- /dev/null +++ b/glusterfs-guts/src/fuse-extra.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef _FUSE_EXTRA_H +#define _FUSE_EXTRA_H + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#include +#include + +uint64_t req_callid (fuse_req_t req); + +int32_t +fuse_reply_vec (fuse_req_t req, + struct iovec *vector, + int32_t count); + +#endif /* _FUSE_EXTRA_H */ diff --git a/glusterfs-guts/src/fuse_kernel.h b/glusterfs-guts/src/fuse_kernel.h new file mode 100644 index 000000000..7ebff8b22 --- /dev/null +++ b/glusterfs-guts/src/fuse_kernel.h @@ -0,0 +1,380 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/* This file defines the kernel interface of FUSE */ + +#ifdef __FreeBSD__ +/* + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2006 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#include +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include +#include +#endif + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 8 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** The major number of the fuse character device */ +#define FUSE_MAJOR MISC_MAJOR + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) + +/** + * INIT request/reply flags + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 padding; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 unused1; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; +}; + +struct fuse_open_in { + __u32 flags; + __u32 mode; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 padding; +}; + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u32 unused; + __u32 max_write; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/glusterfs-guts/src/glusterfs-fuse.h b/glusterfs-guts/src/glusterfs-fuse.h new file mode 100644 index 000000000..f446202fb --- /dev/null +++ b/glusterfs-guts/src/glusterfs-fuse.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef __GLUSTERFS_FUSE_H__ +#define __GLUSTERFS_FUSE_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#define DEFAULT_LOG_FILE DATADIR"/log/glusterfs/glusterfs.log" +#define DEFAULT_GLUSTERFS_CLIENT_VOL CONFDIR "/glusterfs-client.vol" + +#define SPEC_LOCAL_FILE 1 +#define SPEC_REMOTE_FILE 2 + +#if 0 +#define GF_YES 1 +#define GF_NO 0 +#endif + +#ifdef GF_LOG_FUSE_ARGS +#undef GF_LOG_FUSE_ARGS +#endif + +struct gf_spec_location { + int32_t where; + union { + char *file; + struct { + char *ip; + char *port; + char *transport; + }server; + }spec; +}; + +transport_t * glusterfs_mount (glusterfs_ctx_t *ctx, + const char *mount_point); + +#endif /* __GLUSTERFS_FUSE_H__ */ diff --git a/glusterfs-guts/src/glusterfs-guts.c b/glusterfs-guts/src/glusterfs-guts.c new file mode 100644 index 000000000..3efac3a35 --- /dev/null +++ b/glusterfs-guts/src/glusterfs-guts.c @@ -0,0 +1,400 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include + +#include "glusterfs.h" +#include "xlator.h" +#include "glusterfs-guts.h" + +/* argp initializations */ +static char doc[] = "glusterfs-guts is unit testing suite for glusterfs"; +static char argp_doc[] = ""; +const char *argp_program_version = PACKAGE_NAME " " PACKAGE_VERSION " built on " __DATE__; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +guts_ctx_t guts_ctx; +error_t parse_opts (int32_t key, char *arg, struct argp_state *_state); + +static struct argp_option options[] = { + {"spec-file", 'f', "VOLUMESPEC-FILE", 0,\ + "Load VOLUMESPEC-FILE."}, + {"threads", 't', "NUMBER", 0,\ + "Load NUMBER of threads."}, + {"tio-file", 'i', "FILE", 0,\ + "Replay fops from FILE."}, + {"tio-directory", 'I', "DIRECTORY", 0,\ + "Replay fops from files in DIRECTORY. Valid option only when using more than one thread."}, + {"log-level", 'L', "LOGLEVEL", 0, + "LOGLEVEL should be one of DEBUG, WARNING, [ERROR], CRITICAL, NONE"}, + {"log-file", 'l', "LOGFILE", 0, \ + "Specify the file to redirect logs"}, + {"trace", 'T', "MOUNTPOINT", 0, \ + "Run guts in trace mode. Guts mounts glusterfs on MOUNTPOINT specified"}, + {"output", 'o', "OUTPUT-TIOFILE", 0, \ + "Write trace io output to OUTPUT-TIOFILE. Valid only when run in trace(-T) mode."}, + {"version", 'V', 0, 0,\ + "print version information"}, + { 0, } +}; + +static struct argp argp = { options, parse_opts, argp_doc, doc }; + +/* guts_print_version - used by argument parser routine to print version information for guts */ +static int32_t +guts_print_version (void) +{ + printf ("%s\n", argp_program_version); + printf ("Copyright (c) 2006, 2007 Z RESEARCH Inc. \n"); + printf ("GlusterFS comes with ABSOLUTELY NO WARRANTY.\nYou may redistribute copies of GlusterFS under the terms of the GNU General Public License.\n"); + exit (0); +} + +/* parse_opts - argument parsing helper routine for argp library */ +error_t +parse_opts (int32_t key, char *arg, struct argp_state *_state) +{ + guts_ctx_t *state = _state->input; + + switch (key) { + case 'f': + if (!state->specfile) { + state->specfile = strdup (arg); + } + break; + + case 't': + if (!state->threads) { + state->threads = strtol (arg, NULL, 0); + } + break; + + case 'i': + if (state->threads == 1) { + state->file = strdup (arg); + } else { + fprintf (stderr, "glusterfs-guts: -i option is valid only when guts is running single thread\n"); + exit (1); + } + break; + + case 'I': + if (state->threads > 1) { + state->directory = strdup (arg); + } else { + fprintf (stderr, "glusterfs-guts: -I option is valid only when guts is running multiple threads\n"); + exit (1); + } + break; + + case 'L': + /* set log level */ + if (!strncasecmp (arg, "DEBUG", strlen ("DEBUG"))) { + state->loglevel = GF_LOG_DEBUG; + } else if (!strncasecmp (arg, "WARNING", strlen ("WARNING"))) { + state->loglevel = GF_LOG_WARNING; + } else if (!strncasecmp (arg, "CRITICAL", strlen ("CRITICAL"))) { + state->loglevel = GF_LOG_CRITICAL; + } else if (!strncasecmp (arg, "NONE", strlen ("NONE"))) { + state->loglevel = GF_LOG_NONE; + } else if (!strncasecmp (arg, "ERROR", strlen ("ERROR"))) { + state->loglevel = GF_LOG_ERROR; + } else { + fprintf (stderr, "glusterfs-guts: Unrecognized log-level \"%s\", possible values are \"DEBUG|WARNING|[ERROR]|CRITICAL|NONE\"\n", arg); + exit (EXIT_FAILURE); + } + break; + case 'l': + /* set log file */ + state->logfile = strdup (arg); + break; + + case 'T': + state->trace = 1; + state->mountpoint = strdup (arg); + break; + + case 'o': + state->file = strdup (arg); + break; + + case 'V': + guts_print_version (); + break; + + } + return 0; +} + +/* get_xlator_graph - creates a translator graph and returns the pointer to the root of the xlator tree + * + * @ctx: guts context structure + * @conf: file handle to volume specfile + * + * returns pointer to the root of the translator tree + */ +static xlator_t * +get_xlator_graph (glusterfs_ctx_t *ctx, + FILE *conf) +{ + xlator_t *tree, *trav = NULL; + + tree = file_to_xlator_tree (ctx, conf); + trav = tree; + + if (tree == NULL) { + gf_log ("glusterfs-guts", + GF_LOG_ERROR, + "specification file parsing failed, exiting"); + return NULL; + } + + tree = trav; + + return tree; +} + +/* get_spec_fp - get file handle to volume spec file specified. + * + * @ctx: guts context structure + * + * returns FILE pointer to the volume spec file. + */ +static FILE * +get_spec_fp (guts_ctx_t *ctx) +{ + char *specfile = ctx->specfile; + FILE *conf = NULL; + + specfile = ctx->specfile; + + conf = fopen (specfile, "r"); + + if (!conf) { + perror (specfile); + return NULL; + } + gf_log ("glusterfs-guts", + GF_LOG_DEBUG, + "loading spec from %s", + specfile); + + return conf; +} + +static void * +guts_thread_main (void *ctx) +{ + guts_thread_ctx_t *tctx = (guts_thread_ctx_t *) ctx; + + printf ("starting thread main with %s:\n", tctx->file); + guts_replay (tctx); + printf ("ending thread main.\n"); + + return NULL; +} + +/* guts_create_threads - creates different threads based on thread number specified in ctx and assigns a + * tio file to each thread and attaches each thread to the graph created by main(). + * @ctx: guts_ctx_t which contains the context corresponding to the current run of guts + * + * returns the guts_threads_t structure which contains handles to the different threads created. + * + */ +static guts_threads_t * +guts_create_threads (guts_ctx_t *ctx) +{ + guts_threads_t *threads = NULL; + int32_t thread_count = ctx->threads; + + threads = CALLOC (1, sizeof (*threads)); + ERR_ABORT (threads); + + + INIT_LIST_HEAD (&(threads->threads)); + + if (thread_count == 1) { + /* special case: we have only one thread and we are given a tio-file as argument instead of a directory. + * handling differently */ + guts_thread_ctx_t *thread = NULL; + thread = CALLOC (1, sizeof (*thread)); + ERR_ABORT (thread); + list_add (&thread->threads, &threads->threads); + thread->file = strdup (ctx->file); + thread->ctx = ctx; + } else { + /* look for .tio files in the directory given and assign to each of the threads */ + DIR *dir = opendir (ctx->directory); + + if (!dir) { + gf_log ("guts", + GF_LOG_ERROR, + "failed to open directory %s", ctx->directory); + } else { + guts_thread_ctx_t *thread = NULL; + struct dirent *dirp = NULL; + /* to pass through "." and ".." */ + readdir (dir); + readdir (dir); + + while (thread_count > 0) { + char pathname[256] = {0,}; + + thread = CALLOC (1, sizeof (*thread)); + ERR_ABORT (thread); + dirp = NULL; + + list_add (&thread->threads, &threads->threads); + dirp = readdir (dir); + if (dirp) { + sprintf (pathname, "%s/%s", ctx->directory, dirp->d_name); + printf ("file name for thread(%d) is %s\n", thread_count, pathname); + thread->file = strdup (pathname); + thread->ctx = ctx; + } else if (thread_count > 0) { + gf_log ("guts", + GF_LOG_ERROR, + "number of tio files less than %d, number of threads specified", ctx->threads); + /* TODO: cleanup */ + return NULL; + } + --thread_count; + } + } + } + return threads; +} + +/* guts_start_threads - starts all the threads in @threads. + * + * @threads: guts_threads_t structure containing the handles to threads created by guts_create_threads. + * + * returns <0 on error. + * + */ +static void +guts_start_threads (guts_threads_t *gthreads) +{ + guts_thread_ctx_t *thread = NULL; + list_for_each_entry (thread, >hreads->threads, threads) { + if (pthread_create (&thread->pthread, NULL, guts_thread_main, (void *)thread) < 0) { + gf_log ("guts", + GF_LOG_ERROR, + "failed to start thread"); + } else { + gf_log ("guts", + GF_LOG_DEBUG, + "started thread with file %s", thread->file); + } + } +} + +static int32_t +guts_join_threads (guts_threads_t *gthreads) +{ + guts_thread_ctx_t *thread = NULL; + list_for_each_entry (thread, >hreads->threads, threads) { + if (pthread_join (thread->pthread, NULL) < 0) { + gf_log ("guts", + GF_LOG_ERROR, + "failed to join thread"); + } else { + gf_log ("guts", + GF_LOG_DEBUG, + "joined thread with file %s", thread->file); + } + } + return 0; +} + + +int32_t +main (int32_t argc, char *argv[]) +{ + /* glusterfs_ctx_t is required to be passed to + * 1. get_xlator_graph + * 2. glusterfs_mount + */ + glusterfs_ctx_t gfs_ctx = { + .logfile = DATADIR "/log/glusterfs/glusterfs-guts.log", + .loglevel = GF_LOG_DEBUG, + .poll_type = SYS_POLL_TYPE_EPOLL, + }; + + guts_ctx_t guts_ctx = {0,}; + FILE *specfp = NULL; + xlator_t *graph = NULL; + guts_threads_t *threads = NULL; + + argp_parse (&argp, argc, argv, 0, 0, &guts_ctx); + + if (gf_log_init (gfs_ctx.logfile) == -1 ) { + fprintf (stderr, + "glusterfs-guts: failed to open logfile \"%s\"\n", + gfs_ctx.logfile); + return -1; + } + gf_log_set_loglevel (gfs_ctx.loglevel); + + specfp = get_spec_fp (&guts_ctx); + if (!specfp) { + fprintf (stderr, + "glusterfs-guts: could not open specfile\n"); + return -1; + } + + graph = get_xlator_graph (&gfs_ctx, specfp); + if (!graph) { + gf_log ("guts", GF_LOG_ERROR, + "Unable to get xlator graph"); + return -1; + } + fclose (specfp); + + guts_ctx.graph = graph; + + if (guts_ctx.trace) { + return guts_trace (&guts_ctx); + } else { + /* now that we have the xlator graph, we need to create as many threads as requested and assign a tio file + * to each of the threads and tell each thread to attach to the graph we just created. */ + + if (!guts_ctx.file && !guts_ctx.directory) { + fprintf (stderr, + "glusterfs-guts: no tio file specified"); + return -1; + } + + threads = guts_create_threads (&guts_ctx); + + if (threads) { + guts_start_threads (threads); + guts_join_threads (threads); + } else { + gf_log ("guts", GF_LOG_ERROR, + "unable to create threads"); + return 0; + } + } + + return 0; +} + diff --git a/glusterfs-guts/src/glusterfs-guts.h b/glusterfs-guts/src/glusterfs-guts.h new file mode 100644 index 000000000..eda1788a9 --- /dev/null +++ b/glusterfs-guts/src/glusterfs-guts.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __GLUSTERFS_GUTS_H +#define __GLUSTERFS_GUTS_H + +#include "xlator.h" +#include "transport.h" +#include "glusterfs.h" +#include "glusterfs-fuse.h" +#include "timer.h" + +#ifdef DEFAULT_LOG_FILE +#undef DEFAULT_LOG_FILE +#endif + +#define DEFAULT_LOG_FILE DATADIR"/log/glusterfs/glusterfs-guts.log" + + +typedef struct { + int32_t threads; /* number of threads to start in replay mode */ + char *logfile; /* logfile path */ + int32_t loglevel; /* logging level */ + char *directory; /* path to directory containing tio files, when threads > 1 */ + char *file; /* path to tio file, when threads == 1 during replay. in trace mode, path to tio output */ + char *specfile; /* path to specfile to load translator tree */ + xlator_t *graph; /* translator tree after the specfile is loaded */ + int32_t trace; /* if trace == 1, glusterfs-guts runs in trace mode, otherwise in replay mode */ + char *mountpoint; /* valid only when trace == 1, mounpoint to mount glusterfs */ +} guts_ctx_t; + + +typedef struct { + struct list_head threads; + pthread_t pthread; + xlator_t *tree; + char *file; + guts_ctx_t *ctx; +} guts_thread_ctx_t; + +typedef struct { + struct list_head threads; +} guts_threads_t; + +int32_t guts_replay (guts_thread_ctx_t *); +int32_t guts_trace (guts_ctx_t *); +#endif diff --git a/glusterfs-guts/src/guts-extra.c b/glusterfs-guts/src/guts-extra.c new file mode 100644 index 000000000..dd4ad466f --- /dev/null +++ b/glusterfs-guts/src/guts-extra.c @@ -0,0 +1,18 @@ +/* + Copyright (c) 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ diff --git a/glusterfs-guts/src/guts-lowlevel.h b/glusterfs-guts/src/guts-lowlevel.h new file mode 100644 index 000000000..498b5d01e --- /dev/null +++ b/glusterfs-guts/src/guts-lowlevel.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _GUTS_LOWLEVEL_H_ +#define _GUTS_LOWLEVEL_H_ + +int +guts_reply_err (fuse_req_t req, + int err); + +int +guts_reply_none (fuse_req_t req); + +int +guts_reply_entry (fuse_req_t req, + const struct fuse_entry_param *e); + +int +guts_reply_create (fuse_req_t req, + const struct fuse_entry_param *e, + const struct fuse_file_info *f); + +int +guts_reply_attr (fuse_req_t req, + const struct stat *attr, + double attr_timeout); + +int +guts_reply_readlink (fuse_req_t req, + const char *linkname); + +int +guts_reply_open (fuse_req_t req, + const struct fuse_file_info *f); + +int +guts_reply_write (fuse_req_t req, + size_t count); + +int +guts_reply_buf (fuse_req_t req, + const char *buf, + size_t size); + +int +guts_reply_statfs (fuse_req_t req, + const struct statvfs *stbuf); + +int +guts_reply_xattr (fuse_req_t req, + size_t count); + +int +guts_reply_lock (fuse_req_t req, + struct flock *lock); + +/* exploiting the macros to reduce coding work ;) */ +#define fuse_reply_entry guts_reply_entry +#define fuse_reply_err guts_reply_err +#define fuse_reply_none guts_reply_none +#define fuse_reply_attr guts_reply_attr +#define fuse_reply_open guts_reply_open +#define fuse_reply_readlink guts_reply_readlink +#define fuse_reply_create guts_reply_create +#define fuse_reply_write guts_reply_write +#define fuse_reply_buf guts_reply_buf +#define fuse_reply_statfs guts_reply_statfs +#define fuse_reply_xattr guts_reply_xattr +#define fuse_reply_lock guts_reply_lock + +#endif diff --git a/glusterfs-guts/src/guts-parse.c b/glusterfs-guts/src/guts-parse.c new file mode 100644 index 000000000..dd17a737e --- /dev/null +++ b/glusterfs-guts/src/guts-parse.c @@ -0,0 +1,217 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "guts-parse.h" +#include "guts-tables.h" + +/* unavoidable usage of global data.. :'( */ +static int32_t tio_fd = 0; + +int32_t +guts_tio_init (const char *filename) +{ + tio_fd = open (filename, O_WRONLY | O_CREAT); + + if (tio_fd < 0) { + gf_log ("guts", + GF_LOG_ERROR, + "failed to open tio file %s", filename); + } + + return tio_fd; +} + +void +guts_reply_dump (fuse_req_t req, + const void *arg, + int32_t len) +{ + uint8_t *buf = NULL; + uint8_t *ibuf = NULL; + uint32_t buf_size = REP_HEADER_FULL_LEN + len; + + ibuf = buf = CALLOC (1, buf_size); + + /* being paranoid, checking for both ibuf and buf.. ;) */ + if (ibuf && buf) { + memcpy (ibuf, REP_BEGIN, strlen (REP_BEGIN)); + ibuf += strlen (REP_BEGIN); + memcpy (ibuf, req, sizeof (struct fuse_req)); + ibuf += sizeof (struct fuse_req); + memcpy (ibuf, &len, sizeof (len)); + ibuf += sizeof (len); + memcpy (ibuf, arg, len); + + gf_full_write (tio_fd, buf, buf_size); + + free (buf); + } else { + gf_log ("glusterfs-guts", GF_LOG_DEBUG, + "failed to allocate memory while dumping reply"); + } +} + +void +guts_req_dump (struct fuse_in_header *in, + const void *arg, + int32_t len) +{ + /* GUTS_REQUEST_BEGIN::::GUTS_REQUEST_END */ + uint8_t *buf = NULL; + uint8_t *ibuf = NULL; + uint32_t buf_size = REQ_HEADER_FULL_LEN + len; + + ibuf = buf = CALLOC (1, buf_size); + + if (ibuf && buf) { + memcpy (ibuf, REQ_BEGIN, strlen (REQ_BEGIN)); + ibuf += strlen (REQ_BEGIN); + memcpy (ibuf, in, sizeof (*in)); + ibuf += sizeof (*in); + memcpy (ibuf, &len, sizeof (len)); + ibuf += sizeof (len); + memcpy (ibuf, arg, len); + + gf_full_write (tio_fd, buf, buf_size); + + free (buf); + } else { + gf_log ("glusterfs-guts", GF_LOG_DEBUG, + "failed to allocate memory while dumping reply"); + } +} + + + +guts_req_t * +guts_read_entry (guts_replay_ctx_t *ctx) +{ + guts_req_t *req = NULL; + guts_reply_t *reply = NULL; + uint8_t begin[256] = {0,}; + int32_t ret = 0; + int32_t fd = ctx->tio_fd; + + while (!req) { + req = guts_get_request (ctx); + + if (!req) { + ret = read (fd, begin, strlen (REQ_BEGIN)); + + if (ret == 0) { + gf_log ("glusterfs-guts", GF_LOG_DEBUG, + "guts replay finished"); + req = NULL; + } + + if (is_request (begin)) { + req = CALLOC (1, sizeof (*req)); + ERR_ABORT (req); + gf_full_read (fd, (char *)req, REQ_HEADER_LEN); + + req->arg = CALLOC (1, req->arg_len + 1); + ERR_ABORT (req->arg); + gf_full_read (fd, req->arg, req->arg_len); + gf_log ("guts", + GF_LOG_DEBUG, + "%s: fop %s (%d)\n", + begin, guts_log[req->header.opcode].name, req->header.opcode); + guts_add_request (ctx, req); + req = guts_get_request (ctx); + } else { + /* whenever a reply is read, we put it to a hash table and we would like to retrieve it whenever + * we get a reply for any call + */ + reply = CALLOC (1, sizeof (*reply)); + ERR_ABORT (reply); + gf_full_read (fd, (char *)reply, REP_HEADER_LEN); + + reply->arg = CALLOC (1, reply->arg_len + 1); + ERR_ABORT (reply->arg); + gf_full_read (fd, reply->arg, reply->arg_len); + + /* add a new reply to */ + ret = guts_add_reply (ctx, reply); + gf_log ("guts", + GF_LOG_DEBUG, + "got a reply with unique: %ld", reply->req.unique); + } + } + } + return req; +} + +guts_reply_t * +guts_read_reply (guts_replay_ctx_t *ctx, + uint64_t unique) +{ + guts_req_t *req = NULL; + guts_reply_t *reply = NULL, *rep = NULL; + uint8_t begin[256] = {0,}; + int32_t ret = 0; + int32_t fd = ctx->tio_fd; + + while (!rep) { + + ret = read (fd, begin, strlen (REQ_BEGIN)); + + if (ret == 0) { + printf ("\ndone\n"); + return NULL; + } + + if (is_request (begin)) { + req = CALLOC (1, sizeof (*req)); + ERR_ABORT (req); + gf_full_read (fd, (char *)req, REQ_HEADER_LEN); + + req->arg = CALLOC (1, req->arg_len + 1); + ERR_ABORT (req->arg); + gf_full_read (fd, req->arg, req->arg_len); + gf_log ("guts", + GF_LOG_DEBUG, + "%s: fop %s (%d)\n", + begin, guts_log[req->header.opcode].name, req->header.opcode); + + ret = guts_add_request (ctx, req); + + } else { + /* whenever a reply is read, we put it to a hash table and we would like to retrieve it whenever + * we get a reply for any call + */ + reply = CALLOC (1, sizeof (*reply)); + ERR_ABORT (reply); + gf_full_read (fd, (char *)reply, REP_HEADER_LEN); + + reply->arg = CALLOC (1, reply->arg_len + 1); + ERR_ABORT (reply->arg); + gf_full_read (fd, reply->arg, reply->arg_len); + + /* add a new reply to */ + if (reply->req.unique == unique) { + return reply; + } else { + ret = guts_add_reply (ctx, reply); + gf_log ("guts", + GF_LOG_DEBUG, + "got a reply with unique: %ld", reply->req.unique); + } + } + } + return NULL; +} diff --git a/glusterfs-guts/src/guts-parse.h b/glusterfs-guts/src/guts-parse.h new file mode 100644 index 000000000..7791b1215 --- /dev/null +++ b/glusterfs-guts/src/guts-parse.h @@ -0,0 +1,140 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _GUTS_PARSE_H_ +#define _GUTS_PARSE_H_ + +#include "glusterfs.h" +#include "glusterfs-guts.h" +#include "fuse_kernel.h" +#include +#include "list.h" + +#ifndef _FUSE_OPAQUE_ +#define _FUSE_OPAQUE_ + +struct fuse_private { + int fd; + struct fuse *fuse; + struct fuse_session *se; + struct fuse_chan *ch; + char *mountpoint; +}; + +struct fuse_req { + struct fuse_ll *f; + uint64_t unique; + int ctr; + pthread_mutex_t lock; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; + +struct fuse_ll { + int debug; + int allow_root; + struct fuse_lowlevel_ops op; + int got_init; + void *userdata; + uid_t owner; + struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; + int got_destroy; +}; +#endif + +#define REQ_BEGIN "GUTS_REQ_BEGIN:" +#define REQ_HEADER_FULL_LEN (strlen(REQ_BEGIN) + sizeof (struct fuse_in_header) + sizeof (int32_t)) + +#define REP_BEGIN "GUTS_REP_BEGIN:" +#define REP_HEADER_FULL_LEN (strlen(REP_BEGIN) + sizeof (struct fuse_req) + sizeof (int32_t)) + +#define REQ_HEADER_LEN (sizeof (struct fuse_in_header) + sizeof (int32_t)) +#define REP_HEADER_LEN (sizeof (struct fuse_req) + sizeof (int32_t)) + +#define is_request(begin) (0==strcmp(begin, REQ_BEGIN)?1:0) + +typedef void (*func_t)(struct fuse_in_header *, const void *); + +typedef struct { + func_t func; + const char *name; +} guts_log_t; + +typedef struct { + struct fuse_in_header header; + int32_t arg_len; + struct list_head list; + void *arg; +} guts_req_t; + +typedef struct { + struct fuse_req req; + int32_t arg_len; + void *arg; +} guts_reply_t; + +struct guts_replay_ctx { + int32_t tio_fd; + struct fuse_ll *guts_ll; + dict_t *replies; + dict_t *inodes; + dict_t *fds; + struct list_head requests; + dict_t *requests_dict; +}; + +typedef struct guts_replay_ctx guts_replay_ctx_t; + +extern guts_log_t guts_log[]; + +int32_t +guts_tio_init (const char *); + +void +guts_req_dump (struct fuse_in_header *, + const void *, + int32_t); + +guts_req_t * +guts_read_entry (guts_replay_ctx_t *ctx); + +void +guts_reply_dump (fuse_req_t, + const void *, + int32_t); + +guts_reply_t * +guts_read_reply (guts_replay_ctx_t *ctx, + uint64_t unique); + +#endif /* _GUTS_PARSE_H_ */ diff --git a/glusterfs-guts/src/guts-replay.c b/glusterfs-guts/src/guts-replay.c new file mode 100644 index 000000000..a5447464d --- /dev/null +++ b/glusterfs-guts/src/guts-replay.c @@ -0,0 +1,834 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glusterfs-guts.h" +#include "guts-parse.h" +#include +#include "guts-tables.h" +#include "guts-replay.h" +#include "guts-trace.h" + +static void +convert_attr (const struct fuse_setattr_in *attr, + struct stat *stbuf) +{ + stbuf->st_mode = attr->mode; + stbuf->st_uid = attr->uid; + stbuf->st_gid = attr->gid; + stbuf->st_size = attr->size; + stbuf->st_atime = attr->atime; + /* + ST_ATIM_NSEC_SET (stbuf, attr->atimensec); + ST_MTIM_NSEC_SET (stbuf, attr->mtimensec);*/ +} + +static void +guts_replay_lookup (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + char *name = (char *) inargs; + + if (req->f->op.lookup) + req->f->op.lookup(req, ino, name); + else + guts_reply_err (req, ENOSYS); + +} + +static void +guts_replay_forget (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_forget_in *arg = (struct fuse_forget_in *) inargs; + + if (req->f->op.forget) + req->f->op.forget (req, ino, arg->nlookup); + +} + +static void +guts_replay_getattr (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + (void) inargs; + + if (req->f->op.getattr) + req->f->op.getattr (req, ino, NULL); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_setattr (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inargs; + + if (req->f->op.setattr) { + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; + struct stat stbuf; + memset (&stbuf, 0, sizeof (stbuf)); + convert_attr (arg, &stbuf); + if (arg->valid & FATTR_FH) { + arg->valid &= ~FATTR_FH; + memset (&fi_store, 0, sizeof (fi_store)); + fi = &fi_store; + fi->fh = arg->fh; + fi->fh_old = fi->fh; + } + req->f->op.setattr (req, ino, &stbuf, arg->valid, fi); + } else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_access (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_access_in *arg = (struct fuse_access_in *)inargs; + + if (req->f->op.access) + req->f->op.access (req, ino, arg->mask); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_readlink (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + (void) inargs; + + if (req->f->op.readlink) + req->f->op.readlink (req, ino); + else + guts_reply_err (req, ENOSYS); +} + + +static void +guts_replay_mknod (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inargs; + + if (req->f->op.mknod) + req->f->op.mknod (req, ino, PARAM(arg), arg->mode, arg->rdev); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_mkdir (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inargs; + + if (req->f->op.mkdir) + req->f->op.mkdir (req, ino, PARAM(arg), arg->mode); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_unlink (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + char *name = (char *)inargs; + + if (req->f->op.unlink) { + + req->f->op.unlink (req, ino, name); + } else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_rmdir (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + char *name = (char *)inargs; + + if (req->f->op.rmdir) { + req->f->op.rmdir (req, ino, name); + } else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_symlink (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + char *name = (char *) inargs; + char *linkname = ((char *) inargs) + strlen ((char *) inargs) + 1; + + if (req->f->op.symlink) { + req->f->op.symlink (req, linkname, ino, name); + } else + guts_reply_err (req, ENOSYS); +} + + + +static void +guts_replay_rename (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_rename_in *arg = (struct fuse_rename_in *) inargs; + char *oldname = PARAM(arg); + char *newname = oldname + strlen (oldname) + 1; + + if (req->f->op.rename) { + req->f->op.rename (req, ino, oldname, arg->newdir, newname); + } else + guts_reply_err (req, ENOSYS); + +} + +static void +guts_replay_link (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_link_in *arg = (struct fuse_link_in *) inargs; + + if (req->f->op.link) { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + fuse_ino_t old_ino = guts_inode_search (ctx, arg->oldnodeid); + + req->f->op.link (req, old_ino, ino, PARAM(arg)); + } else + guts_reply_err (req, ENOSYS); +} + + +static void +guts_replay_create (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct guts_create_in *arg = (struct guts_create_in *) inargs; + + if (req->f->op.create) { + struct fuse_file_info fi; + memset (&fi, 0, sizeof (fi)); + fi.flags = arg->open_in.flags; + + req->f->op.create (req, ino, arg->name, arg->open_in.mode, &fi); + } else + guts_reply_err (req, ENOSYS); + +} + +static void +guts_replay_open (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inargs; + struct fuse_file_info fi; + + memset (&fi, 0, sizeof (fi)); + fi.flags = arg->flags; + + if (req->f->op.open) { + /* TODO: how efficient is using dict_get here?? */ + req->f->op.open (req, ino, &fi); + } else + guts_reply_open (req, &fi); +} + + +static void +guts_replay_read(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + + if (req->f->op.read){ + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + /* TODO: how efficient is using dict_get here?? */ + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + if (!fi.fh) { + /* TODO: make it more meaningful and organized */ + printf ("readv called without opening the file\n"); + guts_reply_err (req, EBADFD); + } else { + fi.fh_old = fi.fh; + req->f->op.read (req, ino, arg->size, arg->offset, &fi); + } + } else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_write(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_write_in *arg = (struct fuse_write_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + + if (!fi.fh) { + /* TODO: make it more meaningful and organized */ + printf ("writev called without opening the file\n"); + guts_reply_err (req, EBADFD); + } else { + fi.fh_old = fi.fh; + fi.writepage = arg->write_flags & 1; + if (req->f->op.write) + req->f->op.write (req, ino, PARAM(arg), arg->size, arg->offset, &fi); + else + guts_reply_err (req, ENOSYS); + } +} + +static void +guts_replay_flush(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + if (!fi.fh) { + printf ("flush called without calling open\n"); + guts_reply_err (req, EBADFD); + } else { + fi.fh_old = fi.fh; + fi.flush = 1; + + if (req->f->conn.proto_minor >= 7) + fi.lock_owner = arg->lock_owner; + + if (req->f->op.flush) + req->f->op.flush (req, ino, &fi); + else + guts_reply_err (req, ENOSYS); + } +} + +static void +guts_replay_release(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.flags = arg->flags; + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + + if (!fi.fh) { + printf ("release called without calling open\n"); + guts_reply_err (req, EBADFD); + } else { + fi.fh_old = fi.fh; + if (req->f->conn.proto_minor >= 8) { + fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; + fi.lock_owner = arg->lock_owner; + } + if (req->f->op.release) + req->f->op.release (req, ino, &fi); + else + guts_reply_err (req, ENOSYS); + } +} + +static void +guts_replay_fsync(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + fi.fh_old = fi.fh; + + if (req->f->op.fsync) + req->f->op.fsync (req, ino, arg->fsync_flags & 1, &fi); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_opendir (fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inarg; + struct fuse_file_info fi; + + memset (&fi, 0, sizeof (fi)); + fi.flags = arg->flags; + + if (req->f->op.opendir) { + req->f->op.opendir (req, ino, &fi); + } else + guts_reply_open (req, &fi); +} + +static void +guts_replay_readdir(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + + if (!fi.fh) { + /* TODO: make it more meaningful and organized */ + printf ("readdir called without opening the file\n"); + guts_reply_err (req, EBADFD); + } else { + fi.fh_old = fi.fh; + + if (req->f->op.readdir) + req->f->op.readdir (req, ino, arg->size, arg->offset, &fi); + else + guts_reply_err (req, ENOSYS); + } + +} + +static void +guts_replay_releasedir(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.flags = arg->flags; + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + if (!fi.fh) { + printf ("releasedir called without calling opendir\n"); + guts_reply_err (req, EBADFD); + } else { + + fi.fh_old = fi.fh; + if (req->f->op.releasedir) + req->f->op.releasedir (req, ino, &fi); + else + guts_reply_err (req, ENOSYS); + } +} + +static void +guts_replay_fsyncdir(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + + memset (&fi, 0, sizeof (fi)); + fi.fh = (unsigned long) guts_fd_search (ctx, arg->fh); + fi.fh_old = fi.fh; + + if (req->f->op.fsyncdir) + req->f->op.fsyncdir (req, ino, arg->fsync_flags & 1, &fi); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_statfs (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + (void) ino; + (void) inargs; + + if (req->f->op.statfs) { + req->f->op.statfs (req, ino); + } else { + struct statvfs buf = { + .f_namemax = 255, + .f_bsize = 512, + }; + guts_reply_statfs (req, &buf); + } +} + +static void +guts_replay_setxattr(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; + char *name = PARAM(arg); + char *value = name + strlen(name) + 1; + + if (req->f->op.setxattr) + req->f->op.setxattr (req, ino, name, value, arg->size, arg->flags); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_getxattr(fuse_req_t req, + fuse_ino_t ino, + const void *inarg) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; + + if (req->f->op.getxattr) + req->f->op.getxattr (req, ino, PARAM(arg), arg->size); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_listxattr (fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inargs; + + if (req->f->op.listxattr) + req->f->op.listxattr (req, ino, arg->size); + else + guts_reply_err (req, ENOSYS); +} + +static void +guts_replay_removexattr(fuse_req_t req, + fuse_ino_t ino, + const void *inargs) +{ + char *name = (char *)inargs; + + if (req->f->op.removexattr) + req->f->op.removexattr (req, ino, name); + else + guts_reply_err (req, ENOSYS); +} + +guts_replay_t guts_replay_fop[] = { + [FUSE_LOOKUP] = { guts_replay_lookup, "lookup" }, + [FUSE_FORGET] = { guts_replay_forget, "forget" }, + [FUSE_GETATTR] = { guts_replay_getattr, "getattr" }, + [FUSE_SETATTR] = { guts_replay_setattr, "setattr" }, + [FUSE_ACCESS] = { guts_replay_access, "access" }, + [FUSE_READLINK] = { guts_replay_readlink, "readlink" }, + [FUSE_MKNOD] = { guts_replay_mknod, "mknod" }, + [FUSE_MKDIR] = { guts_replay_mkdir, "mkdir" }, + [FUSE_UNLINK] = { guts_replay_unlink, "unlink" }, + [FUSE_RMDIR] = { guts_replay_rmdir, "rmdir" }, + [FUSE_SYMLINK] = { guts_replay_symlink, "symlink" }, + [FUSE_RENAME] = { guts_replay_rename, "rename" }, + [FUSE_LINK] = { guts_replay_link, "link" }, + [FUSE_CREATE] = { guts_replay_create, "create" }, + [FUSE_OPEN] = { guts_replay_open, "open" }, + [FUSE_READ] = { guts_replay_read, "read" }, + [FUSE_WRITE] = { guts_replay_write, "write" }, + [FUSE_FLUSH] = { guts_replay_flush, "flush" }, + [FUSE_RELEASE] = { guts_replay_release, "release" }, + [FUSE_FSYNC] = { guts_replay_fsync, "fsync" }, + [FUSE_OPENDIR] = { guts_replay_opendir, "opendir" }, + [FUSE_READDIR] = { guts_replay_readdir, "readdir" }, + [FUSE_RELEASEDIR] = { guts_replay_releasedir, "releasedir" }, + [FUSE_FSYNCDIR] = { guts_replay_fsyncdir, "fsyncdir" }, + [FUSE_STATFS] = { guts_replay_statfs, "statfs" }, + [FUSE_SETXATTR] = { guts_replay_setxattr, "setxattr" }, + [FUSE_GETXATTR] = { guts_replay_getxattr, "getxattr" }, + [FUSE_LISTXATTR] = { guts_replay_listxattr, "listxattr" }, + [FUSE_REMOVEXATTR] = { guts_replay_removexattr, "removexattr" }, +}; + +static inline void +list_init_req (struct fuse_req *req) +{ + req->next = req; + req->prev = req; +} + + +static int32_t +guts_transport_notify (xlator_t *xl, + int32_t event, + void *data, + ...) +{ + /* dummy, nobody has got anything to notify me.. ;) */ + return 0; +} + +static int32_t +guts_transport_init (transport_t *this, + dict_t *options, + event_notify_fn_t notify) +{ + struct fuse_private *priv = CALLOC (1, sizeof (*priv)); + ERR_ABORT (priv); + + this->notify = NULL; + this->private = (void *)priv; + + /* fuse channel */ + priv->ch = NULL; + + /* fuse session */ + priv->se = NULL; + + /* fuse channel fd */ + priv->fd = -1; + + this->buf = data_ref (data_from_dynptr (NULL, 0)); + this->buf->is_locked = 1; + + priv->mountpoint = NULL; + + transport_ref (this); + + return 0; +} + +static void +guts_transport_fini (transport_t *this) +{ + +} + +static int32_t +guts_transport_disconnect (transport_t *this) +{ + struct fuse_private *priv = this->private; + + gf_log ("glusterfs-guts", + GF_LOG_DEBUG, + "cleaning up fuse transport in disconnect handler"); + + FREE (priv); + priv = NULL; + this->private = NULL; + + /* TODO: need graceful exit. every xlator should be ->fini()'ed + and come out of main poll loop cleanly + */ + return -1; +} + +static struct transport_ops guts_transport_ops = { + .disconnect = guts_transport_disconnect, +}; + +static transport_t guts_transport = { + .ops = &guts_transport_ops, + .private = NULL, + .xl = NULL, + .init = guts_transport_init, + .fini = guts_transport_fini, + .notify = guts_transport_notify +}; + +static inline xlator_t * +fuse_graph (xlator_t *graph) +{ + xlator_t *top = NULL; + xlator_list_t *xlchild; + + top = CALLOC (1, sizeof (*top)); + ERR_ABORT (top); + + xlchild = CALLOC (1, sizeof(*xlchild)); + ERR_ABORT (xlchild); + xlchild->xlator = graph; + top->children = xlchild; + top->ctx = graph->ctx; + top->next = graph; + graph->parent = top; + + return top; +} + +static guts_replay_ctx_t * +guts_replay_init (guts_thread_ctx_t *thread) +{ + guts_replay_ctx_t *ctx = NULL; + int32_t fd = open (thread->file, O_RDONLY); + + if (fd < 0) { + gf_log ("glusterfs-guts", GF_LOG_DEBUG, + "failed to open tio_file %s", thread->file); + return ctx; + } else { + struct fuse_ll *guts_ll = CALLOC (1, sizeof (*guts_ll)); + ERR_ABORT (guts_ll); + + ctx = CALLOC (1, sizeof (*ctx)); + ERR_ABORT (ctx); + + if (ctx) { + /* equivalent to fuse_new_session () */ + guts_ll->conn.async_read = 1; + guts_ll->conn.max_write = UINT_MAX; + guts_ll->conn.max_readahead = UINT_MAX; + memcpy (&guts_ll->op, &fuse_ops, sizeof (struct fuse_lowlevel_ops)); + list_init_req (&guts_ll->list); + list_init_req (&guts_ll->interrupts); + guts_ll->owner = getuid (); + guts_ll->userdata = thread; + + /* TODO: need to create transport_t object which whole of the glusterfs + * so desperately depends on */ + transport_t *guts_trans = CALLOC (1, sizeof (*guts_trans)); + + if (guts_trans) { + memcpy (guts_trans, &guts_transport, sizeof (*guts_trans)); + guts_trans->ops = &guts_transport_ops; + } else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to allocate memory for guts transport object"); + return NULL; + } + + glusterfs_ctx_t *glfs_ctx = CALLOC (1, sizeof (*glfs_ctx));; + if (glfs_ctx) { + guts_trans->xl_private = glfs_ctx; + guts_trans->xl = fuse_graph (thread->ctx->graph); + }else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to allocate memory for glusterfs_ctx_t object"); + return NULL; + } + + call_pool_t *pool = CALLOC (1, sizeof (call_pool_t)); + if (pool) { + glfs_ctx->pool = pool; + LOCK_INIT (&pool->lock); + INIT_LIST_HEAD (&pool->all_frames); + } else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to allocate memory for guts call pool"); + return NULL; + } + + guts_trans->xl->ctx = glfs_ctx; + guts_trans->init (guts_trans, NULL, guts_transport_notify); + guts_ll->userdata = guts_trans; + + /* call fuse_init */ + guts_ll->op.init (guts_trans, NULL); + + { + ctx->guts_ll = guts_ll; + ctx->tio_fd = fd; + ctx->inodes = get_new_dict (); + ctx->fds = get_new_dict (); + ctx->replies = get_new_dict (); + INIT_LIST_HEAD(&ctx->requests); + ctx->requests_dict = get_new_dict (); + } + } else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to allocate memory for guts_ctx_t object"); + return NULL; + } + } + + return ctx; +} + +int32_t +guts_replay (guts_thread_ctx_t *thread) +{ + guts_req_t *entry = NULL; + guts_replay_ctx_t *ctx = guts_replay_init (thread); + + if (!ctx) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to initialize guts_replay"); + return -1; + } else { + while ((entry = guts_read_entry (ctx))) { + /* here we go ... execute the request */ + fuse_req_t req = CALLOC (1, sizeof (struct fuse_req)); + ino_t ino = entry->header.nodeid; + void *arg = entry->arg; + + if (req) { + req->f = ctx->guts_ll; + req->unique = entry->header.unique; + req->ctx.uid = entry->header.uid; + req->ctx.pid = entry->header.pid; + + /* req->u.ni.data is unused void *, while running in replay mode. Making use of available real-estate + * to store useful information of thread specific guts_replay_ctx */ + req->u.ni.data = (void *) ctx; + /* req->ch is of type 'struct fuse_chan', which fuse uses only at the + * time of the response it gets and is useful in sending the reply data to correct channel + * in /dev/fuse. This is not useful for us, so we ignore it by keeping it NULL */ + list_init_req (req); + + fuse_ino_t new_ino = guts_inode_search (ctx, ino); + + if (guts_replay_fop[entry->header.opcode].func) { + printf ("operation: %s && inode: %ld\n", guts_replay_fop[entry->header.opcode].name, new_ino); + guts_replay_fop[entry->header.opcode].func (req, new_ino, arg); + } + + if (entry->arg) + free (entry->arg); + free (entry); + } else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "failed to allocate memory for fuse_req_t object"); + return -1; + } + } + } + return 0; +} diff --git a/glusterfs-guts/src/guts-replay.h b/glusterfs-guts/src/guts-replay.h new file mode 100644 index 000000000..532060d2b --- /dev/null +++ b/glusterfs-guts/src/guts-replay.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) + +void guts_reply_err (fuse_req_t, error_t); +void guts_reply_open (fuse_req_t, struct fuse_file_info *); +void guts_reply_statfs (fuse_req_t, struct statvfs *); + +typedef void (*guts_replay_fop_t)(fuse_req_t, fuse_ino_t, const void *); + +typedef struct { + guts_replay_fop_t func; + const char *name; +} guts_replay_t; + +extern struct fuse_lowlevel_ops fuse_ops; + diff --git a/glusterfs-guts/src/guts-tables.c b/glusterfs-guts/src/guts-tables.c new file mode 100644 index 000000000..2992b3e2c --- /dev/null +++ b/glusterfs-guts/src/guts-tables.c @@ -0,0 +1,248 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#include "guts-parse.h" +#include "dict.h" +#include "guts-tables.h" + + +int32_t +guts_attr_cmp (const struct stat *attr, + const struct stat *old_attr) +{ + return 0; +} + +int32_t +guts_statvfs_cmp (const struct statvfs *stbuf, + const struct statvfs *old_stbuf) +{ + return 0; +} + +int32_t +guts_flock_cmp (struct flock *lock, + struct flock *old_lock) +{ + return 0; +} + + +guts_req_t * +guts_lookup_request (guts_replay_ctx_t *ctx, uint64_t unique) +{ + guts_req_t *req = NULL; + + if (unique == 0) { + if (list_empty (&ctx->requests)) + req = NULL; + else { + /* pick an entry from list, move it out of the list and return it to the caller */ + char *key = NULL; + + req = list_entry (ctx->requests.next, guts_req_t, list); + list_del (&req->list); + + asprintf (&key, "%llu", req->header.unique); + + dict_set (ctx->requests_dict, key, data_from_static_ptr (req)); + + if (key) + free (key); + } + } else { + char *key = NULL; + data_t *req_data = NULL; + + asprintf (&key, "%llu", unique); + + req_data = dict_get (ctx->requests_dict, key); + + if (req_data) + req = data_to_ptr (req_data); + + if (key) + free (key); + } + return req; +} + +guts_req_t * +guts_get_request (guts_replay_ctx_t *ctx) +{ + return guts_lookup_request (ctx, 0); +} + +int32_t +guts_add_request (guts_replay_ctx_t *ctx, + guts_req_t *req) +{ + list_add_tail (&req->list, &ctx->requests); + return 0; +} + +int32_t +guts_add_reply (guts_replay_ctx_t *ctx, + guts_reply_t *reply) +{ + char *key = NULL; + asprintf (&key, "%llu", reply->req.unique); + + dict_set (ctx->replies, key, data_from_static_ptr(reply)); + + if (key) + free(key); + + return 0; +} + + +guts_reply_t * +guts_lookup_reply (guts_replay_ctx_t *ctx, + uint64_t unique) +{ + char *key = NULL; + data_t *reply_data = NULL; + guts_reply_t *new_reply = NULL; + + asprintf (&key, "%llu", unique); + reply_data = dict_get (ctx->replies, key); + + if (reply_data) { + new_reply = data_to_ptr (reply_data); + dict_del (ctx->replies, key); + } else { + /* reply has not yet been read from tio file */ + new_reply = guts_read_reply (ctx, unique); + + if (!new_reply) { + /* failed to fetch reply for 'unique' from tio file */ + new_reply; + } + } + + if (key) + free(key); + + return new_reply; + +} + +int32_t +guts_inode_update (guts_replay_ctx_t *ctx, + fuse_ino_t old_ino, + fuse_ino_t new_ino) +{ + char *key = NULL; + asprintf (&key, "%ld", old_ino); + dict_set (ctx->inodes, key, data_from_uint64 (new_ino)); + + if (key) + free(key); + + return 0; +} + +fuse_ino_t +guts_inode_search (guts_replay_ctx_t *ctx, + fuse_ino_t old_ino) +{ + char *key = NULL; + data_t *ino_data = NULL; + fuse_ino_t new_ino = 0; + + asprintf (&key, "%ld", old_ino); + ino_data = dict_get (ctx->inodes, key); + + if (ino_data) + new_ino = data_to_uint64 (ino_data); + else if (old_ino != /* TODO: FIXME */1 ) { + new_ino = 0; + } else + new_ino = old_ino; + + if (key) + free(key); + + return new_ino; +} + +int32_t +guts_fd_add (guts_replay_ctx_t *ctx, + unsigned long old_fd, + fd_t *new_fd) +{ + char *key = NULL; + asprintf (&key, "%ld", old_fd); + dict_set (ctx->fds, key, data_from_static_ptr (new_fd)); + + if (key) + free(key); + + return 0; +} + +fd_t * +guts_fd_search (guts_replay_ctx_t *ctx, + unsigned long old_fd) +{ + char *key = NULL; + data_t *fd_data = NULL; + fd_t *new_fd = NULL; + + asprintf (&key, "%ld", old_fd); + fd_data = dict_get (ctx->fds, key); + + if (fd_data) + new_fd = data_to_ptr (fd_data); + + if (key) + free(key); + + return new_fd; +} + +int32_t +guts_delete_fd (guts_replay_ctx_t *ctx, + unsigned long old_fd) +{ + char *key = NULL; + data_t *fd_data = NULL; + + asprintf (&key, "%ld", old_fd); + fd_data = dict_get (ctx->fds, key); + + if (fd_data) + dict_del (ctx->fds, key); + + if (key) + free(key); + + return 0; +} + +inline int32_t +guts_get_opcode (guts_replay_ctx_t *ctx, + uint64_t unique) +{ + guts_req_t *req = guts_lookup_request (ctx, unique); + + return ((req == NULL) ? -1 : req->header.opcode); + +} diff --git a/glusterfs-guts/src/guts-tables.h b/glusterfs-guts/src/guts-tables.h new file mode 100644 index 000000000..ff27300fa --- /dev/null +++ b/glusterfs-guts/src/guts-tables.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef _GUTS_TABLES_H_ +#define _GUTS_TABLES_H_ + + +int32_t +guts_attr_cmp (const struct stat *attr, + const struct stat *old_attr); + +int32_t +guts_statvfs_cmp (const struct statvfs *stbuf, + const struct statvfs *old_stbuf); + +int32_t +guts_inode_update (guts_replay_ctx_t *ctx, + fuse_ino_t old_ino, + fuse_ino_t new_ino); + +fuse_ino_t +guts_inode_search (guts_replay_ctx_t *ctx, + fuse_ino_t old_ino); + +int32_t +guts_add_request (guts_replay_ctx_t *, + guts_req_t *); + +guts_req_t * +guts_get_request (guts_replay_ctx_t *ctx); + +guts_req_t * +guts_lookup_request (guts_replay_ctx_t *ctx, + uint64_t unique); + +guts_reply_t * +guts_lookup_reply (guts_replay_ctx_t *ctx, + uint64_t unique); + +int32_t +guts_add_reply (guts_replay_ctx_t *ctx, + guts_reply_t *reply); + +int32_t +guts_flock_cmp (struct flock *lock, + struct flock *old_lock); + +fd_t * +guts_fd_search (guts_replay_ctx_t *ctx, + unsigned long old_fd); + +int32_t +guts_delete_fd (guts_replay_ctx_t *, + unsigned long); + +int32_t +guts_get_opcode (guts_replay_ctx_t *ctx, + uint64_t unique); +int32_t +guts_fd_add (guts_replay_ctx_t *ctx, + unsigned long old_fd, + fd_t *new_fd); + +#endif diff --git a/glusterfs-guts/src/guts-trace.c b/glusterfs-guts/src/guts-trace.c new file mode 100644 index 000000000..51d8a68d6 --- /dev/null +++ b/glusterfs-guts/src/guts-trace.c @@ -0,0 +1,650 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#include "glusterfs-guts.h" +#include + +#include "guts-parse.h" +#include "guts-tables.h" +#include "guts-trace.h" + +static xlator_t * +fuse_graph (xlator_t *graph) +{ + xlator_t *top = NULL; + xlator_list_t *xlchild; + + top = CALLOC (1, sizeof (*top)); + ERR_ABORT (top); + xlchild = CALLOC (1, sizeof(*xlchild)); + ERR_ABORT (xlchild); + xlchild->xlator = graph; + top->children = xlchild; + top->ctx = graph->ctx; + top->next = graph; + graph->parent = top; + + return top; +} + +int32_t +fuse_thread (pthread_t *thread, void *data); + +int32_t +guts_trace (guts_ctx_t *guts_ctx) +{ + transport_t *mp = NULL; + glusterfs_ctx_t ctx = { + .poll_type = SYS_POLL_TYPE_EPOLL, + }; + xlator_t *graph = NULL; + call_pool_t *pool = NULL; + int32_t ret = -1; + pthread_t thread; + /* Ignore SIGPIPE */ + signal (SIGPIPE, SIG_IGN); + +#if HAVE_BACKTRACE + /* Handle SIGABORT and SIGSEGV */ + signal (SIGSEGV, gf_print_trace); + signal (SIGABRT, gf_print_trace); +#endif /* HAVE_BACKTRACE */ + + ret = guts_tio_init (guts_ctx->file); + + if (ret < 0) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "running in trace mode: failed to open tio file %s", guts_ctx->file); + return -1; + } + + pool = ctx.pool = CALLOC (1, sizeof (call_pool_t)); + ERR_ABORT (ctx.pool); + LOCK_INIT (&pool->lock); + INIT_LIST_HEAD (&pool->all_frames); + + /* glusterfs_mount has to be ideally placed after all the initialisation stuff */ + if (!(mp = glusterfs_mount (&ctx, guts_ctx->mountpoint))) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, "Unable to mount glusterfs"); + return -1; + } + + gf_timer_registry_init (&ctx); + graph = guts_ctx->graph; + + if (!graph) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "Unable to get xlator graph for mount_point %s", guts_ctx->mountpoint); + transport_disconnect (mp); + return -1; + } + + ctx.graph = graph; + + mp->xl = fuse_graph (graph); + mp->xl->ctx = &ctx; + + fuse_thread (&thread, mp); + + while (!poll_iteration (&ctx)); + + return 0; +} + + + +static void +guts_name (struct fuse_in_header *in, + const void *inargs) +{ + char *name = (char *) inargs; + + guts_req_dump (in, name, strlen (name)); +} + +static void +guts_noarg (struct fuse_in_header *in, + const void *inargs) +{ + guts_req_dump (in, NULL, 0); +} + + +static void +guts_setattr (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_access (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_access_in *arg = (struct fuse_access_in *)inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_mknod (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_mkdir (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_symlink (struct fuse_in_header *in, + const void *inargs) +{ + char *name = (char *) inargs; + char *linkname = ((char *) inargs) + strlen ((char *) inargs) + 1; + struct guts_symlink_in symlink_in; + + strcpy (symlink_in.name, name); + strcpy (symlink_in.linkname, linkname); + guts_req_dump (in, &symlink_in, sizeof (symlink_in)); +} + +static void +guts_rename (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_rename_in *arg = (struct fuse_rename_in *) inargs; + char *oldname = PARAM(arg); + char *newname = oldname + strlen (oldname) + 1; + struct guts_rename_in rename_in; + + memset (&rename_in, 0, sizeof (rename_in)); + memcpy (&rename_in, arg, sizeof (*arg)); + strcpy (rename_in.oldname, oldname); + strcpy (rename_in.newname, newname); + + guts_req_dump (in, &rename_in, sizeof (rename_in)); + +} + +static void +guts_link (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_link_in *arg = (struct fuse_link_in *) inargs; + + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_open (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inargs; + + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_create (struct fuse_in_header *in, + const void *inargs) +{ + struct guts_create_in create_in; + struct fuse_open_in *arg = (struct fuse_open_in *) inargs; + char *name = PARAM (arg); + + memset (&create_in, 0, sizeof (create_in)); + memcpy (&create_in.open_in, arg, sizeof (*arg)); + memcpy (&create_in.name, name, strlen (name)); + + guts_req_dump (in, &create_in, sizeof (create_in)); +} + + +static void +guts_read(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_write(struct fuse_in_header *in, + const void *inarg) +{ + /* TODO: where the hell is the data to be written??? */ + struct fuse_write_in *arg = (struct fuse_write_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_flush(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_release(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_fsync(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_readdir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_releasedir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_fsyncdir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_setxattr(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; + char *name = PARAM(arg); + char *value = name + strlen(name) + 1; + struct guts_xattr_in setxattr_in; + + memset (&setxattr_in, 0, sizeof (setxattr_in)); + memcpy (&setxattr_in, arg, sizeof (*arg)); + strcpy (setxattr_in.name, name); + strcpy (setxattr_in.value, value); + + guts_req_dump (in, &setxattr_in, sizeof (setxattr_in)); + +} + +static void +guts_getxattr(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +guts_log_t guts_log[] = { + [FUSE_LOOKUP] = { guts_name, "lookup" }, + [FUSE_GETATTR] = { guts_noarg, "getattr" }, + [FUSE_SETATTR] = { guts_setattr, "setattr" }, + [FUSE_ACCESS] = { guts_access, "access" }, + [FUSE_READLINK] = { guts_noarg, "readlink" }, + [FUSE_MKNOD] = { guts_mknod, "mknod" }, + [FUSE_MKDIR] = { guts_mkdir, "mkdir" }, + [FUSE_UNLINK] = { guts_name, "unlink" }, + [FUSE_RMDIR] = { guts_name, "rmdir" }, + [FUSE_SYMLINK] = { guts_symlink, "symlink" }, + [FUSE_RENAME] = { guts_rename, "rename" }, + [FUSE_LINK] = { guts_link, "link" }, + [FUSE_CREATE] = { guts_create, "create" }, + [FUSE_OPEN] = { guts_open, "open" }, + [FUSE_READ] = { guts_read, "read" }, + [FUSE_WRITE] = { guts_write, "write" }, + [FUSE_FLUSH] = { guts_flush, "flush" }, + [FUSE_RELEASE] = { guts_release, "release" }, + [FUSE_FSYNC] = { guts_fsync, "fsync" }, + [FUSE_OPENDIR] = { guts_open, "opendir" }, + [FUSE_READDIR] = { guts_readdir, "readdir" }, + [FUSE_RELEASEDIR] = { guts_releasedir, "releasedir" }, + [FUSE_FSYNCDIR] = { guts_fsyncdir, "fsyncdir" }, + [FUSE_STATFS] = { guts_noarg, "statfs" }, + [FUSE_SETXATTR] = { guts_setxattr, "setxattr" }, + [FUSE_GETXATTR] = { guts_getxattr, "getxattr" }, + [FUSE_LISTXATTR] = { guts_getxattr, "listxattr" }, + [FUSE_REMOVEXATTR] = { guts_name, "removexattr" }, +}; + +/* used for actual tracing task */ + +int32_t +guts_log_req (void *buf, + int32_t len) +{ + struct fuse_in_header *in = buf; + const void *inargs = NULL; + int32_t header_len = sizeof (struct fuse_in_header); + + if (header_len < len ) { + inargs = buf + header_len; + gf_log ("guts-gimmik", GF_LOG_ERROR, + "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n", + (unsigned long long) in->unique, "", + /*opname((enum fuse_opcode) in->opcode),*/ in->opcode, + (unsigned long) in->nodeid, len); + if (guts_log[in->opcode].func) + guts_log[in->opcode].func (in, inargs); + + } else { + gf_log ("guts", GF_LOG_ERROR, + "header is longer than the buffer passed"); + } + + return 0; +} + + +int +guts_reply_err (fuse_req_t req, int err) +{ + if (IS_TRACE(req)) { + /* we are tracing calls, just dump the reply to file and continue with fuse_reply_err() */ + guts_reply_dump (req, &err, sizeof (err)); + return fuse_reply_err (req, err); + } else { + /* we are replaying. ;) */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + int32_t opcode = guts_get_opcode (ctx, req->unique); + + /* see if we are called by close/closedir, if yes remove do a guts_fd_delete () */ + if (opcode == FUSE_RELEASEDIR || opcode == FUSE_RELEASE) { + guts_req_t *request = guts_lookup_request (ctx, req->unique); + struct fuse_release_in *arg = request->arg; + + guts_delete_fd (ctx, arg->fh); + } else if (err == -1) { + /* error while replaying?? just quit as of now + * TODO: this is not the right way */ + printf (":O - glusterfs-guts: replay failed\n"); + exit (0); + } + + return 0; + } +} + +void +guts_reply_none (fuse_req_t req) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, NULL, 0); + fuse_reply_none (req); + } else { + return; + } +} + +int +guts_reply_entry (fuse_req_t req, + const struct fuse_entry_param *e) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, e, sizeof (*e)); + return fuse_reply_entry (req, e); + } else { + /* TODO: is dict_set() the best solution for this case?? */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct fuse_entry_param *old_entry = (struct fuse_entry_param *)reply->arg; + guts_inode_update (ctx, old_entry->ino, e->ino); + return 0; + } +} + +int +guts_reply_create (fuse_req_t req, + const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + if (IS_TRACE(req)) { + struct guts_create_out create_out; + + memset (&create_out, 0, sizeof (create_out)); + memcpy (&create_out.e, e, sizeof (*e)); + memcpy (&create_out.f, f, sizeof (*f)); + + guts_reply_dump (req, &create_out, sizeof (create_out)); + return fuse_reply_create (req, e, f); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct guts_create_out *old_createout = (struct guts_create_out *) reply->arg; + struct fuse_file_info *old_f = &old_createout->f; + + /* add a new fd and map it to the file handle, as stored in tio file */ + guts_fd_add (ctx, old_f->fh, (fd_t *)(long)f->fh); + + return 0; + } +} + + +int +guts_reply_attr (fuse_req_t req, + const struct stat *attr, + double attr_timeout) +{ + if (IS_TRACE(req)) { + struct guts_attr_out attr_out; + + memcpy (&attr_out.attr, attr, sizeof (*attr)); + attr_out.attr_timeout = attr_timeout; + + guts_reply_dump (req, &attr_out, sizeof (attr_out)); + return fuse_reply_attr (req, attr, attr_timeout); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct guts_attr_out *old_attrout = (struct guts_attr_out *) reply->arg; + + if (!guts_attr_cmp (attr, &old_attrout->attr)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "attr failed."); + return -1; + } + } +} + +int +guts_reply_readlink (fuse_req_t req, + const char *linkname) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, linkname, strlen (linkname)); + return fuse_reply_readlink (req, linkname); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + char *old_linkname = (char *) reply->arg; + if (!strcmp (linkname, old_linkname)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "readlink failed. linkname in tio file: %s \n linkname recieved on replay: %s", + old_linkname, linkname); + return -1; + } + } +} + +int +guts_reply_open (fuse_req_t req, + const struct fuse_file_info *f) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, f, sizeof (*f)); + return fuse_reply_open (req, f); + } else { + /* the fd we recieve here is the valid fd for our current session, map the indicative number we have + * in mapping */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + + if (reply) { + struct fuse_file_info *old_f = reply->arg; + + /* add a new fd and map it to the file handle, as stored in tio file */ + guts_fd_add (ctx, old_f->fh, (fd_t *)(long)f->fh); + } + + return 0; + } +} + +int +guts_reply_write (fuse_req_t req, + size_t count) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, &count, sizeof (count)); + return fuse_reply_write (req, count); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + size_t *old_count = reply->arg; + if (count == *old_count) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "writev failed. old writev count: %d \n writev count on replay: %d", + old_count, count); + return -1; + } + } +} + +int +guts_reply_buf (fuse_req_t req, + const char *buf, + size_t size) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, buf, size); + return fuse_reply_buf (req, buf, size); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + char *old_buf = reply->arg; + size_t old_size = reply->arg_len; + if ((size == old_size) && (!memcmp (buf, old_buf, size))) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "readv failed. old readv size: %d \n readv size on replay: %d", + old_size, size); + return -1; + } + } +} + +int +guts_reply_statfs (fuse_req_t req, + const struct statvfs *stbuf) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, stbuf, sizeof (*stbuf)); + return fuse_reply_statfs (req, stbuf); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct statvfs *old_stbuf = reply->arg; + + if (!guts_statvfs_cmp (old_stbuf, stbuf)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "statfs failed."); + return -1; + } + } +} + +int +guts_reply_xattr (fuse_req_t req, + size_t count) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, &count, sizeof (count)); + return fuse_reply_xattr (req, count); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + size_t *old_count = reply->arg; + if (count == *old_count) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "xattr failed. old xattr count: %d \n xattr count on replay: %d", + old_count, count); + return -1; + } + } +} + +int +guts_reply_lock (fuse_req_t req, + struct flock *lock) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, lock , sizeof (*lock)); + return fuse_reply_lock (req, lock); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct flock *old_lock = (struct flock *)reply->arg; + if (!guts_flock_cmp (lock, old_lock)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "lock failed."); + return -1; + } + } +} diff --git a/glusterfs-guts/src/guts-trace.h b/glusterfs-guts/src/guts-trace.h new file mode 100644 index 000000000..c877b2bcf --- /dev/null +++ b/glusterfs-guts/src/guts-trace.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#define IS_TRACE(req) (req->ch != NULL) + +#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) + +struct guts_symlink_in { + char name[NAME_MAX]; + char linkname[NAME_MAX]; +}; + +struct guts_create_in { + struct fuse_open_in open_in; + char name[NAME_MAX]; +}; + +struct guts_xattr_in { + struct fuse_setxattr_in xattr; + char name[NAME_MAX]; + char value[NAME_MAX]; +}; + +struct guts_rename_in { + struct fuse_rename_in rename; + char oldname[NAME_MAX]; + char newname[NAME_MAX]; +}; + +struct guts_create_out { + struct fuse_entry_param e; + struct fuse_file_info f; +}; + +struct guts_attr_out { + struct stat attr; + double attr_timeout; +}; -- cgit