5034470 Fix for bug 4126922 is incomplete, still lofs stack overflow panic possible
authorowenr
Tue, 09 Aug 2005 09:24:51 -0700
changeset 324 170553096c75
parent 323 929679950ffa
child 325 b11f0d93078f
5034470 Fix for bug 4126922 is incomplete, still lofs stack overflow panic possible
usr/src/uts/common/fs/lofs/lofs_subr.c
usr/src/uts/common/fs/lofs/lofs_vfsops.c
usr/src/uts/common/fs/lofs/lofs_vnops.c
usr/src/uts/common/sys/fs/lofs_node.h
--- a/usr/src/uts/common/fs/lofs/lofs_subr.c	Mon Aug 08 23:54:21 2005 -0700
+++ b/usr/src/uts/common/fs/lofs/lofs_subr.c	Tue Aug 09 09:24:51 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -243,18 +243,26 @@
  * in a table hashed by vnode.  If the lnode for
  * this vnode is already in the table return it (ref count is
  * incremented by lfind).  The lnode will be flushed from the
- * table when lo_inactive calls freelonode.
+ * table when lo_inactive calls freelonode.  The creation of
+ * a new lnode can be forced via the LOF_FORCE flag even if
+ * the vnode exists in the table.  This is used in the creation
+ * of a terminating lnode when looping is detected.  A unique
+ * lnode is required for the correct evaluation of the current
+ * working directory.
  * NOTE: vp is assumed to be a held vnode.
  */
 struct vnode *
