Re: git: 6d408ac49073 - main - fusefs: add a regression test for a cluster_read bug

From: Konstantin Belousov <kostikbel_at_gmail.com>
Date: Fri, 24 Oct 2025 08:12:49 UTC
On Thu, Oct 23, 2025 at 01:41:05PM +0000, Alan Somers wrote:
> The branch main has been updated by asomers:
> 
> URL: https://cgit.FreeBSD.org/src/commit/?id=6d408ac490730614b3ed0ebd3caffcd23f303fb4
> 
> commit 6d408ac490730614b3ed0ebd3caffcd23f303fb4
> Author:     Alan Somers <asomers@FreeBSD.org>
> AuthorDate: 2025-10-23 13:40:56 +0000
> Commit:     Alan Somers <asomers@FreeBSD.org>
> CommitDate: 2025-10-23 13:40:56 +0000
> 
>     fusefs: add a regression test for a cluster_read bug
>     
>     VOP_BMAP is purely advisory.  If VOP_BMAP returns an error during
>     readahead, cluster_read should still succeed, because the actual data
>     was still read just fine.
No, VOP_BMAP() is not advisory.  But read-ahead beyond the first buffer is.
The BMAP in question is to translate lblk for read-ahead buffer.

>     
>     Add a regression test for PR 264196, wherein cluster_read would fail if
>     VOP_BMAP did.
>     
>     PR:             264196
>     MFC with:       62aef3f73f38db9fb68bffc12cc8900fecd58f0e
>     Reported by:    danfe
>     Reviewed by:    arrowd
>     Differential Revision: https://reviews.freebsd.org/D51316
> ---
>  tests/sys/fs/fusefs/bmap.cc | 87 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 87 insertions(+)
> 
> diff --git a/tests/sys/fs/fusefs/bmap.cc b/tests/sys/fs/fusefs/bmap.cc
> index 30612079657d..e61dadb6d79e 100644
> --- a/tests/sys/fs/fusefs/bmap.cc
> +++ b/tests/sys/fs/fusefs/bmap.cc
> @@ -177,6 +177,93 @@ TEST_F(Bmap, default_)
>  	leak(fd);
>  }
>  
> +/*
> + * The server returns an error for some reason for FUSE_BMAP.  fusefs should
> + * faithfully report that error up to the caller.
> + */
> +TEST_F(Bmap, einval)
> +{
> +	struct fiobmap2_arg arg;
> +	const off_t filesize = 1 << 30;
> +	int64_t lbn = 100;
> +	const ino_t ino = 42;
> +	int fd;
> +
> +	expect_lookup(RELPATH, 42, filesize);
> +	expect_open(ino, 0, 1);
> +	EXPECT_CALL(*m_mock, process(
> +		ResultOf([=](auto in) {
> +			return (in.header.opcode == FUSE_BMAP &&
> +				in.header.nodeid == ino);
> +		}, Eq(true)),
> +		_)
> +	).WillOnce(Invoke(ReturnErrno(EINVAL)));
> +
> +	fd = open(FULLPATH, O_RDWR);
> +	ASSERT_LE(0, fd) << strerror(errno);
> +
> +	arg.bn = lbn;
> +	arg.runp = -1;
> +	arg.runb = -1;
> +	ASSERT_EQ(-1, ioctl(fd, FIOBMAP2, &arg));
> +	EXPECT_EQ(EINVAL, errno);
> +
> +	leak(fd);
> +}
> +
> +/*
> + * Even if the server returns EINVAL during VOP_BMAP, we should still be able
> + * to successfully read a block.  This is a regression test for
> + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=264196 .  The bug did not
> + * lie in fusefs, but this is a convenient place for a regression test.
> + */
> +TEST_F(Bmap, spurious_einval)
> +{
> +	const off_t filesize = 4ull << 30;
> +	const ino_t ino = 42;
> +	int fd, r;
> +	char buf[1];
> +
> +	expect_lookup(RELPATH, 42, filesize);
> +	expect_open(ino, 0, 1);
> +	EXPECT_CALL(*m_mock, process(
> +		ResultOf([=](auto in) {
> +			return (in.header.opcode == FUSE_BMAP &&
> +				in.header.nodeid == ino);
> +		}, Eq(true)),
> +		_)
> +	).WillRepeatedly(Invoke(ReturnErrno(EINVAL)));
> +	EXPECT_CALL(*m_mock, process(
> +	ResultOf([=](auto in) {
> +		return (in.header.opcode == FUSE_READ &&
> +			in.header.nodeid == ino &&
> +			in.body.read.offset == 0 &&
> +			in.body.read.size == (uint64_t)m_maxbcachebuf);
> +		}, Eq(true)),
> +		_)
> +	).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
> +		size_t osize = in.body.read.size;
> +
> +		assert(osize < sizeof(out.body.bytes));
> +		out.header.len = sizeof(struct fuse_out_header) + osize;
> +		bzero(out.body.bytes, osize);
> +	})));
> +
> +	fd = open(FULLPATH, O_RDWR);
> +	ASSERT_LE(0, fd) << strerror(errno);
> +
> +	/*
> +	 * Read the same block multiple times.  On a system affected by PR
> +	 * 264196 , the second read will fail.
> +	 */
> +	r = read(fd, buf, sizeof(buf));
> +	EXPECT_EQ(r, 1) << strerror(errno);
> +	r = read(fd, buf, sizeof(buf));
> +	EXPECT_EQ(r, 1) << strerror(errno);
> +	r = read(fd, buf, sizeof(buf));
> +	EXPECT_EQ(r, 1) << strerror(errno);
> +}
> +
>  /*
>   * VOP_BMAP should not query the server for the file's size, even if its cached
>   * attributes have expired.