summaryrefslogtreecommitdiffstats
path: root/xlators/mount/fuse/src/fuse-resolve.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/mount/fuse/src/fuse-resolve.c')
-rw-r--r--xlators/mount/fuse/src/fuse-resolve.c747
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;
+}