Module Name:    src
Committed By:   hannken
Date:           Wed Aug 10 06:27:02 UTC 2011

Modified Files:
        src/sys/fs/union: union_vnops.c
        src/tests/fs/union: t_pr.c

Log Message:
For devices, sockets and fifos ignore setting the file size to zero to make
open(..., O_TRUNC) happy and allow them to write through the lower layer.

Fixes PR #43560 (writing to null device in unionfs fails)


To generate a diff of this commit:
cvs rdiff -u -r1.42 -r1.43 src/sys/fs/union/union_vnops.c
cvs rdiff -u -r1.7 -r1.8 src/tests/fs/union/t_pr.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/fs/union/union_vnops.c
diff -u src/sys/fs/union/union_vnops.c:1.42 src/sys/fs/union/union_vnops.c:1.43
--- src/sys/fs/union/union_vnops.c:1.42	Sun Aug  7 06:01:51 2011
+++ src/sys/fs/union/union_vnops.c	Wed Aug 10 06:27:02 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_vnops.c,v 1.42 2011/08/07 06:01:51 hannken Exp $	*/
+/*	$NetBSD: union_vnops.c,v 1.43 2011/08/10 06:27:02 hannken Exp $	*/
 
 /*
  * Copyright (c) 1992, 1993, 1994, 1995
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.42 2011/08/07 06:01:51 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.43 2011/08/10 06:27:02 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -888,12 +888,14 @@
 	struct vattr *vap = ap->a_vap;
 	struct vnode *vp = ap->a_vp;
 	struct union_node *un = VTOUNION(vp);
+	bool size_only;		/* All but va_size are VNOVAL. */
 	int error;
 
-  	if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
-	    vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
-	    vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
-	    (vp->v_mount->mnt_flag & MNT_RDONLY))
+	size_only = (vap->va_flags == VNOVAL && vap->va_uid == (uid_t)VNOVAL &&
+	    vap->va_gid == (gid_t)VNOVAL && vap->va_atime.tv_sec == VNOVAL &&
+	    vap->va_mtime.tv_sec == VNOVAL && vap->va_mode == (mode_t)VNOVAL);
+
+	if (!size_only && (vp->v_mount->mnt_flag & MNT_RDONLY))
 		return (EROFS);
 	if (vap->va_size != VNOVAL) {
  		switch (vp->v_type) {
@@ -931,8 +933,9 @@
 	}
 
 	/*
-	 * Try to set attributes in upper layer,
-	 * otherwise return read-only filesystem error.
+	 * Try to set attributes in upper layer, ignore size change to zero
+	 * for devices to handle O_TRUNC and return read-only filesystem error
+	 * otherwise.
 	 */
 	if (un->un_uppervp != NULLVP) {
 		FIXUP(un);
@@ -940,7 +943,22 @@
 		if ((error == 0) && (vap->va_size != VNOVAL))
 			union_newsize(ap->a_vp, vap->va_size, VNOVAL);
 	} else {
-		error = EROFS;
+		KASSERT(un->un_lowervp != NULLVP);
+		switch (un->un_lowervp->v_type) {
+ 		case VCHR:
+ 		case VBLK:
+ 		case VSOCK:
+ 		case VFIFO:
+			if (size_only &&
+			    (vap->va_size == 0 || vap->va_size == VNOVAL))
+				error = 0;
+			else
+				error = EROFS;
+			break;
+		default:
+			error = EROFS;
+			break;
+		}
 	}
 
 	return (error);
@@ -1003,8 +1021,23 @@
 	struct union_node *un = VTOUNION(ap->a_vp);
 
 	vp = UPPERVP(ap->a_vp);
-	if (vp == NULLVP)
-		panic("union: missing upper layer in write");
+	if (vp == NULLVP) {
+		vp = LOWERVP(ap->a_vp);
+		KASSERT(vp != NULL);
+		switch (vp->v_type) {
+		case VBLK:
+		case VCHR:
+		case VSOCK:
+		case VFIFO:
+			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+			error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag,
+			    ap->a_cred);
+			VOP_UNLOCK(vp);
+			return error;
+		default:
+			panic("union: missing upper layer in write");
+		}
+	}
 
 	FIXUP(un);
 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);

Index: src/tests/fs/union/t_pr.c
diff -u src/tests/fs/union/t_pr.c:1.7 src/tests/fs/union/t_pr.c:1.8
--- src/tests/fs/union/t_pr.c:1.7	Sat Jul  3 13:37:22 2010
+++ src/tests/fs/union/t_pr.c	Wed Aug 10 06:27:02 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_pr.c,v 1.7 2010/07/03 13:37:22 pooka Exp $	*/
+/*	$NetBSD: t_pr.c,v 1.8 2011/08/10 06:27:02 hannken Exp $	*/
 
 #include <sys/types.h>
 #include <sys/mount.h>
@@ -50,7 +50,6 @@
 	unionargs.target = __UNCONST("/Tunion2/B");
 	unionargs.mntflags = UNMNT_BELOW;
 
-	/* atf_tc_expect_signal(-1, "PR kern/23986"); */
 	rump_sys_mount(MOUNT_UNION, "/Tunion", 0,&unionargs,sizeof(unionargs));
 }
 
@@ -64,7 +63,7 @@
 ATF_TC_BODY(devnull1, tc)
 {
 	struct union_args unionargs;
-	int fd;
+	int fd, res;
 
 	rump_init();
 
@@ -80,14 +79,12 @@
 
 	fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_TRUNC);
 
-	atf_tc_expect_fail("PR kern/43560");
-	if (fd == -1 && errno == EROFS) {
-		atf_tc_fail("open returned EROFS");
-	} else if (fd == -1) {
-		atf_tc_expect_pass();
-		atf_tc_fail_errno("open fail");
-	}
+	if (fd == -1)
+		atf_tc_fail_errno("open");
 
+	res = rump_sys_write(fd, &fd, sizeof(fd));
+	if (res != sizeof(fd))
+		atf_tc_fail("write");
 }
 
 ATF_TC(devnull2);
@@ -100,7 +97,7 @@
 ATF_TC_BODY(devnull2, tc)
 {
 	struct union_args unionargs;
-	int fd;
+	int fd, res;
 
 	rump_init();
 
@@ -118,8 +115,9 @@
 	if (fd == -1)
 		atf_tc_fail_errno("open");
 
-	atf_tc_expect_signal(-1, "PR kern/43560");
-	rump_sys_write(fd, &fd, sizeof(fd));
+	res = rump_sys_write(fd, &fd, sizeof(fd));
+	if (res != sizeof(fd))
+		atf_tc_fail("write");
 }
 
 ATF_TP_ADD_TCS(tp)

Reply via email to