git: d088dc76e1a6 - main - Fix NFS exports of FUSE file systems for big directories

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Fri, 04 Feb 2022 23:31:41 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=d088dc76e1a62ecb6c05bd2b14ee48a9f9a7e2bd

commit d088dc76e1a62ecb6c05bd2b14ee48a9f9a7e2bd
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2022-01-02 17:18:47 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2022-02-04 23:30:49 +0000

    Fix NFS exports of FUSE file systems for big directories
    
    The FUSE protocol does not require that a directory entry's d_off field
    outlive the lifetime of its directory's file handle.  Since the NFS
    server must reopen the directory on every VOP_READDIR call, that means
    it can't pass uio->uio_offset down to the FUSE server.  Instead, it must
    read the directory from 0 each time.  It may need to issue multiple
    FUSE_READDIR operations until it finds the d_off field that it's looking
    for.  That was the intention behind SVN r348209 and r297887, but a logic
    bug prevented subsequent FUSE_READDIR operations from ever being issued,
    rendering large directories incompletely browseable.
    
    MFC after:      3 weeks
    Reviewed by:    rmacklem
---
 sys/fs/fuse/fuse_internal.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
index 273b4f5b8522..eb8f1f87d90f 100644
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -586,11 +586,7 @@ fuse_internal_readdir(struct vnode *vp,
 		fnd_start = 1;
 	while (uio_resid(uio) > 0) {
 		fdi.iosize = sizeof(*fri);
-		if (fri == NULL)
-			fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
-		else
-			fdisp_refresh_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
-
+		fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
 		fri = fdi.indata;
 		fri->fh = fufh->fh_id;
 		fri->offset = uio_offset(uio);
@@ -628,6 +624,8 @@ fuse_internal_readdir_processdata(struct uio *uio,
 	int err = 0;
 	int oreclen;
 	size_t freclen;
+	int ents_copied = 0;
+	int ents_seen = 0;
 
 	struct dirent *de;
 	struct fuse_dirent *fudge;
@@ -638,7 +636,7 @@ fuse_internal_readdir_processdata(struct uio *uio,
 		return -1;
 	for (;;) {
 		if (bufsize < FUSE_NAME_OFFSET) {
-			err = -1;
+			err = (ents_seen == 0 || ents_copied > 0) ?  -1 : 0;
 			break;
 		}
 		fudge = (struct fuse_dirent *)buf;
@@ -649,7 +647,7 @@ fuse_internal_readdir_processdata(struct uio *uio,
 			 * This indicates a partial directory entry at the
 			 * end of the directory data.
 			 */
-			err = -1;
+			err = (ents_seen == 0 || ents_copied > 0) ?  -1 : 0;
 			break;
 		}
 #ifdef ZERO_PAD_INCOMPLETE_BUFS
@@ -671,6 +669,7 @@ fuse_internal_readdir_processdata(struct uio *uio,
 			err = -1;
 			break;
 		}
+		ents_seen++;
 		/*
 		 * Don't start to copy the directory entries out until
 		 * the requested offset in the directory is found.
@@ -702,6 +701,7 @@ fuse_internal_readdir_processdata(struct uio *uio,
 				cookies++;
 				(*ncookies)--;
 			}
+			ents_copied++;
 		} else if (startoff == fudge->off)
 			*fnd_start = 1;
 		buf = (char *)buf + freclen;