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
This commit is contained in:
parent
a375ec52a7
commit
71befc3506
@ -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) -
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user