Skip site navigation (1)Skip section navigation (2)
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>