/* Copyright (c) 2010-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 "fuse-bridge.h" static int fuse_resolve_all(fuse_state_t *state); int fuse_resolve_continue(fuse_state_t *state); int fuse_resolve_entry_simple(fuse_state_t *state); int fuse_resolve_inode_simple(fuse_state_t *state); int fuse_migrate_fd(xlator_t *this, fd_t *fd, xlator_t *old_subvol, xlator_t *new_subvol); fuse_fd_ctx_t * fuse_fd_ctx_get(xlator_t *this, fd_t *fd); static int fuse_resolve_loc_touchup(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; loc_t *loc = NULL; resolve = state->resolve_now; loc = state->loc_now; loc_touchup(loc, resolve->bname); return 0; } int fuse_resolve_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 *xattr, struct iatt *postparent) { fuse_state_t *state = NULL; fuse_resolve_t *resolve = NULL; inode_t *link_inode = NULL; loc_t *resolve_loc = NULL; uint64_t ctx_value = LOOKUP_NOT_NEEDED; state = frame->root->state; resolve = state->resolve_now; resolve_loc = &resolve->resolve_loc; STACK_DESTROY(frame->root); if (op_ret == -1) { gf_log(this->name, (op_errno == ENOENT) ? GF_LOG_DEBUG : GF_LOG_WARNING, "%s/%s: failed to resolve (%s)", uuid_utoa(resolve_loc->pargfid), resolve_loc->name, strerror(op_errno)); resolve->op_ret = -1; resolve->op_errno = op_errno; goto out; } link_inode = inode_link(inode, resolve_loc->parent, resolve_loc->name, buf); if (link_inode == inode) inode_ctx_set(link_inode, this, &ctx_value); state->loc_now->inode = link_inode; out: loc_wipe(resolve_loc); fuse_resolve_continue(state); return 0; } int fuse_resolve_entry(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; loc_t *resolve_loc = NULL; resolve = state->resolve_now; resolve_loc = &resolve->resolve_loc; resolve_loc->parent = inode_ref(state->loc_now->parent); gf_uuid_copy(resolve_loc->pargfid, state->loc_now->pargfid); resolve_loc->name = resolve->bname; resolve_loc->inode = inode_grep(state->itable, resolve->parhint, resolve->bname); if (!resolve_loc->inode) { resolve_loc->inode = inode_new(state->itable); } inode_path(resolve_loc->parent, resolve_loc->name, (char **)&resolve_loc->path); FUSE_FOP(state, fuse_resolve_entry_cbk, GF_FOP_LOOKUP, lookup, resolve_loc, NULL); return 0; } int fuse_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 *xattr, struct iatt *postparent) { fuse_state_t *state = NULL; fuse_resolve_t *resolve = NULL; inode_t *link_inode = NULL; loc_t *loc_now = NULL; inode_t *tmp_inode = NULL; uint64_t ctx_value = LOOKUP_NOT_NEEDED; state = frame->root->state; resolve = state->resolve_now; loc_now = state->loc_now; STACK_DESTROY(frame->root); if (op_ret == -1) { gf_log(this->name, (op_errno == ENOENT) ? GF_LOG_DEBUG : GF_LOG_WARNING, "%s: failed to resolve (%s)", uuid_utoa(resolve->resolve_loc.gfid), strerror(op_errno)); loc_wipe(&resolve->resolve_loc); /* resolve->op_ret can have 3 values: 0, -1, -2. * 0 : resolution was successful. * -1: parent inode could not be resolved. * -2: entry (inode corresponding to path) could not be resolved */ if (gf_uuid_is_null(resolve->gfid)) { resolve->op_ret = -1; } else { resolve->op_ret = -2; } resolve->op_errno = op_errno; goto out; } link_inode = inode_link(inode, NULL, NULL, buf); if (link_inode == inode) inode_ctx_set(link_inode, this, &ctx_value); loc_wipe(&resolve->resolve_loc); if (!link_inode) goto out; if (!gf_uuid_is_null(resolve->gfid)) { loc_now->inode = link_inode; goto out; } loc_now->parent = link_inode; gf_uuid_copy(loc_now->pargfid, link_inode->gfid); tmp_inode = inode_grep(state->itable, link_inode, resolve->bname); if (tmp_inode && (!inode_needs_lookup(tmp_inode, THIS))) { loc_now->inode = tmp_inode; goto out; } inode_unref(tmp_inode); fuse_resolve_entry(state); return 0; out: fuse_resolve_continue(state); return 0; } int fuse_resolve_gfid(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; loc_t *resolve_loc = NULL; int ret = 0; 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); } /* inode may already exist in case we are looking up an inode which was linked through readdirplus */ resolve_loc->inode = inode_find(state->itable, resolve_loc->gfid); if (!resolve_loc->inode) resolve_loc->inode = inode_new(state->itable); ret = loc_path(resolve_loc, NULL); if (ret <= 0) { gf_log(THIS->name, GF_LOG_WARNING, "failed to get the path for inode %s", uuid_utoa(resolve->gfid)); } FUSE_FOP(state, fuse_resolve_gfid_cbk, GF_FOP_LOOKUP, lookup, resolve_loc, NULL); return 0; } /* * Return value: * 0 - resolved parent and entry (as necessary) * -1 - resolved parent but not entry (though necessary) * 1 - resolved neither parent nor entry */ int fuse_resolve_parent_simple(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; loc_t *loc = NULL; inode_t *parent = NULL; inode_t *inode = NULL; xlator_t *this = NULL; resolve = state->resolve_now; loc = state->loc_now; this = state->this; loc->name = resolve->bname; parent = resolve->parhint; if (parent->table == state->itable) { if (inode_needs_lookup(parent, THIS)) return 1; /* no graph switches since */ loc->parent = inode_ref(parent); gf_uuid_copy(loc->pargfid, parent->gfid); loc->inode = inode_grep(state->itable, parent, loc->name); /* nodeid for root is 1 and we blindly take the latest graph's * table->root as the parhint and because of this there is * ambiguity whether the entry should have existed or not, and * we took the conservative approach of assuming entry should * have been there even though it need not have (bug #804592). */ if (loc->inode && inode_needs_lookup(loc->inode, THIS)) { inode_unref(loc->inode); loc->inode = NULL; return -1; } if ((loc->inode == NULL) && __is_root_gfid(parent->gfid)) { /* non decisive result - entry missing */ return -1; } /* decisive result - resolution success */ return 0; } parent = inode_find(state->itable, resolve->pargfid); if (!parent) { /* non decisive result - parent missing */ return 1; } if (inode_needs_lookup(parent, THIS)) { inode_unref(parent); return 1; } loc->parent = parent; gf_uuid_copy(loc->pargfid, resolve->pargfid); inode = inode_grep(state->itable, parent, loc->name); if (inode && !inode_needs_lookup(inode, this)) { loc->inode = inode; /* decisive result - resolution success */ return 0; } /* non decisive result - entry missing */ return -1; } int fuse_resolve_parent(fuse_state_t *state) { int ret = 0; ret = fuse_resolve_parent_simple(state); if (ret > 0) { fuse_resolve_gfid(state); return 0; } if (ret < 0) { fuse_resolve_entry(state); return 0; } fuse_resolve_continue(state); return 0; } int fuse_resolve_inode_simple(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; loc_t *loc = NULL; inode_t *inode = NULL; resolve = state->resolve_now; loc = state->loc_now; inode = resolve->hint; if (inode->table == state->itable) inode_ref(inode); else inode = inode_find(state->itable, resolve->gfid); if (inode) { if (!inode_needs_lookup(inode, THIS)) goto found; /* inode was linked through readdirplus */ inode_unref(inode); } return 1; found: loc->inode = inode; return 0; } int fuse_resolve_inode(fuse_state_t *state) { int ret = 0; ret = fuse_resolve_inode_simple(state); if (ret > 0) { fuse_resolve_gfid(state); return 0; } fuse_resolve_continue(state); return 0; } int fuse_migrate_fd_task(void *data) { int ret = -1; fuse_state_t *state = NULL; fd_t *basefd = NULL, *oldfd = NULL; fuse_fd_ctx_t *basefd_ctx = NULL; xlator_t *old_subvol = NULL; state = data; if (state == NULL) { goto out; } basefd = state->fd; basefd_ctx = fuse_fd_ctx_get(state->this, basefd); if (!basefd_ctx) goto out; LOCK(&basefd->lock); { oldfd = basefd_ctx->activefd ? basefd_ctx->activefd : basefd; fd_ref(oldfd); } UNLOCK(&basefd->lock); old_subvol = oldfd->inode->table->xl; ret = fuse_migrate_fd(state->this, basefd, old_subvol, state->active_subvol); LOCK(&basefd->lock); { if (ret < 0) { basefd_ctx->migration_failed = 1; } else { basefd_ctx->migration_failed = 0; } } UNLOCK(&basefd->lock); ret = 0; out: if (oldfd) fd_unref(oldfd); return ret; } static int fuse_migrate_fd_error(xlator_t *this, fd_t *fd) { fuse_fd_ctx_t *fdctx = NULL; char error = 0; fdctx = fuse_fd_ctx_get(this, fd); if (fdctx != NULL) { if (fdctx->migration_failed) { error = 1; } } return error; } #define FUSE_FD_GET_ACTIVE_FD(activefd, basefd) \ do { \ LOCK(&basefd->lock); \ { \ activefd = basefd_ctx->activefd ? basefd_ctx->activefd : basefd; \ if (activefd != basefd) { \ fd_ref(activefd); \ } \ } \ UNLOCK(&basefd->lock); \ \ if (activefd == basefd) { \ fd_ref(activefd); \ } \ } while (0); static int fuse_resolve_fd(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; fd_t *basefd = NULL, *activefd = NULL; xlator_t *active_subvol = NULL, *this = NULL; int ret = 0; char fd_migration_error = 0; fuse_fd_ctx_t *basefd_ctx = NULL; resolve = state->resolve_now; this = state->this; basefd = resolve->fd; basefd_ctx = fuse_fd_ctx_get(this, basefd); if (basefd_ctx == NULL) { gf_log(state->this->name, GF_LOG_WARNING, "fdctx is NULL for basefd (ptr:%p inode-gfid:%s), " "resolver erroring out with errno EINVAL", basefd, uuid_utoa(basefd->inode->gfid)); resolve->op_ret = -1; resolve->op_errno = EINVAL; goto resolve_continue; } FUSE_FD_GET_ACTIVE_FD(activefd, basefd); active_subvol = activefd->inode->table->xl; fd_migration_error = fuse_migrate_fd_error(state->this, basefd); if (fd_migration_error) { resolve->op_ret = -1; resolve->op_errno = EBADF; } else if (state->active_subvol != active_subvol) { ret = synctask_new(state->this->ctx->env, fuse_migrate_fd_task, NULL, NULL, state); fd_migration_error = fuse_migrate_fd_error(state->this, basefd); fd_unref(activefd); FUSE_FD_GET_ACTIVE_FD(activefd, basefd); active_subvol = activefd->inode->table->xl; if ((ret == -1) || fd_migration_error || (state->active_subvol != active_subvol)) { if (ret == -1) { gf_log(state->this->name, GF_LOG_WARNING, "starting sync-task to migrate " "basefd (ptr:%p inode-gfid:%s) failed " "(old-subvolume:%s-%d " "new-subvolume:%s-%d)", basefd, uuid_utoa(basefd->inode->gfid), active_subvol->name, active_subvol->graph->id, state->active_subvol->name, state->active_subvol->graph->id); } else { gf_log(state->this->name, GF_LOG_WARNING, "fd migration of basefd " "(ptr:%p inode-gfid:%s) failed " "(old-subvolume:%s-%d " "new-subvolume:%s-%d)", basefd, uuid_utoa(basefd->inode->gfid), active_subvol->name, active_subvol->graph->id, state->active_subvol->name, state->active_subvol->graph->id); } resolve->op_ret = -1; resolve->op_errno = EBADF; } else { gf_log(state->this->name, GF_LOG_DEBUG, "basefd (ptr:%p inode-gfid:%s) migrated " "successfully in resolver " "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, uuid_utoa(basefd->inode->gfid), active_subvol->name, active_subvol->graph->id, state->active_subvol->name, state->active_subvol->graph->id); } } if ((resolve->op_ret == -1) && (resolve->op_errno == EBADF)) { gf_log("fuse-resolve", GF_LOG_WARNING, "migration of basefd (ptr:%p inode-gfid:%s) " "did not complete, failing fop with EBADF " "(old-subvolume:%s-%d new-subvolume:%s-%d)", basefd, uuid_utoa(basefd->inode->gfid), active_subvol->name, active_subvol->graph->id, state->active_subvol->name, state->active_subvol->graph->id); } if (activefd != basefd) { state->fd = fd_ref(activefd); fd_unref(basefd); } /* state->active_subvol = active_subvol; */ resolve_continue: if (activefd != NULL) { fd_unref(activefd); } fuse_resolve_continue(state); return 0; } int fuse_gfid_set(fuse_state_t *state) { int ret = 0; if (gf_uuid_is_null(state->gfid)) goto out; if (!state->xdata) state->xdata = dict_new(); if (!state->xdata) { ret = -1; goto out; } ret = dict_set_gfuuid(state->xdata, "gfid-req", state->gfid, true); out: return ret; } int fuse_resolve_entry_init(fuse_state_t *state, fuse_resolve_t *resolve, ino_t par, char *name) { inode_t *parent = NULL; parent = fuse_ino_to_inode(par, state->this); gf_uuid_copy(resolve->pargfid, parent->gfid); resolve->parhint = parent; resolve->bname = gf_strdup(name); return 0; } int fuse_resolve_inode_init(fuse_state_t *state, fuse_resolve_t *resolve, ino_t ino) { inode_t *inode = NULL; inode = fuse_ino_to_inode(ino, state->this); gf_uuid_copy(resolve->gfid, inode->gfid); resolve->hint = inode; return 0; } int fuse_resolve_fd_init(fuse_state_t *state, fuse_resolve_t *resolve, fd_t *fd) { resolve->fd = fd_ref(fd); return 0; } static int fuse_resolve(fuse_state_t *state) { fuse_resolve_t *resolve = NULL; resolve = state->resolve_now; if (resolve->fd) { fuse_resolve_fd(state); } else if (!gf_uuid_is_null(resolve->pargfid)) { fuse_resolve_parent(state); } else if (!gf_uuid_is_null(resolve->gfid)) { fuse_resolve_inode(state); } else { fuse_resolve_all(state); } return 0; } static int fuse_resolve_done(fuse_state_t *state) { fuse_fop_resume(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 fuse_resolve_all(fuse_state_t *state) { if (state->resolve_now == NULL) { state->resolve_now = &state->resolve; state->loc_now = &state->loc; fuse_resolve(state); } else if (state->resolve_now == &state->resolve) { state->resolve_now = &state->resolve2; state->loc_now = &state->loc2; fuse_resolve(state); } else if (state->resolve_now == &state->resolve2) { fuse_resolve_done(state); } else { gf_log("fuse-resolve", GF_LOG_ERROR, "Invalid pointer for state->resolve_now"); } return 0; } int fuse_resolve_continue(fuse_state_t *state) { fuse_resolve_loc_touchup(state); fuse_resolve_all(state); return 0; } int fuse_resolve_and_resume(fuse_state_t *state, fuse_resume_fn_t fn) { fuse_gfid_set(state); state->resume_fn = fn; fuse_resolve_all(state); return 0; }