diff options
Diffstat (limited to 'xlators/protocol/server/src/server-dentry.c')
-rw-r--r-- | xlators/protocol/server/src/server-dentry.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/xlators/protocol/server/src/server-dentry.c b/xlators/protocol/server/src/server-dentry.c new file mode 100644 index 00000000000..d3a69a393fc --- /dev/null +++ b/xlators/protocol/server/src/server-dentry.c @@ -0,0 +1,413 @@ +#include "glusterfs.h" +#include "xlator.h" +#include "server-protocol.h" +#include "server-helpers.h" +#include <libgen.h> + +/* SERVER_DENTRY_STATE_PREPARE - prepare a fresh state for use + * + * @state - an empty state + * @loc - loc_t which needs to resolved + * @parent - most immediate parent of @loc available in dentry cache + * @resolved - component of @loc->path which has been resolved + * through dentry cache + */ +#define SERVER_DENTRY_STATE_PREPARE(_state,_loc,_parent,_resolved) do { \ + size_t pathlen = 0; \ + size_t resolvedlen = 0; \ + char *path = NULL; \ + int pad = 0; \ + pathlen = strlen (_loc->path) + 1; \ + path = CALLOC (1, pathlen); \ + _state->loc.parent = inode_ref (_parent); \ + _state->loc.inode = inode_new (_state->itable); \ + if (_resolved) { \ + resolvedlen = strlen (_resolved); \ + strncpy (path, _resolved, resolvedlen); \ + _state->resolved = memdup (path, pathlen); \ + if (resolvedlen == 1) /* only root resolved */ \ + pad = 0; \ + else { \ + pad = 1; \ + path[resolvedlen] = '/'; \ + } \ + strcpy_till (path + resolvedlen + pad, loc->path + resolvedlen + pad, '/'); \ + } else { \ + strncpy (path, _loc->path, pathlen); \ + } \ + _state->loc.path = path; \ + _state->loc.name = strrchr (path, '/'); \ + if (_state->loc.name) \ + _state->loc.name++; \ + _state->path = strdup (_loc->path); \ + }while (0); + +/* SERVER_DENTRY_UPDATE_STATE - update a server_state_t, to prepare state + * for new lookup + * + * @state - state to be updated. + */ +#define SERVER_DENTRY_UPDATE_STATE(_state) do { \ + char *path = NULL; \ + size_t pathlen = 0; \ + strcpy (_state->resolved, _state->loc.path); \ + pathlen = strlen (_state->loc.path); \ + if (!strcmp (_state->resolved, _state->path)) { \ + free (_state->resolved); \ + _state->resolved = NULL; \ + goto resume; \ + } \ + \ + path = (char *)(_state->loc.path + pathlen); \ + path[0] = '/'; \ + strcpy_till (path + 1, \ + _state->path + pathlen + 1, '/'); \ + _state->loc.name = strrchr (_state->loc.path, '/'); \ + if (_state->loc.name) \ + _state->loc.name++; \ + inode_unref (_state->loc.parent); \ + _state->loc.parent = inode_ref (_state->loc.inode); \ + inode_unref (_state->loc.inode); \ + _state->loc.inode = inode_new (_state->itable); \ + }while (0); + +/* NOTE: should be used only for a state which was created by __do_path_resolve + * using any other state will result in double free corruption. + */ +#define SERVER_STATE_CLEANUP(_state) do { \ + if (_state->resolved) \ + free (_state->resolved); \ + if (_state->path) \ + free (_state->path); \ + server_loc_wipe (&_state->loc); \ + free_state (_state); \ + } while (0); + +/* strcpy_till - copy @dname to @dest, until 'delim' is encountered in @dest + * @dest - destination string + * @dname - source string + * @delim - delimiter character + * + * return - NULL is returned if '0' is encountered in @dname, otherwise returns + * a pointer to remaining string begining in @dest. + */ +static char * +strcpy_till (char *dest, const char *dname, char delim) +{ + char *src = NULL; + int idx = 0; + char *ret = NULL; + + src = (char *)dname; + while (src[idx] && (src[idx] != delim)) { + dest[idx] = src[idx]; + idx++; + } + + dest[idx] = 0; + + if (src[idx] == 0) + ret = NULL; + else + ret = &(src[idx]); + + return ret; +} + +/* __server_path_to_parenti - derive parent inode for @path. if immediate parent is + * not available in the dentry cache, return nearest + * available parent inode and set @reslv to the path of + * the returned directory. + * + * @itable - inode table + * @path - path whose parent has to be looked up. + * @reslv - if immediate parent is not available, reslv will be set to path of the + * resolved parent. + * + * return - should never return NULL. should at least return '/' inode. + */ +static inode_t * +__server_path_to_parenti (inode_table_t *itable, + const char *path, + char **reslv) +{ + char *resolved_till = NULL; + char *strtokptr = NULL; + char *component = NULL; + char *next_component = NULL; + char *pathdup = NULL; + inode_t *curr = NULL; + inode_t *parent = NULL; + size_t pathlen = 0; + + + pathlen = STRLEN_0 (path); + resolved_till = CALLOC (1, pathlen); + + GF_VALIDATE_OR_GOTO("server-dentry", resolved_till, out); + pathdup = strdup (path); + GF_VALIDATE_OR_GOTO("server-dentry", pathdup, out); + + parent = inode_ref (itable->root); + curr = NULL; + + component = strtok_r (pathdup, "/", &strtokptr); + + while (component) { + curr = inode_search (itable, parent->ino, component); + if (!curr) { + /* if current component was the last component + set it to NULL + */ + component = strtok_r (NULL, "/", &strtokptr); + break; + } + + /* It is OK to append the component even if it is the + last component in the path, because, if 'next_component' + returns NULL, @parent will remain the same and + @resolved_till will not be sent back + */ + + strcat (resolved_till, "/"); + strcat (resolved_till, component); + + next_component = strtok_r (NULL, "/", &strtokptr); + + if (next_component) { + inode_unref (parent); + parent = curr; + curr = NULL; + } else { + /* will break */ + inode_unref (curr); + } + + component = next_component; + } + + free (pathdup); + + if (component) { + *reslv = resolved_till; + } else { + free (resolved_till); + } +out: + return parent; +} + + +/* __do_path_resolve_cbk - + * + * @frame - + * @cookie - + * @this - + * @op_ret - + * @op_errno - + * @inode - + * @stbuf - + * @dict - + * + */ +static int32_t +__do_path_resolve_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + inode_t *inode, + struct stat *stbuf, + dict_t *dict) +{ + server_state_t *state = NULL; + call_stub_t *stub = NULL; + inode_t *parent = NULL; + + stub = frame->local; + state = CALL_STATE(frame); + + parent = state->loc.parent; + + if (op_ret == -1) { + if (strcmp (state->path, state->loc.path)) + parent = NULL; + + server_stub_resume (stub, op_ret, op_errno, NULL, parent); + goto cleanup; + } else { + if (inode->ino == 0) { + gf_log (BOUND_XL(frame)->name, GF_LOG_DEBUG, + "looked up for %s (%"PRId64"/%s)", + state->loc.path, state->loc.parent->ino, state->loc.name); + inode_link (inode, state->loc.parent, state->loc.name, stbuf); + inode_lookup (inode); + } + + if (state->resolved) { + SERVER_DENTRY_UPDATE_STATE(state); + + gf_log (BOUND_XL(frame)->name, GF_LOG_DEBUG, + "looking up for %s (%"PRId64"/%s)", + state->loc.path, state->loc.parent->ino, state->loc.name); + + STACK_WIND (frame, + __do_path_resolve_cbk, + BOUND_XL(frame), + BOUND_XL(frame)->fops->lookup, + &(state->loc), + 0); + + goto out; + } + resume: + /* we are done, call stub_resume() to do rest of the job */ + server_stub_resume (stub, op_ret, op_errno, inode, parent); + cleanup: + SERVER_STATE_CLEANUP(state); + /* stub will be freed by stub_resume, leave no traces */ + frame->local = NULL; + STACK_DESTROY (frame->root); + } +out: + return 0; +} + +/* __do_path_resolve - resolve @loc->path into @loc->inode and @loc->parent. also + * update the dentry cache + * + * @stub - call stub to resume after resolving @loc->path + * @loc - loc to resolve before resuming @stub. + * + * return - return value of __do_path_resolve doesn't matter to the caller, if @stub + * is not NULL. + */ +static int32_t +__do_path_resolve (call_stub_t *stub, + const loc_t *loc) +{ + int32_t ret = -1; + char *resolved = NULL; + call_frame_t *new_frame = NULL; + server_state_t *state = NULL, *new_state = NULL; + inode_t *parent = NULL; + + state = CALL_STATE(stub->frame); + parent = loc->parent; + if (parent) { + inode_ref (parent); + gf_log (BOUND_XL(stub->frame)->name, GF_LOG_DEBUG, + "loc->parent(%"PRId64") already present. sending lookup " + "for %"PRId64"/%s", parent->ino, parent->ino, loc->name); + resolved = strdup (loc->path); + resolved = dirname (resolved); + } else { + parent = __server_path_to_parenti (state->itable, loc->path, &resolved); + } + + if (parent == NULL) { + /* fire in the bush.. run! run!! run!!! */ + gf_log ("server", + GF_LOG_CRITICAL, + "failed to get parent inode number"); + goto panic; + } + + if (resolved) { + gf_log (BOUND_XL(stub->frame)->name, + GF_LOG_DEBUG, + "resolved path(%s) till %"PRId64"(%s). " + "sending lookup for remaining path", + loc->path, parent->ino, resolved); + } + + { + new_frame = server_copy_frame (stub->frame); + new_state = CALL_STATE(new_frame); + + SERVER_DENTRY_STATE_PREPARE(new_state, loc, parent, resolved); + + if (parent) + inode_unref (parent); /* __server_path_to_parenti()'s inode_ref */ + free (resolved); + /* now interpret state as: + * state->path - compelete pathname to resolve + * state->resolved - pathname resolved from dentry cache + */ + new_frame->local = stub; + STACK_WIND (new_frame, + __do_path_resolve_cbk, + BOUND_XL(new_frame), + BOUND_XL(new_frame)->fops->lookup, + &(new_state->loc), + 0); + goto out; + } +panic: + server_stub_resume (stub, -1, ENOENT, NULL, NULL); +out: + return ret; +} + + +/* + * do_path_lookup - transform a pathname into inode, with the compelete + * dentry tree upto inode built. + * + * @stub - call stub to resume after completing pathname to inode transform + * @loc - location. valid fields that do_path_lookup() uses in @loc are + * @loc->path - pathname + * @loc->ino - inode number + * + * return - do_path_lookup returns only after complete dentry tree is built + * upto @loc->path. + */ +int32_t +do_path_lookup (call_stub_t *stub, + const loc_t *loc) +{ + char *pathname = NULL; + char *directory = NULL; + inode_t *inode = NULL; + inode_t *parent = NULL; + server_state_t *state = NULL; + + state = CALL_STATE(stub->frame); + + inode = inode_from_path (state->itable, loc->path); + pathname = strdup (loc->path); + directory = dirname (pathname); + parent = inode_from_path (state->itable, directory); + + if (inode && parent) { + gf_log (BOUND_XL(stub->frame)->name, + GF_LOG_DEBUG, + "resolved path(%s) to %"PRId64"/%"PRId64"(%s)", + loc->path, parent->ino, inode->ino, loc->name); + server_stub_resume (stub, 0, 0, inode, parent); + inode_unref (inode); + inode_unref (parent); + } else { + gf_log (BOUND_XL(stub->frame)->name, + GF_LOG_DEBUG, + "resolved path(%s) to %p(%"PRId64")/%p(%"PRId64")", + loc->path, parent, (parent ? parent->ino : 0), + inode, (inode ? inode->ino : 0)); + if (parent) { + inode_unref (parent); + } else if (inode) { + inode_unref (inode); + gf_log (BOUND_XL(stub->frame)->name, + GF_LOG_ERROR, + "undesired behaviour. inode(%"PRId64") for %s " + "exists without parent (%s)", + inode->ino, loc->path, directory); + } + __do_path_resolve (stub, loc); + } + + if (pathname) + free (pathname); + + return 0; +} |