diff options
Diffstat (limited to 'glusterfs-guts/src/fuse-bridge.c')
| -rw-r--r-- | glusterfs-guts/src/fuse-bridge.c | 2724 | 
1 files changed, 2724 insertions, 0 deletions
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); +} + +  | 
