5034470 Fix for bug 4126922 is incomplete, still lofs stack overflow panic possible
--- 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;