Date: Sat, 13 Feb 2021 04:51:23 GMT From: Alan Somers <asomers@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 71befc35061b - main - fusefs: set d_off during VOP_READDIR Message-ID: <202102130451.11D4pNTB056181@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by asomers: 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(-) diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c index 60f9a7319e00..a5f646e5f449 100644 --- a/sys/fs/fuse/fuse_internal.c +++ b/sys/fs/fuse/fuse_internal.c @@ -583,7 +583,7 @@ fuse_internal_readdir_processdata(struct uio *uio, u_long **cookiesp) { int err = 0; - int bytesavail; + int oreclen; size_t freclen; struct dirent *de; @@ -620,10 +620,10 @@ fuse_internal_readdir_processdata(struct uio *uio, err = EINVAL; break; } - bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *) + oreclen = GENERIC_DIRSIZ((struct pseudo_dirent *) &fudge->namelen); - if (bytesavail > uio_resid(uio)) { + if (oreclen > uio_resid(uio)) { /* Out of space for the dir so we are done. */ err = -1; break; @@ -633,12 +633,13 @@ fuse_internal_readdir_processdata(struct uio *uio, * the requested offset in the directory is found. */ if (*fnd_start != 0) { - fiov_adjust(cookediov, bytesavail); - bzero(cookediov->base, bytesavail); + fiov_adjust(cookediov, oreclen); + bzero(cookediov->base, oreclen); de = (struct dirent *)cookediov->base; de->d_fileno = fudge->ino; - de->d_reclen = bytesavail; + de->d_off = fudge->off; + de->d_reclen = oreclen; de->d_type = fudge->type; de->d_namlen = fudge->namelen; memcpy((char *)cookediov->base + sizeof(struct dirent) - diff --git a/tests/sys/fs/fusefs/Makefile b/tests/sys/fs/fusefs/Makefile index 2c858ff42dd1..832b30dc6911 100644 --- a/tests/sys/fs/fusefs/Makefile +++ b/tests/sys/fs/fusefs/Makefile @@ -71,6 +71,9 @@ FUSEFS= ${SRCTOP}/sys/fs/fuse MOUNT= ${SRCTOP}/sbin/mount # Suppress warnings that GCC generates for the libc++ and gtest headers. CXXWARNFLAGS.gcc+= -Wno-placement-new -Wno-attributes +# Suppress Wcast-align for readdir.cc, because it is unavoidable when using +# getdirentries. +CXXWARNFLAGS.readdir.cc+= -Wno-cast-align .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 80000 CXXWARNFLAGS+= -Wno-class-memaccess .endif diff --git a/tests/sys/fs/fusefs/readdir.cc b/tests/sys/fs/fusefs/readdir.cc index 8f0b9742890e..b6b807f350e9 100644 --- a/tests/sys/fs/fusefs/readdir.cc +++ b/tests/sys/fs/fusefs/readdir.cc @@ -62,6 +62,9 @@ void expect_lookup(const char *relpath, uint64_t ino) } }; +const char dot[] = "."; +const char dotdot[] = ".."; + /* FUSE_READDIR returns nothing but "." and ".." */ TEST_F(Readdir, dots) { @@ -72,8 +75,6 @@ TEST_F(Readdir, dots) struct dirent *de; vector<struct dirent> ents(2); vector<struct dirent> empty_ents(0); - const char dot[] = "."; - const char dotdot[] = ".."; expect_lookup(RELPATH, ino); expect_opendir(ino); @@ -98,11 +99,6 @@ TEST_F(Readdir, dots) de = readdir(dir); ASSERT_NE(nullptr, de) << strerror(errno); EXPECT_EQ(2ul, de->d_fileno); - /* - * fuse(4) doesn't actually set d_off, which is ok for now because - * nothing uses it. - */ - //EXPECT_EQ(2000, de->d_off); EXPECT_EQ(DT_DIR, de->d_type); EXPECT_EQ(sizeof(dotdot), de->d_namlen); EXPECT_EQ(0, strcmp(dotdot, de->d_name)); @@ -111,7 +107,6 @@ TEST_F(Readdir, dots) de = readdir(dir); ASSERT_NE(nullptr, de) << strerror(errno); EXPECT_EQ(3ul, de->d_fileno); - //EXPECT_EQ(3000, de->d_off); EXPECT_EQ(DT_DIR, de->d_type); EXPECT_EQ(sizeof(dot), de->d_namlen); EXPECT_EQ(0, strcmp(dot, de->d_name)); @@ -153,8 +148,11 @@ TEST_F(Readdir, eio) leakdir(dir); } -/* getdirentries(2) can use a larger buffer size than readdir(3) */ -TEST_F(Readdir, getdirentries) +/* + * getdirentries(2) can use a larger buffer size than readdir(3). It also has + * some additional non-standardized fields in the returned dirent. + */ +TEST_F(Readdir, getdirentries_empty) { const char FULLPATH[] = "mountpoint/some_dir"; const char RELPATH[] = "some_dir"; @@ -186,6 +184,62 @@ TEST_F(Readdir, getdirentries) leak(fd); } +/* + * The dirent.d_off field can be used with lseek to position the directory so + * that getdirentries will return the subsequent dirent. + */ +TEST_F(Readdir, getdirentries_seek) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + vector<struct dirent> ents0(2); + vector<struct dirent> ents1(1); + uint64_t ino = 42; + int fd; + const size_t bufsize = 8192; + char buf[bufsize]; + struct dirent *de0, *de1; + ssize_t r; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + + ents0[0].d_fileno = 2; + ents0[0].d_off = 2000; + ents0[0].d_namlen = sizeof(dotdot); + ents0[0].d_type = DT_DIR; + strncpy(ents0[0].d_name, dotdot, ents0[0].d_namlen); + expect_readdir(ino, 0, ents0); + ents0[1].d_fileno = 3; + ents0[1].d_off = 3000; + ents0[1].d_namlen = sizeof(dot); + ents0[1].d_type = DT_DIR; + ents1[0].d_fileno = 3; + ents1[0].d_off = 3000; + ents1[0].d_namlen = sizeof(dot); + ents1[0].d_type = DT_DIR; + strncpy(ents1[0].d_name, dot, ents1[0].d_namlen); + expect_readdir(ino, 0, ents0); + expect_readdir(ino, 2000, ents1); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + r = getdirentries(fd, buf, sizeof(buf), 0); + ASSERT_LT(0, r) << strerror(errno); + de0 = (struct dirent*)&buf[0]; + ASSERT_EQ(2000, de0->d_off); + ASSERT_LT(de0->d_reclen + offsetof(struct dirent, d_fileno), bufsize); + de1 = (struct dirent*)(&(buf[de0->d_reclen])); + ASSERT_EQ(3ul, de1->d_fileno); + + r = lseek(fd, de0->d_off, SEEK_SET); + ASSERT_LE(0, r); + r = getdirentries(fd, buf, sizeof(buf), 0); + ASSERT_LT(0, r) << strerror(errno); + de0 = (struct dirent*)&buf[0]; + ASSERT_EQ(3000, de0->d_off); +} + /* * Nothing bad should happen if getdirentries is called on two file descriptors * which were concurrently open, but one has already been closed.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102130451.11D4pNTB056181>