Bug 253411 - [fusefs] driver doesn't populate dirent->d_off
Summary: [fusefs] driver doesn't populate dirent->d_off
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 12.2-RELEASE
Hardware: Any Any
: --- Affects Only Me
Assignee: Alan Somers
URL: https://reviews.freebsd.org/D28605
Keywords:
Depends on:
Blocks:
 
Reported: 2021-02-10 14:05 UTC by John Millikin
Modified: 2021-04-08 21:56 UTC (History)
3 users (show)

See Also:
asomers: mfc-stable13+
asomers: mfc-stable12+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description John Millikin 2021-02-10 14:05:31 UTC
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;
}
Comment 1 Alan Somers freebsd_committer freebsd_triage 2021-02-10 16:28:17 UTC
Yep, this looks like a bug.  telldir/seekdir works because they ignore d_off.  But getdirentries/lseek doesn't.
Comment 2 Conrad Meyer freebsd_committer freebsd_triage 2021-02-10 16:53:28 UTC
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.
Comment 3 Alan Somers freebsd_committer freebsd_triage 2021-02-12 01:04:40 UTC
John, could you please test the code in the linked review?
Comment 4 John Millikin 2021-02-12 10:13:22 UTC
(In reply to Alan Somers from comment #3)
Thanks, I've verified the linked review fixes this bug for me.
Comment 5 commit-hook freebsd_committer freebsd_triage 2021-02-13 04:51:50 UTC
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(-)
Comment 6 commit-hook freebsd_committer freebsd_triage 2021-03-21 02:14:42 UTC
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(-)
Comment 7 commit-hook freebsd_committer freebsd_triage 2021-04-08 21:35:47 UTC
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(-)