/* Copyright (c) 2006-2009 Gluster, Inc. This file is part of GlusterFS. GlusterFS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GlusterFS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif /* _CONFIG_H */ #include "glusterfs.h" #include "logging.h" #include "xlator.h" #include "glusterfs.h" #include "transport.h" #include "defaults.h" #include "common-utils.h" #include #include "fuse-extra.h" #include "list.h" #include "guts-lowlevel.h" #define BIG_FUSE_CHANNEL_SIZE 1048576 struct fuse_private { int fd; struct fuse *fuse; struct fuse_session *se; struct fuse_chan *ch; char *mountpoint; }; char glusterfs_fuse_direct_io_mode = 1; float glusterfs_fuse_entry_timeout = 1.0; float glusterfs_fuse_attr_timeout = 1.0; #define FI_TO_FD(fi) ((fd_t *)((long)fi->fh)) #define FUSE_FOP(state, ret, op, args ...) \ do { \ call_frame_t *frame = get_call_frame_for_req (state, 1); \ xlator_t *xl = frame->this->children ? \ frame->this->children->xlator : NULL; \ dict_t *refs = frame->root->req_refs; \ frame->root->state = state; \ STACK_WIND (frame, ret, xl, xl->fops->op, args); \ dict_unref (refs); \ } while (0) #define FUSE_FOP_NOREPLY(state, op, args ...) \ do { \ call_frame_t *_frame = get_call_frame_for_req (state, 0); \ xlator_t *xl = _frame->this->children->xlator; \ _frame->root->req_refs = NULL; \ STACK_WIND (_frame, fuse_nop_cbk, xl, xl->fops->op, args); \ } while (0) typedef struct { loc_t loc; inode_t *parent; inode_t *inode; char *name; } fuse_loc_t; typedef struct { void *pool; xlator_t *this; inode_table_t *itable; fuse_loc_t fuse_loc; fuse_loc_t fuse_loc2; fuse_req_t req; int32_t flags; off_t off; size_t size; unsigned long nlookup; fd_t *fd; dict_t *dict; char *name; char is_revalidate; } fuse_state_t; static void loc_wipe (loc_t *loc) { if (loc->inode) { inode_unref (loc->inode); loc->inode = NULL; } if (loc->path) { FREE (loc->path); loc->path = NULL; } } static inode_t * dummy_inode (inode_table_t *table) { inode_t *dummy; dummy = CALLOC (1, sizeof (*dummy)); ERR_ABORT (dummy); dummy->table = table; INIT_LIST_HEAD (&dummy->list); INIT_LIST_HEAD (&dummy->inode_hash); INIT_LIST_HEAD (&dummy->fds); INIT_LIST_HEAD (&dummy->dentry.name_hash); INIT_LIST_HEAD (&dummy->dentry.inode_list); dummy->ref = 1; dummy->ctx = get_new_dict (); LOCK_INIT (&dummy->lock); return dummy; } static void fuse_loc_wipe (fuse_loc_t *fuse_loc) { loc_wipe (&fuse_loc->loc); if (fuse_loc->name) { FREE (fuse_loc->name); fuse_loc->name = NULL; } if (fuse_loc->inode) { inode_unref (fuse_loc->inode); fuse_loc->inode = NULL; } if (fuse_loc->parent) { inode_unref (fuse_loc->parent); fuse_loc->parent = NULL; } } static void free_state (fuse_state_t *state) { fuse_loc_wipe (&state->fuse_loc); fuse_loc_wipe (&state->fuse_loc2); if (state->dict) { dict_unref (state->dict); state->dict = (void *)0xaaaaeeee; } if (state->name) { FREE (state->name); state->name = NULL; } #ifdef DEBUG memset (state, 0x90, sizeof (*state)); #endif FREE (state); state = NULL; } static int32_t fuse_nop_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno) { if (frame->root->state) free_state (frame->root->state); frame->root->state = EEEEKS; STACK_DESTROY (frame->root); return 0; } fuse_state_t * state_from_req (fuse_req_t req) { fuse_state_t *state; transport_t *trans = fuse_req_userdata (req); state = (void *)calloc (1, sizeof (*state)); ERR_ABORT (state); state->pool = trans->xl->ctx->pool; state->itable = trans->xl->itable; state->req = req; state->this = trans->xl; return state; } static call_frame_t * get_call_frame_for_req (fuse_state_t *state, char d) { call_pool_t *pool = state->pool; fuse_req_t req = state->req; const struct fuse_ctx *ctx = NULL; call_ctx_t *cctx = NULL; transport_t *trans = NULL; cctx = CALLOC (1, sizeof (*cctx)); ERR_ABORT (cctx); cctx->frames.root = cctx; if (req) { ctx = fuse_req_ctx(req); cctx->uid = ctx->uid; cctx->gid = ctx->gid; cctx->pid = ctx->pid; cctx->unique = req_callid (req); } if (req) { trans = fuse_req_userdata (req); cctx->frames.this = trans->xl; cctx->trans = trans; } else { cctx->frames.this = state->this; } if (d) { cctx->req_refs = dict_ref (get_new_dict ()); dict_set (cctx->req_refs, NULL, trans->buf); cctx->req_refs->is_locked = 1; } cctx->pool = pool; LOCK (&pool->lock); list_add (&cctx->all_frames, &pool->all_frames); UNLOCK (&pool->lock); return &cctx->frames; } static void fuse_loc_fill (fuse_loc_t *fuse_loc, fuse_state_t *state, ino_t ino, const char *name) { size_t n; inode_t *inode, *parent = NULL; /* resistance against multiple invocation of loc_fill not to get reference leaks via inode_search() */ inode = fuse_loc->inode; if (!inode) { inode = inode_search (state->itable, ino, name); } fuse_loc->inode = inode; if (name) { if (!fuse_loc->name) fuse_loc->name = strdup (name); parent = fuse_loc->parent; if (!parent) { if (inode) parent = inode_parent (inode, ino); else parent = inode_search (state->itable, ino, NULL); } } fuse_loc->parent = parent; if (inode) { fuse_loc->loc.inode = inode_ref (inode); fuse_loc->loc.ino = inode->ino; } if (parent) { n = inode_path (parent, name, NULL, 0) + 1; fuse_loc->loc.path = CALLOC (1, n); ERR_ABORT (fuse_loc->loc.path); inode_path (parent, name, (char *)fuse_loc->loc.path, n); } else if (inode) { n = inode_path (inode, NULL, NULL, 0) + 1; fuse_loc->loc.path = CALLOC (1, n); ERR_ABORT (fuse_loc->loc.path); inode_path (inode, NULL, (char *)fuse_loc->loc.path, n); } } static int32_t fuse_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct stat *stat, dict_t *dict, struct stat *postparent); 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); }