/*
 *	$Source: /usr2/luicat/tcfs/RCS/tcfs_vnops.c,v $
 *	$State: Exp $
 *	$Revision: 1.1 $
 *	$Author: luicat $
 *	$Date: 1998/07/12 11:54:14 $
 *	$Locker:  $
 */
/* RCSHEADERENDSHERE */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <miscfs/tcfs/tcfs.h>
#include <miscfs/tcfs/tcfs_fileinfo.h>

int
tcfs_lookup(v)
	void *v;
{
	register struct vop_lookup_args /* {
		struct vnodeop_desc *a_desc;
		struct vnode *a_dvp;
		struct vnode **a_vpp;
		struct componentname *a_cnp;
	} */ *ap = v;
	register int error;
	register struct vnode *dvp;
	int flags = ap->a_cnp->cn_flags;


#ifdef TCFS_DIAGNOSTIC
	printf("tcfs_lookup: dvp=%p, name='%s'\n",
	    ap->a_dvp, ap->a_cnp->cn_nameptr);
#endif
	/*
	 * the starting dir (ap->a_dvp) comes in locked.
	 */

	/* set LOCKPARENT to hold on to it until done below */
	ap->a_cnp->cn_flags |= LOCKPARENT;
		error=tcfs_bypass(ap);

	
	if (!(flags & LOCKPARENT))
		ap->a_cnp->cn_flags &= ~LOCKPARENT;

	if (error)
		/*
		 * starting dir is still locked/has been relocked
		 * on error return.
		 */
		 return error;

	if (ap->a_dvp != *ap->a_vpp) {
		/*
		 * Lookup returns node locked; we mark both lower and
		 * upper nodes as locked by setting the lower lock
		 * flag (it came back locked), and then call lock to
		 * set upper lock flag & record pid, etc.  see
		 * tcfs_node_create()
		 */
		VTOTCFS(*ap->a_vpp)->tcfs_flags |= TCFS_LLOCK;

		dvp = ap->a_dvp;
		if (flags & ISDOTDOT) {
			/*
			 * If we're looking up `..' and this isn't the
			 * last component, then the starting directory
			 * ("parent") is _unlocked_ as a side-effect
			 * of lookups.  This is to avoid deadlocks:
			 * lock order is always parent, child, so
			 * looking up `..'  requires dropping the lock
			 * on the starting directory.
			 */
			/* see ufs_lookup() for hairy ugly locking protocol
			   examples */
			/*
			 * underlying starting dir comes back locked if flags &
			 * LOCKPARENT (which we artificially set above) and
			 * ISLASTCN.
			 */
			if (flags & ISLASTCN) {
				VTOTCFS(dvp)->tcfs_flags |= TCFS_LLOCK;	/* no-op, right? */
			} else {
				VTOTCFS(dvp)->tcfs_flags &= ~TCFS_LLOCK;
			}
			/*
			 * locking order: drop lock on lower-in-tree
			 * element, then get lock on higher-in-tree
			 * element, then (if needed) re-fetch lower
			 * lock.  No need for vget() since we hold a
			 * refcount to the starting directory
			 */
			VOP_UNLOCK(dvp);
			VOP_LOCK(*ap->a_vpp);
			/*
			 * we should return our directory locked if
			 * (flags & LOCKPARENT) and (flags & ISLASTCN)
			 */
			if ((flags & LOCKPARENT) && (flags & ISLASTCN))
				VOP_LOCK(dvp);
		} else {
			/*
			 * Normal directory locking order: we hold the starting
			 * directory locked; now lock our layer of the target.
			 */
			VOP_LOCK(*ap->a_vpp);
			/*
			 * underlying starting dir comes back locked
			 * if lockparent (we set it) and no error
			 * (this leg) and ISLASTCN
			 */
			if (flags & ISLASTCN) {
				VTOTCFS(dvp)->tcfs_flags |= TCFS_LLOCK;	/* no op, right? */
			} else {
				VTOTCFS(dvp)->tcfs_flags &= ~TCFS_LLOCK;
			}
			/*
			 * we should return our directory unlocked if
			 * our caller didn't want the parent locked,
			 * !(flags & LOCKPARENT), or we're not at the
			 * end yet, !(flags & ISLASTCN)
			 */
			if (!(flags & LOCKPARENT) || !(flags & ISLASTCN))
				VOP_UNLOCK(dvp);
		}
	}
	return error;
}
