Bug 62278 - [nfs] [patch] NFS server may not set eof flag when reading last chunk of file
Summary: [nfs] [patch] NFS server may not set eof flag when reading last chunk of file
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
Depends on:
Reported: 2004-02-02 18:50 UTC by Xiaolin Zang
Modified: 2018-05-20 23:51 UTC (History)
0 users

See Also:


Note You need to log in before you can comment on or make changes to this bug.
Description Xiaolin Zang 2004-02-02 18:50:18 UTC
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;  /*
 			*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).


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 @@
 	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;
 			*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.
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
Comment 1 iedowse freebsd_committer 2004-02-03 02:29:36 UTC
State Changed
From-To: open->analyzed

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). 

Comment 2 iedowse freebsd_committer 2004-02-03 02:29:36 UTC
Responsible Changed
From-To: freebsd-bugs->iedowse

I'll take this as a reminder to look at it.
Comment 3 Mark Linimon freebsd_committer freebsd_triage 2007-07-31 15:55:02 UTC
Responsible Changed
From-To: iedowse->freebsd-bugs

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.
Comment 4 Eitan Adler freebsd_committer freebsd_triage 2018-05-20 23:51:22 UTC
For bugs matching the following conditions:
- Status == In Progress
- Assignee == "bugs@FreeBSD.org"
- Last Modified Year <= 2017

- Set Status to "Open"