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
This commit is contained in:
Alan Somers 2022-01-02 10:18:47 -07:00
parent c454c57163
commit d088dc76e1

View file

@ -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;