From nobody@FreeBSD.org  Mon Feb  2 10:41:04 2004
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 01D0C16A4D0
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  2 Feb 2004 10:41:04 -0800 (PST)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 5AF1343D48
	for <freebsd-gnats-submit@FreeBSD.org>; Mon,  2 Feb 2004 10:40:52 -0800 (PST)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.12.10/8.12.10) with ESMTP id i12IdtdL060647
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 2 Feb 2004 10:39:55 -0800 (PST)
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.12.10/8.12.10/Submit) id i12Idtgp060646;
	Mon, 2 Feb 2004 10:39:55 -0800 (PST)
	(envelope-from nobody)
Message-Id: <200402021839.i12Idtgp060646@www.freebsd.org>
Date: Mon, 2 Feb 2004 10:39:55 -0800 (PST)
From: Xiaolin Zang <czang@panasas.FreeBSD.ORG>
To: freebsd-gnats-submit@FreeBSD.org
Subject: NFS server may not set eof flag when reading last chunk of file
X-Send-Pr-Version: www-2.0

>Number:         62278
>Category:       kern
>Synopsis:       [nfs] [patch] NFS server may not set eof flag when reading last chunk of file
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          analyzed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Feb 02 10:50:18 PST 2004
>Closed-Date:    
>Last-Modified:  Tue Jul 31 14:55:50 GMT 2007
>Originator:     Xiaolin Zang
>Release:        4.6.2
>Organization:
Panasas, Inc
>Environment:
FreeBSD rack-bsd14 4.6.2-RELEASE FreeBSD 4.6.2-RELEASE #0: Wed Aug 14 21:23:26 GMT 2002     murray@builder.freebsdmall.com:/usr/src/sys/compile/GENERIC  i386

>Description:
According to RCF 1813 (NFS v3 spec), when the read has reached the end
of the file and it should set the eof flag to true in the reply.
However in freebsd 4.6.2 (may be the newer versions, too) the eof flag
is not set when it should be in the following cases.

Suppose the number of bytes requested by the client is N and (N mod 4) == 0.
And the read is getting the last chunk of the file and the size of
this chunk is M.  (N - M) is 1, 2 or 3.

The cause is the following lines near the end of the nfsrv_read function.

 	if (v3) {
 		*tl++ = txdr_unsigned(cnt);
		if (len < reqlen)   /*
 			*tl++ = nfs_true;  /*
 		else
 			*tl++ = nfs_false;

Note variable len is the round-up (to a 4-byte word) of the actually
number of bytes read (in variable cnt).


>How-To-Repeat:
This was found when using RH-8.0 2.4.20-19.8 as the NFS client against
a FreeBsd NFS server since that RH Linux uses the eof flag to check
the correctness of a read when fewer bytes are read than requested.
The RH NFS client always asks for chunks of 4096 bytes.  Thus reading
(cat, cp, etc) any file of size (N * 4096 - n) where N > 0 and n is 1,
2 or 3 yields input/output error.  But the same operation will succeed
if repeated immediately, probably because the chunks are in the cache
already.
>Fix:
The following change should fix the problem.

==== freebsd/src/sys/nfs/nfs_serv.c#3 - freebsd/src/sys/nfs/nfs_serv.c ====
@@ -785,7 +785,7 @@
 	struct uio io, *uiop = &io;
 	struct vattr va, *vap = &va;
 	struct nfsheur *nh;
-	off_t off;
+	off_t off, initial_off;
 	int ioflag = 0;
 	u_quad_t frev;
 
@@ -794,10 +794,10 @@
 	nfsm_srvmtofh(fhp);
 	if (v3) {
 		nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-		off = fxdr_hyper(tl);
+		initial_off = off = fxdr_hyper(tl);
 	} else {
 		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-		off = (off_t)fxdr_unsigned(u_int32_t, *tl);
+		initial_off = off = (off_t)fxdr_unsigned(u_int32_t, *tl);
 	}
 	nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd));
 
@@ -991,7 +991,7 @@
 		nfsm_adj(mb, len - tlen, tlen - cnt);
 	if (v3) {
 		*tl++ = txdr_unsigned(cnt);
-		if (len < reqlen)
+		if (initial_off + cnt >= vap->va_size)
 			*tl++ = nfs_true;
 		else
 			*tl++ = nfs_false;

By the way I have made a robot that can read the image and enter it in 
the little yellow space -- just kidding.
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->analyzed 
State-Changed-By: iedowse 
State-Changed-When: Mon Feb 2 18:29:36 PST 2004 
State-Changed-Why:  

This has already been fixed in FreeBSD 5.2 and FreeBSD 4.9, but I 
think your patch possibly results in more correct behaviour than 
the current code (your patch returns eof=true when the request ends 
exactly at the end of the file, whereas the current code returns 
false but the next read returns 0 bytes with eof=true). 


Responsible-Changed-From-To: freebsd-bugs->iedowse 
Responsible-Changed-By: iedowse 
Responsible-Changed-When: Mon Feb 2 18:29:36 PST 2004 
Responsible-Changed-Why:  

I'll take this as a reminder to look at it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=62278 
Responsible-Changed-From-To: iedowse->freebsd-bugs 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Tue Jul 31 14:55:02 UTC 2007 
Responsible-Changed-Why:  
With permission, reassign from committer who is away from FreeBSD work for now. 

From the audit trail, it sounds as though the problem may have been fixed 
another way by now. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=62278 
>Unformatted:
