diff options
| author | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
|---|---|---|
| committer | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
| commit | 77adf4cd648dce41f89469dd185deec6b6b53a0b (patch) | |
| tree | 02e155a5753b398ee572b45793f889b538efab6b /xlators/protocol/server/src/server-dentry.c | |
| parent | f3b2e6580e5663292ee113c741343c8a43ee133f (diff) | |
Added all files
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; +}  | 
