Subject: Re: Unionfs write problem with RO bottom layer.
To: None <tech-kern@netbsd.org>
From: Thor Lancelot Simon <tls@rek.tjls.com>
List: tech-kern
Date: 11/27/2007 21:27:14
On Tue, Nov 27, 2007 at 08:08:49PM -0500, Thor Lancelot Simon wrote:
> 
> I considered adding an new "docopy" value to union_copyup() to cause it
> to hold the vnode locked and remove the new upper-layer node after
> confirming that it can create it.  Unfortunately this would, I think,
> cause real problems with NFS as an upper layer.

I tried this (see below).  It doesn't work, with a locking-against-myself
panic down in FFS from the VOP_LOOKUP called by relookup().  It's not uvp
that it's trying to lock, nor un_dirvp, so I'm a bit confused what in
fact is doubly-locked here -- but if I remove the relookup, I get a
different panic down in the FFS code, so clearly the relookup is needed.

I don't know if this _should_ work, but certainly it doesn't.  Advice
would be much appreciated.  The docopy == 2 code is lifted from one
of the other union_subr functions, union_vn_create I believe.

Index: union_subr.c
===================================================================
RCS file: /workspaces/cvsroot/nbsrc/sys/fs/union/union_subr.c,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 union_subr.c
--- union_subr.c	18 Oct 2007 20:25:20 -0000	1.1.1.2
+++ union_subr.c	28 Nov 2007 02:13:45 -0000
@@ -724,7 +724,7 @@
 
 	lvp = un->un_lowervp;
 
-	if (docopy) {
+	if (docopy == 1) {
 		/*
 		 * XX - should not ignore errors
 		 * from VOP_CLOSE
@@ -753,7 +753,47 @@
 
 	}
 	union_vn_close(uvp, FWRITE, cred, l);
-
+	
+	if (docopy == 2)
+	{
+		struct componentname cn;
+		/*
+		 * Build a new componentname structure (for the same
+		 * reasons outlines in union_mkshadow).
+		 * The difference here is that the file is owned by
+		 * the current user, rather than by the person who
+		 * did the mount, since the current user needs to be
+		 * able to write the file (that's why it is being
+		 * copied in the first place).
+		 */
+		cn.cn_namelen = strlen(un->un_path);
+		if ((cn.cn_namelen + 1) > MAXPATHLEN)
+			return (ENAMETOOLONG);
+		cn.cn_pnbuf = PNBUF_GET();
+		memcpy(cn.cn_pnbuf, un->un_path, cn.cn_namelen+1);
+		cn.cn_nameiop = DELETE;
+		cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|ISLASTCN);
+		cn.cn_lwp = l;
+		cn.cn_cred = l->l_cred;
+		cn.cn_nameptr = cn.cn_pnbuf;
+		cn.cn_hash = un->un_hash;
+		cn.cn_consume = 0;
+
+		vn_lock(un->un_dirvp, LK_EXCLUSIVE | LK_RETRY);
+		error = relookup(un->un_dirvp, &uvp, &cn);
+		if (error) {
+			VOP_UNLOCK(un->un_dirvp, 0);
+			panic("relookup failed in copyup called by access");
+		}
+		error = VOP_REMOVE(un->un_dirvp, uvp, &cn);
+		if (error)
+			panic("Remove of upper layer turd failed");
+		else {
+			union_removed_upper(un);
+			return 0;
+		}
+	}
+	
 	/*
 	 * Subsequent IOs will go to the top layer, so
 	 * call close on the lower vnode and open on the
Index: union_vnops.c
===================================================================
RCS file: /workspaces/cvsroot/nbsrc/sys/fs/union/union_vnops.c,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 union_vnops.c
--- union_vnops.c	18 Oct 2007 20:25:21 -0000	1.1.1.2
+++ union_vnops.c	28 Nov 2007 01:12:09 -0000
@@ -755,7 +755,8 @@
 		case VDIR:
 		case VLNK:
 		case VREG:
-			if (vp->v_mount->mnt_flag & MNT_RDONLY)
+			if (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY)
+			/* if (vp->v_mount->mnt_flag & MNT_RDONLY) */
 				return (EROFS);
 			break;
 		case VBAD:
@@ -778,6 +779,14 @@
 
 	if ((vp = un->un_lowervp) != NULLVP) {
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+		if (ap->a_mode & VWRITE) {
+			error = union_copyup(un, 2, ap->a_cred, ap->a_l);
+			VOP_UNLOCK(vp, 0);
+			return (error);
+		}
+					
+				
 		ap->a_vp = vp;
 		error = VCALL(vp, VOFFSET(vop_access), ap);
 		if (error == 0) {