git: d088dc76e1a6 - main - Fix NFS exports of FUSE file systems for big directories
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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;