Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 Oct 2019 00:19:45 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r353710 - projects/nfsv42/sys/fs/nfsserver
Message-ID:  <201910180019.x9I0Jj92065543@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Fri Oct 18 00:19:45 2019
New Revision: 353710
URL: https://svnweb.freebsd.org/changeset/base/353710

Log:
  Add vfs.nfsd.checkcopysize to enable a check for Copy past input file EOF.
  
  RFC-7862 requires that a NFSv4.2 server reply NFSERR_INVAL when the
  offset of the input file plus length exceeds the file's size.
  However, the Linux NFSv4.2 client likes to do this and will break when
  this check is done.
  As such, this patch adds the check, but it only enabled when
  vfs.nfsd.checkcopysize is set non-zero.
  The patch also adds a needed check for nd->nd_repstat == 0 before calling
  vn_copy_file_range().
  Found during interoperability testing with Linux.

Modified:
  projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c

Modified: projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c	Fri Oct 18 00:00:17 2019	(r353709)
+++ projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c	Fri Oct 18 00:19:45 2019	(r353710)
@@ -80,6 +80,10 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLF
 static int	nfsrv_linuxseekdata = 1;
 SYSCTL_INT(_vfs_nfsd, OID_AUTO, linuxseekdata, CTLFLAG_RW,
     &nfsrv_linuxseekdata, 0, "Return EINVAL for SEEK_DATA at EOF");
+static int	nfsrv_checkcopysize = 0;
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, checkcopysize, CTLFLAG_RW,
+    &nfsrv_checkcopysize, 0,
+    "Enable check for Copy inoff + len > file_size");
 
 /*
  * This list defines the GSS mechanisms supported.
@@ -5340,24 +5344,39 @@ nfsrvd_copy_file_range(struct nfsrv_descript *nd, __un
 			vn_rangelock_unlock(vp, rl_rcookie);
 		}
 
-		/*
-		 * If len == 0, set it based on invp's size.  Since it is range
-		 * locked, it should not change in size.
-		 */
-		if (len == 0) {
-			error = NFSVOPLOCK(vp, LK_SHARED);
-			if (error == 0) {
-				ret = nfsvno_getattr(vp, &at, nd, curthread, 1,
-				    NULL);
-				/* If offset past EOF, just leave len == 0. */
-				if (ret == 0 && at.na_size > inoff)
+		error = NFSVOPLOCK(vp, LK_SHARED);
+		if (error == 0) {
+			ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
+			if (ret == 0) {
+				/*
+				 * Since invp is range locked, na_size should
+				 * not change.
+				 */
+				if (len == 0 && at.na_size > inoff) {
+					/*
+					 * If len == 0, set it based on invp's 
+					 * size. If offset is past EOF, just
+					 * leave len == 0.
+					 */
 					len = at.na_size - inoff;
-				NFSVOPUNLOCK(vp, 0);
-				if (ret != 0 && nd->nd_repstat == 0)
-					nd->nd_repstat = ret;
-			} else if (nd->nd_repstat == 0)
-				nd->nd_repstat = error;
-		}
+				} else if (nfsrv_checkcopysize != 0 &&
+				    inoff + len > at.na_size) {
+					/*
+					 * RFC-7862 says that NFSERR_INVAL must
+					 * be returned when inoff + len exceeds
+					 * the file size, however the NFSv4.2
+					 * Linux client likes to do this, so
+					 * only check if nfsrv_checkcopysize
+					 * is set.
+					 */
+					nd->nd_repstat = NFSERR_INVAL;
+				}
+			}
+			NFSVOPUNLOCK(vp, 0);
+			if (ret != 0 && nd->nd_repstat == 0)
+				nd->nd_repstat = ret;
+		} else if (nd->nd_repstat == 0)
+			nd->nd_repstat = error;
 	}
 
 	/*
@@ -5369,10 +5388,12 @@ nfsrvd_copy_file_range(struct nfsrv_descript *nd, __un
 		xfer = nfs_maxcopyrange;
 	else
 		xfer = len;
-	nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff, &xfer, 0,
-	    nd->nd_cred, nd->nd_cred, NULL);
-	if (nd->nd_repstat == 0)
-		len = xfer;
+	if (nd->nd_repstat == 0) {
+		nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
+		    &xfer, 0, nd->nd_cred, nd->nd_cred, NULL);
+		if (nd->nd_repstat == 0)
+			len = xfer;
+	}
 
 	/* Unlock the ranges. */
 	if (rl_rcookie != NULL)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201910180019.x9I0Jj92065543>