Module Name:    src
Committed By:   hannken
Date:           Wed Feb 20 10:05:20 UTC 2019

Modified Files:
        src/sys/kern: vfs_syscalls.c
        src/sys/nfs: nfs_serv.c

Log Message:
Bracket do_sys_renameat() and nfsrv_rename() with fstrans.

The v_mount field for vnodes on the same file system as "from"
is now stable for referenced vnodes.

VFS_RENAMELOCK no longer may use lock from an unreferenced and
freed "struct mount".


To generate a diff of this commit:
cvs rdiff -u -r1.525 -r1.526 src/sys/kern/vfs_syscalls.c
cvs rdiff -u -r1.176 -r1.177 src/sys/nfs/nfs_serv.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/kern/vfs_syscalls.c
diff -u src/sys/kern/vfs_syscalls.c:1.525 src/sys/kern/vfs_syscalls.c:1.526
--- src/sys/kern/vfs_syscalls.c:1.525	Tue Feb 19 06:55:28 2019
+++ src/sys/kern/vfs_syscalls.c	Wed Feb 20 10:05:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_syscalls.c,v 1.525 2019/02/19 06:55:28 mlelstv Exp $	*/
+/*	$NetBSD: vfs_syscalls.c,v 1.526 2019/02/20 10:05:20 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.525 2019/02/19 06:55:28 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.526 2019/02/20 10:05:20 hannken Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_fileassoc.h"
@@ -4224,9 +4224,18 @@ do_sys_renameat(struct lwp *l, int fromf
 	 */
 	fdvp = fnd.ni_dvp;
 	fvp = fnd.ni_vp;
+	mp = fdvp->v_mount;
 	KASSERT(fdvp != NULL);
 	KASSERT(fvp != NULL);
 	KASSERT((fdvp == fvp) || (VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE));
+	/*
+	 * Bracket the operation with fstrans_start()/fstrans_done().
+	 *
+	 * Inside the bracket this file system cannot be unmounted so
+	 * a vnode on this file system cannot change its v_mount.
+	 * A vnode on another file system may still change to dead mount.
+	 */
+	fstrans_start(mp);
 
 	/*
 	 * Make sure neither fdvp nor fvp is locked.
@@ -4311,38 +4320,16 @@ do_sys_renameat(struct lwp *l, int fromf
 	}
 
 	/*
-	 * Get the mount point.  If the file system has been unmounted,
-	 * which it may be because we're not holding any vnode locks,
-	 * then v_mount will be NULL.  We're not really supposed to
-	 * read v_mount without holding the vnode lock, but since we
-	 * have fdvp referenced, if fdvp->v_mount changes then at worst
-	 * it will be set to NULL, not changed to another mount point.
-	 * And, of course, since it is up to the file system to
-	 * determine the real lock order, we can't lock both fdvp and
-	 * tdvp at the same time.
-	 */
-	mp = fdvp->v_mount;
-	if (mp == NULL) {
-		error = ENOENT;
-		goto abort1;
-	}
-
-	/*
-	 * Make sure the mount points match.  Again, although we don't
-	 * hold any vnode locks, the v_mount fields may change -- but
-	 * at worst they will change to NULL, so this will never become
-	 * a cross-device rename, because we hold vnode references.
+	 * Make sure the mount points match.  Although we don't hold
+	 * any vnode locks, the v_mount on fdvp file system are stable.
 	 *
-	 * XXX Because nothing is locked and the compiler may reorder
-	 * things here, unmounting the file system at an inopportune
-	 * moment may cause rename to fail with EXDEV when it really
-	 * should fail with ENOENT.
+	 * Unmounting another file system at an inopportune moment may
+	 * cause tdvp to disappear and change its v_mount to dead.
+	 *
+	 * So in either case different v_mount means cross-device rename.
 	 */
+	KASSERT(mp != NULL);
 	tmp = tdvp->v_mount;
-	if (tmp == NULL) {
-		error = ENOENT;
-		goto abort1;
-	}
 
 	if (mp != tmp) {
 		error = EXDEV;
@@ -4497,6 +4484,7 @@ do_sys_renameat(struct lwp *l, int fromf
 	 * destroy the pathbufs.
 	 */
 	VFS_RENAMELOCK_EXIT(mp);
+	fstrans_done(mp);
 	goto out2;
 
 abort3:	if ((tvp != NULL) && (tvp != tdvp))
@@ -4510,6 +4498,7 @@ abort1:	VOP_ABORTOP(tdvp, &tnd.ni_cnd);
 abort0:	VOP_ABORTOP(fdvp, &fnd.ni_cnd);
 	vrele(fdvp);
 	vrele(fvp);
+	fstrans_done(mp);
 out2:	pathbuf_destroy(tpb);
 out1:	pathbuf_destroy(fpb);
 out0:	return error;

Index: src/sys/nfs/nfs_serv.c
diff -u src/sys/nfs/nfs_serv.c:1.176 src/sys/nfs/nfs_serv.c:1.177
--- src/sys/nfs/nfs_serv.c:1.176	Sun Feb  3 03:19:28 2019
+++ src/sys/nfs/nfs_serv.c	Wed Feb 20 10:05:20 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_serv.c,v 1.176 2019/02/03 03:19:28 mrg Exp $	*/
+/*	$NetBSD: nfs_serv.c,v 1.177 2019/02/20 10:05:20 hannken Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -55,7 +55,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_serv.c,v 1.176 2019/02/03 03:19:28 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_serv.c,v 1.177 2019/02/20 10:05:20 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -64,6 +64,7 @@ __KERNEL_RCSID(0, "$NetBSD: nfs_serv.c,v
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/mount.h>
+#include <sys/fstrans.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/mbuf.h>
@@ -1957,12 +1958,13 @@ nfsrv_rename(struct nfsrv_descript *nfsd
 		}
 		return (0);
 	}
+	localfs = fromnd.ni_dvp->v_mount;
+	fstrans_start(localfs);
 	if (fromnd.ni_dvp != fromnd.ni_vp) {
 		VOP_UNLOCK(fromnd.ni_dvp);
 	}
 	fvp = fromnd.ni_vp;
 
-	localfs = fvp->v_mount;
 	error = VFS_RENAMELOCK_ENTER(localfs);
 	if (error) {
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
@@ -2130,6 +2132,7 @@ out1:
 	pathbuf_destroy(fromnd.ni_pathbuf);
 	fromnd.ni_pathbuf = NULL;
 	fromnd.ni_cnd.cn_nameiop = 0;
+	fstrans_done(localfs);
 	localfs = NULL;
 	nfsm_reply(2 * NFSX_WCCDATA(v3));
 	if (v3) {
@@ -2153,6 +2156,7 @@ nfsmout:
 	}
 	if (localfs) {
 		VFS_RENAMELOCK_EXIT(localfs);
+		fstrans_done(localfs);
 	}
 	if (fromnd.ni_cnd.cn_nameiop) {
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);

Reply via email to