Documentation for functions such as getdirentries(2) or getdents(2) describe a `d_off' field of `struct dirent' as a cookie that can be passed to lseek(2) to resume a directory seek at a given position. This field is not set for dirents read from a FUSE filesystem. I believe this may be caused by a missing field assignment in `fuse_internal_readdir_processdata()'. Below is a simple test driver that will print out dirent fields for a provided directory. Run it on a UFS volume and a fusefs volume to see different `d_off' behavior. ------------ #include <sys/types.h> #include <dirent.h> #include <stdio.h> int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s DIRPATH\n", argv[0]); return 1; } DIR *dir_p = opendir(argv[1]); int dir_fd = dirfd(dir_p); char buf[4096]; ssize_t rc = getdents(dir_fd, buf, 4096); ssize_t offset = 0; int ii = 0; while (offset < rc) { struct dirent *dent = (struct dirent*)(buf + offset); printf("dents[%d] = {\n", ii); printf(" .d_fileno = %lu,\n", dent->d_fileno); printf(" .d_off = %ld,\n", dent->d_off); printf(" .d_name = \"%s\",\n", dent->d_name); printf("}\n"); offset += dent->d_reclen; ii += 1; } return 0; }
Yep, this looks like a bug. telldir/seekdir works because they ignore d_off. But getdirentries/lseek doesn't.
FWIW, d_off is also used for NFS cookies, I think. This could mean that large FUSE directory listings on a FreeBSD NFS server wouldn't work correctly for clients.
John, could you please test the code in the linked review?
(In reply to Alan Somers from comment #3) Thanks, I've verified the linked review fixes this bug for me.
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=71befc35061b3c9d8cc07e34c5dce622c848fcdb commit 71befc35061b3c9d8cc07e34c5dce622c848fcdb Author: Alan Somers <asomers@FreeBSD.org> AuthorDate: 2021-02-12 01:01:10 +0000 Commit: Alan Somers <asomers@FreeBSD.org> CommitDate: 2021-02-13 04:50:52 +0000 fusefs: set d_off during VOP_READDIR This allows d_off to be used with lseek to position the file so that getdirentries(2) will return the next entry. It is not used by readdir(3). PR: 253411 Reported by: John Millikin <jmillikin@gmail.com> Reviewed by: cem MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D28605 sys/fs/fuse/fuse_internal.c | 13 ++++---- tests/sys/fs/fusefs/Makefile | 3 ++ tests/sys/fs/fusefs/readdir.cc | 74 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 16 deletions(-)
A commit in branch stable/13 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=dc559c846d052ccb1996adcac2e6aba1675b4627 commit dc559c846d052ccb1996adcac2e6aba1675b4627 Author: Alan Somers <asomers@FreeBSD.org> AuthorDate: 2021-02-12 01:01:10 +0000 Commit: Alan Somers <asomers@FreeBSD.org> CommitDate: 2021-03-21 02:00:33 +0000 fusefs: set d_off during VOP_READDIR This allows d_off to be used with lseek to position the file so that getdirentries(2) will return the next entry. It is not used by readdir(3). PR: 253411 Reported by: John Millikin <jmillikin@gmail.com> Reviewed by: cem Differential Revision: https://reviews.freebsd.org/D28605 (cherry picked from commit 71befc35061b3c9d8cc07e34c5dce622c848fcdb) sys/fs/fuse/fuse_internal.c | 13 ++++---- tests/sys/fs/fusefs/Makefile | 3 ++ tests/sys/fs/fusefs/readdir.cc | 74 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 16 deletions(-)
A commit in branch stable/12 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=e02c4d8d9bc7c8a0ba4477c31cbac5b67283ea06 commit e02c4d8d9bc7c8a0ba4477c31cbac5b67283ea06 Author: Alan Somers <asomers@FreeBSD.org> AuthorDate: 2021-02-12 01:01:10 +0000 Commit: Alan Somers <asomers@FreeBSD.org> CommitDate: 2021-04-08 21:32:59 +0000 fusefs: set d_off during VOP_READDIR This allows d_off to be used with lseek to position the file so that getdirentries(2) will return the next entry. It is not used by readdir(3). PR: 253411 Reported by: John Millikin <jmillikin@gmail.com> Reviewed by: cem Differential Revision: https://reviews.freebsd.org/D28605 (cherry picked from commit 71befc35061b3c9d8cc07e34c5dce622c848fcdb) sys/fs/fuse/fuse_internal.c | 13 ++++---- tests/sys/fs/fusefs/Makefile | 3 ++ tests/sys/fs/fusefs/readdir.cc | 74 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 16 deletions(-)