diff options
Diffstat (limited to 'xlators/mount/fuse/src/fuse-resolve.c')
-rw-r--r-- | xlators/mount/fuse/src/fuse-resolve.c | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/xlators/mount/fuse/src/fuse-resolve.c b/xlators/mount/fuse/src/fuse-resolve.c new file mode 100644 index 00000000000..b90ef3d5f8d --- /dev/null +++ b/xlators/mount/fuse/src/fuse-resolve.c @@ -0,0 +1,747 @@ +/* + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "fuse-bridge.h" + +static int +gf_resolve_all (fuse_state_t *state); +static int +resolve_entry_simple (fuse_state_t *state); +static int +resolve_inode_simple (fuse_state_t *state); +static int +resolve_path_simple (fuse_state_t *state); + +static int +component_count (const char *path) +{ + int count = 0; + const char *trav = NULL; + + for (trav = path; *trav; trav++) { + if (*trav == '/') + count++; + } + + return count + 2; +} + + +static int +prepare_components (fuse_state_t *state) +{ + xlator_t *active_xl = NULL; + gf_resolve_t *resolve = NULL; + char *resolved = NULL; + struct gf_resolve_comp *components = NULL; + char *trav = NULL; + int count = 0; + int i = 0; + + resolve = state->resolve_now; + + resolved = gf_strdup (resolve->path); + resolve->resolved = resolved; + + count = component_count (resolve->path); + components = GF_CALLOC (sizeof (*components), count, 0); //TODO + if (!components) + goto out; + resolve->components = components; + + active_xl = fuse_active_subvol (state->this); + + components[0].basename = ""; + components[0].ino = 1; + components[0].gen = 0; + components[0].inode = inode_ref (active_xl->itable->root); + + i = 1; + for (trav = resolved; *trav; trav++) { + if (*trav == '/') { + components[i].basename = trav + 1; + *trav = 0; + i++; + } + } +out: + return 0; +} + + +static int +resolve_loc_touchup (fuse_state_t *state) +{ + gf_resolve_t *resolve = NULL; + loc_t *loc = NULL; + char *path = NULL; + int ret = 0; + + resolve = state->resolve_now; + loc = state->loc_now; + + if (!loc->path) { + if (loc->parent) { + ret = inode_path (loc->parent, resolve->bname, &path); + } else if (loc->inode) { + ret = inode_path (loc->inode, NULL, &path); + } + if (ret) + gf_log ("", GF_LOG_TRACE, + "return value inode_path %d", ret); + + if (!path) + path = gf_strdup (resolve->path); + + loc->path = path; + } + + loc->name = strrchr (loc->path, '/'); + if (loc->name) + loc->name++; + + if (!loc->parent && loc->inode) { + loc->parent = inode_parent (loc->inode, 0, NULL); + } + + return 0; +} + +static int +fuse_resolve_newfd_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd) +{ + fuse_state_t *state = NULL; + gf_resolve_t *resolve = NULL; + fd_t *old_fd = NULL; + fd_t *tmp_fd = NULL; + uint64_t tmp_fd_ctx = 0; + int ret = 0; + + state = frame->root->state; + resolve = state->resolve_now; + + STACK_DESTROY (frame->root); + + if (op_ret == -1) { + goto out; + } + + old_fd = resolve->fd; + + state->fd = fd_ref (fd); + + fd_bind (fd); + + resolve->fd = NULL; + ret = fd_ctx_del (old_fd, state->this, &tmp_fd_ctx); + if (!ret) { + tmp_fd = (fd_t *)(long)tmp_fd_ctx; + fd_unref (tmp_fd); + } + ret = fd_ctx_set (old_fd, state->this, (uint64_t)(long)fd); + if (ret) + gf_log ("resolve", GF_LOG_WARNING, + "failed to set the fd ctx with resolved fd"); +out: + gf_resolve_all (state); + return 0; +} + +static void +gf_resolve_new_fd (fuse_state_t *state) +{ + gf_resolve_t *resolve = NULL; + fd_t *new_fd = NULL; + fd_t *fd = NULL; + + resolve = state->resolve_now; + fd = resolve->fd; + + new_fd = fd_create (state->loc.inode, state->finh->pid); + new_fd->flags = (fd->flags & ~O_TRUNC); + + gf_log ("resolve", GF_LOG_DEBUG, + "%"PRIu64": OPEN %s", state->finh->unique, + state->loc.path); + + FUSE_FOP (state, fuse_resolve_newfd_cbk, GF_FOP_OPEN, + open, &state->loc, new_fd->flags, new_fd, 0); +} + +static int +resolve_deep_continue (fuse_state_t *state) +{ + gf_resolve_t *resolve = NULL; + int ret = 0; + + resolve = state->resolve_now; + + resolve->op_ret = 0; + resolve->op_errno = 0; + + if (resolve->par) + ret = resolve_entry_simple (state); + else if (resolve->ino) + ret = resolve_inode_simple (state); + else if (resolve->path) + ret = resolve_path_simple (state); + if (ret) + gf_log ("resolve", GF_LOG_TRACE, + "return value of resolve_*_simple %d", ret); + + resolve_loc_touchup (state); + + /* This function is called by either fd resolve or inode resolve */ + if (!resolve->fd) + gf_resolve_all (state); + else + gf_resolve_new_fd (state); + + return 0; +} + + +static int +resolve_deep_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, struct iatt *buf, + dict_t *xattr, struct iatt *postparent) +{ + xlator_t *active_xl = NULL; + fuse_state_t *state = NULL; + gf_resolve_t *resolve = NULL; + struct gf_resolve_comp *components = NULL; + inode_t *link_inode = NULL; + int i = 0; + + state = frame->root->state; + resolve = state->resolve_now; + components = resolve->components; + + i = (long) cookie; + + STACK_DESTROY (frame->root); + + if (op_ret == -1) { + goto get_out_of_here; + } + + if (i != 0) { + inode_ref (inode); + /* no linking for root inode */ + link_inode = inode_link (inode, resolve->deep_loc.parent, + resolve->deep_loc.name, buf); + components[i].inode = inode_ref (link_inode); + link_inode = NULL; + } + inode_ref (resolve->deep_loc.parent); + inode_ref (inode); + loc_wipe (&resolve->deep_loc); + i++; /* next component */ + + if (!components[i].basename) { + /* all components of the path are resolved */ + goto get_out_of_here; + } + + /* join the current component with the path resolved until now */ + *(components[i].basename - 1) = '/'; + + active_xl = fuse_active_subvol (state->this); + + resolve->deep_loc.path = gf_strdup (resolve->resolved); + resolve->deep_loc.parent = inode_ref (components[i-1].inode); + resolve->deep_loc.inode = inode_new (active_xl->itable); + resolve->deep_loc.name = components[i].basename; + + FUSE_FOP_COOKIE (state, active_xl, resolve_deep_cbk, (void *)(long)i, + GF_FOP_LOOKUP, lookup, &resolve->deep_loc, NULL); + return 0; + +get_out_of_here: + resolve_deep_continue (state); + return 0; +} + + +static int +resolve_path_deep (fuse_state_t *state) +{ + xlator_t *active_xl = NULL; + gf_resolve_t *resolve = NULL; + struct gf_resolve_comp *components = NULL; + inode_t *inode = NULL; + long i = 0; + + resolve = state->resolve_now; + + prepare_components (state); + + components = resolve->components; + + /* start from the root */ + active_xl = fuse_active_subvol (state->this); + resolve->deep_loc.inode = inode_ref (active_xl->itable->root); + resolve->deep_loc.path = gf_strdup ("/"); + resolve->deep_loc.name = ""; + + for (i = 1; components[i].basename; i++) { + *(components[i].basename - 1) = '/'; + inode = inode_grep (active_xl->itable, components[i-1].inode, + components[i].basename); + if (!inode) + break; + components[i].inode = inode_ref (inode); + } + + if (!components[i].basename) + goto resolved; + + resolve->deep_loc.path = gf_strdup (resolve->resolved); + resolve->deep_loc.parent = inode_ref (components[i-1].inode); + resolve->deep_loc.inode = inode_new (active_xl->itable); + resolve->deep_loc.name = components[i].basename; + + FUSE_FOP_COOKIE (state, active_xl, resolve_deep_cbk, (void *)(long)i, + GF_FOP_LOOKUP, lookup, &resolve->deep_loc, NULL); + + return 0; +resolved: + resolve_deep_continue (state); + return 0; +} + + +static int +resolve_path_simple (fuse_state_t *state) +{ + gf_resolve_t *resolve = NULL; + struct gf_resolve_comp *components = NULL; + int ret = -1; + int par_idx = 0; + int ino_idx = 0; + int i = 0; + + resolve = state->resolve_now; + components = resolve->components; + + if (!components) { + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + goto out; + } + + for (i = 0; components[i].basename; i++) { + par_idx = ino_idx; + ino_idx = i; + } + + if (!components[par_idx].inode) { + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + goto out; + } + + if (!components[ino_idx].inode && + (resolve->type == RESOLVE_MUST || resolve->type == RESOLVE_EXACT)) { + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + goto out; + } + + if (components[ino_idx].inode && resolve->type == RESOLVE_NOT) { + resolve->op_ret = -1; + resolve->op_errno = EEXIST; + goto out; + } + + if (components[ino_idx].inode) + state->loc_now->inode = inode_ref (components[ino_idx].inode); + state->loc_now->parent = inode_ref (components[par_idx].inode); + + ret = 0; + +out: + return ret; +} + +/* + 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 +*/ + +static int +resolve_entry_simple (fuse_state_t *state) +{ + xlator_t *this = NULL; + xlator_t *active_xl = NULL; + gf_resolve_t *resolve = NULL; + inode_t *parent = NULL; + inode_t *inode = NULL; + int ret = 0; + + this = state->this; + resolve = state->resolve_now; + + active_xl = fuse_active_subvol (this); + + parent = inode_get (active_xl->itable, resolve->par, 0); + if (!parent) { + /* simple resolution is indecisive. need to perform + deep resolution */ + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + ret = 1; + + inode = inode_grep (active_xl->itable, parent, resolve->bname); + if (inode != NULL) { + gf_log (this->name, GF_LOG_DEBUG, "%"PRId64": inode " + "(pointer:%p ino: %"PRIu64") present but parent" + " is NULL for path (%s)", 0L, + inode, inode->ino, resolve->path); + inode_unref (inode); + } + goto out; + } + + /* expected @parent was found from the inode cache */ + state->loc_now->parent = inode_ref (parent); + + inode = inode_grep (active_xl->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_log (this->name, GF_LOG_DEBUG, "inode (pointer: %p ino:%" + PRIu64") found for path (%s) while type is RESOLVE_NOT", + inode, inode->ino, 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; +} + + +static int +gf_resolve_entry (fuse_state_t *state) +{ + int ret = 0; + loc_t *loc = NULL; + + loc = state->loc_now; + + ret = resolve_entry_simple (state); + + if (ret > 0) { + loc_wipe (loc); + resolve_path_deep (state); + return 0; + } + + if (ret == 0) + resolve_loc_touchup (state); + + gf_resolve_all (state); + + return 0; +} + + +static int +resolve_inode_simple (fuse_state_t *state) +{ + xlator_t *active_xl = NULL; + gf_resolve_t *resolve = NULL; + inode_t *inode = NULL; + int ret = 0; + + resolve = state->resolve_now; + + active_xl = fuse_active_subvol (state->this); + + if (resolve->type == RESOLVE_EXACT) { + inode = inode_get (active_xl->itable, resolve->ino, + resolve->gen); + } else { + inode = inode_get (active_xl->itable, resolve->ino, 0); + } + + if (!inode) { + resolve->op_ret = -1; + resolve->op_errno = ENOENT; + ret = 1; + goto out; + } + + ret = 0; + + state->loc_now->inode = inode_ref (inode); + +out: + if (inode) + inode_unref (inode); + + return ret; +} + + +static int +gf_resolve_inode (fuse_state_t *state) +{ + int ret = 0; + loc_t *loc = NULL; + + loc = state->loc_now; + + ret = resolve_inode_simple (state); + + if (ret > 0) { + loc_wipe (loc); + resolve_path_deep (state); + return 0; + } + + if (ret == 0) + resolve_loc_touchup (state); + + gf_resolve_all (state); + + return 0; +} + + +static int +gf_resolve_fd (fuse_state_t *state) +{ + gf_resolve_t *resolve = NULL; + fd_t *fd = NULL; + int ret = 0; + uint64_t tmp_fd_ctx = 0; + char *path = NULL; + char *name = NULL; + + resolve = state->resolve_now; + + fd = resolve->fd; + + ret = fd_ctx_get (fd, state->this, &tmp_fd_ctx); + if (!ret) { + state->fd = (fd_t *)(long)tmp_fd_ctx; + fd_ref (state->fd); + gf_resolve_all (state); + goto out; + } + + ret = inode_path (fd->inode, 0, &path); + if (!ret || !path) + gf_log ("", GF_LOG_WARNING, + "failed to do inode-path on fd %d %s", ret, path); + + name = strrchr (path, '/'); + if (name) + name++; + + resolve->path = path; + resolve->bname = gf_strdup (name); + + state->loc_now = &state->loc; + + resolve_path_deep (state); + +out: + return 0; +} + + +static int +gf_resolve (fuse_state_t *state) + { + gf_resolve_t *resolve = NULL; + + resolve = state->resolve_now; + + if (resolve->fd) { + + gf_resolve_fd (state); + + } else if (resolve->par) { + + gf_resolve_entry (state); + + } else if (resolve->ino) { + + gf_resolve_inode (state); + + } else if (resolve->path) { + + resolve_path_deep (state); + + } else { + + resolve->op_ret = -1; + resolve->op_errno = EINVAL; + + gf_resolve_all (state); + } + + return 0; +} + + +static int +gf_resolve_done (fuse_state_t *state) +{ + fuse_resume_fn_t fn = NULL; + + fn = state->resume_fn; + if (fn) + fn (state); + + 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 + */ +static int +gf_resolve_all (fuse_state_t *state) +{ + xlator_t *this = NULL; + + if (state->resolve_now == NULL) { + + state->resolve_now = &state->resolve; + state->loc_now = &state->loc; + + gf_resolve (state); + + } else if (state->resolve_now == &state->resolve) { + + state->resolve_now = &state->resolve2; + state->loc_now = &state->loc2; + + gf_resolve (state); + + } else if (state->resolve_now == &state->resolve2) { + + gf_resolve_done (state); + + } else { + gf_log (this->name, GF_LOG_ERROR, + "Invalid pointer for state->resolve_now"); + } + + return 0; +} + + +int +fuse_resolve_and_resume (fuse_state_t *state, fuse_resume_fn_t fn) +{ + xlator_t *inode_xl = NULL; + xlator_t *active_xl = NULL; + + state->resume_fn = fn; + + active_xl = fuse_active_subvol (state->this); + inode_xl = fuse_state_subvol (state); + if (!inode_xl && state->loc.parent) + inode_xl = state->loc.parent->table->xl; + + /* If inode or fd is already in new graph, goto resume */ + if (inode_xl == active_xl) + goto resume; + + /* If the resolve is for 'fd' and its open with 'write' flag + set, don't switch to new graph yet */ + if (state->fd && ((state->fd->flags & O_RDWR) || + (state->fd->flags & O_WRONLY))) + goto resume; + + if (state->loc.path) { + state->resolve.path = gf_strdup (state->loc.path); + state->resolve.bname = gf_strdup (state->loc.name); + /* TODO: make sure there is no leaks in inode refs */ + //loc_wipe (&state->loc); + state->loc.inode = NULL; + state->loc.parent = NULL; + } + + /* Needed for rename and link */ + if (state->loc2.path) { + state->resolve2.path = gf_strdup (state->loc2.path); + state->resolve2.bname = gf_strdup (state->loc2.name); + //loc_wipe (&state->loc2); + state->loc2.inode = NULL; + state->loc2.parent = NULL; + } + + if (state->fd) { + state->resolve.fd = state->fd; + /* TODO: check if its a leak, if yes, then do 'unref' */ + state->fd = NULL; + } + + gf_resolve_all (state); + + return 0; +resume: + fn (state); + + return 0; +} |