-makelonode(struct vnode *vp, struct loinfo *li)
+makelonode(struct vnode *vp, struct loinfo *li, int flag)
 {
 	lnode_t *lp, *tlp;
 	struct vfs *vfsp;
 	vnode_t *nvp;
 
+	lp = NULL;
 	TABLE_LOCK_ENTER(vp, li);
-	if ((lp = lfind(vp, li)) == NULL) {
+	if (flag != LOF_FORCE)
+		lp = lfind(vp, li);
+	if ((flag == LOF_FORCE) || (lp == NULL)) {
 		/*
 		 * Optimistically assume that we won't need to sleep.
 		 */
@@ -270,8 +278,11 @@
 			if (nvp == NULL) {
 				nvp = vn_alloc(KM_SLEEP);
 			}
+			lp = NULL;
 			TABLE_LOCK_ENTER(vp, li);
-			if ((lp = lfind(vp, li)) != NULL) {
+			if (flag != LOF_FORCE)
+				lp = lfind(vp, li);
+			if (lp != NULL) {
 				kmem_cache_free(lnode_cache, tlp);
 				vn_free(nvp);
 				VN_RELE(vp);
--- a/usr/src/uts/common/fs/lofs/lofs_vfsops.c	Mon Aug 08 23:54:21 2005 -0700
+++ b/usr/src/uts/common/fs/lofs/lofs_vfsops.c	Tue Aug 09 09:24:51 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -352,7 +352,7 @@
 	/*
 	 * Make the root vnode
 	 */
-	srootvp = makelonode(realrootvp, li);
+	srootvp = makelonode(realrootvp, li, 0);
 	srootvp->v_flag |= VROOT;
 	li->li_rootvp = srootvp;
 
--- a/usr/src/uts/common/fs/lofs/lofs_vnops.c	Mon Aug 08 23:54:21 2005 -0700
+++ b/usr/src/uts/common/fs/lofs/lofs_vnops.c	Tue Aug 09 09:24:51 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -77,7 +77,7 @@
 		 * the FS which we called should have released the
 		 * new reference on vp
 		 */
-		*vpp = makelonode(rvp, vtoli(oldvp->v_vfsp));
+		*vpp = makelonode(rvp, vtoli(oldvp->v_vfsp), 0);
 		if (IS_DEVVP(*vpp)) {
 			vnode_t *svp;
 
@@ -278,10 +278,10 @@
  * returning the shadow of the autonode corresponding to
  * "/net/jurassic/net/jurassic" will not terminate the
  * loop.   To solve this problem we allow the loop to go
- * through one more level component lookup.  If it hit
- * "net" after the loop as in "/net/jurassic/net/jurassic/net",
- * then returning the vnode covered by the autonode "net"
- * will terminate the loop.
+ * through one more level component lookup.  Whichever
+ * directory is then looked up in "/net/jurassic/net/jurassic"
+ * the vnode returned is the vnode covered by the autonode
+ * "net" and this will terminate the loop.
  *
  * Lookup for dot dot has to be dealt with separately.
  * It will be nice to have a "one size fits all" kind
@@ -306,8 +306,10 @@
 	vnode_t *realdvp = realvp(dvp);
 	struct loinfo *li = vtoli(dvp->v_vfsp);
 	int looping = 0;
+	int autoloop = 0;
 	int doingdotdot = 0;
 	int nosub = 0;
+	int mkflag = 0;
 
 	/*
 	 * If name is empty and no XATTR flags are set, then return
@@ -352,120 +354,99 @@
 	}
 
 	if (doingdotdot) {
-		if ((vtol(dvp))->lo_looping) {
+		if ((vtol(dvp))->lo_looping & LO_LOOPING) {
 			vfs_t *vfsp;
 
 			error = vn_vfswlock_wait(realdvp);
 			if (error)
 				goto out;
 			vfsp = vn_mountedvfs(realdvp);
-			if (vfsp != NULL) {
+			/*
+			 * In the standard case if the looping flag is set and
+			 * performing dotdot we would be returning from a
+			 * covered vnode, implying vfsp could not be null. The
+			 * exceptions being if we have looping and overlay
+			 * mounts or looping and covered file systems.
+			 */
+			if (vfsp == NULL) {
 				/*
-				 * if looping get the actual found vnode
-				 * instead of the vnode covered
-				 * Here we have to hold the lock for realdvp
-				 * since an unmount during the traversal to the
-				 * root vnode would turn *vfsp into garbage
-				 * which would be fatal.
+				 * Overlay mount or covered file system,
+				 * so just make the shadow node.
 				 */
-				vfs_lock_wait(vfsp);
 				vn_vfsunlock(realdvp);
-
-				error = VFS_ROOT(vfsp, &tvp);
+				*vpp = makelonode(vp, li, 0);
+				(vtol(*vpp))->lo_looping |= LO_LOOPING;
+				return (0);
+			}
+			/*
+			 * When looping get the actual found vnode
+			 * instead of the vnode covered.
+			 * Here we have to hold the lock for realdvp
+			 * since an unmount during the traversal to the
+			 * root vnode would turn *vfsp into garbage
+			 * which would be fatal.
+			 */
+			vfs_lock_wait(vfsp);
+			vn_vfsunlock(realdvp);
 
-				vfs_unlock(vfsp);
-				if (error)
-					goto out;
-				if ((tvp == li->li_rootvp)&&
-				    (vp == realvp(tvp))) {
-					/*
-					 * we're back at the real vnode
-					 * of the rootvp
-					 *
-					 * return the rootvp
-					 * Ex: /mnt/mnt/..
-					 * where / has been lofs-mounted
-					 * onto /mnt.  Return the lofs
-					 * node mounted at /mnt.
-					 */
-					*vpp = tvp;
-					VN_RELE(vp);
-					return (0);
-				} else {
-					/*
-					 * We are returning from a covered
-					 * node whose vfs_mountedhere is
-					 * not pointing to vfs of the current
-					 * root vnode.
-					 * This is a condn where in we
-					 * returned a covered node say Zc
-					 * but Zc is not the cover of current
-					 * root.
-					 * i.e.., if X is the root vnode
-					 * lookup(Zc,"..") is taking us to
-					 * X.
-					 * Ex: /net/X/net/X/net
-					 * We are encountering cover of net.
-					 * doing a dotdot from here means we
-					 * to take the lookup to the same state
-					 * that would have happened when we do
-					 * lookup of any Y under /net/X/net/X
-					 */
-					VN_RELE(tvp);
-					if (vp == realvp(li->li_rootvp)) {
-						VN_RELE(vp);
-						vp = li->li_rootvp;
-						vp = vp->v_vfsp->
-							vfs_vnodecovered;
-						VN_HOLD(vp);
-						*vpp = makelonode(vp, li);
-						(vtol(*vpp))->lo_looping = 1;
-						return (0);
-					}
-				}
+			error = VFS_ROOT(vfsp, &tvp);
+
+			vfs_unlock(vfsp);
+			if (error)
+				goto out;
+			if ((tvp == li->li_rootvp) && (vp == realvp(tvp))) {
+				/*
+				 * we're back at the real vnode
+				 * of the rootvp
+				 *
+				 * return the rootvp
+				 * Ex: /mnt/mnt/..
+				 * where / has been lofs-mounted
+				 * onto /mnt.  Return the lofs
+				 * node mounted at /mnt.
+				 */
+				*vpp = tvp;
+				VN_RELE(vp);
+				return (0);
 			} else {
 				/*
-				 * We are returning from a looping dvp.
-				 * If we are returning to rootvp return
-				 * the covered node with looping bit set.
-				 *
-				 * This means we are not returning from cover
-				 * but we should return to the root node by
-				 * giving the covered node with looping flag
-				 * set. We are returning from a non-covernode
-				 * with looping bit set means we couldn't stop
-				 * by giving the cover of root vnode.
+				 * We are returning from a covered
+				 * node whose vfs_mountedhere is
+				 * not pointing to vfs of the current
+				 * root vnode.
+				 * This is a condn where in we
+				 * returned a covered node say Zc
+				 * but Zc is not the cover of current
+				 * root.
+				 * i.e.., if X is the root vnode
+				 * lookup(Zc,"..") is taking us to
+				 * X.
+				 * Ex: /net/X/net/X/Y
 				 *
-				 *	Say X is the root vnode and lookup of
-				 * X again under X returns Xc(due to looping
-				 * condn). let Z=lookup(Xc,"path") and
-				 * if lookup(Z,"..") returns  the root vp X
-				 * return Xc with looping bit set or if a new
-				 * node Z.. is returned make a shadow with a
-				 * looping flag.
-				 *
-				 * Ex:- lookup of /net/X/net/X/Y/.. or
-				 * lookup of /net/X/net/X/Y/Z/.. .
-				 * In the first case we are returning to root
-				 * we will return the cover of root with
-				 * looping bit set.
+				 * If LO_AUTOLOOP (autofs/lofs looping detected)
+				 * has been set then we are encountering the
+				 * cover of Y (Y being any directory vnode
+				 * under /net/X/net/X/).
+				 * When performing a dotdot set the
+				 * returned vp to the vnode covered
+				 * by the mounted lofs, ie /net/X/net/X
 				 */
-				vn_vfsunlock(realdvp);
-				if (vp == li->li_rootvp) {
-					tvp = vp;
-					vp = (vp)->v_vfsp->vfs_vnodecovered;
-					VN_RELE(tvp);
+				VN_RELE(tvp);
+				if ((vtol(dvp))->lo_looping & LO_AUTOLOOP) {
+					VN_RELE(vp);
+					vp = li->li_rootvp;
+					vp = vp->v_vfsp->vfs_vnodecovered;
 					VN_HOLD(vp);
+					*vpp = makelonode(vp, li, 0);
+					(vtol(*vpp))->lo_looping |= LO_LOOPING;
+					return (0);
 				}
-				*vpp = makelonode(vp, li);
-				(vtol(*vpp))->lo_looping = 1;
-				return (0);
 			}
 		} else {
 			/*
 			 * No frills just make the shadow node.
 			 */
-			*vpp = makelonode(vp, li);
+			*vpp = makelonode(vp, li, 0);
 			return (0);
 		}
 	}
@@ -484,7 +465,7 @@
 	 * Make a lnode for the real vnode.
 	 */
 	if (vp->v_type != VDIR || nosub) {
-		*vpp = makelonode(vp, li);
+		*vpp = makelonode(vp, li, 0);
 		if (IS_DEVVP(*vpp)) {
 			vnode_t *svp;
 
@@ -514,7 +495,7 @@
 		 * or indirectly, return the covered node.
 		 */
 
-		if (!(vtol(dvp))->lo_looping) {
+		if (!((vtol(dvp))->lo_looping & LO_LOOPING)) {
 			if (vp == li->li_rootvp) {
 				/*
 				 * Direct looping condn.
@@ -590,9 +571,15 @@
 			 * We come here only when we are called with X_l as dvp
 			 * and look for something underneath.
 			 *
-			 * We need to find out if the vnode, which vp is
-			 * shadowing, is the rootvp of the autofs.
-			 *
+			 * Now that an autofs/lofs looping condition has been
+			 * identified any directory vnode contained within
+			 * dvp will be set to the vnode covered by the
+			 * mounted autofs. Thus all directories within dvp
+			 * will appear empty hence teminating the looping.
+			 * The LO_AUTOLOOP flag is set on the returned lonode
+			 * to indicate the termination of the autofs/lofs
+			 * looping. This is required for the correct behaviour
+			 * when performing a dotdot.
 			 */
 			realdvp = realvp(dvp);
 			while (vfs_matchops(realdvp->v_vfsp, lo_vfsops)) {
@@ -604,34 +591,39 @@
 				goto out;
 			/*
 			 * tvp now contains the rootvp of the vfs of the
-			 * real vnode of dvp
+			 * real vnode of dvp. The directory vnode vp is set
+			 * to the covered vnode to terminate looping. No
+			 * distinction is made between any vp as all directory
+			 * vnodes contained in dvp are returned as the covered
+			 * vnode.
 			 */
+			VN_RELE(vp);
+			vp = tvp;	/* this is an autonode */
 
-			if (realvp(dvp)->v_vfsp == realvp(vp)->v_vfsp &&
-			    tvp == realvp(vp)) {
-				/*
-				 * vp is the shadow of "net",
-				 * the rootvp of autofs
-				 */
-				VN_RELE(vp);
-				vp = tvp;	/* this is an autonode */
-
-				/*
-				 * Need to find the covered vnode
-				 */
-				vp = vp->v_vfsp->vfs_vnodecovered;
-				ASSERT(vp);
-				VN_HOLD(vp);
-				VN_RELE(tvp);
-			} else {
-				VN_RELE(tvp);
-			}
+			/*
+			 * Need to find the covered vnode
+			 */
+			vp = vp->v_vfsp->vfs_vnodecovered;
+			ASSERT(vp);
+			VN_HOLD(vp);
+			VN_RELE(tvp);
+			/*
+			 * Force the creation of a new lnode even if the hash
+			 * table contains a lnode that references this vnode.
+			 */
+			mkflag = LOF_FORCE;
+			autoloop++;
 		}
 	}
-	*vpp = makelonode(vp, li);
+	*vpp = makelonode(vp, li, mkflag);
 
-	if ((looping) || ((vtol(dvp))->lo_looping && !doingdotdot)) {
-		(vtol(*vpp))->lo_looping = 1;
+	if ((looping) ||
+	    (((vtol(dvp))->lo_looping & LO_LOOPING) && !doingdotdot)) {
+		(vtol(*vpp))->lo_looping |= LO_LOOPING;
+	}
+
+	if (autoloop) {
+		(vtol(*vpp))->lo_looping |= LO_AUTOLOOP;
 	}
 
 out:
@@ -680,7 +672,7 @@
 
 	error = VOP_CREATE(realvp(dvp), nm, va, exclusive, mode, &vp, cr, flag);
 	if (!error) {
-		*vpp = makelonode(vp, vtoli(dvp->v_vfsp));
+		*vpp = makelonode(vp, vtoli(dvp->v_vfsp), 0);
 		if (IS_DEVVP(*vpp)) {
 			vnode_t *svp;
 
@@ -806,7 +798,7 @@
 		return (EACCES);
 	error = VOP_MKDIR(realvp(dvp), nm, va, vpp, cr);
 	if (!error)
-		*vpp = makelonode(*vpp, vtoli(dvp->v_vfsp));
+		*vpp = makelonode(*vpp, vtoli(dvp->v_vfsp), 0);
 	return (error);
 }
 
--- a/usr/src/uts/common/sys/fs/lofs_node.h	Mon Aug 08 23:54:21 2005 -0700
+++ b/usr/src/uts/common/sys/fs/lofs_node.h	Tue Aug 09 09:24:51 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1989-1991, 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -47,11 +47,22 @@
 typedef struct lnode {
 	struct lnode	*lo_next;	/* link for hash chain */
 	struct vnode	*lo_vp;		/* pointer to real vnode */
-	uint_t		lo_looping; 	/* detect looping */
+	uint_t		lo_looping; 	/* looping flags (see below) */
 	struct vnode	*lo_vnode;	/* place holder vnode for file */
 } lnode_t;
 
 /*
+ * Flags used when looping has been detected.
+ */
+#define	LO_LOOPING	0x01	/* Looping detected */
+#define	LO_AUTOLOOP	0x02	/* Autonode looping detected */
+
+/*
+ * Flag passed to makelonode().
+ */
+#define	LOF_FORCE	0x1	/* Force creation of new lnode */
+
+/*
  * Convert between vnode and lnode
  */
 #define	ltov(lp)	(((lp)->lo_vnode))
@@ -59,7 +70,7 @@
 #define	realvp(vp)	(vtol(vp)->lo_vp)
 
 #ifdef _KERNEL
-extern vnode_t *makelonode(vnode_t *, struct loinfo *);
+extern vnode_t *makelonode(vnode_t *, struct loinfo *, int);
 extern void freelonode(lnode_t *);
 
 extern struct vnode kvp;