diff options
Diffstat (limited to 'xlators/protocol/server/src/server-resolve.c')
| -rw-r--r-- | xlators/protocol/server/src/server-resolve.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/xlators/protocol/server/src/server-resolve.c b/xlators/protocol/server/src/server-resolve.c new file mode 100644 index 00000000000..ec768acba44 --- /dev/null +++ b/xlators/protocol/server/src/server-resolve.c @@ -0,0 +1,683 @@ +/* + Copyright (c) 2010-2013 Red Hat, Inc. <http://www.redhat.com> + 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 "server.h" +#include "server-helpers.h" +#include "server-messages.h" + +int +server_resolve_all(call_frame_t *frame); +int +resolve_entry_simple(call_frame_t *frame); +int +resolve_inode_simple(call_frame_t *frame); +int +resolve_continue(call_frame_t *frame); +int +resolve_anonfd_simple(call_frame_t *frame); + +int +resolve_loc_touchup(call_frame_t *frame) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + loc_t *loc = NULL; + + state = CALL_STATE(frame); + + resolve = state->resolve_now; + loc = state->loc_now; + + loc_touchup(loc, resolve->bname); + return 0; +} + +int +resolve_gfid_entry_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, struct iatt *postparent) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + inode_t *link_inode = NULL; + loc_t *resolve_loc = NULL; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + resolve_loc = &resolve->resolve_loc; + + if (op_ret == -1) { + if (op_errno == ENOENT) { + gf_msg_debug(this->name, 0, "%s/%s: failed to resolve (%s)", + uuid_utoa(resolve_loc->pargfid), resolve_loc->name, + strerror(op_errno)); + if (resolve->type == RESOLVE_NOT) { + do { + inode = inode_grep(state->itable, resolve_loc->parent, + resolve->bname); + + if (inode) { + gf_msg_debug(this->name, 0, + "%s/%s: " + "removing stale dentry", + uuid_utoa(resolve_loc->pargfid), + resolve->bname); + inode_unlink(inode, resolve_loc->parent, + resolve->bname); + } + } while (inode); + } + } else { + gf_msg(this->name, GF_LOG_WARNING, op_errno, + PS_MSG_GFID_RESOLVE_FAILED, + "%s/%s: failed to " + "resolve (%s)", + uuid_utoa(resolve_loc->pargfid), resolve_loc->name, + strerror(op_errno)); + } + goto out; + } + + link_inode = inode_link(inode, resolve_loc->parent, resolve_loc->name, buf); + + if (!link_inode) + goto out; + + inode_lookup(link_inode); + + inode_unref(link_inode); + +out: + loc_wipe(resolve_loc); + + resolve_continue(frame); + return 0; +} + +int +resolve_gfid_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata, + struct iatt *postparent) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + inode_t *link_inode = NULL; + loc_t *resolve_loc = NULL; + dict_t *dict = NULL; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + resolve_loc = &resolve->resolve_loc; + + if (op_ret == -1) { + if (op_errno == ENOENT) { + gf_msg_debug(this->name, GF_LOG_DEBUG, "%s: failed to resolve (%s)", + uuid_utoa(resolve_loc->gfid), strerror(op_errno)); + } else { + gf_msg(this->name, GF_LOG_WARNING, op_errno, + PS_MSG_GFID_RESOLVE_FAILED, "%s: failed to resolve (%s)", + uuid_utoa(resolve_loc->gfid), strerror(op_errno)); + } + loc_wipe(&resolve->resolve_loc); + goto out; + } + + link_inode = inode_link(inode, NULL, NULL, buf); + + if (!link_inode) { + loc_wipe(resolve_loc); + goto out; + } + + inode_lookup(link_inode); + + /* wipe the loc only after the inode has been linked to the inode + table. Otherwise before inode gets linked to the inode table, + inode would have been unrefed (this might have been destroyed + if refcount becomes 0, and put back to mempool). So once the + inode gets destroyed, inode_link is a redundant operation. But + without knowing that the destroyed inode's pointer is saved in + the resolved_loc as parent (while constructing loc for resolving + the entry) and the inode_new call for resolving the entry will + return the same pointer to the inode as the parent (because in + reality the inode is a free inode present in cold list of the + inode mem-pool). + */ + loc_wipe(resolve_loc); + + if (gf_uuid_is_null(resolve->pargfid)) { + inode_unref(link_inode); + goto out; + } + + resolve_loc->parent = link_inode; + gf_uuid_copy(resolve_loc->pargfid, resolve_loc->parent->gfid); + + resolve_loc->name = resolve->bname; + + resolve_loc->inode = server_inode_new(state->itable, resolve_loc->gfid); + + inode_path(resolve_loc->parent, resolve_loc->name, + (char **)&resolve_loc->path); + + if (state->xdata) { + dict = dict_copy_with_ref(state->xdata, NULL); + if (!dict) + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, PS_MSG_NO_MEMORY, + "BUG: dict allocation failed (pargfid: %s, name: %s), " + "still continuing", + uuid_utoa(resolve_loc->gfid), resolve_loc->name); + } + + STACK_WIND(frame, resolve_gfid_entry_cbk, frame->root->client->bound_xl, + frame->root->client->bound_xl->fops->lookup, + &resolve->resolve_loc, dict); + if (dict) + dict_unref(dict); + return 0; +out: + resolve_continue(frame); + return 0; +} + +int +resolve_gfid(call_frame_t *frame) +{ + server_state_t *state = NULL; + xlator_t *this = NULL; + server_resolve_t *resolve = NULL; + loc_t *resolve_loc = NULL; + dict_t *xdata = NULL; + + state = CALL_STATE(frame); + this = frame->this; + resolve = state->resolve_now; + resolve_loc = &resolve->resolve_loc; + + if (!gf_uuid_is_null(resolve->pargfid)) + gf_uuid_copy(resolve_loc->gfid, resolve->pargfid); + else if (!gf_uuid_is_null(resolve->gfid)) + gf_uuid_copy(resolve_loc->gfid, resolve->gfid); + + resolve_loc->inode = server_inode_new(state->itable, resolve_loc->gfid); + (void)loc_path(resolve_loc, NULL); + + if (state->xdata) { + xdata = dict_copy_with_ref(state->xdata, NULL); + if (!xdata) + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, PS_MSG_NO_MEMORY, + "BUG: dict allocation failed (gfid: %s), " + "still continuing", + uuid_utoa(resolve_loc->gfid)); + } + + STACK_WIND(frame, resolve_gfid_cbk, frame->root->client->bound_xl, + frame->root->client->bound_xl->fops->lookup, + &resolve->resolve_loc, xdata); + + if (xdata) + dict_unref(xdata); + + return 0; +} + +int +resolve_continue(call_frame_t *frame) +{ + server_state_t *state = NULL; + xlator_t *this = NULL; + server_resolve_t *resolve = NULL; + int ret = 0; + + state = CALL_STATE(frame); + this = frame->this; + resolve = state->resolve_now; + + resolve->op_ret = 0; + resolve->op_errno = 0; + + if (resolve->fd_no != -1) { + ret = resolve_anonfd_simple(frame); + goto out; + } else if (!gf_uuid_is_null(resolve->pargfid)) + ret = resolve_entry_simple(frame); + else if (!gf_uuid_is_null(resolve->gfid)) + ret = resolve_inode_simple(frame); + if (ret) + gf_msg_debug(this->name, 0, + "return value of resolve_*_" + "simple %d", + ret); + + resolve_loc_touchup(frame); +out: + server_resolve_all(frame); + + return 0; +} + +/* + Check if the requirements are fulfilled by entries in the inode cache itself + Return value: + <= 0 - simple resolution was decisive and complete (either success or failure) + > 0 - indecisive, need to perform deep resolution +*/ + +int +resolve_entry_simple(call_frame_t *frame) +{ + server_state_t *state = NULL; + xlator_t *this = NULL; + server_resolve_t *resolve = NULL; + inode_t *parent = NULL; + inode_t *inode = NULL; + int ret = 0; + + state = CALL_STATE(frame); + this = frame->this; + resolve = state->resolve_now; + + parent = inode_find(state->itable, resolve->pargfid); + if (!parent) { + /* simple resolution is indecisive. need to perform + deep resolution */ + resolve->op_ret = -1; + resolve->op_errno = ESTALE; + ret = 1; + goto out; + } + + if (parent->ia_type != IA_IFDIR) { + /* Parent type should be 'directory', and nothing else */ + gf_msg(this->name, GF_LOG_ERROR, EPERM, PS_MSG_GFID_RESOLVE_FAILED, + "%s: parent type not directory (%d)", uuid_utoa(parent->gfid), + parent->ia_type); + resolve->op_ret = -1; + resolve->op_errno = EPERM; + ret = 1; + goto out; + } + + /* expected @parent was found from the inode cache */ + gf_uuid_copy(state->loc_now->pargfid, resolve->pargfid); + state->loc_now->parent = inode_ref(parent); + if (strchr(resolve->bname, '/')) { + /* basename should be a string (without '/') in a directory, + it can't span multiple levels. This can also lead to + resolving outside the parent's tree, which is not allowed */ + gf_msg(this->name, GF_LOG_ERROR, EPERM, PS_MSG_GFID_RESOLVE_FAILED, + "%s: basename sent by client not allowed", resolve->bname); + resolve->op_ret = -1; + resolve->op_errno = EPERM; + ret = 1; + goto out; + } + + state->loc_now->name = resolve->bname; + + inode = inode_grep(state->itable, parent, resolve->bname); + if (!inode) { + switch (resolve->type) { + case RESOLVE_DONTCARE: + case RESOLVE_NOT: + ret = 0; + break; + case RESOLVE_MAY: + ret = 1; + break; + default: + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + ret = 1; + break; + } + + goto out; + } + + if (resolve->type == RESOLVE_NOT) { + gf_msg_debug(this->name, 0, + "inode (pointer: %p gfid:%s found" + " for path (%s) while type is RESOLVE_NOT. " + "Performing lookup on backend to rule out any " + "possible stale dentries in inode table", + inode, uuid_utoa(inode->gfid), resolve->path); + resolve->op_ret = -1; + resolve->op_errno = EEXIST; + ret = 1; + goto out; + } + + ret = 0; + + state->loc_now->inode = inode_ref(inode); + +out: + if (parent) + inode_unref(parent); + + if (inode) + inode_unref(inode); + + return ret; +} + +int +server_resolve_entry(call_frame_t *frame) +{ + server_state_t *state = NULL; + int ret = 0; + loc_t *loc = NULL; + + state = CALL_STATE(frame); + loc = state->loc_now; + + ret = resolve_entry_simple(frame); + + if (ret > 0) { + loc_wipe(loc); + resolve_gfid(frame); + return 0; + } + + if (ret == 0) + resolve_loc_touchup(frame); + + server_resolve_all(frame); + + return 0; +} + +int +resolve_inode_simple(call_frame_t *frame) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + inode_t *inode = NULL; + int ret = 0; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + + inode = inode_find(state->itable, resolve->gfid); + + if (!inode) { + resolve->op_ret = -1; + resolve->op_errno = ESTALE; + ret = 1; + goto out; + } + + ret = 0; + + state->loc_now->inode = inode_ref(inode); + gf_uuid_copy(state->loc_now->gfid, resolve->gfid); + +out: + if (inode) + inode_unref(inode); + + return ret; +} + +int +server_resolve_inode(call_frame_t *frame) +{ + server_state_t *state = NULL; + int ret = 0; + loc_t *loc = NULL; + + state = CALL_STATE(frame); + loc = state->loc_now; + + ret = resolve_inode_simple(frame); + + if (ret > 0) { + loc_wipe(loc); + resolve_gfid(frame); + return 0; + } + + if (ret == 0) + resolve_loc_touchup(frame); + + server_resolve_all(frame); + + return 0; +} + +int +resolve_anonfd_simple(call_frame_t *frame) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + inode_t *inode = NULL; + int ret = 0; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + + inode = inode_find(state->itable, resolve->gfid); + + if (!inode) { + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + ret = 1; + goto out; + } + + ret = 0; + + if (frame->root->op == GF_FOP_READ || frame->root->op == GF_FOP_WRITE) + state->fd = fd_anonymous_with_flags(inode, state->flags); + else + state->fd = fd_anonymous(inode); +out: + if (inode) + inode_unref(inode); + + if (ret != 0) + gf_msg_debug("server", 0, + "inode for the gfid" + "(%s) is not found. anonymous fd creation failed", + uuid_utoa(resolve->gfid)); + return ret; +} + +int +server_resolve_anonfd(call_frame_t *frame) +{ + server_state_t *state = NULL; + int ret = 0; + loc_t *loc = NULL; + + state = CALL_STATE(frame); + loc = state->loc_now; + + ret = resolve_anonfd_simple(frame); + + if (ret > 0) { + loc_wipe(loc); + resolve_gfid(frame); + return 0; + } + + server_resolve_all(frame); + + return 0; +} + +int +server_resolve_fd(call_frame_t *frame) +{ + server_ctx_t *serv_ctx = NULL; + server_state_t *state = NULL; + client_t *client = NULL; + server_resolve_t *resolve = NULL; + uint64_t fd_no = -1; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + + fd_no = resolve->fd_no; + + if (fd_no == GF_ANON_FD_NO) { + server_resolve_anonfd(frame); + return 0; + } + + client = frame->root->client; + + serv_ctx = server_ctx_get(client, client->this); + + if (serv_ctx == NULL) { + gf_msg("", GF_LOG_INFO, ENOMEM, PS_MSG_NO_MEMORY, + "server_ctx_get() failed"); + resolve->op_ret = -1; + resolve->op_errno = ENOMEM; + return 0; + } + + /* + * With copy_file_range, there will be 2 fds to resolve. + * This same function is called to resolve both the source + * fd and the destination fd. As of now, this function does + * not have any mechanism to distinguish between the 2 fds + * being resolved except for checking the value of state->fd. + * The assumption is that, if source fd the one which is + * being resolved here, then state->fd would be NULL. If it + * is not NULL, then it is the destination fd which is being + * resolved. + * This method (provided the above assumption is true) is + * to achieve the ability to distinguish between 2 fds with + * minimum changes being done to this function. If this way + * is not correct, then more changes might be needed. + */ + if (!state->fd) { + state->fd = gf_fd_fdptr_get(serv_ctx->fdtable, fd_no); + if (!state->fd) { + gf_msg("", GF_LOG_INFO, EBADF, PS_MSG_FD_NOT_FOUND, + "fd not " + "found in context"); + resolve->op_ret = -1; + resolve->op_errno = EBADF; + } + } else { + state->fd_out = gf_fd_fdptr_get(serv_ctx->fdtable, fd_no); + if (!state->fd_out) { + gf_msg("", GF_LOG_INFO, EBADF, PS_MSG_FD_NOT_FOUND, + "fd not " + "found in context"); + resolve->op_ret = -1; + resolve->op_errno = EBADF; + } + } + + server_resolve_all(frame); + + return 0; +} + +int +server_resolve(call_frame_t *frame) +{ + server_state_t *state = NULL; + server_resolve_t *resolve = NULL; + + state = CALL_STATE(frame); + resolve = state->resolve_now; + + if (resolve->fd_no != -1) { + server_resolve_fd(frame); + + } else if (!gf_uuid_is_null(resolve->pargfid)) { + server_resolve_entry(frame); + + } else if (!gf_uuid_is_null(resolve->gfid)) { + server_resolve_inode(frame); + + } else { + if (resolve == &state->resolve) + gf_msg(frame->this->name, GF_LOG_WARNING, 0, PS_MSG_INVALID_ENTRY, + "no resolution type for %s (%s)", resolve->path, + gf_fop_list[frame->root->op]); + + resolve->op_ret = -1; + resolve->op_errno = EINVAL; + + server_resolve_all(frame); + } + + return 0; +} + +int +server_resolve_done(call_frame_t *frame) +{ + server_state_t *state = NULL; + + state = CALL_STATE(frame); + + server_print_request(frame); + + state->resume_fn(frame, frame->root->client->bound_xl); + + return 0; +} + +/* + * This function is called multiple times, once per resolving one location/fd. + * state->resolve_now is used to decide which location/fd is to be resolved now + */ +int +server_resolve_all(call_frame_t *frame) +{ + server_state_t *state = NULL; + xlator_t *this = NULL; + + this = frame->this; + state = CALL_STATE(frame); + + if (state->resolve_now == NULL) { + state->resolve_now = &state->resolve; + state->loc_now = &state->loc; + + server_resolve(frame); + + } else if (state->resolve_now == &state->resolve) { + state->resolve_now = &state->resolve2; + state->loc_now = &state->loc2; + + server_resolve(frame); + + } else if (state->resolve_now == &state->resolve2) { + server_resolve_done(frame); + + } else { + gf_msg(this->name, GF_LOG_ERROR, EINVAL, PS_MSG_INVALID_ENTRY, + "Invalid pointer for " + "state->resolve_now"); + } + + return 0; +} + +int +resolve_and_resume(call_frame_t *frame, server_resume_fn_t fn) +{ + server_state_t *state = NULL; + + state = CALL_STATE(frame); + state->resume_fn = fn; + + server_resolve_all(frame); + + return 0; +} |
