diff options
| author | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
|---|---|---|
| committer | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
| commit | 77adf4cd648dce41f89469dd185deec6b6b53a0b (patch) | |
| tree | 02e155a5753b398ee572b45793f889b538efab6b /glusterfs-guts/src | |
| parent | f3b2e6580e5663292ee113c741343c8a43ee133f (diff) | |
Added all files
Diffstat (limited to 'glusterfs-guts/src')
| -rw-r--r-- | glusterfs-guts/src/Makefile.am | 17 | ||||
| -rw-r--r-- | glusterfs-guts/src/fuse-bridge.c | 2724 | ||||
| -rw-r--r-- | glusterfs-guts/src/fuse-extra.c | 137 | ||||
| -rw-r--r-- | glusterfs-guts/src/fuse-extra.h | 38 | ||||
| -rw-r--r-- | glusterfs-guts/src/fuse_kernel.h | 380 | ||||
| -rw-r--r-- | glusterfs-guts/src/glusterfs-fuse.h | 58 | ||||
| -rw-r--r-- | glusterfs-guts/src/glusterfs-guts.c | 400 | ||||
| -rw-r--r-- | glusterfs-guts/src/glusterfs-guts.h | 62 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-extra.c | 18 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-lowlevel.h | 86 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-parse.c | 217 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-parse.h | 140 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-replay.c | 834 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-replay.h | 33 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-tables.c | 248 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-tables.h | 80 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-trace.c | 650 | ||||
| -rw-r--r-- | glusterfs-guts/src/guts-trace.h | 54 | 
18 files changed, 6176 insertions, 0 deletions
diff --git a/glusterfs-guts/src/Makefile.am b/glusterfs-guts/src/Makefile.am new file mode 100644 index 00000000000..bb8c7b17680 --- /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 00000000000..0972563c65e --- /dev/null +++ b/glusterfs-guts/src/fuse-bridge.c @@ -0,0 +1,2724 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + + +#include <stdint.h> +#include <signal.h> +#include <pthread.h> + +#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 <fuse/fuse_lowlevel.h> + +#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 00000000000..93574d174d5 --- /dev/null +++ b/glusterfs-guts/src/fuse-extra.c @@ -0,0 +1,137 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#include "fuse-extra.h" +#include "common-utils.h" +#include <stdio.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#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 00000000000..c7d2877c019 --- /dev/null +++ b/glusterfs-guts/src/fuse-extra.h @@ -0,0 +1,38 @@ +/* +   Copyright (c) 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#ifndef _FUSE_EXTRA_H +#define _FUSE_EXTRA_H + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif /* _CONFIG_H */ + +#include <stdlib.h> +#include <fuse/fuse_lowlevel.h> + +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 00000000000..7ebff8b22f9 --- /dev/null +++ b/glusterfs-guts/src/fuse_kernel.h @@ -0,0 +1,380 @@ +/* +    FUSE: Filesystem in Userspace +    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu> + +    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 <sys/types.h> +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include <asm/types.h> +#include <linux/major.h> +#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 00000000000..f446202fbea --- /dev/null +++ b/glusterfs-guts/src/glusterfs-fuse.h @@ -0,0 +1,58 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..3efac3a35be --- /dev/null +++ b/glusterfs-guts/src/glusterfs-guts.c @@ -0,0 +1,400 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <argp.h> +#include <string.h> + +#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. <http://www.zresearch.com>\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 00000000000..eda1788a9de --- /dev/null +++ b/glusterfs-guts/src/glusterfs-guts.h @@ -0,0 +1,62 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..dd4ad466ff4 --- /dev/null +++ b/glusterfs-guts/src/guts-extra.c @@ -0,0 +1,18 @@ +/* +   Copyright (c) 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ diff --git a/glusterfs-guts/src/guts-lowlevel.h b/glusterfs-guts/src/guts-lowlevel.h new file mode 100644 index 00000000000..498b5d01ec6 --- /dev/null +++ b/glusterfs-guts/src/guts-lowlevel.h @@ -0,0 +1,86 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..dd17a737ebb --- /dev/null +++ b/glusterfs-guts/src/guts-parse.c @@ -0,0 +1,217 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#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:<fuse_in_header>:<arg-len>:<args>: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 00000000000..7791b1215a2 --- /dev/null +++ b/glusterfs-guts/src/guts-parse.h @@ -0,0 +1,140 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _GUTS_PARSE_H_ +#define _GUTS_PARSE_H_ + +#include "glusterfs.h" +#include "glusterfs-guts.h" +#include "fuse_kernel.h" +#include <fuse/fuse_lowlevel.h> +#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 00000000000..a5447464d75 --- /dev/null +++ b/glusterfs-guts/src/guts-replay.c @@ -0,0 +1,834 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#include "glusterfs-guts.h" +#include "guts-parse.h" +#include <signal.h> +#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 00000000000..532060d2b60 --- /dev/null +++ b/glusterfs-guts/src/guts-replay.h @@ -0,0 +1,33 @@ +/* +   Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..2992b3e2c75 --- /dev/null +++ b/glusterfs-guts/src/guts-tables.c @@ -0,0 +1,248 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..ff27300fa83 --- /dev/null +++ b/glusterfs-guts/src/guts-tables.h @@ -0,0 +1,80 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#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 00000000000..51d8a68d602 --- /dev/null +++ b/glusterfs-guts/src/guts-trace.c @@ -0,0 +1,650 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#include "glusterfs-guts.h" +#include <signal.h> + +#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, "<null>", +	      /*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 00000000000..c877b2bcfec --- /dev/null +++ b/glusterfs-guts/src/guts-trace.h @@ -0,0 +1,54 @@ +/* +   Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> +   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 +   <http://www.gnu.org/licenses/>. +*/ + +#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; +};  | 
