/* Copyright (c) 2006-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include "fuse-bridge.h" #include "mount-gluster-compat.h" #include "glusterfs.h" #include "byte-order.h" #include "compat-errno.h" #include "glusterfs-acl.h" #ifdef __NetBSD__ #undef open /* in perfuse.h, pulled from mount-gluster-compat.h */ #endif static int gf_fuse_conn_err_log; static int gf_fuse_xattr_enotsup_log; void fini (xlator_t *this_xl); static void fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino); /* * Send an invalidate notification up to fuse to purge the file from local * page cache. */ static int32_t fuse_invalidate(xlator_t *this, inode_t *inode) { fuse_private_t *priv = this->private; uint64_t nodeid; /* * NOTE: We only invalidate at the moment if fopen_keep_cache is * enabled because otherwise this is a departure from default * behavior. Specifically, the performance/write-behind xlator * causes unconditional invalidations on write requests. */ if (!priv->fopen_keep_cache) return 0; nodeid = inode_to_fuse_nodeid(inode); gf_log(this->name, GF_LOG_DEBUG, "Invalidate inode id %"GF_PRI_INODE"." , nodeid); fuse_log_eh (this, "Sending invalidate inode id: %"GF_PRI_INODE" gfid: %s", nodeid, uuid_utoa (inode->gfid)); fuse_invalidate_inode(this, nodeid); return 0; } static int32_t fuse_forget_cbk (xlator_t *this, inode_t *inode) { //Nothing to free in inode ctx, hence return. return 0; } void fuse_inode_set_need_lookup (inode_t *inode, xlator_t *this) { uint64_t need_lookup = 1; if (!inode || !this) return; inode_ctx_set (inode, this, &need_lookup); return; } gf_boolean_t fuse_inode_needs_lookup (inode_t *inode, xlator_t *this) { uint64_t need_lookup = 0; gf_boolean_t ret = _gf_false; if (!inode || !this) return ret; inode_ctx_get (inode, this, &need_lookup); if (need_lookup) ret = _gf_true; need_lookup = 0; inode_ctx_set (inode, this, &need_lookup); return ret; } fuse_fd_ctx_t * __fuse_fd_ctx_check_n_create (xlator_t *this, fd_t *fd) { uint64_t val = 0; int32_t ret = 0; fuse_fd_ctx_t *fd_ctx = NULL; ret = __fd_ctx_get (fd, this, &val); fd_ctx = (fuse_fd_ctx_t *)(unsigned long) val; if (fd_ctx == NULL) { fd_ctx = GF_CALLOC (1, sizeof (*fd_ctx), gf_fuse_mt_fd_ctx_t); if (!fd_ctx) { goto out; } ret = __fd_ctx_set (fd, this, (uint64_t)(unsigned long)fd_ctx); if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "fd-ctx-set failed"); GF_FREE (fd_ctx); fd_ctx = NULL; } } out: return fd_ctx; } fuse_fd_ctx_t * fuse_fd_ctx_check_n_create (xlator_t *this, fd_t *fd) { fuse_fd_ctx_t *fd_ctx = NULL; if ((fd == NULL) || (this == NULL)) { goto out; } LOCK (&fd->lock); { fd_ctx = __fuse_fd_ctx_check_n_create (this, fd); } UNLOCK (&fd->lock); out: return fd_ctx; } fuse_fd_ctx_t * fuse_fd_ctx_get (xlator_t *this, fd_t *fd) { fuse_fd_ctx_t *fdctx = NULL; uint64_t value = 0; int ret = 0; ret = fd_ctx_get (fd, this, &value); if (ret < 0) { goto out; } fdctx = (fuse_fd_ctx_t *) (unsigned long)value; out: return fdctx; } /* * iov_out should contain a fuse_out_header at zeroth position. * The error value of this header is sent to kernel. */ static int send_fuse_iov (xlator_t *this, fuse_in_header_t *finh, struct iovec *iov_out, int count) { fuse_private_t *priv = NULL; struct fuse_out_header *fouh = NULL; int res, i; if (!this || !finh || !iov_out) { gf_log ("send_fuse_iov", GF_LOG_ERROR,"Invalid arguments"); return EINVAL; } priv = this->private; fouh = iov_out[0].iov_base; iov_out[0].iov_len = sizeof (*fouh); fouh->len = 0; for (i = 0; i < count; i++) fouh->len += iov_out[i].iov_len; fouh->unique = finh->unique; res = writev (priv->fd, iov_out, count); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "writev() result %d/%d %s", res, fouh->len, res == -1 ? strerror (errno) : ""); if (res == -1) return errno; if (res != fouh->len) return EINVAL; if (priv->fuse_dump_fd != -1) { char w = 'W'; pthread_mutex_lock (&priv->fuse_dump_mutex); res = write (priv->fuse_dump_fd, &w, 1); if (res != -1) res = writev (priv->fuse_dump_fd, iov_out, count); pthread_mutex_unlock (&priv->fuse_dump_mutex); if (res == -1) gf_log ("glusterfs-fuse", GF_LOG_ERROR, "failed to dump fuse message (W): %s", strerror (errno)); } return 0; } static int send_fuse_data (xlator_t *this, fuse_in_header_t *finh, void *data, size_t size) { struct fuse_out_header fouh = {0, }; struct iovec iov_out[2]; int ret = 0; fouh.error = 0; iov_out[0].iov_base = &fouh; iov_out[1].iov_base = data; iov_out[1].iov_len = size; ret = send_fuse_iov (this, finh, iov_out, 2); if (ret != 0) gf_log ("glusterfs-fuse", GF_LOG_ERROR, "send_fuse_iov() " "failed: %s", strerror (ret)); return ret; } #define send_fuse_obj(this, finh, obj) \ send_fuse_data (this, finh, obj, sizeof (*(obj))) #if FUSE_KERNEL_MINOR_VERSION >= 11 static void fuse_invalidate_entry (xlator_t *this, uint64_t fuse_ino) { struct fuse_out_header *fouh = NULL; struct fuse_notify_inval_entry_out *fnieo = NULL; fuse_private_t *priv = NULL; dentry_t *dentry = NULL; inode_t *inode = NULL; size_t nlen = 0; int rv = 0; char inval_buf[INVAL_BUF_SIZE] = {0,}; fouh = (struct fuse_out_header *)inval_buf; fnieo = (struct fuse_notify_inval_entry_out *)(fouh + 1); priv = this->private; if (priv->revchan_out == -1) return; fouh->unique = 0; fouh->error = FUSE_NOTIFY_INVAL_ENTRY; inode = fuse_ino_to_inode (fuse_ino, this); list_for_each_entry (dentry, &inode->dentry_list, inode_list) { nlen = strlen (dentry->name); fouh->len = sizeof (*fouh) + sizeof (*fnieo) + nlen + 1; fnieo->parent = inode_to_fuse_nodeid (dentry->parent); fnieo->namelen = nlen; strcpy (inval_buf + sizeof (*fouh) + sizeof (*fnieo), dentry->name); rv = write (priv->revchan_out, inval_buf, fouh->len); if (rv != fouh->len) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "kernel notification daemon defunct"); close (priv->fd); break; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "INVALIDATE entry: " "%"PRIu64"/%s", fnieo->parent, dentry->name); if (dentry->parent) { fuse_log_eh (this, "Invalidated entry %s (parent: %s)", dentry->name, uuid_utoa (dentry->parent->gfid)); } else { fuse_log_eh (this, "Invalidated entry %s(nodeid: %" PRIu64 ")", dentry->name, fnieo->parent); } } if (inode) inode_unref (inode); } #endif /* * Send an inval inode notification to fuse. This causes an invalidation of the * entire page cache mapping on the inode. */ static void fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino) { #if FUSE_KERNEL_MINOR_VERSION >= 11 struct fuse_out_header *fouh = NULL; struct fuse_notify_inval_inode_out *fniio = NULL; fuse_private_t *priv = NULL; int rv = 0; char inval_buf[INVAL_BUF_SIZE] = {0}; inode_t *inode = NULL; fouh = (struct fuse_out_header *) inval_buf; fniio = (struct fuse_notify_inval_inode_out *) (fouh + 1); priv = this->private; if (priv->revchan_out < 0) return; fouh->unique = 0; fouh->error = FUSE_NOTIFY_INVAL_INODE; fouh->len = sizeof(struct fuse_out_header) + sizeof(struct fuse_notify_inval_inode_out); /* inval the entire mapping until we learn how to be more granular */ fniio->ino = fuse_ino; fniio->off = 0; fniio->len = -1; inode = fuse_ino_to_inode (fuse_ino, this); rv = write(priv->revchan_out, inval_buf, fouh->len); if (rv != fouh->len) { gf_log("glusterfs-fuse", GF_LOG_ERROR, "kernel notification " "daemon defunct"); close(priv->fd); } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "INVALIDATE inode: %" PRIu64, fuse_ino); if (inode) { fuse_log_eh (this, "Invalidated inode %" PRIu64 " (gfid: %s)", fuse_ino, uuid_utoa (inode->gfid)); } else { fuse_log_eh (this, "Invalidated inode %" PRIu64, fuse_ino); } if (inode) inode_unref (inode); #else gf_log ("glusterfs-fuse", GF_LOG_WARNING, "fuse_invalidate_inode not implemented on OS X due to missing FUSE notification"); #endif } int send_fuse_err (xlator_t *this, fuse_in_header_t *finh, int error) { struct fuse_out_header fouh = {0, }; struct iovec iov_out; inode_t *inode = NULL; fouh.error = -error; iov_out.iov_base = &fouh; inode = fuse_ino_to_inode (finh->nodeid, this); // filter out ENOENT if (error != ENOENT) { if (inode) { fuse_log_eh (this,"Sending %s for operation %d on " "inode %s", strerror (error), finh->opcode, uuid_utoa (inode->gfid)); } else { fuse_log_eh (this, "Sending %s for operation %d on " "inode %" GF_PRI_INODE, strerror (error), finh->opcode, finh->nodeid); } } if (inode) inode_unref (inode); return send_fuse_iov (this, finh, &iov_out, 1); } static int fuse_entry_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; struct fuse_entry_out feo = {0, }; fuse_private_t *priv = NULL; inode_t *linked_inode = NULL; priv = this->private; state = frame->root->state; finh = state->finh; if (op_ret == 0) { if (__is_root_gfid (state->loc.inode->gfid)) buf->ia_ino = 1; if (gf_uuid_is_null (buf->ia_gfid)) { /* With a NULL gfid inode linking is not possible. Let's not pretend this call was a "success". */ gf_log ("glusterfs-fuse", GF_LOG_WARNING, "Received NULL gfid for %s. Forcing EIO", state->loc.path); op_ret = -1; op_errno = EIO; } } /* log into the event-history after the null uuid check is done, since * the op_ret and op_errno are being changed if the gfid is NULL. */ fuse_log_eh (this, "op_ret: %d op_errno: %d " "%"PRIu64": %s() %s => %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, (op_ret == 0)? uuid_utoa(buf->ia_gfid):uuid_utoa(state->loc.gfid)); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %"PRIu64, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, buf->ia_ino); buf->ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (buf, &feo.attr, priv->enable_ino32); if (!buf->ia_ino) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s returning inode 0", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path); } linked_inode = inode_link (inode, state->loc.parent, state->loc.name, buf); if (linked_inode != inode) { } inode_lookup (linked_inode); feo.nodeid = inode_to_fuse_nodeid (linked_inode); inode_unref (linked_inode); feo.entry_valid = calc_timeout_sec (priv->entry_timeout); feo.entry_valid_nsec = calc_timeout_nsec (priv->entry_timeout); feo.attr_valid = calc_timeout_sec (priv->attribute_timeout); feo.attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); #if FUSE_KERNEL_MINOR_VERSION >= 9 priv->proto_minor >= 9 ? send_fuse_obj (this, finh, &feo) : send_fuse_data (this, finh, &feo, FUSE_COMPAT_ENTRY_OUT_SIZE); #else send_fuse_obj (this, finh, &feo); #endif } else { gf_log ("glusterfs-fuse", (op_errno == ENOENT ? GF_LOG_TRACE : GF_LOG_WARNING), "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, strerror (op_errno)); if ((op_errno == ENOENT) && (priv->negative_timeout != 0)) { feo.entry_valid = calc_timeout_sec (priv->negative_timeout); feo.entry_valid_nsec = calc_timeout_nsec (priv->negative_timeout); send_fuse_obj (this, finh, &feo); } else { send_fuse_err (this, state->finh, op_errno); } } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } static int fuse_newentry_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent, dict_t *xdata) { fuse_entry_cbk (frame, cookie, this, op_ret, op_errno, inode, buf, xdata); return 0; } static int fuse_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *stat, dict_t *dict, struct iatt *postparent) { fuse_state_t *state = NULL; call_frame_t *prev = NULL; inode_table_t *itable = NULL; state = frame->root->state; prev = cookie; if (op_ret == -1 && state->is_revalidate == 1) { itable = state->itable; /* * A stale mapping might exist for a dentry/inode that has been * removed from another client. */ if (op_errno == ENOENT) inode_unlink(state->loc.inode, state->loc.parent, state->loc.name); inode_unref (state->loc.inode); state->loc.inode = inode_new (itable); state->is_revalidate = 2; if (gf_uuid_is_null (state->gfid)) gf_uuid_generate (state->gfid); fuse_gfid_set (state); STACK_WIND (frame, fuse_lookup_cbk, prev->this, prev->this->fops->lookup, &state->loc, state->xdata); return 0; } fuse_entry_cbk (frame, cookie, this, op_ret, op_errno, inode, stat, dict); return 0; } void fuse_fop_resume (fuse_state_t *state) { fuse_resume_fn_t fn = NULL; /* * Fail fd resolution failures right away. */ if (state->resolve.fd && state->resolve.op_ret < 0) { send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } fn = state->resume_fn; fn (state); } void fuse_lookup_resume (fuse_state_t *state) { if (!state->loc.parent && !state->loc.inode) { gf_log ("fuse", GF_LOG_ERROR, "failed to resolve path %s", state->loc.path); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } /* parent was resolved, entry could not, may be a missing gfid? * Hence try to do a regular lookup */ if ((state->resolve.op_ret == -1) && (state->resolve.op_errno == ENODATA)) { state->resolve.op_ret = 0; } if (state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": LOOKUP %s(%s)", state->finh->unique, state->loc.path, uuid_utoa (state->loc.inode->gfid)); state->is_revalidate = 1; } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": LOOKUP %s", state->finh->unique, state->loc.path); state->loc.inode = inode_new (state->loc.parent->table); if (gf_uuid_is_null (state->gfid)) gf_uuid_generate (state->gfid); fuse_gfid_set (state); } FUSE_FOP (state, fuse_lookup_cbk, GF_FOP_LOOKUP, lookup, &state->loc, state->xdata); } static void fuse_lookup (xlator_t *this, fuse_in_header_t *finh, void *msg) { char *name = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); (void) fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); fuse_resolve_and_resume (state, fuse_lookup_resume); return; } static inline void do_forget(xlator_t *this, uint64_t unique, uint64_t nodeid, uint64_t nlookup) { inode_t *fuse_inode = fuse_ino_to_inode(nodeid, this); fuse_log_eh(this, "%"PRIu64": FORGET %"PRIu64"/%"PRIu64" gfid: (%s)", unique, nodeid, nlookup, uuid_utoa(fuse_inode->gfid)); inode_forget(fuse_inode, nlookup); inode_unref(fuse_inode); } static void fuse_forget (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_forget_in *ffi = msg; if (finh->nodeid == 1) { GF_FREE (finh); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": FORGET %"PRIu64"/%"PRIu64, finh->unique, finh->nodeid, ffi->nlookup); do_forget(this, finh->unique, finh->nodeid, ffi->nlookup); GF_FREE (finh); } #if FUSE_KERNEL_MINOR_VERSION >= 16 static void fuse_batch_forget(xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_batch_forget_in *fbfi = msg; struct fuse_forget_one *ffo = (struct fuse_forget_one *) (fbfi + 1); int i; gf_log("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": BATCH_FORGET %"PRIu64"/%"PRIu32, finh->unique, finh->nodeid, fbfi->count); for (i = 0; i < fbfi->count; i++) { if (ffo[i].nodeid == 1) continue; do_forget(this, finh->unique, ffo[i].nodeid, ffo[i].nlookup); } GF_FREE(finh); } #endif static int fuse_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { fuse_state_t *state; fuse_in_header_t *finh; fuse_private_t *priv = NULL; struct fuse_attr_out fao; priv = this->private; state = frame->root->state; finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %"PRIu64, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", prebuf->ia_ino); postbuf->ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (postbuf, &fao.attr, priv->enable_ino32); fao.attr_valid = calc_timeout_sec (priv->attribute_timeout); fao.attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); #if FUSE_KERNEL_MINOR_VERSION >= 9 priv->proto_minor >= 9 ? send_fuse_obj (this, finh, &fao) : send_fuse_data (this, finh, &fao, FUSE_COMPAT_ATTR_OUT_SIZE); #else send_fuse_obj (this, finh, &fao); #endif } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } static int fuse_attr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf, dict_t *xdata) { fuse_state_t *state; fuse_in_header_t *finh; fuse_private_t *priv = NULL; struct fuse_attr_out fao; priv = this->private; state = frame->root->state; finh = state->finh; fuse_log_eh (this, "op_ret: %d, op_errno: %d, %"PRIu64": %s() %s => " "gfid: %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, state->loc.inode ? uuid_utoa (state->loc.inode->gfid) : ""); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %"PRIu64, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", buf->ia_ino); buf->ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (buf, &fao.attr, priv->enable_ino32); fao.attr_valid = calc_timeout_sec (priv->attribute_timeout); fao.attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); #if FUSE_KERNEL_MINOR_VERSION >= 9 priv->proto_minor >= 9 ? send_fuse_obj (this, finh, &fao) : send_fuse_data (this, finh, &fao, FUSE_COMPAT_ATTR_OUT_SIZE); #else send_fuse_obj (this, finh, &fao); #endif } else { GF_LOG_OCCASIONALLY ( gf_fuse_conn_err_log, "glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } static int fuse_root_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *stat, dict_t *dict, struct iatt *postparent) { fuse_attr_cbk (frame, cookie, this, op_ret, op_errno, stat, dict); return 0; } void fuse_getattr_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": GETATTR %"PRIu64" (%s) resolution failed", state->finh->unique, state->finh->nodeid, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (!IA_ISDIR (state->loc.inode->ia_type)) { state->fd = fd_lookup (state->loc.inode, state->finh->pid); } if (!state->fd) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": GETATTR %"PRIu64" (%s)", state->finh->unique, state->finh->nodeid, state->loc.path); FUSE_FOP (state, fuse_attr_cbk, GF_FOP_STAT, stat, &state->loc, state->xdata); } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": FGETATTR %"PRIu64" (%s/%p)", state->finh->unique, state->finh->nodeid, state->loc.path, state->fd); FUSE_FOP (state, fuse_attr_cbk, GF_FOP_FSTAT, fstat, state->fd, state->xdata); } } static void fuse_getattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { fuse_state_t *state; int32_t ret = -1; GET_STATE (this, finh, state); if (finh->nodeid == 1) { state->gfid[15] = 1; ret = fuse_loc_fill (&state->loc, state, finh->nodeid, 0, NULL); if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": GETATTR on / (fuse_loc_fill() failed)", finh->unique); send_fuse_err (this, finh, ENOENT); free_fuse_state (state); return; } fuse_gfid_set (state); FUSE_FOP (state, fuse_root_lookup_cbk, GF_FOP_LOOKUP, lookup, &state->loc, state->xdata); return; } fuse_resolve_inode_init (state, &state->resolve, state->finh->nodeid); fuse_resolve_and_resume (state, fuse_getattr_resume); } static int32_t fuse_fd_inherit_directio (xlator_t *this, fd_t *fd, struct fuse_open_out *foo) { int32_t ret = 0; fuse_fd_ctx_t *fdctx = NULL, *tmp_fdctx = NULL; fd_t *tmp_fd = NULL; GF_VALIDATE_OR_GOTO_WITH_ERROR ("glusterfs-fuse", this, out, ret, -EINVAL); GF_VALIDATE_OR_GOTO_WITH_ERROR ("glusterfs-fuse", fd, out, ret, -EINVAL); GF_VALIDATE_OR_GOTO_WITH_ERROR ("glusterfs-fuse", foo, out, ret, -EINVAL); fdctx = fuse_fd_ctx_get (this, fd); if (!fdctx) { ret = -ENOMEM; goto out; } tmp_fd = fd_lookup (fd->inode, 0); if (tmp_fd) { tmp_fdctx = fuse_fd_ctx_get (this, tmp_fd); if (tmp_fdctx) { foo->open_flags &= ~FOPEN_DIRECT_IO; foo->open_flags |= (tmp_fdctx->open_flags & FOPEN_DIRECT_IO); } } fdctx->open_flags |= (foo->open_flags & FOPEN_DIRECT_IO); if (tmp_fd != NULL) { fd_unref (tmp_fd); } ret = 0; out: return ret; } gf_boolean_t direct_io_mode (dict_t *xdata) { if (xdata && dict_get (xdata, "direct-io-mode")) return _gf_true; return _gf_false; } static int fuse_fd_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; fuse_private_t *priv = NULL; int32_t ret = 0; struct fuse_open_out foo = {0, }; priv = this->private; state = frame->root->state; finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret >= 0) { foo.fh = (uintptr_t) fd; foo.open_flags = 0; if (!IA_ISDIR (fd->inode->ia_type)) { if (((priv->direct_io_mode == 2) && ((state->flags & O_ACCMODE) != O_RDONLY)) || (priv->direct_io_mode == 1) || (direct_io_mode (xdata))) foo.open_flags |= FOPEN_DIRECT_IO; #ifdef GF_DARWIN_HOST_OS /* In Linux: by default, buffer cache * is purged upon open, setting * FOPEN_KEEP_CACHE implies no-purge * * In MacFUSE: by default, buffer cache * is left intact upon open, setting * FOPEN_PURGE_UBC implies purge * * [[Interesting...]] */ if (!priv->fopen_keep_cache) foo.open_flags |= FOPEN_PURGE_UBC; #else /* * If fopen-keep-cache is enabled, we set the associated * flag here such that files are not invalidated on open. * File invalidations occur either in fuse or explicitly * when the cache is set invalid on the inode. */ if (priv->fopen_keep_cache) foo.open_flags |= FOPEN_KEEP_CACHE; #endif } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %p", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, fd); ret = fuse_fd_inherit_directio (this, fd, &foo); if (ret < 0) { op_errno = -ret; gf_log ("glusterfs-fuse", GF_LOG_WARNING, "cannot inherit direct-io values for fd " "(ptr:%p inode-gfid:%s) from fds already " "opened", fd, uuid_utoa (fd->inode->gfid)); goto err; } if (send_fuse_obj (this, finh, &foo) == ENOENT) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "open(%s) got EINTR", state->loc.path); gf_fd_put (priv->fdtable, state->fd_no); goto out; } fd_bind (fd); } else { err: gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, strerror (op_errno)); send_fuse_err (this, finh, op_errno); gf_fd_put (priv->fdtable, state->fd_no); } out: free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } static void fuse_do_truncate (fuse_state_t *state) { if (state->fd) { FUSE_FOP (state, fuse_truncate_cbk, GF_FOP_FTRUNCATE, ftruncate, state->fd, state->off, state->xdata); } else { FUSE_FOP (state, fuse_truncate_cbk, GF_FOP_TRUNCATE, truncate, &state->loc, state->off, state->xdata); } return; } static int fuse_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *statpre, struct iatt *statpost, dict_t *xdata) { fuse_state_t *state; fuse_in_header_t *finh; fuse_private_t *priv = NULL; struct fuse_attr_out fao; int op_done = 0; priv = this->private; state = frame->root->state; finh = state->finh; fuse_log_eh(this, "op_ret: %d, op_errno: %d, %"PRIu64", %s() %s => " "gfid: %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, state->loc.inode ? uuid_utoa (state->loc.inode->gfid) : ""); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %"PRIu64, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", statpost->ia_ino); statpost->ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (statpost, &fao.attr, priv->enable_ino32); fao.attr_valid = calc_timeout_sec (priv->attribute_timeout); fao.attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); if (state->truncate_needed) { fuse_do_truncate (state); } else { #if FUSE_KERNEL_MINOR_VERSION >= 9 priv->proto_minor >= 9 ? send_fuse_obj (this, finh, &fao) : send_fuse_data (this, finh, &fao, FUSE_COMPAT_ATTR_OUT_SIZE); #else send_fuse_obj (this, finh, &fao); #endif op_done = 1; } } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", strerror (op_errno)); send_fuse_err (this, finh, op_errno); op_done = 1; } if (op_done) { free_fuse_state (state); } STACK_DESTROY (frame->root); return 0; } static int32_t fattr_to_gf_set_attr (int32_t valid) { int32_t gf_valid = 0; if (valid & FATTR_MODE) gf_valid |= GF_SET_ATTR_MODE; if (valid & FATTR_UID) gf_valid |= GF_SET_ATTR_UID; if (valid & FATTR_GID) gf_valid |= GF_SET_ATTR_GID; if (valid & FATTR_ATIME) gf_valid |= GF_SET_ATTR_ATIME; if (valid & FATTR_MTIME) gf_valid |= GF_SET_ATTR_MTIME; if (valid & FATTR_SIZE) gf_valid |= GF_SET_ATTR_SIZE; return gf_valid; } #define FATTR_MASK (FATTR_SIZE \ | FATTR_UID | FATTR_GID \ | FATTR_ATIME | FATTR_MTIME \ | FATTR_MODE) void fuse_setattr_resume (fuse_state_t *state) { if (!state->fd && !state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": SETATTR %"PRIu64" (%s) resolution failed", state->finh->unique, state->finh->nodeid, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": SETATTR (%"PRIu64")%s", state->finh->unique, state->finh->nodeid, state->loc.path); #ifdef GF_TEST_FFOP /* this is for calls like 'fchmod()' */ if (!state->fd) state->fd = fd_lookup (state->loc.inode, state->finh->pid); #endif /* GF_TEST_FFOP */ if ((state->valid & (FATTR_MASK)) != FATTR_SIZE) { if (state->fd && !((state->valid & FATTR_ATIME) || (state->valid & FATTR_MTIME))) { /* there is no "futimes" call, so don't send fsetattr if ATIME or MTIME is set */ FUSE_FOP (state, fuse_setattr_cbk, GF_FOP_FSETATTR, fsetattr, state->fd, &state->attr, fattr_to_gf_set_attr (state->valid), state->xdata); } else { FUSE_FOP (state, fuse_setattr_cbk, GF_FOP_SETATTR, setattr, &state->loc, &state->attr, fattr_to_gf_set_attr (state->valid), state->xdata); } } else { fuse_do_truncate (state); } } static void fuse_setattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_setattr_in *fsi = msg; #if FUSE_KERNEL_MINOR_VERSION >= 9 fuse_private_t *priv = NULL; #endif fuse_state_t *state = NULL; GET_STATE (this, finh, state); if (fsi->valid & FATTR_FH && !(fsi->valid & (FATTR_ATIME|FATTR_MTIME))) { /* We need no loc if kernel sent us an fd and * we are not fiddling with times */ state->fd = FH_TO_FD (fsi->fh); fuse_resolve_fd_init (state, &state->resolve, state->fd); } else { fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); } /* * This is just stub code demonstrating how to retrieve * lock_owner in setattr, according to the FUSE proto. * We do not make use of ATM. Its purpose is supporting * mandatory locking, but getting that right is further * down the road. Cf. * * http://thread.gmane.org/gmane.comp.file-systems.fuse.devel/ * 4962/focus=4982 * * http://git.kernel.org/?p=linux/kernel/git/torvalds/ * linux-2.6.git;a=commit;h=v2.6.23-5896-gf333211 */ #if FUSE_KERNEL_MINOR_VERSION >= 9 priv = this->private; if (priv->proto_minor >= 9 && fsi->valid & FATTR_LOCKOWNER) state->lk_owner = fsi->lock_owner; #endif state->valid = fsi->valid; if ((fsi->valid & (FATTR_MASK)) != FATTR_SIZE) { if (fsi->valid & FATTR_SIZE) { state->off = fsi->size; state->truncate_needed = _gf_true; } state->attr.ia_size = fsi->size; state->attr.ia_atime = fsi->atime; state->attr.ia_mtime = fsi->mtime; state->attr.ia_atime_nsec = fsi->atimensec; state->attr.ia_mtime_nsec = fsi->mtimensec; state->attr.ia_prot = ia_prot_from_st_mode (fsi->mode); state->attr.ia_uid = fsi->uid; state->attr.ia_gid = fsi->gid; } else { state->off = fsi->size; } fuse_resolve_and_resume (state, fuse_setattr_resume); } static int fuse_err_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, dict_t *xdata) { fuse_state_t *state = frame->root->state; fuse_in_header_t *finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => 0", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR"); send_fuse_err (this, finh, 0); } else if (GF_IGNORE_IF_GSYNCD_SAFE_ERROR(frame, op_errno)) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path ? state->loc.path : "ERR", strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } static int fuse_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { return fuse_err_cbk (frame, cookie, this, op_ret, op_errno, xdata); } static int fuse_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, dict_t *xdata) { if (op_ret == -1 && op_errno == ENOTSUP) GF_LOG_OCCASIONALLY (gf_fuse_xattr_enotsup_log, "glusterfs-fuse", GF_LOG_CRITICAL, "extended attribute not supported " "by the backend storage"); return fuse_err_cbk (frame, cookie, this, op_ret, op_errno, xdata); } static int fuse_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *preparent, struct iatt *postparent, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; state = frame->root->state; finh = state->finh; fuse_log_eh (this, "op_ret: %d, op_errno: %d, %"PRIu64": %s() %s => " "gfid: %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, state->loc.inode ? uuid_utoa (state->loc.inode->gfid) : ""); if (op_ret == 0) { inode_unlink (state->loc.inode, state->loc.parent, state->loc.name); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => 0", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path); send_fuse_err (this, finh, 0); } else { gf_log ("glusterfs-fuse", op_errno == ENOTEMPTY ? GF_LOG_DEBUG : GF_LOG_WARNING, "%"PRIu64": %s() %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_access_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": ACCESS %"PRIu64" (%s) resolution failed", state->finh->unique, state->finh->nodeid, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64" ACCESS %s/%"PRIu64" mask=%d", state->finh->unique, state->loc.path, state->finh->nodeid, state->mask); FUSE_FOP (state, fuse_err_cbk, GF_FOP_ACCESS, access, &state->loc, state->mask, state->xdata); } static void fuse_access (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_access_in *fai = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); state->mask = fai->mask; fuse_resolve_and_resume (state, fuse_access_resume); return; } static int fuse_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, const char *linkname, struct iatt *buf, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; state = frame->root->state; finh = state->finh; fuse_log_eh (this, "op_ret: %d, op_errno: %d %"PRIu64": %s() => %s" " linkname: %s, gfid: %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.gfid, linkname, uuid_utoa (state->loc.gfid)); if (op_ret > 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s => %s (size:%d)", frame->root->unique, state->loc.path, linkname, op_ret); send_fuse_data (this, finh, (void *)linkname, op_ret); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s => -1 (%s)", frame->root->unique, state->loc.path, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_readlink_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "READLINK %"PRIu64" (%s) resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64" READLINK %s/%s", state->finh->unique, state->loc.path, uuid_utoa (state->loc.inode->gfid)); FUSE_FOP (state, fuse_readlink_cbk, GF_FOP_READLINK, readlink, &state->loc, 4096, state->xdata); } static void fuse_readlink (xlator_t *this, fuse_in_header_t *finh, void *msg) { fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); fuse_resolve_and_resume (state, fuse_readlink_resume); return; } void fuse_mknod_resume (fuse_state_t *state) { if (!state->loc.parent) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "MKNOD %"PRIu64"/%s (%s/%s) resolution failed", state->finh->nodeid, state->resolve.bname, uuid_utoa (state->resolve.gfid), state->resolve.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (state->resolve.op_errno == ENOENT) { state->resolve.op_ret = 0; state->resolve.op_errno = 0; } if (state->loc.inode) { gf_log (state->this->name, GF_LOG_DEBUG, "inode already present"); inode_unref (state->loc.inode); state->loc.inode = NULL; } state->loc.inode = inode_new (state->loc.parent->table); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": MKNOD %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_newentry_cbk, GF_FOP_MKNOD, mknod, &state->loc, state->mode, state->rdev, state->umask, state->xdata); } static void fuse_mknod (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_mknod_in *fmi = msg; char *name = (char *)(fmi + 1); fuse_state_t *state = NULL; #if FUSE_KERNEL_MINOR_VERSION >= 12 fuse_private_t *priv = NULL; int32_t ret = -1; priv = this->private; if (priv->proto_minor < 12) name = (char *)msg + FUSE_COMPAT_MKNOD_IN_SIZE; #endif GET_STATE (this, finh, state); gf_uuid_generate (state->gfid); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); state->mode = fmi->mode; state->rdev = fmi->rdev; #if FUSE_KERNEL_MINOR_VERSION >=12 priv = this->private; FUSE_ENTRY_CREATE(this, priv, finh, state, fmi, "MKNOD"); #endif fuse_resolve_and_resume (state, fuse_mknod_resume); return; } void fuse_mkdir_resume (fuse_state_t *state) { if (!state->loc.parent) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "MKDIR %"PRIu64" (%s/%s) resolution failed", state->finh->nodeid, uuid_utoa (state->resolve.gfid), state->resolve.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (state->resolve.op_errno == ENOENT) { state->resolve.op_ret = 0; state->resolve.op_errno = 0; } if (state->loc.inode) { gf_log (state->this->name, GF_LOG_DEBUG, "inode already present"); inode_unref (state->loc.inode); state->loc.inode = NULL; } state->loc.inode = inode_new (state->loc.parent->table); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": MKDIR %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_newentry_cbk, GF_FOP_MKDIR, mkdir, &state->loc, state->mode, state->umask, state->xdata); } static void fuse_mkdir (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_mkdir_in *fmi = msg; char *name = (char *)(fmi + 1); #if FUSE_KERNEL_MINOR_VERSION >=12 fuse_private_t *priv = NULL; int32_t ret = -1; #endif fuse_state_t *state; GET_STATE (this, finh, state); gf_uuid_generate (state->gfid); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); state->mode = fmi->mode; #if FUSE_KERNEL_MINOR_VERSION >=12 priv = this->private; FUSE_ENTRY_CREATE(this, priv, finh, state, fmi, "MKDIR"); #endif fuse_resolve_and_resume (state, fuse_mkdir_resume); return; } void fuse_unlink_resume (fuse_state_t *state) { if (!state->loc.parent || !state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "UNLINK %"PRIu64" (%s/%s) resolution failed", state->finh->nodeid, uuid_utoa (state->resolve.gfid), state->resolve.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": UNLINK %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_unlink_cbk, GF_FOP_UNLINK, unlink, &state->loc, 0, state->xdata); } static void fuse_unlink (xlator_t *this, fuse_in_header_t *finh, void *msg) { char *name = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); fuse_resolve_and_resume (state, fuse_unlink_resume); return; } void fuse_rmdir_resume (fuse_state_t *state) { if (!state->loc.parent || !state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "RMDIR %"PRIu64" (%s/%s) resolution failed", state->finh->nodeid, uuid_utoa (state->resolve.gfid), state->resolve.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": RMDIR %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_unlink_cbk, GF_FOP_RMDIR, rmdir, &state->loc, 0, state->xdata); } static void fuse_rmdir (xlator_t *this, fuse_in_header_t *finh, void *msg) { char *name = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); fuse_resolve_and_resume (state, fuse_rmdir_resume); return; } void fuse_symlink_resume (fuse_state_t *state) { if (!state->loc.parent) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "SYMLINK %"PRIu64" (%s/%s) -> %s resolution failed", state->finh->nodeid, uuid_utoa (state->resolve.gfid), state->resolve.bname, state->name); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (state->resolve.op_errno == ENOENT) { state->resolve.op_ret = 0; state->resolve.op_errno = 0; } if (state->loc.inode) { gf_log (state->this->name, GF_LOG_DEBUG, "inode already present"); inode_unref (state->loc.inode); state->loc.inode = NULL; } state->loc.inode = inode_new (state->loc.parent->table); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": SYMLINK %s -> %s", state->finh->unique, state->loc.path, state->name); FUSE_FOP (state, fuse_newentry_cbk, GF_FOP_SYMLINK, symlink, state->name, &state->loc, state->umask, state->xdata); } static void fuse_symlink (xlator_t *this, fuse_in_header_t *finh, void *msg) { char *name = msg; char *linkname = name + strlen (name) + 1; fuse_state_t *state = NULL; GET_STATE (this, finh, state); gf_uuid_generate (state->gfid); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); state->name = gf_strdup (linkname); fuse_resolve_and_resume (state, fuse_symlink_resume); return; } int fuse_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf, struct iatt *preoldparent, struct iatt *postoldparent, struct iatt *prenewparent, struct iatt *postnewparent, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; state = frame->root->state; finh = state->finh; fuse_log_eh (this, "op_ret: %d, op_errno: %d, %"PRIu64": %s() " "path: %s parent: %s ==> path: %s parent: %s" "gfid: %s", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, state->loc.parent?uuid_utoa (state->loc.parent->gfid):"", state->loc2.path, state->loc2.parent?uuid_utoa (state->loc2.parent->gfid):"", state->loc.inode?uuid_utoa (state->loc.inode->gfid):""); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s -> %s => 0 (buf->ia_ino=%"PRIu64")", frame->root->unique, state->loc.path, state->loc2.path, buf->ia_ino); { /* ugly ugly - to stay blind to situation where rename happens on a new inode */ buf->ia_type = state->loc.inode->ia_type; } buf->ia_blksize = this->ctx->page_size; inode_rename (state->loc.parent->table, state->loc.parent, state->loc.name, state->loc2.parent, state->loc2.name, state->loc.inode, buf); send_fuse_err (this, finh, 0); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s -> %s => -1 (%s)", frame->root->unique, state->loc.path, state->loc2.path, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_rename_resume (fuse_state_t *state) { char loc_uuid[64] = {0,}; char loc2_uuid[64] = {0,}; if (!state->loc.parent || !state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "RENAME %"PRIu64" %s/%s -> %s/%s src resolution failed", state->finh->unique, uuid_utoa_r (state->resolve.gfid, loc_uuid), state->resolve.bname, uuid_utoa_r (state->resolve2.gfid, loc2_uuid), state->resolve2.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (!state->loc2.parent) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "RENAME %"PRIu64" %s/%s -> %s/%s dst resolution failed", state->finh->unique, uuid_utoa_r (state->resolve.gfid, loc_uuid), state->resolve.bname, uuid_utoa_r (state->resolve2.gfid, loc2_uuid), state->resolve2.bname); send_fuse_err (state->this, state->finh, ENOENT); free_fuse_state (state); return; } state->resolve.op_ret = 0; state->resolve2.op_ret = 0; gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": RENAME `%s (%s)' -> `%s (%s)'", state->finh->unique, state->loc.path, loc_uuid, state->loc2.path, loc2_uuid); FUSE_FOP (state, fuse_rename_cbk, GF_FOP_RENAME, rename, &state->loc, &state->loc2, state->xdata); } static void fuse_rename (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_rename_in *fri = msg; char *oldname = (char *)(fri + 1); char *newname = oldname + strlen (oldname) + 1; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, oldname); fuse_resolve_entry_init (state, &state->resolve2, fri->newdir, newname); fuse_resolve_and_resume (state, fuse_rename_resume); return; } void fuse_link_resume (fuse_state_t *state) { if (!state->loc2.inode || !state->loc.parent) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "fuse_loc_fill() failed %"PRIu64": LINK %s %s", state->finh->unique, state->loc2.path, state->loc.path); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } state->resolve.op_ret = 0; state->resolve2.op_ret = 0; if (state->loc.inode) { inode_unref (state->loc.inode); state->loc.inode = NULL; } state->loc.inode = inode_ref (state->loc2.inode); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": LINK() %s -> %s", state->finh->unique, state->loc2.path, state->loc.path); FUSE_FOP (state, fuse_newentry_cbk, GF_FOP_LINK, link, &state->loc2, &state->loc, state->xdata); } static void fuse_link (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_link_in *fli = msg; char *name = (char *)(fli + 1); fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve2, fli->oldnodeid); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); fuse_resolve_and_resume (state, fuse_link_resume); return; } static int 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 iatt *buf, struct iatt *preparent, struct iatt *postparent, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; fuse_private_t *priv = NULL; struct fuse_out_header fouh = {0, }; struct fuse_entry_out feo = {0, }; struct fuse_open_out foo = {0, }; struct iovec iov_out[3]; inode_t *linked_inode = NULL; state = frame->root->state; priv = this->private; finh = state->finh; foo.open_flags = 0; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret >= 0) { foo.fh = (uintptr_t) fd; if (((priv->direct_io_mode == 2) && ((state->flags & O_ACCMODE) != O_RDONLY)) || (priv->direct_io_mode == 1) || direct_io_mode (xdata)) foo.open_flags |= FOPEN_DIRECT_IO; gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %p (ino=%"PRIu64")", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, fd, buf->ia_ino); buf->ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (buf, &feo.attr, priv->enable_ino32); linked_inode = inode_link (inode, state->loc.parent, state->loc.name, buf); if (linked_inode != inode) { /* VERY racy code (if used anywhere else) -- don't do this without understanding */ inode_unref (fd->inode); fd->inode = inode_ref (linked_inode); } inode_lookup (linked_inode); inode_unref (linked_inode); feo.nodeid = inode_to_fuse_nodeid (linked_inode); feo.entry_valid = calc_timeout_sec (priv->entry_timeout); feo.entry_valid_nsec = calc_timeout_nsec (priv->entry_timeout); feo.attr_valid = calc_timeout_sec (priv->attribute_timeout); feo.attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); fouh.error = 0; iov_out[0].iov_base = &fouh; iov_out[1].iov_base = &feo; #if FUSE_KERNEL_MINOR_VERSION >= 9 iov_out[1].iov_len = priv->proto_minor >= 9 ? sizeof (feo) : FUSE_COMPAT_ENTRY_OUT_SIZE; #else iov_out[1].iov_len = sizeof (feo); #endif iov_out[2].iov_base = &foo; iov_out[2].iov_len = sizeof (foo); if (send_fuse_iov (this, finh, iov_out, 3) == ENOENT) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "create(%s) got EINTR", state->loc.path); inode_forget (inode, 1); gf_fd_put (priv->fdtable, state->fd_no); goto out; } fd_bind (fd); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s => -1 (%s)", finh->unique, state->loc.path, strerror (op_errno)); send_fuse_err (this, finh, op_errno); gf_fd_put (priv->fdtable, state->fd_no); } out: free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_create_resume (fuse_state_t *state) { fd_t *fd = NULL; fuse_private_t *priv = NULL; fuse_fd_ctx_t *fdctx = NULL; if (!state->loc.parent) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64" CREATE %s/%s resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid), state->resolve.bname); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } if (state->resolve.op_errno == ENOENT) { state->resolve.op_ret = 0; state->resolve.op_errno = 0; } if (state->loc.inode) { gf_log (state->this->name, GF_LOG_DEBUG, "inode already present"); inode_unref (state->loc.inode); } state->loc.inode = inode_new (state->loc.parent->table); fd = fd_create (state->loc.inode, state->finh->pid); if (fd == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64" CREATE cannot create a new fd", state->finh->unique); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } fdctx = fuse_fd_ctx_check_n_create (state->this, fd); if (fdctx == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64" CREATE creation of fdctx failed", state->finh->unique); fd_unref (fd); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } priv = state->this->private; state->fd_no = gf_fd_unused_get (priv->fdtable, fd); state->fd = fd_ref (fd); fd->flags = state->flags; gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": CREATE %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_create_cbk, GF_FOP_CREATE, create, &state->loc, state->flags, state->mode, state->umask, fd, state->xdata); } static void fuse_create (xlator_t *this, fuse_in_header_t *finh, void *msg) { #if FUSE_KERNEL_MINOR_VERSION >= 12 struct fuse_create_in *fci = msg; fuse_private_t *priv = NULL; int32_t ret = -1; #else struct fuse_open_in *fci = msg; #endif char *name = (char *)(fci + 1); fuse_state_t *state = NULL; #if FUSE_KERNEL_MINOR_VERSION >= 12 priv = this->private; if (priv->proto_minor < 12) name = (char *)((struct fuse_open_in *)msg + 1); #endif GET_STATE (this, finh, state); gf_uuid_generate (state->gfid); fuse_resolve_entry_init (state, &state->resolve, finh->nodeid, name); state->mode = fci->mode; state->flags = fci->flags; #if FUSE_KERNEL_MINOR_VERSION >=12 priv = this->private; FUSE_ENTRY_CREATE(this, priv, finh, state, fci, "CREATE"); #endif fuse_resolve_and_resume (state, fuse_create_resume); return; } void fuse_open_resume (fuse_state_t *state) { fd_t *fd = NULL; fuse_private_t *priv = NULL; fuse_fd_ctx_t *fdctx = NULL; if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": OPEN %s resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } fd = fd_create (state->loc.inode, state->finh->pid); if (!fd) { gf_log ("fuse", GF_LOG_ERROR, "fd is NULL"); send_fuse_err (state->this, state->finh, ENOENT); free_fuse_state (state); return; } fdctx = fuse_fd_ctx_check_n_create (state->this, fd); if (fdctx == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": OPEN creation of fdctx failed", state->finh->unique); fd_unref (fd); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } priv = state->this->private; state->fd_no = gf_fd_unused_get (priv->fdtable, fd); state->fd = fd_ref (fd); fd->flags = state->flags; gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": OPEN %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_fd_cbk, GF_FOP_OPEN, open, &state->loc, state->flags, fd, state->xdata); } static void fuse_open (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_open_in *foi = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); state->flags = foi->flags; fuse_resolve_and_resume (state, fuse_open_resume); return; } static int 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 iatt *stbuf, struct iobref *iobref, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; struct fuse_out_header fouh = {0, }; struct iovec *iov_out = NULL; state = frame->root->state; finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret >= 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READ => %d/%"GF_PRI_SIZET",%"PRId64"/%"PRIu64, frame->root->unique, op_ret, state->size, state->off, stbuf->ia_size); iov_out = GF_CALLOC (count + 1, sizeof (*iov_out), gf_fuse_mt_iovec); if (iov_out) { fouh.error = 0; iov_out[0].iov_base = &fouh; memcpy (iov_out + 1, vector, count * sizeof (*iov_out)); send_fuse_iov (this, finh, iov_out, count + 1); GF_FREE (iov_out); } else send_fuse_err (this, finh, ENOMEM); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": READ => %d (%s)", frame->root->unique, op_ret, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_readv_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READ (%p, size=%zu, offset=%"PRIu64")", state->finh->unique, state->fd, state->size, state->off); FUSE_FOP (state, fuse_readv_cbk, GF_FOP_READ, readv, state->fd, state->size, state->off, state->io_flags, state->xdata); } static void fuse_readv (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_read_in *fri = msg; #if FUSE_KERNEL_MINOR_VERSION >= 9 fuse_private_t *priv = NULL; #endif fuse_state_t *state = NULL; fd_t *fd = NULL; GET_STATE (this, finh, state); fd = FH_TO_FD (fri->fh); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); /* See comment by similar code in fuse_settatr */ #if FUSE_KERNEL_MINOR_VERSION >= 9 priv = this->private; if (priv->proto_minor >= 9 && fri->read_flags & FUSE_READ_LOCKOWNER) state->lk_owner = fri->lock_owner; #endif state->size = fri->size; state->off = fri->offset; /* lets ignore 'fri->read_flags', but just consider 'fri->flags' */ #if FUSE_KERNEL_MINOR_VERSION >= 9 state->io_flags = fri->flags; #endif fuse_resolve_and_resume (state, fuse_readv_resume); } static int fuse_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *stbuf, struct iatt *postbuf, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; struct fuse_write_out fwo = {0, }; state = frame->root->state; finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret >= 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": WRITE => %d/%"GF_PRI_SIZET",%"PRId64"/%"PRIu64, frame->root->unique, op_ret, state->size, state->off, stbuf->ia_size); fwo.size = op_ret; send_fuse_obj (this, finh, &fwo); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": WRITE => -1 (%s)", frame->root->unique, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_write_resume (fuse_state_t *state) { struct iobref *iobref = NULL; struct iobuf *iobuf = NULL; iobref = iobref_new (); if (!iobref) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": WRITE iobref allocation failed", state->finh->unique); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } iobuf = ((fuse_private_t *) (state->this->private))->iobuf; iobref_add (iobref, iobuf); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": WRITE (%p, size=%"GF_PRI_SIZET", offset=%"PRId64")", state->finh->unique, state->fd, state->size, state->off); FUSE_FOP (state, fuse_writev_cbk, GF_FOP_WRITE, writev, state->fd, &state->vector, 1, state->off, state->io_flags, iobref, state->xdata); iobref_unref (iobref); } static void fuse_write (xlator_t *this, fuse_in_header_t *finh, void *msg) { /* WRITE is special, metadata is attached to in_header, * and msg is the payload as-is. */ struct fuse_write_in *fwi = (struct fuse_write_in *) (finh + 1); fuse_state_t *state = NULL; fd_t *fd = NULL; #if FUSE_KERNEL_MINOR_VERSION >= 9 fuse_private_t *priv = NULL; priv = this->private; #endif GET_STATE (this, finh, state); fd = FH_TO_FD (fwi->fh); state->fd = fd; state->size = fwi->size; state->off = fwi->offset; /* lets ignore 'fwi->write_flags', but just consider 'fwi->flags' */ #if FUSE_KERNEL_MINOR_VERSION >= 9 state->io_flags = fwi->flags; #else state->io_flags = fwi->write_flags; #endif /* TODO: may need to handle below flag (fwi->write_flags & FUSE_WRITE_CACHE); */ fuse_resolve_fd_init (state, &state->resolve, fd); /* See comment by similar code in fuse_settatr */ #if FUSE_KERNEL_MINOR_VERSION >= 9 priv = this->private; if (priv->proto_minor >= 9 && fwi->write_flags & FUSE_WRITE_LOCKOWNER) state->lk_owner = fwi->lock_owner; #endif state->vector.iov_base = msg; state->vector.iov_len = fwi->size; fuse_resolve_and_resume (state, fuse_write_resume); return; } void fuse_flush_resume (fuse_state_t *state) { FUSE_FOP (state, fuse_err_cbk, GF_FOP_FLUSH, flush, state->fd, state->xdata); } static void fuse_flush (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_flush_in *ffi = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; GET_STATE (this, finh, state); fd = FH_TO_FD (ffi->fh); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); state->lk_owner = ffi->lock_owner; gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": FLUSH %p", finh->unique, fd); fuse_resolve_and_resume (state, fuse_flush_resume); return; } int fuse_internal_release (xlator_t *this, fd_t *fd) { //This is a place holder function to prevent "xlator does not implement //release_cbk" Warning log. //Actual release happens as part of fuse_release which gets executed //when kernel fuse sends it. return 0; } static void fuse_release (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_release_in *fri = msg; fd_t *activefd = NULL; fd_t *fd = NULL; uint64_t val = 0; int ret = 0; fuse_state_t *state = NULL; fuse_fd_ctx_t *fdctx = NULL; fuse_private_t *priv = NULL; GET_STATE (this, finh, state); fd = FH_TO_FD (fri->fh); state->fd = fd; priv = this->private; fuse_log_eh (this, "RELEASE(): %"PRIu64":, fd: %p, gfid: %s", finh->unique, fd, uuid_utoa (fd->inode->gfid)); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": RELEASE %p", finh->unique, state->fd); ret = fd_ctx_del (fd, this, &val); if (!ret) { fdctx = (fuse_fd_ctx_t *)(unsigned long)val; if (fdctx) { activefd = fdctx->activefd; if (activefd) { fd_unref (activefd); } GF_FREE (fdctx); } } fd_unref (fd); state->fd = NULL; gf_fdptr_put (priv->fdtable, fd); send_fuse_err (this, finh, 0); free_fuse_state (state); return; } void fuse_fsync_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": FSYNC %p", state->finh->unique, state->fd); /* fsync_flags: 1 means "datasync" (no defines for this) */ FUSE_FOP (state, fuse_fsync_cbk, GF_FOP_FSYNC, fsync, state->fd, (state->flags & 1), state->xdata); } static void fuse_fsync (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_fsync_in *fsi = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; GET_STATE (this, finh, state); fd = FH_TO_FD (fsi->fh); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); state->flags = fsi->fsync_flags; fuse_resolve_and_resume (state, fuse_fsync_resume); return; } void fuse_opendir_resume (fuse_state_t *state) { fd_t *fd = NULL; fuse_private_t *priv = NULL; fuse_fd_ctx_t *fdctx = NULL; priv = state->this->private; if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": OPENDIR (%s) resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } fd = fd_create (state->loc.inode, state->finh->pid); if (fd == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": OPENDIR fd creation failed", state->finh->unique); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } fdctx = fuse_fd_ctx_check_n_create (state->this, fd); if (fdctx == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": OPENDIR creation of fdctx failed", state->finh->unique); fd_unref (fd); send_fuse_err (state->this, state->finh, ENOMEM); free_fuse_state (state); return; } state->fd = fd_ref (fd); state->fd_no = gf_fd_unused_get (priv->fdtable, fd); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": OPENDIR %s", state->finh->unique, state->loc.path); FUSE_FOP (state, fuse_fd_cbk, GF_FOP_OPENDIR, opendir, &state->loc, fd, state->xdata); } static void fuse_opendir (xlator_t *this, fuse_in_header_t *finh, void *msg) { /* struct fuse_open_in *foi = msg; */ fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); fuse_resolve_and_resume (state, fuse_opendir_resume); } unsigned char d_type_from_stat (struct iatt *buf) { unsigned char d_type; if (IA_ISLNK (buf->ia_type)) { d_type = DT_LNK; } else if (IA_ISDIR (buf->ia_type)) { d_type = DT_DIR; } else if (IA_ISFIFO (buf->ia_type)) { d_type = DT_FIFO; } else if (IA_ISSOCK (buf->ia_type)) { d_type = DT_SOCK; } else if (IA_ISCHR (buf->ia_type)) { d_type = DT_CHR; } else if (IA_ISBLK (buf->ia_type)) { d_type = DT_BLK; } else if (IA_ISREG (buf->ia_type)) { d_type = DT_REG; } else { d_type = DT_UNKNOWN; } return d_type; } static int fuse_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, gf_dirent_t *entries, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; size_t size = 0; size_t max_size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_dirent *fde = NULL; fuse_private_t *priv = NULL; state = frame->root->state; finh = state->finh; priv = state->this->private; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": READDIR => -1 (%s)", frame->root->unique, strerror (op_errno)); send_fuse_err (this, finh, op_errno); goto out; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READDIR => %d/%"GF_PRI_SIZET",%"PRId64, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { size_t fde_size = FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET + strlen (entry->d_name)); max_size += fde_size; if (max_size > state->size) { /* we received too many entries to fit in the reply */ max_size -= fde_size; break; } } if (max_size == 0) { send_fuse_data (this, finh, 0, 0); goto out; } buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIR => -1 (%s)", frame->root->unique, strerror (ENOMEM)); send_fuse_err (this, finh, ENOMEM); goto out; } size = 0; list_for_each_entry (entry, &entries->list, list) { fde = (struct fuse_dirent *)(buf + size); gf_fuse_fill_dirent (entry, fde, priv->enable_ino32); size += FUSE_DIRENT_SIZE (fde); if (size == max_size) break; } send_fuse_data (this, finh, buf, size); /* TODO: */ /* gf_link_inodes_from_dirent (this, state->fd->inode, entries); */ out: free_fuse_state (state); STACK_DESTROY (frame->root); GF_FREE (buf); return 0; } void fuse_readdir_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READDIR (%p, size=%"GF_PRI_SIZET", offset=%"PRId64")", state->finh->unique, state->fd, state->size, state->off); FUSE_FOP (state, fuse_readdir_cbk, GF_FOP_READDIR, readdir, state->fd, state->size, state->off, state->xdata); } static void fuse_readdir (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_read_in *fri = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; GET_STATE (this, finh, state); state->size = fri->size; state->off = fri->offset; fd = FH_TO_FD (fri->fh); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); fuse_resolve_and_resume (state, fuse_readdir_resume); } #if FUSE_KERNEL_MINOR_VERSION >= 20 static int fuse_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, gf_dirent_t *entries, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; size_t max_size = 0; size_t size = 0; char *buf = NULL; gf_dirent_t *entry = NULL; struct fuse_direntplus *fde = NULL; struct fuse_entry_out *feo = NULL; fuse_private_t *priv = NULL; state = frame->root->state; finh = state->finh; priv = this->private; if (op_ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, strerror (op_errno)); send_fuse_err (this, finh, op_errno); goto out; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READDIRP => %d/%"GF_PRI_SIZET",%"PRId64, frame->root->unique, op_ret, state->size, state->off); list_for_each_entry (entry, &entries->list, list) { size_t fdes = FUSE_DIRENT_ALIGN (FUSE_NAME_OFFSET_DIRENTPLUS + strlen (entry->d_name)); max_size += fdes; if (max_size > state->size) { /* we received too many entries to fit in the reply */ max_size -= fdes; break; } } if (max_size == 0) { send_fuse_data (this, finh, 0, 0); goto out; } buf = GF_CALLOC (1, max_size, gf_fuse_mt_char); if (!buf) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": READDIRP => -1 (%s)", frame->root->unique, strerror (ENOMEM)); send_fuse_err (this, finh, ENOMEM); goto out; } size = 0; list_for_each_entry (entry, &entries->list, list) { inode_t *linked_inode; fde = (struct fuse_direntplus *)(buf + size); feo = &fde->entry_out; fde->dirent.ino = entry->d_ino; fde->dirent.off = entry->d_off; fde->dirent.type = entry->d_type; fde->dirent.namelen = strlen (entry->d_name); strncpy (fde->dirent.name, entry->d_name, fde->dirent.namelen); size += FUSE_DIRENTPLUS_SIZE (fde); if (!entry->inode) goto next_entry; entry->d_stat.ia_blksize = this->ctx->page_size; gf_fuse_stat2attr (&entry->d_stat, &feo->attr, priv->enable_ino32); linked_inode = inode_link (entry->inode, state->fd->inode, entry->d_name, &entry->d_stat); if (!linked_inode) goto next_entry; inode_lookup (linked_inode); feo->nodeid = inode_to_fuse_nodeid (linked_inode); fuse_inode_set_need_lookup (linked_inode, this); inode_unref (linked_inode); feo->entry_valid = calc_timeout_sec (priv->entry_timeout); feo->entry_valid_nsec = calc_timeout_nsec (priv->entry_timeout); feo->attr_valid = calc_timeout_sec (priv->attribute_timeout); feo->attr_valid_nsec = calc_timeout_nsec (priv->attribute_timeout); next_entry: if (size == max_size) break; } send_fuse_data (this, finh, buf, size); out: free_fuse_state (state); STACK_DESTROY (frame->root); GF_FREE (buf); return 0; } void fuse_readdirp_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": READDIRP (%p, size=%"GF_PRI_SIZET", offset=%"PRId64")", state->finh->unique, state->fd, state->size, state->off); FUSE_FOP (state, fuse_readdirp_cbk, GF_FOP_READDIRP, readdirp, state->fd, state->size, state->off, state->xdata); } static void fuse_readdirp (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_read_in *fri = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; GET_STATE (this, finh, state); state->size = fri->size; state->off = fri->offset; fd = FH_TO_FD (fri->fh); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); fuse_resolve_and_resume (state, fuse_readdirp_resume); } #endif #if FUSE_KERNEL_MINOR_VERSION >= 19 #ifdef FALLOC_FL_KEEP_SIZE static int fuse_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, dict_t *xdata) { return fuse_err_cbk(frame, cookie, this, op_ret, op_errno, xdata); } static void fuse_fallocate_resume(fuse_state_t *state) { gf_log("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": FALLOCATE (%p, flags=%d, size=%zu, offset=%"PRId64")", state->finh->unique, state->fd, state->flags, state->size, state->off); if (state->flags & FALLOC_FL_PUNCH_HOLE) FUSE_FOP(state, fuse_fallocate_cbk, GF_FOP_DISCARD, discard, state->fd, state->off, state->size, state->xdata); else FUSE_FOP(state, fuse_fallocate_cbk, GF_FOP_FALLOCATE, fallocate, state->fd, (state->flags & FALLOC_FL_KEEP_SIZE), state->off, state->size, state->xdata); } static void fuse_fallocate(xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_fallocate_in *ffi = msg; fuse_state_t *state = NULL; GET_STATE(this, finh, state); state->off = ffi->offset; state->size = ffi->length; state->flags = ffi->mode; state->fd = FH_TO_FD(ffi->fh); fuse_resolve_fd_init(state, &state->resolve, state->fd); fuse_resolve_and_resume(state, fuse_fallocate_resume); } #endif /* FALLOC_FL_KEEP_SIZE */ #endif /* FUSE minor version >= 19 */ static void fuse_releasedir (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_release_in *fri = msg; fd_t *activefd = NULL; uint64_t val = 0; int ret = 0; fuse_state_t *state = NULL; fuse_fd_ctx_t *fdctx = NULL; fuse_private_t *priv = NULL; GET_STATE (this, finh, state); state->fd = FH_TO_FD (fri->fh); priv = this->private; fuse_log_eh (this, "RELEASEDIR (): %"PRIu64": fd: %p, gfid: %s", finh->unique, state->fd, uuid_utoa (state->fd->inode->gfid)); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": RELEASEDIR %p", finh->unique, state->fd); ret = fd_ctx_del (state->fd, this, &val); if (!ret) { fdctx = (fuse_fd_ctx_t *)(unsigned long)val; if (fdctx) { activefd = fdctx->activefd; if (activefd) { fd_unref (activefd); } GF_FREE (fdctx); } } fd_unref (state->fd); gf_fdptr_put (priv->fdtable, state->fd); state->fd = NULL; send_fuse_err (this, finh, 0); free_fuse_state (state); return; } void fuse_fsyncdir_resume (fuse_state_t *state) { FUSE_FOP (state, fuse_err_cbk, GF_FOP_FSYNCDIR, fsyncdir, state->fd, (state->flags & 1), state->xdata); } static void fuse_fsyncdir (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_fsync_in *fsi = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; fd = FH_TO_FD (fsi->fh); GET_STATE (this, finh, state); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); state->flags = fsi->fsync_flags; fuse_resolve_and_resume (state, fuse_fsyncdir_resume); return; } static int fuse_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct statvfs *buf, dict_t *xdata) { fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; fuse_private_t *priv = NULL; struct fuse_statfs_out fso = {{0, }, }; state = frame->root->state; priv = this->private; finh = state->finh; fuse_log_eh (this, "op_ret: %d, op_errno: %d, %"PRIu64": %s()", op_ret, op_errno, frame->root->unique, gf_fop_list[frame->root->op]); if (op_ret == 0) { #ifndef GF_DARWIN_HOST_OS /* MacFUSE doesn't respect anyof these tweaks */ buf->f_blocks *= buf->f_frsize; buf->f_blocks /= this->ctx->page_size; buf->f_bavail *= buf->f_frsize; buf->f_bavail /= this->ctx->page_size; buf->f_bfree *= buf->f_frsize; buf->f_bfree /= this->ctx->page_size; buf->f_frsize = buf->f_bsize =this->ctx->page_size; #endif /* GF_DARWIN_HOST_OS */ fso.st.bsize = buf->f_bsize; fso.st.frsize = buf->f_frsize; fso.st.blocks = buf->f_blocks; fso.st.bfree = buf->f_bfree; fso.st.bavail = buf->f_bavail; fso.st.files = buf->f_files; fso.st.ffree = buf->f_ffree; fso.st.namelen = buf->f_namemax; priv->proto_minor >= 4 ? send_fuse_obj (this, finh, &fso) : send_fuse_data (this, finh, &fso, FUSE_COMPAT_STATFS_SIZE); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": ERR => -1 (%s)", frame->root->unique, strerror (op_errno)); send_fuse_err (this, finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_statfs_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": STATFS (%s) resolution fail", state->finh->unique, uuid_utoa (state->resolve.gfid)); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": STATFS", state->finh->unique); FUSE_FOP (state, fuse_statfs_cbk, GF_FOP_STATFS, statfs, &state->loc, state->xdata); } static void fuse_statfs (xlator_t *this, fuse_in_header_t *finh, void *msg) { fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); fuse_resolve_and_resume (state, fuse_statfs_resume); } void fuse_setxattr_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": SETXATTR %s/%"PRIu64" (%s) " "resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid), state->finh->nodeid, state->name); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } #ifdef GF_TEST_FFOP state->fd = fd_lookup (state->loc.inode, state->finh->pid); #endif /* GF_TEST_FFOP */ if (state->fd) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": SETXATTR %p/%"PRIu64" (%s)", state->finh->unique, state->fd, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_setxattr_cbk, GF_FOP_FSETXATTR, fsetxattr, state->fd, state->xattr, state->flags, state->xdata); } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": SETXATTR %s/%"PRIu64" (%s)", state->finh->unique, state->loc.path, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_setxattr_cbk, GF_FOP_SETXATTR, setxattr, &state->loc, state->xattr, state->flags, state->xdata); } } static void fuse_setxattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_setxattr_in *fsi = msg; char *name = (char *)(fsi + 1); char *value = name + strlen (name) + 1; struct fuse_private *priv = NULL; fuse_state_t *state = NULL; char *dict_value = NULL; int32_t ret = -1; char *newkey = NULL; priv = this->private; GET_STATE (this, finh, state); #ifdef GF_DARWIN_HOST_OS if (fsi->position) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": SETXATTR %s/%"PRIu64" (%s):" "refusing positioned setxattr", finh->unique, state->loc.path, finh->nodeid, name); send_fuse_err (this, finh, EINVAL); FREE (finh); return; } #endif if (fuse_ignore_xattr_set (priv, name)) { (void) send_fuse_err (this, finh, 0); return; } if (!priv->acl) { if ((strcmp (name, POSIX_ACL_ACCESS_XATTR) == 0) || (strcmp (name, POSIX_ACL_DEFAULT_XATTR) == 0)) { send_fuse_err (this, finh, EOPNOTSUPP); GF_FREE (finh); return; } } if (!priv->selinux) { if (strncmp (name, "security.", 9) == 0) { send_fuse_err (this, finh, EOPNOTSUPP); GF_FREE (finh); return; } } /* Check if the command is for changing the log level of process or specific xlator */ ret = is_gf_log_command (this, name, value); if (ret >= 0) { send_fuse_err (this, finh, ret); GF_FREE (finh); return; } if (!strcmp ("inode-invalidate", name)) { gf_log ("fuse", GF_LOG_TRACE, "got request to invalidate %"PRIu64, finh->nodeid); send_fuse_err (this, finh, 0); #if FUSE_KERNEL_MINOR_VERSION >= 11 fuse_invalidate_entry (this, finh->nodeid); #endif GF_FREE (finh); return; } if (!strcmp (GFID_XATTR_KEY, name) || !strcmp (GF_XATTR_VOL_ID_KEY, name)) { send_fuse_err (this, finh, EPERM); GF_FREE (finh); return; } state->size = fsi->size; fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); state->xattr = get_new_dict (); if (!state->xattr) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "%"PRIu64": SETXATTR dict allocation failed", finh->unique); send_fuse_err (this, finh, ENOMEM); free_fuse_state (state); return; } ret = fuse_flip_xattr_ns (priv, name, &newkey); if (ret) { send_fuse_err (this, finh, ENOMEM); free_fuse_state (state); return; } if (fsi->size > 0) { dict_value = memdup (value, fsi->size); } dict_set (state->xattr, newkey, data_from_dynptr ((void *)dict_value, fsi->size)); dict_ref (state->xattr); state->flags = fsi->flags; state->name = newkey; fuse_resolve_and_resume (state, fuse_setxattr_resume); return; } static void send_fuse_xattr (xlator_t *this, fuse_in_header_t *finh, const char *value, size_t size, size_t expected) { struct fuse_getxattr_out fgxo; /* linux kernel limits the size of xattr value to 64k */ if (size > GLUSTERFS_XATTR_LEN_MAX) send_fuse_err (this, finh, E2BIG); else if (expected) { /* if callback for getxattr and asks for value */ if (size > expected) /* reply would be bigger than * what was asked by kernel */ send_fuse_err (this, finh, ERANGE); else send_fuse_data (this, finh, (void *)value, size); } else { fgxo.size = size; send_fuse_obj (this, finh, &fgxo); } } /* filter out xattrs that need not be visible on the * mount point. this is _specifically_ for geo-rep * as of now, to prevent Rsync from crying out loud * when it tries to setxattr() for selinux xattrs */ static int fuse_filter_xattr(char *key) { int need_filter = 0; struct fuse_private *priv = THIS->private; if ((priv->client_pid == GF_CLIENT_PID_GSYNCD) && fnmatch ("*.selinux*", key, FNM_PERIOD) == 0) need_filter = 1; return need_filter; } static int fuse_xattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, dict_t *dict, dict_t *xdata) { char *value = ""; fuse_state_t *state = NULL; fuse_in_header_t *finh = NULL; data_t *value_data = NULL; int ret = -1; int32_t len = 0; int32_t len_next = 0; state = frame->root->state; finh = state->finh; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret >= 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": %s() %s => %d", frame->root->unique, gf_fop_list[frame->root->op], state->loc.path, op_ret); /* if successful */ if (state->name) { /* if callback for getxattr */ 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; send_fuse_xattr (this, finh, value, ret, state->size); /* if(ret >...)...else if...else */ } else { send_fuse_err (this, finh, ENODATA); } /* if(value_data)...else */ } else { /* if callback for listxattr */ /* we need to invoke fuse_filter_xattr() twice. Once * while counting size and then while filling buffer */ len = dict_keys_join (NULL, 0, dict, fuse_filter_xattr); if (len < 0) goto out; value = alloca (len + 1); if (!value) goto out; len_next = dict_keys_join (value, len, dict, fuse_filter_xattr); if (len_next != len) gf_log (THIS->name, GF_LOG_ERROR, "sizes not equal %d != %d", len, len_next); send_fuse_xattr (this, finh, value, len, state->size); } /* if(state->name)...else */ } else { /* if failure - no need to check if listxattr or getxattr */ if (op_errno != ENODATA && op_errno != ENOATTR) { if (op_errno == ENOTSUP) { GF_LOG_OCCASIONALLY (gf_fuse_xattr_enotsup_log, "glusterfs-fuse", GF_LOG_ERROR, "extended attribute not " "supported by the backend " "storage"); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": %s(%s) %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->name, state->loc.path, strerror (op_errno)); } } else { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": %s(%s) %s => -1 (%s)", frame->root->unique, gf_fop_list[frame->root->op], state->name, state->loc.path, strerror (op_errno)); } /* if(op_errno!= ENODATA)...else */ send_fuse_err (this, finh, op_errno); } /* if(op_ret>=0)...else */ out: free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_getxattr_resume (fuse_state_t *state) { char *value = NULL; if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": GETXATTR %s/%"PRIu64" (%s) " "resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid), state->finh->nodeid, state->name); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } #ifdef GF_TEST_FFOP state->fd = fd_lookup (state->loc.inode, state->finh->pid); #endif /* GF_TEST_FFOP */ if (state->name && (strcmp (state->name, VIRTUAL_GFID_XATTR_KEY) == 0)) { /* send glusterfs gfid in binary form */ value = GF_CALLOC (16 + 1, sizeof(char), gf_common_mt_char); if (!value) { send_fuse_err (state->this, state->finh, ENOMEM); goto internal_out; } memcpy (value, state->loc.inode->gfid, 16); send_fuse_xattr (THIS, state->finh, value, 16, state->size); GF_FREE (value); internal_out: free_fuse_state (state); return; } if (state->name && (strcmp (state->name, VIRTUAL_GFID_XATTR_KEY_STR) == 0)) { /* transform binary gfid to canonical form */ value = GF_CALLOC (UUID_CANONICAL_FORM_LEN + 1, sizeof(char), gf_common_mt_char); if (!value) { send_fuse_err (state->this, state->finh, ENOMEM); goto internal_out1; } uuid_utoa_r (state->loc.inode->gfid, value); send_fuse_xattr (THIS, state->finh, value, UUID_CANONICAL_FORM_LEN, state->size); GF_FREE (value); internal_out1: free_fuse_state (state); return; } if (state->fd) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": GETXATTR %p/%"PRIu64" (%s)", state->finh->unique, state->fd, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_xattr_cbk, GF_FOP_FGETXATTR, fgetxattr, state->fd, state->name, state->xdata); } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": GETXATTR %s/%"PRIu64" (%s)", state->finh->unique, state->loc.path, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_xattr_cbk, GF_FOP_GETXATTR, getxattr, &state->loc, state->name, state->xdata); } } static void fuse_getxattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_getxattr_in *fgxi = msg; char *name = (char *)(fgxi + 1); fuse_state_t *state = NULL; struct fuse_private *priv = NULL; int rv = 0; int op_errno = EINVAL; char *newkey = NULL; priv = this->private; GET_STATE (this, finh, state); #ifdef GF_DARWIN_HOST_OS if (fgxi->position) { /* position can be used only for * resource fork queries which we * don't support anyway... so handling * it separately is just sort of a * matter of aesthetics, not strictly * necessary. */ gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": GETXATTR %s/%"PRIu64" (%s):" "refusing positioned getxattr", finh->unique, state->loc.path, finh->nodeid, name); op_errno = EINVAL; goto err; } #endif if (!priv->acl) { if ((strcmp (name, POSIX_ACL_ACCESS_XATTR) == 0) || (strcmp (name, POSIX_ACL_DEFAULT_XATTR) == 0)) { op_errno = ENOTSUP; goto err; } } if (!priv->selinux) { if (strncmp (name, "security.", 9) == 0) { op_errno = ENODATA; goto err; } } fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); rv = fuse_flip_xattr_ns (priv, name, &newkey); if (rv) { op_errno = ENOMEM; goto err; } state->size = fgxi->size; state->name = newkey; fuse_resolve_and_resume (state, fuse_getxattr_resume); return; err: send_fuse_err (this, finh, op_errno); free_fuse_state (state); return; } void fuse_listxattr_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": LISTXATTR %s/%"PRIu64 "resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid), state->finh->nodeid); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } #ifdef GF_TEST_FFOP state->fd = fd_lookup (state->loc.inode, state->finh->pid); #endif /* GF_TEST_FFOP */ if (state->fd) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": LISTXATTR %p/%"PRIu64, state->finh->unique, state->fd, state->finh->nodeid); FUSE_FOP (state, fuse_xattr_cbk, GF_FOP_FGETXATTR, fgetxattr, state->fd, NULL, state->xdata); } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": LISTXATTR %s/%"PRIu64, state->finh->unique, state->loc.path, state->finh->nodeid); FUSE_FOP (state, fuse_xattr_cbk, GF_FOP_GETXATTR, getxattr, &state->loc, NULL, state->xdata); } } static void fuse_listxattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_getxattr_in *fgxi = msg; fuse_state_t *state = NULL; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); state->size = fgxi->size; fuse_resolve_and_resume (state, fuse_listxattr_resume); return; } void fuse_removexattr_resume (fuse_state_t *state) { if (!state->loc.inode) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "%"PRIu64": REMOVEXATTR %s/%"PRIu64" (%s) " "resolution failed", state->finh->unique, uuid_utoa (state->resolve.gfid), state->finh->nodeid, state->name); send_fuse_err (state->this, state->finh, state->resolve.op_errno); free_fuse_state (state); return; } #ifdef GF_TEST_FFOP state->fd = fd_lookup (state->loc.inode, state->finh->pid); #endif /* GF_TEST_FFOP */ if (state->fd) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": REMOVEXATTR %p/%"PRIu64" (%s)", state->finh->unique, state->fd, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_err_cbk, GF_FOP_FREMOVEXATTR, fremovexattr, state->fd, state->name, state->xdata); } else { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": REMOVEXATTR %s/%"PRIu64" (%s)", state->finh->unique, state->loc.path, state->finh->nodeid, state->name); FUSE_FOP (state, fuse_err_cbk, GF_FOP_REMOVEXATTR, removexattr, &state->loc, state->name, state->xdata); } } static void fuse_removexattr (xlator_t *this, fuse_in_header_t *finh, void *msg) { char *name = msg; fuse_state_t *state = NULL; fuse_private_t *priv = NULL; int32_t ret = -1; char *newkey = NULL; if (!strcmp (GFID_XATTR_KEY, name) || !strcmp (GF_XATTR_VOL_ID_KEY, name)) { send_fuse_err (this, finh, EPERM); GF_FREE (finh); return; } priv = this->private; GET_STATE (this, finh, state); fuse_resolve_inode_init (state, &state->resolve, finh->nodeid); ret = fuse_flip_xattr_ns (priv, name, &newkey); if (ret) { send_fuse_err (this, finh, ENOMEM); free_fuse_state (state); return; } state->name = newkey; fuse_resolve_and_resume (state, fuse_removexattr_resume); return; } static int gf_fuse_lk_enosys_log; static int fuse_getlk_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct gf_flock *lock, dict_t *xdata) { fuse_state_t *state = NULL; state = frame->root->state; struct fuse_lk_out flo = {{0, }, }; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": ERR => 0", frame->root->unique); flo.lk.type = lock->l_type; flo.lk.pid = lock->l_pid; if (lock->l_type == F_UNLCK) flo.lk.start = flo.lk.end = 0; else { flo.lk.start = lock->l_start; flo.lk.end = lock->l_len ? (lock->l_start + lock->l_len - 1) : OFFSET_MAX; } send_fuse_obj (this, state->finh, &flo); } else { if (op_errno == ENOSYS) { gf_fuse_lk_enosys_log++; if (!(gf_fuse_lk_enosys_log % GF_UNIVERSAL_ANSWER)) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "GETLK not supported. loading " "'features/posix-locks' on server side " "will add GETLK support."); } } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": ERR => -1 (%s)", frame->root->unique, strerror (op_errno)); } send_fuse_err (this, state->finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_getlk_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": GETLK %p", state->finh->unique, state->fd); FUSE_FOP (state, fuse_getlk_cbk, GF_FOP_LK, lk, state->fd, F_GETLK, &state->lk_lock, state->xdata); } static void fuse_getlk (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_lk_in *fli = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; fd = FH_TO_FD (fli->fh); GET_STATE (this, finh, state); state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); convert_fuse_file_lock (&fli->lk, &state->lk_lock, fli->owner); state->lk_owner = fli->owner; fuse_resolve_and_resume (state, fuse_getlk_resume); return; } static int fuse_setlk_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct gf_flock *lock, dict_t *xdata) { uint32_t op = 0; fuse_state_t *state = NULL; state = frame->root->state; op = state->finh->opcode; fuse_log_eh_fop(this, state, frame, op_ret, op_errno); if (op_ret == 0) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": ERR => 0", frame->root->unique); fd_lk_insert_and_merge (state->fd, (op == FUSE_SETLK) ? F_SETLK : F_SETLKW, &state->lk_lock); send_fuse_err (this, state->finh, 0); } else { if (op_errno == ENOSYS) { gf_fuse_lk_enosys_log++; if (!(gf_fuse_lk_enosys_log % GF_UNIVERSAL_ANSWER)) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "SETLK not supported. loading " "'features/posix-locks' on server side " "will add SETLK support."); } } else if (op_errno == EAGAIN) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "Returning EAGAIN Flock: " "start=%llu, len=%llu, pid=%llu, lk-owner=%s", (unsigned long long) state->lk_lock.l_start, (unsigned long long) state->lk_lock.l_len, (unsigned long long) state->lk_lock.l_pid, lkowner_utoa (&frame->root->lk_owner)); } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "%"PRIu64": ERR => -1 (%s)", frame->root->unique, strerror (op_errno)); } send_fuse_err (this, state->finh, op_errno); } free_fuse_state (state); STACK_DESTROY (frame->root); return 0; } void fuse_setlk_resume (fuse_state_t *state) { gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRIu64": SETLK%s %p", state->finh->unique, state->finh->opcode == FUSE_SETLK ? "" : "W", state->fd); FUSE_FOP (state, fuse_setlk_cbk, GF_FOP_LK, lk, state->fd, state->finh->opcode == FUSE_SETLK ? F_SETLK : F_SETLKW, &state->lk_lock, state->xdata); } static void fuse_setlk (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_lk_in *fli = msg; fuse_state_t *state = NULL; fd_t *fd = NULL; fd = FH_TO_FD (fli->fh); GET_STATE (this, finh, state); state->finh = finh; state->fd = fd; fuse_resolve_fd_init (state, &state->resolve, fd); convert_fuse_file_lock (&fli->lk, &state->lk_lock, fli->owner); state->lk_owner = fli->owner; fuse_resolve_and_resume (state, fuse_setlk_resume); return; } #if FUSE_KERNEL_MINOR_VERSION >= 11 static void * notify_kernel_loop (void *data) { xlator_t *this = NULL; fuse_private_t *priv = NULL; struct fuse_out_header *fouh = NULL; int rv = 0; char inval_buf[INVAL_BUF_SIZE] = {0,}; this = data; priv = this->private; for (;;) { rv = read (priv->revchan_in, inval_buf, sizeof (*fouh)); if (rv != sizeof (*fouh)) break; fouh = (struct fuse_out_header *)inval_buf; rv = read (priv->revchan_in, inval_buf + sizeof (*fouh), fouh->len - sizeof (*fouh)); if (rv != fouh->len - sizeof (*fouh)) break; rv = write (priv->fd, inval_buf, fouh->len); if (rv != fouh->len && !(rv == -1 && errno == ENOENT)) break; } close (priv->revchan_in); close (priv->revchan_out); gf_log ("glusterfs-fuse", GF_LOG_INFO, "kernel notifier loop terminated"); return NULL; } #endif static void fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg) { struct fuse_init_in *fini = msg; struct fuse_init_out fino = {0,}; fuse_private_t *priv = NULL; int ret = 0; #if FUSE_KERNEL_MINOR_VERSION >= 9 int pfd[2] = {0,}; pthread_t messenger; #endif priv = this->private; if (priv->init_recvd) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "got INIT after first message"); close (priv->fd); goto out; } priv->init_recvd = 1; if (fini->major != FUSE_KERNEL_VERSION) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "unsupported FUSE protocol version %d.%d", fini->major, fini->minor); close (priv->fd); goto out; } priv->proto_minor = fini->minor; fino.major = FUSE_KERNEL_VERSION; fino.minor = FUSE_KERNEL_MINOR_VERSION; fino.max_readahead = 1 << 17; fino.max_write = 1 << 17; fino.flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS; #if FUSE_KERNEL_MINOR_VERSION >= 17 if (fini->minor >= 17) fino.flags |= FUSE_FLOCK_LOCKS; #endif #if FUSE_KERNEL_MINOR_VERSION >= 12 if (fini->minor >= 12) { /* let fuse leave the umask processing to us, so that it does not * break extended POSIX ACL defaults on server */ fino.flags |= FUSE_DONT_MASK; } #endif #if FUSE_KERNEL_MINOR_VERSION >= 9 if (fini->minor >= 6 /* fuse_init_in has flags */ && fini->flags & FUSE_BIG_WRITES) { /* no need for direct I/O mode by default if big writes are supported */ if (priv->direct_io_mode == 2) priv->direct_io_mode = 0; fino.flags |= FUSE_BIG_WRITES; } /* Used for 'reverse invalidation of inode' */ if (fini->minor >= 12) { if (pipe(pfd) == -1) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "cannot create pipe pair (%s)", strerror(errno)); close (priv->fd); goto out; } priv->revchan_in = pfd[0]; priv->revchan_out = pfd[1]; ret = gf_thread_create (&messenger, NULL, notify_kernel_loop, this); if (ret != 0) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "failed to start messenger daemon (%s)", strerror(errno)); close (priv->fd); goto out; } priv->reverse_fuse_thread_started = _gf_true; } else { /* * FUSE minor < 12 does not implement invalidate notifications. * This mechanism is required for fopen-keep-cache to operate * correctly. Disable and warn the user. */ if (priv->fopen_keep_cache) { gf_log("glusterfs-fuse", GF_LOG_WARNING, "FUSE version " "%d.%d does not support inval notifications. " "fopen-keep-cache disabled.", fini->major, fini->minor); priv->fopen_keep_cache = 0; } } if (fini->minor >= 13) { fino.max_background = priv->background_qlen; fino.congestion_threshold = priv->congestion_threshold; } if (fini->minor < 9) *priv->msg0_len_p = sizeof(*finh) + FUSE_COMPAT_WRITE_IN_SIZE; if (priv->use_readdirp) { if (fini->flags & FUSE_DO_READDIRPLUS) fino.flags |= FUSE_DO_READDIRPLUS; } #endif if (priv->fopen_keep_cache == 2) { /* If user did not explicitly set --fopen-keep-cache[=off], then check if kernel support FUSE_AUTO_INVAL_DATA and ... */ #if FUSE_KERNEL_MINOR_VERSION >= 20 if (fini->flags & FUSE_AUTO_INVAL_DATA) { /* ... enable fopen_keep_cache mode if supported. */ gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "Detected " "support for FUSE_AUTO_INVAL_DATA. Enabling " "fopen_keep_cache automatically."); fino.flags |= FUSE_AUTO_INVAL_DATA; priv->fopen_keep_cache = 1; } else #endif { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "No support " "for FUSE_AUTO_INVAL_DATA. Disabling " "fopen_keep_cache."); /* ... else disable. */ priv->fopen_keep_cache = 0; } } else if (priv->fopen_keep_cache == 1) { /* If user explicitly set --fopen-keep-cache[=on], then enable FUSE_AUTO_INVAL_DATA if possible. */ #if FUSE_KERNEL_MINOR_VERSION >= 20 if (fini->flags & FUSE_AUTO_INVAL_DATA) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "fopen_keep_cache " "is explicitly set. Enabling FUSE_AUTO_INVAL_DATA"); fino.flags |= FUSE_AUTO_INVAL_DATA; } else #endif { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "fopen_keep_cache " "is explicitly set. Support for " "FUSE_AUTO_INVAL_DATA is missing"); } } #if FUSE_KERNEL_MINOR_VERSION >= 22 if (fini->flags & FUSE_ASYNC_DIO) fino.flags |= FUSE_ASYNC_DIO; #endif ret = send_fuse_obj (this, finh, &fino); if (ret == 0) gf_log ("glusterfs-fuse", GF_LOG_INFO, "FUSE inited with protocol versions:" " glusterfs %d.%d kernel %d.%d", FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION, fini->major, fini->minor); else { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "FUSE init failed (%s)", strerror (ret)); close (priv->fd); } out: GF_FREE (finh); } static void fuse_enosys (xlator_t *this, fuse_in_header_t *finh, void *msg) { send_fuse_err (this, finh, ENOSYS); GF_FREE (finh); } static void fuse_destroy (xlator_t *this, fuse_in_header_t *finh, void *msg) { send_fuse_err (this, finh, 0); GF_FREE (finh); } struct fuse_first_lookup { pthread_mutex_t mutex; pthread_cond_t cond; char fin; }; int fuse_first_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xattr, struct iatt *postparent) { struct fuse_first_lookup *stub = NULL; stub = frame->local; if (op_ret == 0) { gf_log (this->name, GF_LOG_TRACE, "first lookup on root succeeded."); } else { gf_log (this->name, GF_LOG_DEBUG, "first lookup on root failed."); } pthread_mutex_lock (&stub->mutex); { stub->fin = 1; pthread_cond_broadcast (&stub->cond); } pthread_mutex_unlock (&stub->mutex); return 0; } int fuse_first_lookup (xlator_t *this) { fuse_private_t *priv = NULL; loc_t loc = {0, }; call_frame_t *frame = NULL; xlator_t *xl = NULL; dict_t *dict = NULL; struct fuse_first_lookup stub; uuid_t gfid; int ret; priv = this->private; loc.path = "/"; loc.name = ""; loc.inode = fuse_ino_to_inode (1, this); gf_uuid_copy (loc.gfid, loc.inode->gfid); loc.parent = NULL; dict = dict_new (); frame = create_frame (this, this->ctx->pool); if (!frame) { gf_log ("fuse", GF_LOG_ERROR, "failed to create frame"); return -1; } frame->root->type = GF_OP_TYPE_FOP; xl = priv->active_subvol; pthread_mutex_init (&stub.mutex, NULL); pthread_cond_init (&stub.cond, NULL); stub.fin = 0; frame->local = &stub; memset (gfid, 0, 16); gfid[15] = 1; ret = dict_set_static_bin (dict, "gfid-req", gfid, 16); if (ret) gf_log (xl->name, GF_LOG_ERROR, "failed to set 'gfid-req'"); STACK_WIND (frame, fuse_first_lookup_cbk, xl, xl->fops->lookup, &loc, dict); dict_unref (dict); pthread_mutex_lock (&stub.mutex); { while (!stub.fin) { pthread_cond_wait (&stub.cond, &stub.mutex); } } pthread_mutex_unlock (&stub.mutex); pthread_mutex_destroy (&stub.mutex); pthread_cond_destroy (&stub.cond); frame->local = NULL; STACK_DESTROY (frame->root); return 0; } int fuse_nameless_lookup (xlator_t *xl, uuid_t gfid, loc_t *loc) { int ret = -1; dict_t *xattr_req = NULL; struct iatt iatt = {0, }; inode_t *linked_inode = NULL; if ((loc == NULL) || (xl == NULL)) { ret = -EINVAL; goto out; } if (loc->inode == NULL) { loc->inode = inode_new (xl->itable); if (loc->inode == NULL) { ret = -ENOMEM; goto out; } } gf_uuid_copy (loc->gfid, gfid); xattr_req = dict_new (); if (xattr_req == NULL) { ret = -ENOMEM; goto out; } ret = syncop_lookup (xl, loc, &iatt, NULL, xattr_req, NULL); if (ret < 0) goto out; linked_inode = inode_link (loc->inode, NULL, NULL, &iatt); inode_unref (loc->inode); loc->inode = linked_inode; ret = 0; out: if (xattr_req != NULL) { dict_unref (xattr_req); } return ret; } int fuse_migrate_fd_open (xlator_t *this, fd_t *basefd, fd_t *oldfd, xlator_t *old_subvol, xlator_t *new_subvol) { loc_t loc = {0, }; fd_t *newfd = NULL, *old_activefd = NULL; fuse_fd_ctx_t *basefd_ctx = NULL; fuse_fd_ctx_t *newfd_ctx = NULL; int ret = 0, flags = 0; ret = inode_path (basefd->inode, NULL, (char **)&loc.path); if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "cannot construct path of gfid (%s) failed" "(old-subvolume:%s-%d new-subvolume:%s-%d)", uuid_utoa (basefd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); goto out; } gf_uuid_copy (loc.gfid, basefd->inode->gfid); loc.inode = inode_find (new_subvol->itable, basefd->inode->gfid); if (loc.inode == NULL) { ret = fuse_nameless_lookup (new_subvol, basefd->inode->gfid, &loc); if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "name-less lookup of gfid (%s) failed (%s)" "(old-subvolume:%s-%d new-subvolume:%s-%d)", uuid_utoa (basefd->inode->gfid), strerror (-ret), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; goto out; } } basefd_ctx = fuse_fd_ctx_get (this, basefd); GF_VALIDATE_OR_GOTO ("glusterfs-fuse", basefd_ctx, out); newfd = fd_create (loc.inode, basefd->pid); if (newfd == NULL) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "cannot create new fd, hence not migrating basefd " "(ptr:%p inode-gfid:%s) " "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, uuid_utoa (loc.inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; goto out; } newfd->flags = basefd->flags; if (newfd->lk_ctx) fd_lk_ctx_unref (newfd->lk_ctx); newfd->lk_ctx = fd_lk_ctx_ref (oldfd->lk_ctx); newfd_ctx = fuse_fd_ctx_check_n_create (this, newfd); GF_VALIDATE_OR_GOTO ("glusterfs-fuse", newfd_ctx, out); if (IA_ISDIR (basefd->inode->ia_type)) { ret = syncop_opendir (new_subvol, &loc, newfd, NULL, NULL); } else { flags = basefd->flags & ~(O_CREAT | O_EXCL | O_TRUNC); ret = syncop_open (new_subvol, &loc, flags, newfd, NULL, NULL); } if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "open on basefd (ptr:%p inode-gfid:%s) failed (%s)" "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, uuid_utoa (basefd->inode->gfid), strerror (-ret), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; goto out; } fd_bind (newfd); LOCK (&basefd->lock); { if (basefd_ctx->activefd != NULL) { old_activefd = basefd_ctx->activefd; } basefd_ctx->activefd = newfd; } UNLOCK (&basefd->lock); if (old_activefd != NULL) { fd_unref (old_activefd); } gf_log ("glusterfs-fuse", GF_LOG_INFO, "migrated basefd (%p) to newfd (%p) (inode-gfid:%s)" "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, newfd, uuid_utoa (basefd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = 0; out: loc_wipe (&loc); return ret; } int fuse_migrate_locks (xlator_t *this, fd_t *basefd, fd_t *oldfd, xlator_t *old_subvol, xlator_t *new_subvol) { int ret = -1; dict_t *lockinfo = NULL; void *ptr = NULL; fd_t *newfd = NULL; fuse_fd_ctx_t *basefd_ctx = NULL; if (!oldfd->lk_ctx || fd_lk_ctx_empty (oldfd->lk_ctx)) return 0; basefd_ctx = fuse_fd_ctx_get (this, basefd); GF_VALIDATE_OR_GOTO ("glusterfs-fuse", basefd_ctx, out); LOCK (&basefd->lock); { newfd = fd_ref (basefd_ctx->activefd); } UNLOCK (&basefd->lock); ret = syncop_fgetxattr (old_subvol, oldfd, &lockinfo, GF_XATTR_LOCKINFO_KEY, NULL, NULL); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "getting lockinfo failed while migrating locks" "(oldfd:%p newfd:%p inode-gfid:%s)" "(old-subvol:%s-%d new-subvol:%s-%d)", oldfd, newfd, uuid_utoa (newfd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; goto out; } ret = dict_get_ptr (lockinfo, GF_XATTR_LOCKINFO_KEY, &ptr); if (ptr == NULL) { ret = 0; gf_log (this->name, GF_LOG_INFO, "No lockinfo present on any of the bricks " "(oldfd: %p newfd:%p inode-gfid:%s) " "(old-subvol:%s-%d new-subvol:%s-%d)", oldfd, newfd, uuid_utoa (newfd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); goto out; } ret = syncop_fsetxattr (new_subvol, newfd, lockinfo, 0, NULL, NULL); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "migrating locks failed (oldfd:%p newfd:%p " "inode-gfid:%s) (old-subvol:%s-%d new-subvol:%s-%d)", oldfd, newfd, uuid_utoa (newfd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; goto out; } out: if (newfd) fd_unref (newfd); if (lockinfo != NULL) { dict_unref (lockinfo); } return ret; } int fuse_migrate_fd (xlator_t *this, fd_t *basefd, xlator_t *old_subvol, xlator_t *new_subvol) { int ret = -1; char create_in_progress = 0; fuse_fd_ctx_t *basefd_ctx = NULL; fd_t *oldfd = NULL; basefd_ctx = fuse_fd_ctx_get (this, basefd); GF_VALIDATE_OR_GOTO ("glusterfs-fuse", basefd_ctx, out); LOCK (&basefd->lock); { oldfd = basefd_ctx->activefd ? basefd_ctx->activefd : basefd; fd_ref (oldfd); } UNLOCK (&basefd->lock); LOCK (&oldfd->inode->lock); { if (gf_uuid_is_null (oldfd->inode->gfid)) { create_in_progress = 1; } else { create_in_progress = 0; } } UNLOCK (&oldfd->inode->lock); if (create_in_progress) { gf_log ("glusterfs-fuse", GF_LOG_INFO, "create call on fd (%p) is in progress " "(basefd-ptr:%p basefd-inode.gfid:%s), " "hence deferring migration till application does an " "fd based operation on this fd" "(old-subvolume:%s-%d, new-subvolume:%s-%d)", oldfd, basefd, uuid_utoa (basefd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = 0; goto out; } if (oldfd->inode->table->xl == old_subvol) { ret = syncop_fsync (old_subvol, oldfd, 0, NULL, NULL); if (ret < 0) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "syncop_fsync failed (%s) on fd (%p)" "(basefd:%p basefd-inode.gfid:%s) " "(old-subvolume:%s-%d new-subvolume:%s-%d)", strerror (-ret), oldfd, basefd, uuid_utoa (basefd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); ret = -1; } } else { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "basefd (ptr:%p inode-gfid:%s) was not " "migrated during previous graph switch" "(old-subvolume:%s-%d new-subvolume: %s-%d)", basefd, basefd->inode->gfid, old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); } ret = fuse_migrate_fd_open (this, basefd, oldfd, old_subvol, new_subvol); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "open corresponding to " "basefd (ptr:%p inode-gfid:%s) in new graph failed " "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, uuid_utoa (basefd->inode->gfid), old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); goto out; } ret = fuse_migrate_locks (this, basefd, oldfd, old_subvol, new_subvol); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "migrating locks from old-subvolume (%s-%d) to " "new-subvolume (%s-%d) failed (inode-gfid:%s oldfd:%p " "basefd:%p)", old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id, uuid_utoa (basefd->inode->gfid), oldfd, basefd); } out: if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "migration of basefd " "(ptr:%p inode-gfid:%s) failed" "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, oldfd ? uuid_utoa (oldfd->inode->gfid) : NULL, old_subvol->name, old_subvol->graph->id, new_subvol->name, new_subvol->graph->id); } fd_unref (oldfd); return ret; } int fuse_handle_opened_fds (xlator_t *this, xlator_t *old_subvol, xlator_t *new_subvol) { fuse_private_t *priv = NULL; fdentry_t *fdentries = NULL; uint32_t count = 0; fdtable_t *fdtable = NULL; int i = 0; fd_t *fd = NULL; int32_t ret = 0; fuse_fd_ctx_t *fdctx = NULL; priv = this->private; fdtable = priv->fdtable; fdentries = gf_fd_fdtable_copy_all_fds (fdtable, &count); if (fdentries != NULL) { for (i = 0; i < count; i++) { fd = fdentries[i].fd; if (fd == NULL) continue; ret = fuse_migrate_fd (this, fd, old_subvol, new_subvol); fdctx = fuse_fd_ctx_get (this, fd); if (fdctx) { LOCK (&fd->lock); { if (ret < 0) { fdctx->migration_failed = 1; } else { fdctx->migration_failed = 0; } } UNLOCK (&fd->lock); } } for (i = 0; i < count ; i++) { fd = fdentries[i].fd; if (fd) fd_unref (fd); } GF_FREE (fdentries); } return 0; } static int fuse_handle_blocked_locks (xlator_t *this, xlator_t *old_subvol, xlator_t *new_subvol) { return 0; } static int fuse_graph_switch_task (void *data) { fuse_graph_switch_args_t *args = NULL; args = data; if (args == NULL) { goto out; } /* don't change the order of handling open fds and blocked locks, since * the act of opening files also reacquires granted locks in new graph. */ fuse_handle_opened_fds (args->this, args->old_subvol, args->new_subvol); fuse_handle_blocked_locks (args->this, args->old_subvol, args->new_subvol); out: return 0; } fuse_graph_switch_args_t * fuse_graph_switch_args_alloc (void) { fuse_graph_switch_args_t *args = NULL; args = GF_CALLOC (1, sizeof (*args), gf_fuse_mt_graph_switch_args_t); if (args == NULL) { goto out; } out: return args; } void fuse_graph_switch_args_destroy (fuse_graph_switch_args_t *args) { if (args == NULL) { goto out; } GF_FREE (args); out: return; } int fuse_handle_graph_switch (xlator_t *this, xlator_t *old_subvol, xlator_t *new_subvol) { call_frame_t *frame = NULL; int32_t ret = -1; fuse_graph_switch_args_t *args = NULL; frame = create_frame (this, this->ctx->pool); if (frame == NULL) { goto out; } args = fuse_graph_switch_args_alloc (); if (args == NULL) { goto out; } args->this = this; args->old_subvol = old_subvol; args->new_subvol = new_subvol; ret = synctask_new (this->ctx->env, fuse_graph_switch_task, NULL, frame, args); if (ret == -1) { gf_log (this->name, GF_LOG_WARNING, "starting sync-task to " "handle graph switch failed"); goto out; } ret = 0; out: if (args != NULL) { fuse_graph_switch_args_destroy (args); } if (frame != NULL) { STACK_DESTROY (frame->root); } return ret; } int fuse_graph_sync (xlator_t *this) { fuse_private_t *priv = NULL; int need_first_lookup = 0; int ret = 0; xlator_t *old_subvol = NULL, *new_subvol = NULL; uint64_t winds_on_old_subvol = 0; priv = this->private; pthread_mutex_lock (&priv->sync_mutex); { if (!priv->next_graph) goto unlock; old_subvol = priv->active_subvol; new_subvol = priv->active_subvol = priv->next_graph->top; priv->next_graph = NULL; need_first_lookup = 1; while (!priv->event_recvd) { ret = pthread_cond_wait (&priv->sync_cond, &priv->sync_mutex); if (ret != 0) { gf_log (this->name, GF_LOG_DEBUG, "timedwait returned non zero value " "ret: %d errno: %d", ret, errno); break; } } } unlock: pthread_mutex_unlock (&priv->sync_mutex); if (need_first_lookup) { fuse_first_lookup (this); } if ((old_subvol != NULL) && (new_subvol != NULL)) { fuse_handle_graph_switch (this, old_subvol, new_subvol); pthread_mutex_lock (&priv->sync_mutex); { old_subvol->switched = 1; winds_on_old_subvol = old_subvol->winds; } pthread_mutex_unlock (&priv->sync_mutex); if (winds_on_old_subvol == 0) { xlator_notify (old_subvol, GF_EVENT_PARENT_DOWN, old_subvol, NULL); } } return 0; } int fuse_get_mount_status (xlator_t *this) { int kid_status = -1; fuse_private_t *priv = this->private; if (read(priv->status_pipe[0],&kid_status, sizeof(kid_status)) < 0) { gf_log (this->name, GF_LOG_ERROR, "could not get mount status"); kid_status = -1; } gf_log (this->name, GF_LOG_DEBUG, "mount status is %d", kid_status); close(priv->status_pipe[0]); close(priv->status_pipe[1]); return kid_status; } static void * fuse_thread_proc (void *data) { char *mount_point = NULL; xlator_t *this = NULL; fuse_private_t *priv = NULL; ssize_t res = 0; struct iobuf *iobuf = NULL; fuse_in_header_t *finh = NULL; struct iovec iov_in[2]; void *msg = NULL; const size_t msg0_size = sizeof (*finh) + 128; fuse_handler_t **fuse_ops = NULL; struct pollfd pfd[2] = {{0,}}; gf_boolean_t mount_finished = _gf_false; this = data; priv = this->private; fuse_ops = priv->fuse_ops; THIS = this; iov_in[0].iov_len = sizeof (*finh) + sizeof (struct fuse_write_in); iov_in[1].iov_len = ((struct iobuf_pool *)this->ctx->iobuf_pool) ->default_page_size; priv->msg0_len_p = &iov_in[0].iov_len; for (;;) { /* THIS has to be reset here */ THIS = this; if (!mount_finished) { memset(pfd,0,sizeof(pfd)); pfd[0].fd = priv->status_pipe[0]; pfd[0].events = POLLIN | POLLHUP | POLLERR; pfd[1].fd = priv->fd; pfd[1].events = POLLIN | POLLHUP | POLLERR; if (poll(pfd,2,-1) < 0) { gf_log (this->name, GF_LOG_ERROR, "poll error %s", strerror(errno)); break; } if (pfd[0].revents & POLLIN) { if (fuse_get_mount_status(this) != 0) { break; } mount_finished = _gf_true; } else if (pfd[0].revents) { gf_log (this->name, GF_LOG_ERROR, "mount pipe closed without status"); break; } if (!pfd[1].revents) { continue; } } /* * We don't want to block on readv while we're still waiting * for mount status. That means we only want to get here if * mount_status is true (meaning that our wait completed * already) or if we already called poll(2) on priv->fd to * make sure it's ready. */ if (priv->init_recvd) fuse_graph_sync (this); /* TODO: This place should always get maximum supported buffer size from 'fuse', which is as of today 128KB. If we bring in support for higher block sizes support, then we should be changing this one too */ iobuf = iobuf_get (this->ctx->iobuf_pool); /* Add extra 128 byte to the first iov so that it can * accommodate "ordinary" non-write requests. It's not * guaranteed to be big enough, as SETXATTR and namespace * operations with very long names may grow behind it, * but it's good enough in most cases (and we can handle * rest via realloc). */ iov_in[0].iov_base = GF_CALLOC (1, msg0_size, gf_fuse_mt_iov_base); if (!iobuf || !iov_in[0].iov_base) { gf_log (this->name, GF_LOG_ERROR, "Out of memory"); if (iobuf) iobuf_unref (iobuf); GF_FREE (iov_in[0].iov_base); sleep (10); continue; } iov_in[1].iov_base = iobuf->ptr; res = readv (priv->fd, iov_in, 2); if (res == -1) { if (errno == ENODEV || errno == EBADF) { gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "terminating upon getting %s when " "reading /dev/fuse", errno == ENODEV ? "ENODEV" : "EBADF"); fuse_log_eh (this, "glusterfs-fuse: terminating" " upon getting %s when " "reading /dev/fuse", errno == ENODEV ? "ENODEV": "EBADF"); break; } if (errno != EINTR) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "read from /dev/fuse returned -1 (%s)", strerror (errno)); fuse_log_eh (this, "glusterfs-fuse: read from " "/dev/fuse returned -1 (%s)", strerror (errno)); } goto cont_err; } if (res < sizeof (finh)) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "short read on /dev/fuse"); fuse_log_eh (this, "glusterfs-fuse: short read on " "/dev/fuse"); break; } finh = (fuse_in_header_t *)iov_in[0].iov_base; if (res != finh->len #ifdef GF_DARWIN_HOST_OS /* work around fuse4bsd/MacFUSE msg size miscalculation bug, * that is, payload size is not taken into account for * buffered writes */ && !(finh->opcode == FUSE_WRITE && finh->len == sizeof(*finh) + sizeof(struct fuse_write_in) && res == finh->len + ((struct fuse_write_in *)(finh + 1))->size) #endif ) { gf_log ("glusterfs-fuse", GF_LOG_WARNING, "inconsistent read on /dev/fuse"); fuse_log_eh (this, "glusterfs-fuse: inconsistent read " "on /dev/fuse"); break; } priv->iobuf = iobuf; if (finh->opcode == FUSE_WRITE) msg = iov_in[1].iov_base; else { if (res > msg0_size) { void *b = GF_REALLOC (iov_in[0].iov_base, res); if (b) { iov_in[0].iov_base = b; finh = (fuse_in_header_t *) iov_in[0].iov_base; } else { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); send_fuse_err (this, finh, ENOMEM); goto cont_err; } } if (res > iov_in[0].iov_len) memcpy (iov_in[0].iov_base + iov_in[0].iov_len, iov_in[1].iov_base, res - iov_in[0].iov_len); msg = finh + 1; } if (priv->uid_map_root && finh->uid == priv->uid_map_root) finh->uid = 0; if (finh->opcode >= FUSE_OP_HIGH) /* turn down MacFUSE specific messages */ fuse_enosys (this, finh, msg); else fuse_ops[finh->opcode] (this, finh, msg); iobuf_unref (iobuf); continue; cont_err: iobuf_unref (iobuf); GF_FREE (iov_in[0].iov_base); } /* * We could be in all sorts of states with respect to iobuf and iov_in * by the time we get here, and it's just not worth untangling them if * we're about to kill ourselves anyway. */ if (dict_get (this->options, ZR_MOUNTPOINT_OPT)) mount_point = data_to_str (dict_get (this->options, ZR_MOUNTPOINT_OPT)); if (mount_point) { gf_log (this->name, GF_LOG_INFO, "unmounting %s", mount_point); } /* Kill the whole process, not just this thread. */ kill (getpid(), SIGTERM); return NULL; } int32_t fuse_itable_dump (xlator_t *this) { if (!this) return -1; gf_proc_dump_add_section("xlator.mount.fuse.itable"); inode_table_dump(this->itable, "xlator.mount.fuse.itable"); return 0; } int32_t fuse_priv_dump (xlator_t *this) { fuse_private_t *private = NULL; if (!this) return -1; private = this->private; if (!private) return -1; gf_proc_dump_add_section("xlator.mount.fuse.priv"); gf_proc_dump_write("fd", "%d", private->fd); gf_proc_dump_write("proto_minor", "%u", private->proto_minor); gf_proc_dump_write("volfile", "%s", private->volfile?private->volfile:"None"); gf_proc_dump_write("volfile_size", "%d", private->volfile_size); gf_proc_dump_write("mount_point", "%s", private->mount_point); gf_proc_dump_write("iobuf", "%u", private->iobuf); gf_proc_dump_write("fuse_thread_started", "%d", (int)private->fuse_thread_started); gf_proc_dump_write("direct_io_mode", "%d", private->direct_io_mode); gf_proc_dump_write("entry_timeout", "%lf", private->entry_timeout); gf_proc_dump_write("attribute_timeout", "%lf", private->attribute_timeout); gf_proc_dump_write("init_recvd", "%d", (int)private->init_recvd); gf_proc_dump_write("strict_volfile_check", "%d", (int)private->strict_volfile_check); gf_proc_dump_write("reverse_thread_started", "%d", (int)private->reverse_fuse_thread_started); gf_proc_dump_write("use_readdirp", "%d", private->use_readdirp); return 0; } int fuse_history_dump (xlator_t *this) { int ret = -1; char key_prefix[GF_DUMP_MAX_BUF_LEN] = {0,}; GF_VALIDATE_OR_GOTO ("fuse", this, out); GF_VALIDATE_OR_GOTO (this->name, this->history, out); gf_proc_dump_build_key (key_prefix, "xlator.mount.fuse", "history"); gf_proc_dump_add_section (key_prefix); eh_dump (this->history, NULL, dump_history_fuse); ret = 0; out: return ret; } int dump_history_fuse (circular_buffer_t *cb, void *data) { char timestr[256] = {0,}; gf_time_fmt (timestr, sizeof timestr, cb->tv.tv_sec, gf_timefmt_F_HMS); snprintf (timestr + strlen (timestr), 256 - strlen (timestr), ".%"GF_PRI_SUSECONDS, cb->tv.tv_usec); gf_proc_dump_write ("TIME", "%s", timestr); gf_proc_dump_write ("message", "%s\n", cb->data); return 0; } int fuse_graph_setup (xlator_t *this, glusterfs_graph_t *graph) { inode_table_t *itable = NULL; int ret = 0, winds = 0; fuse_private_t *priv = NULL; glusterfs_graph_t *prev_graph = NULL; priv = this->private; pthread_mutex_lock (&priv->sync_mutex); { /* handle the case of more than one CHILD_UP on same graph */ if ((priv->active_subvol == graph->top) || graph->used) { goto unlock; } itable = inode_table_new (0, graph->top); if (!itable) { ret = -1; goto unlock; } ((xlator_t *)graph->top)->itable = itable; prev_graph = priv->next_graph; if ((prev_graph != NULL) && (prev_graph->id > graph->id)) { /* there was a race and an old graph was initialised * before new one. */ prev_graph = graph; } else { priv->next_graph = graph; priv->event_recvd = 0; } if (prev_graph != NULL) winds = ((xlator_t *)prev_graph->top)->winds; /* set post initializing next_graph i to preserve * critical section update and bails on error */ graph->used = 1; } pthread_mutex_unlock (&priv->sync_mutex); if ((prev_graph != NULL) && (winds == 0)) { xlator_notify (prev_graph->top, GF_EVENT_PARENT_DOWN, prev_graph->top, NULL); } gf_log ("fuse", GF_LOG_INFO, "switched to graph %d", ((graph) ? graph->id : 0)); return ret; unlock: pthread_mutex_unlock (&priv->sync_mutex); return ret; } int notify (xlator_t *this, int32_t event, void *data, ...) { int32_t ret = 0; fuse_private_t *private = NULL; gf_boolean_t start_thread = _gf_false; glusterfs_graph_t *graph = NULL; private = this->private; graph = data; gf_log ("fuse", GF_LOG_DEBUG, "got event %d on graph %d", event, ((graph) ? graph->id : 0)); switch (event) { case GF_EVENT_GRAPH_NEW: break; case GF_EVENT_CHILD_UP: case GF_EVENT_CHILD_DOWN: case GF_EVENT_CHILD_CONNECTING: { if (graph) { ret = fuse_graph_setup (this, graph); if (ret) gf_log (this->name, GF_LOG_WARNING, "failed to setup the graph"); } if ((event == GF_EVENT_CHILD_UP) || (event == GF_EVENT_CHILD_DOWN)) { pthread_mutex_lock (&private->sync_mutex); { private->event_recvd = 1; pthread_cond_broadcast (&private->sync_cond); } pthread_mutex_unlock (&private->sync_mutex); } pthread_mutex_lock (&private->sync_mutex); { if (!private->fuse_thread_started) { private->fuse_thread_started = 1; start_thread = _gf_true; } } pthread_mutex_unlock (&private->sync_mutex); if (start_thread) { ret = gf_thread_create (&private->fuse_thread, NULL, fuse_thread_proc, this); if (ret != 0) { gf_log (this->name, GF_LOG_DEBUG, "pthread_create() failed (%s)", strerror (errno)); break; } } break; } case GF_EVENT_AUTH_FAILED: { /* Authentication failure is an error and glusterfs should stop */ gf_log (this->name, GF_LOG_ERROR, "Server authenication failed. Shutting down."); fini (this); break; } default: break; } return ret; } int32_t mem_acct_init (xlator_t *this) { int ret = -1; if (!this) return ret; ret = xlator_mem_acct_init (this, gf_fuse_mt_end + 1); if (ret != 0) { gf_log (this->name, GF_LOG_ERROR, "Memory accounting init" "failed"); return ret; } return ret; } static fuse_handler_t *fuse_std_ops[FUSE_OP_HIGH] = { [FUSE_LOOKUP] = fuse_lookup, [FUSE_FORGET] = fuse_forget, [FUSE_GETATTR] = fuse_getattr, [FUSE_SETATTR] = fuse_setattr, [FUSE_READLINK] = fuse_readlink, [FUSE_SYMLINK] = fuse_symlink, [FUSE_MKNOD] = fuse_mknod, [FUSE_MKDIR] = fuse_mkdir, [FUSE_UNLINK] = fuse_unlink, [FUSE_RMDIR] = fuse_rmdir, [FUSE_RENAME] = fuse_rename, [FUSE_LINK] = fuse_link, [FUSE_OPEN] = fuse_open, [FUSE_READ] = fuse_readv, [FUSE_WRITE] = fuse_write, [FUSE_STATFS] = fuse_statfs, [FUSE_RELEASE] = fuse_release, [FUSE_FSYNC] = fuse_fsync, [FUSE_SETXATTR] = fuse_setxattr, [FUSE_GETXATTR] = fuse_getxattr, [FUSE_LISTXATTR] = fuse_listxattr, [FUSE_REMOVEXATTR] = fuse_removexattr, [FUSE_FLUSH] = fuse_flush, [FUSE_INIT] = fuse_init, [FUSE_OPENDIR] = fuse_opendir, [FUSE_READDIR] = fuse_readdir, [FUSE_RELEASEDIR] = fuse_releasedir, [FUSE_FSYNCDIR] = fuse_fsyncdir, [FUSE_GETLK] = fuse_getlk, [FUSE_SETLK] = fuse_setlk, [FUSE_SETLKW] = fuse_setlk, [FUSE_ACCESS] = fuse_access, [FUSE_CREATE] = fuse_create, /* [FUSE_INTERRUPT] */ /* [FUSE_BMAP] */ [FUSE_DESTROY] = fuse_destroy, /* [FUSE_IOCTL] */ /* [FUSE_POLL] */ /* [FUSE_NOTIFY_REPLY] */ #if FUSE_KERNEL_MINOR_VERSION >= 16 [FUSE_BATCH_FORGET]= fuse_batch_forget, #endif #if FUSE_KERNEL_MINOR_VERSION >= 19 #ifdef FALLOC_FL_KEEP_SIZE [FUSE_FALLOCATE] = fuse_fallocate, #endif /* FALLOC_FL_KEEP_SIZE */ #endif #if FUSE_KERNEL_MINOR_VERSION >= 21 [FUSE_READDIRPLUS] = fuse_readdirp, #endif }; static fuse_handler_t *fuse_dump_ops[FUSE_OP_HIGH]; static void fuse_dumper (xlator_t *this, fuse_in_header_t *finh, void *msg) { fuse_private_t *priv = NULL; struct iovec diov[3]; char r = 'R'; int ret = 0; priv = this->private; diov[0].iov_base = &r; diov[0].iov_len = 1; diov[1].iov_base = finh; diov[1].iov_len = sizeof (*finh); diov[2].iov_base = msg; diov[2].iov_len = finh->len - sizeof (*finh); pthread_mutex_lock (&priv->fuse_dump_mutex); ret = writev (priv->fuse_dump_fd, diov, 3); pthread_mutex_unlock (&priv->fuse_dump_mutex); if (ret == -1) gf_log ("glusterfs-fuse", GF_LOG_ERROR, "failed to dump fuse message (R): %s", strerror (errno)); priv->fuse_ops0[finh->opcode] (this, finh, msg); } int init (xlator_t *this_xl) { int ret = 0; dict_t *options = NULL; char *value_string = NULL; cmd_args_t *cmd_args = NULL; char *fsname = NULL; fuse_private_t *priv = NULL; struct stat stbuf = {0,}; int i = 0; int xl_name_allocated = 0; int fsname_allocated = 0; glusterfs_ctx_t *ctx = NULL; gf_boolean_t sync_to_mount = _gf_false; gf_boolean_t fopen_keep_cache = _gf_false; unsigned long mntflags = 0; char *mnt_args = NULL; eh_t *event = NULL; if (this_xl == NULL) return -1; if (this_xl->options == NULL) return -1; ctx = this_xl->ctx; if (!ctx) return -1; options = this_xl->options; if (this_xl->name == NULL) { this_xl->name = gf_strdup ("fuse"); if (!this_xl->name) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); goto cleanup_exit; } xl_name_allocated = 1; } priv = GF_CALLOC (1, sizeof (*priv), gf_fuse_mt_fuse_private_t); if (!priv) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); goto cleanup_exit; } this_xl->private = (void *) priv; priv->mount_point = NULL; priv->fd = -1; priv->revchan_in = -1; priv->revchan_out = -1; /* get options from option dictionary */ ret = dict_get_str (options, ZR_MOUNTPOINT_OPT, &value_string); if (ret == -1 || value_string == NULL) { gf_log ("fuse", GF_LOG_ERROR, "Mandatory option 'mountpoint' is not specified."); goto cleanup_exit; } if (stat (value_string, &stbuf) != 0) { if (errno == ENOENT) { gf_log (this_xl->name, GF_LOG_ERROR, "%s %s does not exist", ZR_MOUNTPOINT_OPT, value_string); } else if (errno == ENOTCONN) { gf_log (this_xl->name, GF_LOG_ERROR, "Mountpoint %s seems to have a stale " "mount, run 'umount %s' and try again.", value_string, value_string); } else { gf_log (this_xl->name, GF_LOG_DEBUG, "%s %s : stat returned %s", ZR_MOUNTPOINT_OPT, value_string, strerror (errno)); } goto cleanup_exit; } if (S_ISDIR (stbuf.st_mode) == 0) { gf_log (this_xl->name, GF_LOG_ERROR, "%s %s is not a directory", ZR_MOUNTPOINT_OPT, value_string); goto cleanup_exit; } priv->mount_point = gf_strdup (value_string); if (!priv->mount_point) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); goto cleanup_exit; } GF_OPTION_INIT (ZR_ATTR_TIMEOUT_OPT, priv->attribute_timeout, double, cleanup_exit); GF_OPTION_INIT (ZR_ENTRY_TIMEOUT_OPT, priv->entry_timeout, double, cleanup_exit); GF_OPTION_INIT (ZR_NEGATIVE_TIMEOUT_OPT, priv->negative_timeout, double, cleanup_exit); GF_OPTION_INIT ("client-pid", priv->client_pid, int32, cleanup_exit); /* have to check & register the presence of client-pid manually */ priv->client_pid_set = !!dict_get (this_xl->options, "client-pid"); GF_OPTION_INIT ("uid-map-root", priv->uid_map_root, uint32, cleanup_exit); priv->direct_io_mode = 2; ret = dict_get_str (options, ZR_DIRECT_IO_OPT, &value_string); if (ret == 0) { ret = gf_string2boolean (value_string, &priv->direct_io_mode); GF_ASSERT (ret == 0); } GF_OPTION_INIT (ZR_STRICT_VOLFILE_CHECK, priv->strict_volfile_check, bool, cleanup_exit); GF_OPTION_INIT ("acl", priv->acl, bool, cleanup_exit); if (priv->uid_map_root) priv->acl = 1; GF_OPTION_INIT ("selinux", priv->selinux, bool, cleanup_exit); GF_OPTION_INIT ("read-only", priv->read_only, bool, cleanup_exit); GF_OPTION_INIT ("enable-ino32", priv->enable_ino32, bool, cleanup_exit); GF_OPTION_INIT ("use-readdirp", priv->use_readdirp, bool, cleanup_exit); priv->fuse_dump_fd = -1; ret = dict_get_str (options, "dump-fuse", &value_string); if (ret == 0) { ret = unlink (value_string); if (ret != -1 || errno == ENOENT) ret = open (value_string, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); if (ret == -1) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "cannot open fuse dump file %s", value_string); goto cleanup_exit; } priv->fuse_dump_fd = ret; } sync_to_mount = _gf_false; ret = dict_get_str (options, "sync-to-mount", &value_string); if (ret == 0) { ret = gf_string2boolean (value_string, &sync_to_mount); GF_ASSERT (ret == 0); } priv->fopen_keep_cache = 2; if (dict_get (options, "fopen-keep-cache")) { GF_OPTION_INIT("fopen-keep-cache", fopen_keep_cache, bool, cleanup_exit); priv->fopen_keep_cache = fopen_keep_cache; } GF_OPTION_INIT("gid-timeout", priv->gid_cache_timeout, int32, cleanup_exit); GF_OPTION_INIT ("fuse-mountopts", priv->fuse_mountopts, str, cleanup_exit); if (gid_cache_init(&priv->gid_cache, priv->gid_cache_timeout) < 0) { gf_log("glusterfs-fuse", GF_LOG_ERROR, "Failed to initialize " "group cache."); goto cleanup_exit; } /* default values seemed to work fine during testing */ GF_OPTION_INIT ("background-qlen", priv->background_qlen, int32, cleanup_exit); GF_OPTION_INIT ("congestion-threshold", priv->congestion_threshold, int32, cleanup_exit); GF_OPTION_INIT("no-root-squash", priv->no_root_squash, bool, cleanup_exit); /* change the client_pid to no-root-squash pid only if the client is none of defrag process, hadoop access and gsyncd process. */ if (!priv->client_pid_set) { if (priv->no_root_squash == _gf_true) { priv->client_pid_set = _gf_true; priv->client_pid = GF_CLIENT_PID_NO_ROOT_SQUASH; } } /* user has set only background-qlen, not congestion-threshold, use the fuse kernel driver formula to set congestion. ie, 75% */ if (dict_get (this_xl->options, "background-qlen") && !dict_get (this_xl->options, "congestion-threshold")) { priv->congestion_threshold = (priv->background_qlen * 3) / 4; gf_log (this_xl->name, GF_LOG_INFO, "setting congestion control as 75%% of " "background-queue length (ie, (.75 * %d) = %d", priv->background_qlen, priv->congestion_threshold); } /* congestion should not be higher than background queue length */ if (priv->congestion_threshold > priv->background_qlen) { gf_log (this_xl->name, GF_LOG_INFO, "setting congestion control same as " "background-queue length (%d)", priv->background_qlen); priv->congestion_threshold = priv->background_qlen; } cmd_args = &this_xl->ctx->cmd_args; fsname = cmd_args->volfile; if (!fsname && cmd_args->volfile_server) { if (cmd_args->volfile_id) { fsname = GF_MALLOC ( strlen (cmd_args->volfile_server) + 1 + strlen (cmd_args->volfile_id) + 1, gf_fuse_mt_fuse_private_t); if (!fsname) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); goto cleanup_exit; } fsname_allocated = 1; strcpy (fsname, cmd_args->volfile_server); strcat (fsname, ":"); strcat (fsname, cmd_args->volfile_id); } else fsname = cmd_args->volfile_server; } if (!fsname) fsname = "glusterfs"; priv->fdtable = gf_fd_fdtable_alloc (); if (priv->fdtable == NULL) { gf_log ("glusterfs-fuse", GF_LOG_ERROR, "Out of memory"); goto cleanup_exit; } if (priv->read_only) mntflags |= MS_RDONLY; gf_asprintf (&mnt_args, "%s%s%sallow_other,max_read=131072", priv->acl ? "" : "default_permissions,", priv->fuse_mountopts ? priv->fuse_mountopts : "", priv->fuse_mountopts ? "," : ""); if (!mnt_args) goto cleanup_exit; if (pipe(priv->status_pipe) < 0) { gf_log (this_xl->name, GF_LOG_ERROR, "could not create pipe to separate mount process"); goto cleanup_exit; } priv->fd = gf_fuse_mount (priv->mount_point, fsname, mntflags, mnt_args, sync_to_mount ? &ctx->mnt_pid : NULL, priv->status_pipe[1]); if (priv->fd == -1) goto cleanup_exit; event = eh_new (FUSE_EVENT_HISTORY_SIZE, _gf_false, NULL); if (!event) { gf_log (this_xl->name, GF_LOG_ERROR, "could not create a new event history"); goto cleanup_exit; } this_xl->history = event; pthread_mutex_init (&priv->fuse_dump_mutex, NULL); pthread_cond_init (&priv->sync_cond, NULL); pthread_mutex_init (&priv->sync_mutex, NULL); priv->event_recvd = 0; for (i = 0; i < FUSE_OP_HIGH; i++) { if (!fuse_std_ops[i]) fuse_std_ops[i] = fuse_enosys; if (!fuse_dump_ops[i]) fuse_dump_ops[i] = fuse_dumper; } priv->fuse_ops = fuse_std_ops; if (priv->fuse_dump_fd != -1) { priv->fuse_ops0 = priv->fuse_ops; priv->fuse_ops = fuse_dump_ops; } if (fsname_allocated) GF_FREE (fsname); GF_FREE (mnt_args); return 0; cleanup_exit: if (xl_name_allocated) GF_FREE (this_xl->name); if (fsname_allocated) GF_FREE (fsname); if (priv) { GF_FREE (priv->mount_point); if (priv->fd != -1) close (priv->fd); if (priv->fuse_dump_fd != -1) close (priv->fuse_dump_fd); GF_FREE (priv); } GF_FREE (mnt_args); return -1; } void fini (xlator_t *this_xl) { fuse_private_t *priv = NULL; char *mount_point = NULL; if (this_xl == NULL) return; if ((priv = this_xl->private) == NULL) return; pthread_mutex_lock (&priv->sync_mutex); { if (!(priv->fini_invoked)) { priv->fini_invoked = _gf_true; } else { pthread_mutex_unlock (&priv->sync_mutex); return; } } pthread_mutex_unlock (&priv->sync_mutex); if (dict_get (this_xl->options, ZR_MOUNTPOINT_OPT)) mount_point = data_to_str (dict_get (this_xl->options, ZR_MOUNTPOINT_OPT)); if (mount_point != NULL) { gf_log (this_xl->name, GF_LOG_INFO, "Unmounting '%s'.", mount_point); gf_fuse_unmount (mount_point, priv->fd); close (priv->fuse_dump_fd); dict_del (this_xl->options, ZR_MOUNTPOINT_OPT); } /* Process should terminate once fuse xlator is finished. * Required for AUTH_FAILED event. */ kill (getpid (), SIGTERM); } struct xlator_fops fops; struct xlator_cbks cbks = { .invalidate = fuse_invalidate, .forget = fuse_forget_cbk, .release = fuse_internal_release }; struct xlator_dumpops dumpops = { .priv = fuse_priv_dump, .inode = fuse_itable_dump, .history = fuse_history_dump, }; struct volume_options options[] = { { .key = {"direct-io-mode"}, .type = GF_OPTION_TYPE_BOOL }, { .key = {ZR_MOUNTPOINT_OPT, "mount-point"}, .type = GF_OPTION_TYPE_PATH }, { .key = {ZR_DUMP_FUSE, "fuse-dumpfile"}, .type = GF_OPTION_TYPE_PATH }, { .key = {ZR_ATTR_TIMEOUT_OPT}, .type = GF_OPTION_TYPE_DOUBLE, .default_value = "1.0" }, { .key = {ZR_ENTRY_TIMEOUT_OPT}, .type = GF_OPTION_TYPE_DOUBLE, .default_value = "1.0" }, { .key = {ZR_NEGATIVE_TIMEOUT_OPT}, .type = GF_OPTION_TYPE_DOUBLE, .default_value = "0.0" }, { .key = {ZR_STRICT_VOLFILE_CHECK}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false" }, { .key = {"client-pid"}, .type = GF_OPTION_TYPE_INT }, { .key = {"uid-map-root"}, .type = GF_OPTION_TYPE_INT }, { .key = {"sync-to-mount"}, .type = GF_OPTION_TYPE_BOOL }, { .key = {"read-only"}, .type = GF_OPTION_TYPE_BOOL }, { .key = {"fopen-keep-cache"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false" }, { .key = {"gid-timeout"}, .type = GF_OPTION_TYPE_INT, .default_value = "2" }, { .key = {"acl"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false" }, { .key = {"selinux"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false" }, { .key = {"enable-ino32"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false" }, { .key = {"background-qlen"}, .type = GF_OPTION_TYPE_INT, .default_value = "64", .min = 16, .max = (64 * GF_UNIT_KB), }, { .key = {"congestion-threshold"}, .type = GF_OPTION_TYPE_INT, .default_value = "48", .min = 12, .max = (64 * GF_UNIT_KB), }, { .key = {"fuse-mountopts"}, .type = GF_OPTION_TYPE_STR }, { .key = {"use-readdirp"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "yes" }, { .key = {"no-root-squash"}, .type = GF_OPTION_TYPE_BOOL, .default_value = "false", .description = "This is the mount option for disabling the " "root squash for the client irrespective of whether the root-squash " "option for the volume is set or not. But this option is honoured " "only for the trusted clients. For non trusted clients this value " "does not have any affect and the volume option for root-squash is " "honoured.", }, { .key = {NULL} }, };