diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -1754,6 +1754,8 @@ { struct vnode *vp = ap->a_vp; struct mount *mp; + struct fuse_filehandle *fufh; + int err; switch (ap->a_name) { case _PC_FILESIZEBITS: @@ -1784,8 +1786,24 @@ off_t offset = 0; /* Issue a FUSE_LSEEK to find out if it's implemented */ - fuse_vnop_do_lseek(vp, curthread, curthread->td_ucred, - curthread->td_proc->p_pid, &offset, SEEK_DATA); + err = fuse_vnop_do_lseek(vp, curthread, + curthread->td_ucred, curthread->td_proc->p_pid, + &offset, SEEK_HOLE); + if (err == EBADF) { + /* + * pathconf() doesn't necessarily open the + * file. So we may need to do it here. + */ + fuse_filehandle_open(vp, FREAD, &fufh, + curthread, curthread->td_ucred); + err = fuse_vnop_do_lseek(vp, curthread, + curthread->td_ucred, + curthread->td_proc->p_pid, &offset, + SEEK_HOLE); + fuse_filehandle_close(vp, fufh, curthread, + curthread->td_ucred); + } + } if (fsess_is_impl(mp, FUSE_LSEEK)) { diff --git a/tests/sys/fs/fusefs/lseek.cc b/tests/sys/fs/fusefs/lseek.cc --- a/tests/sys/fs/fusefs/lseek.cc +++ b/tests/sys/fs/fusefs/lseek.cc @@ -141,6 +141,34 @@ leak(fd); } +/* + * Use pathconf, rather than fpathconf, on a file not already opened. + * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=278135 + */ +TEST_F(LseekPathconf, pathconf) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + off_t fsize = 1 << 30; /* 1 GiB */ + off_t offset_out = 1 << 29; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_LSEEK); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { + SET_OUT_HEADER_LEN(out, lseek); + out.body.lseek.offset = offset_out; + }))); + expect_release(ino, FuseTest::FH); + + EXPECT_EQ(1, pathconf(FULLPATH, _PC_MIN_HOLE_SIZE)) << strerror(errno); +} + /* * If no FUSE_LSEEK operation has been attempted since mount, try one as soon * as a pathconf request comes in. This is the typical pattern of bsdtar. It