1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 The FreeBSD Foundation
5 *
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD$
31 */
32
33extern "C" {
34#include <sys/param.h>
35#include <sys/mman.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38#include <sys/uio.h>
39
40#include <aio.h>
41#include <fcntl.h>
42#include <semaphore.h>
43#include <unistd.h>
44}
45
46#include "mockfs.hh"
47#include "utils.hh"
48
49using namespace testing;
50
51class Read: public FuseTest {
52
53public:
54void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
55{
56	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
57}
58};
59
60class Read_7_8: public FuseTest {
61public:
62virtual void SetUp() {
63	m_kernel_minor_version = 8;
64	FuseTest::SetUp();
65}
66
67void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
68{
69	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
70}
71};
72
73class AioRead: public Read {
74public:
75virtual void SetUp() {
76	if (!is_unsafe_aio_enabled())
77		GTEST_SKIP() <<
78			"vfs.aio.enable_unsafe must be set for this test";
79	FuseTest::SetUp();
80}
81};
82
83class AsyncRead: public AioRead {
84	virtual void SetUp() {
85		m_init_flags = FUSE_ASYNC_READ;
86		AioRead::SetUp();
87	}
88};
89
90class ReadAhead: public Read,
91		 public WithParamInterface<tuple<bool, int>>
92{
93	virtual void SetUp() {
94		int val;
95		const char *node = "vfs.maxbcachebuf";
96		size_t size = sizeof(val);
97		ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
98			<< strerror(errno);
99
100		m_maxreadahead = val * get<1>(GetParam());
101		m_noclusterr = get<0>(GetParam());
102		Read::SetUp();
103	}
104};
105
106/* AIO reads need to set the header's pid field correctly */
107/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
108TEST_F(AioRead, aio_read)
109{
110	const char FULLPATH[] = "mountpoint/some_file.txt";
111	const char RELPATH[] = "some_file.txt";
112	const char *CONTENTS = "abcdefgh";
113	uint64_t ino = 42;
114	int fd;
115	ssize_t bufsize = strlen(CONTENTS);
116	uint8_t buf[bufsize];
117	struct aiocb iocb, *piocb;
118
119	expect_lookup(RELPATH, ino, bufsize);
120	expect_open(ino, 0, 1);
121	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
122
123	fd = open(FULLPATH, O_RDONLY);
124	ASSERT_LE(0, fd) << strerror(errno);
125
126	iocb.aio_nbytes = bufsize;
127	iocb.aio_fildes = fd;
128	iocb.aio_buf = buf;
129	iocb.aio_offset = 0;
130	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
131	ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
132	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
133	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
134
135	leak(fd);
136}
137
138/*
139 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
140 * is at most one outstanding read operation per file handle
141 */
142TEST_F(AioRead, async_read_disabled)
143{
144	const char FULLPATH[] = "mountpoint/some_file.txt";
145	const char RELPATH[] = "some_file.txt";
146	uint64_t ino = 42;
147	int fd;
148	ssize_t bufsize = 50;
149	char buf0[bufsize], buf1[bufsize];
150	off_t off0 = 0;
151	off_t off1 = m_maxbcachebuf;
152	struct aiocb iocb0, iocb1;
153	volatile sig_atomic_t read_count = 0;
154
155	expect_lookup(RELPATH, ino, 131072);
156	expect_open(ino, 0, 1);
157	EXPECT_CALL(*m_mock, process(
158		ResultOf([=](auto in) {
159			return (in.header.opcode == FUSE_READ &&
160				in.header.nodeid == ino &&
161				in.body.read.fh == FH &&
162				in.body.read.offset == (uint64_t)off0);
163		}, Eq(true)),
164		_)
165	).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
166		read_count++;
167		/* Filesystem is slow to respond */
168	}));
169	EXPECT_CALL(*m_mock, process(
170		ResultOf([=](auto in) {
171			return (in.header.opcode == FUSE_READ &&
172				in.header.nodeid == ino &&
173				in.body.read.fh == FH &&
174				in.body.read.offset == (uint64_t)off1);
175		}, Eq(true)),
176		_)
177	).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
178		read_count++;
179		/* Filesystem is slow to respond */
180	}));
181
182	fd = open(FULLPATH, O_RDONLY);
183	ASSERT_LE(0, fd) << strerror(errno);
184
185	/*
186	 * Submit two AIO read requests, and respond to neither.  If the
187	 * filesystem ever gets the second read request, then we failed to
188	 * limit outstanding reads.
189	 */
190	iocb0.aio_nbytes = bufsize;
191	iocb0.aio_fildes = fd;
192	iocb0.aio_buf = buf0;
193	iocb0.aio_offset = off0;
194	iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
195	ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
196
197	iocb1.aio_nbytes = bufsize;
198	iocb1.aio_fildes = fd;
199	iocb1.aio_buf = buf1;
200	iocb1.aio_offset = off1;
201	iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
202	ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
203
204	/*
205	 * Sleep for awhile to make sure the kernel has had a chance to issue
206	 * the second read, even though the first has not yet returned
207	 */
208	nap();
209	EXPECT_EQ(read_count, 1);
210
211	m_mock->kill_daemon();
212	/* Wait for AIO activity to complete, but ignore errors */
213	(void)aio_waitcomplete(NULL, NULL);
214
215	leak(fd);
216}
217
218/*
219 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
220 * simultaneous read requests on the same file handle.
221 */
222TEST_F(AsyncRead, async_read)
223{
224	const char FULLPATH[] = "mountpoint/some_file.txt";
225	const char RELPATH[] = "some_file.txt";
226	uint64_t ino = 42;
227	int fd;
228	ssize_t bufsize = 50;
229	char buf0[bufsize], buf1[bufsize];
230	off_t off0 = 0;
231	off_t off1 = m_maxbcachebuf;
232	off_t fsize = 2 * m_maxbcachebuf;
233	struct aiocb iocb0, iocb1;
234	sem_t sem;
235
236	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
237
238	expect_lookup(RELPATH, ino, fsize);
239	expect_open(ino, 0, 1);
240	EXPECT_CALL(*m_mock, process(
241		ResultOf([=](auto in) {
242			return (in.header.opcode == FUSE_READ &&
243				in.header.nodeid == ino &&
244				in.body.read.fh == FH &&
245				in.body.read.offset == (uint64_t)off0);
246		}, Eq(true)),
247		_)
248	).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
249		sem_post(&sem);
250		/* Filesystem is slow to respond */
251	}));
252	EXPECT_CALL(*m_mock, process(
253		ResultOf([=](auto in) {
254			return (in.header.opcode == FUSE_READ &&
255				in.header.nodeid == ino &&
256				in.body.read.fh == FH &&
257				in.body.read.offset == (uint64_t)off1);
258		}, Eq(true)),
259		_)
260	).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
261		sem_post(&sem);
262		/* Filesystem is slow to respond */
263	}));
264
265	fd = open(FULLPATH, O_RDONLY);
266	ASSERT_LE(0, fd) << strerror(errno);
267
268	/*
269	 * Submit two AIO read requests, but respond to neither.  Ensure that
270	 * we received both.
271	 */
272	iocb0.aio_nbytes = bufsize;
273	iocb0.aio_fildes = fd;
274	iocb0.aio_buf = buf0;
275	iocb0.aio_offset = off0;
276	iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
277	ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
278
279	iocb1.aio_nbytes = bufsize;
280	iocb1.aio_fildes = fd;
281	iocb1.aio_buf = buf1;
282	iocb1.aio_offset = off1;
283	iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
284	ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
285
286	/* Wait until both reads have reached the daemon */
287	ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
288	ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
289
290	m_mock->kill_daemon();
291	/* Wait for AIO activity to complete, but ignore errors */
292	(void)aio_waitcomplete(NULL, NULL);
293
294	leak(fd);
295}
296
297/* 0-length reads shouldn't cause any confusion */
298TEST_F(Read, direct_io_read_nothing)
299{
300	const char FULLPATH[] = "mountpoint/some_file.txt";
301	const char RELPATH[] = "some_file.txt";
302	uint64_t ino = 42;
303	int fd;
304	uint64_t offset = 100;
305	char buf[80];
306
307	expect_lookup(RELPATH, ino, offset + 1000);
308	expect_open(ino, FOPEN_DIRECT_IO, 1);
309
310	fd = open(FULLPATH, O_RDONLY);
311	ASSERT_LE(0, fd) << strerror(errno);
312
313	ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
314	leak(fd);
315}
316
317/*
318 * With direct_io, reads should not fill the cache.  They should go straight to
319 * the daemon
320 */
321TEST_F(Read, direct_io_pread)
322{
323	const char FULLPATH[] = "mountpoint/some_file.txt";
324	const char RELPATH[] = "some_file.txt";
325	const char *CONTENTS = "abcdefgh";
326	uint64_t ino = 42;
327	int fd;
328	uint64_t offset = 100;
329	ssize_t bufsize = strlen(CONTENTS);
330	uint8_t buf[bufsize];
331
332	expect_lookup(RELPATH, ino, offset + bufsize);
333	expect_open(ino, FOPEN_DIRECT_IO, 1);
334	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
335
336	fd = open(FULLPATH, O_RDONLY);
337	ASSERT_LE(0, fd) << strerror(errno);
338
339	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
340	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
341
342	// With FOPEN_DIRECT_IO, the cache should be bypassed.  The server will
343	// get a 2nd read request.
344	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
345	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
346	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
347	leak(fd);
348}
349
350/*
351 * With direct_io, filesystems are allowed to return less data than is
352 * requested.  fuse(4) should return a short read to userland.
353 */
354TEST_F(Read, direct_io_short_read)
355{
356	const char FULLPATH[] = "mountpoint/some_file.txt";
357	const char RELPATH[] = "some_file.txt";
358	const char *CONTENTS = "abcdefghijklmnop";
359	uint64_t ino = 42;
360	int fd;
361	uint64_t offset = 100;
362	ssize_t bufsize = strlen(CONTENTS);
363	ssize_t halfbufsize = bufsize / 2;
364	uint8_t buf[bufsize];
365
366	expect_lookup(RELPATH, ino, offset + bufsize);
367	expect_open(ino, FOPEN_DIRECT_IO, 1);
368	expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
369
370	fd = open(FULLPATH, O_RDONLY);
371	ASSERT_LE(0, fd) << strerror(errno);
372
373	ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
374		<< strerror(errno);
375	ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
376	leak(fd);
377}
378
379TEST_F(Read, eio)
380{
381	const char FULLPATH[] = "mountpoint/some_file.txt";
382	const char RELPATH[] = "some_file.txt";
383	const char *CONTENTS = "abcdefgh";
384	uint64_t ino = 42;
385	int fd;
386	ssize_t bufsize = strlen(CONTENTS);
387	uint8_t buf[bufsize];
388
389	expect_lookup(RELPATH, ino, bufsize);
390	expect_open(ino, 0, 1);
391	EXPECT_CALL(*m_mock, process(
392		ResultOf([=](auto in) {
393			return (in.header.opcode == FUSE_READ);
394		}, Eq(true)),
395		_)
396	).WillOnce(Invoke(ReturnErrno(EIO)));
397
398	fd = open(FULLPATH, O_RDONLY);
399	ASSERT_LE(0, fd) << strerror(errno);
400
401	ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
402	ASSERT_EQ(EIO, errno);
403	leak(fd);
404}
405
406/*
407 * If the server returns a short read when direct io is not in use, that
408 * indicates EOF, because of a server-side truncation.  We should invalidate
409 * all cached attributes.  We may update the file size,
410 */
411TEST_F(Read, eof)
412{
413	const char FULLPATH[] = "mountpoint/some_file.txt";
414	const char RELPATH[] = "some_file.txt";
415	const char *CONTENTS = "abcdefghijklmnop";
416	uint64_t ino = 42;
417	int fd;
418	uint64_t offset = 100;
419	ssize_t bufsize = strlen(CONTENTS);
420	ssize_t partbufsize = 3 * bufsize / 4;
421	ssize_t r;
422	uint8_t buf[bufsize];
423	struct stat sb;
424
425	expect_lookup(RELPATH, ino, offset + bufsize);
426	expect_open(ino, 0, 1);
427	expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
428	expect_getattr(ino, offset + partbufsize);
429
430	fd = open(FULLPATH, O_RDONLY);
431	ASSERT_LE(0, fd) << strerror(errno);
432
433	r = pread(fd, buf, bufsize, offset);
434	ASSERT_LE(0, r) << strerror(errno);
435	EXPECT_EQ(partbufsize, r) << strerror(errno);
436	ASSERT_EQ(0, fstat(fd, &sb));
437	EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
438	leak(fd);
439}
440
441/* Like Read.eof, but causes an entire buffer to be invalidated */
442TEST_F(Read, eof_of_whole_buffer)
443{
444	const char FULLPATH[] = "mountpoint/some_file.txt";
445	const char RELPATH[] = "some_file.txt";
446	const char *CONTENTS = "abcdefghijklmnop";
447	uint64_t ino = 42;
448	int fd;
449	ssize_t bufsize = strlen(CONTENTS);
450	off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
451	uint8_t buf[bufsize];
452	struct stat sb;
453
454	expect_lookup(RELPATH, ino, old_filesize);
455	expect_open(ino, 0, 1);
456	expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
457	expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
458	expect_getattr(ino, m_maxbcachebuf);
459
460	fd = open(FULLPATH, O_RDONLY);
461	ASSERT_LE(0, fd) << strerror(errno);
462
463	/* Cache the third block */
464	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
465		<< strerror(errno);
466	/* Try to read the 2nd block, but it's past EOF */
467	ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
468		<< strerror(errno);
469	ASSERT_EQ(0, fstat(fd, &sb));
470	EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
471	leak(fd);
472}
473
474/*
475 * With the keep_cache option, the kernel may keep its read cache across
476 * multiple open(2)s.
477 */
478TEST_F(Read, keep_cache)
479{
480	const char FULLPATH[] = "mountpoint/some_file.txt";
481	const char RELPATH[] = "some_file.txt";
482	const char *CONTENTS = "abcdefgh";
483	uint64_t ino = 42;
484	int fd0, fd1;
485	ssize_t bufsize = strlen(CONTENTS);
486	uint8_t buf[bufsize];
487
488	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
489	expect_open(ino, FOPEN_KEEP_CACHE, 2);
490	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
491
492	fd0 = open(FULLPATH, O_RDONLY);
493	ASSERT_LE(0, fd0) << strerror(errno);
494	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
495
496	fd1 = open(FULLPATH, O_RDWR);
497	ASSERT_LE(0, fd1) << strerror(errno);
498
499	/*
500	 * This read should be serviced by cache, even though it's on the other
501	 * file descriptor
502	 */
503	ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
504
505	leak(fd0);
506	leak(fd1);
507}
508
509/*
510 * Without the keep_cache option, the kernel should drop its read caches on
511 * every open
512 */
513TEST_F(Read, keep_cache_disabled)
514{
515	const char FULLPATH[] = "mountpoint/some_file.txt";
516	const char RELPATH[] = "some_file.txt";
517	const char *CONTENTS = "abcdefgh";
518	uint64_t ino = 42;
519	int fd0, fd1;
520	ssize_t bufsize = strlen(CONTENTS);
521	uint8_t buf[bufsize];
522
523	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
524	expect_open(ino, 0, 2);
525	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
526
527	fd0 = open(FULLPATH, O_RDONLY);
528	ASSERT_LE(0, fd0) << strerror(errno);
529	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
530
531	fd1 = open(FULLPATH, O_RDWR);
532	ASSERT_LE(0, fd1) << strerror(errno);
533
534	/*
535	 * This read should not be serviced by cache, even though it's on the
536	 * original file descriptor
537	 */
538	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
539	ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
540	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
541
542	leak(fd0);
543	leak(fd1);
544}
545
546TEST_F(Read, mmap)
547{
548	const char FULLPATH[] = "mountpoint/some_file.txt";
549	const char RELPATH[] = "some_file.txt";
550	const char *CONTENTS = "abcdefgh";
551	uint64_t ino = 42;
552	int fd;
553	ssize_t len;
554	size_t bufsize = strlen(CONTENTS);
555	void *p;
556
557	len = getpagesize();
558
559	expect_lookup(RELPATH, ino, bufsize);
560	expect_open(ino, 0, 1);
561	EXPECT_CALL(*m_mock, process(
562		ResultOf([=](auto in) {
563			return (in.header.opcode == FUSE_READ &&
564				in.header.nodeid == ino &&
565				in.body.read.fh == Read::FH &&
566				in.body.read.offset == 0 &&
567				in.body.read.size == bufsize);
568		}, Eq(true)),
569		_)
570	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
571		out.header.len = sizeof(struct fuse_out_header) + bufsize;
572		memmove(out.body.bytes, CONTENTS, bufsize);
573	})));
574
575	fd = open(FULLPATH, O_RDONLY);
576	ASSERT_LE(0, fd) << strerror(errno);
577
578	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
579	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
580
581	ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
582
583	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
584	leak(fd);
585}
586
587/*
588 * A read via mmap comes up short, indicating that the file was truncated
589 * server-side.
590 */
591TEST_F(Read, mmap_eof)
592{
593	const char FULLPATH[] = "mountpoint/some_file.txt";
594	const char RELPATH[] = "some_file.txt";
595	const char *CONTENTS = "abcdefgh";
596	uint64_t ino = 42;
597	int fd;
598	ssize_t len;
599	size_t bufsize = strlen(CONTENTS);
600	struct stat sb;
601	void *p;
602
603	len = getpagesize();
604
605	expect_lookup(RELPATH, ino, m_maxbcachebuf);
606	expect_open(ino, 0, 1);
607	EXPECT_CALL(*m_mock, process(
608		ResultOf([=](auto in) {
609			return (in.header.opcode == FUSE_READ &&
610				in.header.nodeid == ino &&
611				in.body.read.fh == Read::FH &&
612				in.body.read.offset == 0 &&
613				in.body.read.size == (uint32_t)m_maxbcachebuf);
614		}, Eq(true)),
615		_)
616	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
617		out.header.len = sizeof(struct fuse_out_header) + bufsize;
618		memmove(out.body.bytes, CONTENTS, bufsize);
619	})));
620	expect_getattr(ino, bufsize);
621
622	fd = open(FULLPATH, O_RDONLY);
623	ASSERT_LE(0, fd) << strerror(errno);
624
625	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
626	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
627
628	/* The file size should be automatically truncated */
629	ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
630	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
631	EXPECT_EQ((off_t)bufsize, sb.st_size);
632
633	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
634	leak(fd);
635}
636
637/*
638 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
639 * cache and to straight to the daemon
640 */
641TEST_F(Read, o_direct)
642{
643	const char FULLPATH[] = "mountpoint/some_file.txt";
644	const char RELPATH[] = "some_file.txt";
645	const char *CONTENTS = "abcdefgh";
646	uint64_t ino = 42;
647	int fd;
648	ssize_t bufsize = strlen(CONTENTS);
649	uint8_t buf[bufsize];
650
651	expect_lookup(RELPATH, ino, bufsize);
652	expect_open(ino, 0, 1);
653	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
654
655	fd = open(FULLPATH, O_RDONLY);
656	ASSERT_LE(0, fd) << strerror(errno);
657
658	// Fill the cache
659	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
660	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
661
662	// Reads with o_direct should bypass the cache
663	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
664	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
665	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
666	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
667	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
668
669	leak(fd);
670}
671
672TEST_F(Read, pread)
673{
674	const char FULLPATH[] = "mountpoint/some_file.txt";
675	const char RELPATH[] = "some_file.txt";
676	const char *CONTENTS = "abcdefgh";
677	uint64_t ino = 42;
678	int fd;
679	/*
680	 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
681	 * to read from.  Without this, the read might start at a lower offset.
682	 */
683	uint64_t offset = m_maxbcachebuf;
684	ssize_t bufsize = strlen(CONTENTS);
685	uint8_t buf[bufsize];
686
687	expect_lookup(RELPATH, ino, offset + bufsize);
688	expect_open(ino, 0, 1);
689	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
690
691	fd = open(FULLPATH, O_RDONLY);
692	ASSERT_LE(0, fd) << strerror(errno);
693
694	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
695	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
696	leak(fd);
697}
698
699TEST_F(Read, read)
700{
701	const char FULLPATH[] = "mountpoint/some_file.txt";
702	const char RELPATH[] = "some_file.txt";
703	const char *CONTENTS = "abcdefgh";
704	uint64_t ino = 42;
705	int fd;
706	ssize_t bufsize = strlen(CONTENTS);
707	uint8_t buf[bufsize];
708
709	expect_lookup(RELPATH, ino, bufsize);
710	expect_open(ino, 0, 1);
711	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
712
713	fd = open(FULLPATH, O_RDONLY);
714	ASSERT_LE(0, fd) << strerror(errno);
715
716	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
717	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
718
719	leak(fd);
720}
721
722TEST_F(Read_7_8, read)
723{
724	const char FULLPATH[] = "mountpoint/some_file.txt";
725	const char RELPATH[] = "some_file.txt";
726	const char *CONTENTS = "abcdefgh";
727	uint64_t ino = 42;
728	int fd;
729	ssize_t bufsize = strlen(CONTENTS);
730	uint8_t buf[bufsize];
731
732	expect_lookup(RELPATH, ino, bufsize);
733	expect_open(ino, 0, 1);
734	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
735
736	fd = open(FULLPATH, O_RDONLY);
737	ASSERT_LE(0, fd) << strerror(errno);
738
739	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
740	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
741
742	leak(fd);
743}
744
745/*
746 * If cacheing is enabled, the kernel should try to read an entire cache block
747 * at a time.
748 */
749TEST_F(Read, cache_block)
750{
751	const char FULLPATH[] = "mountpoint/some_file.txt";
752	const char RELPATH[] = "some_file.txt";
753	const char *CONTENTS0 = "abcdefghijklmnop";
754	uint64_t ino = 42;
755	int fd;
756	ssize_t bufsize = 8;
757	ssize_t filesize = m_maxbcachebuf * 2;
758	char *contents;
759	char buf[bufsize];
760	const char *contents1 = CONTENTS0 + bufsize;
761
762	contents = (char*)calloc(1, filesize);
763	ASSERT_NE(nullptr, contents);
764	memmove(contents, CONTENTS0, strlen(CONTENTS0));
765
766	expect_lookup(RELPATH, ino, filesize);
767	expect_open(ino, 0, 1);
768	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
769		contents);
770
771	fd = open(FULLPATH, O_RDONLY);
772	ASSERT_LE(0, fd) << strerror(errno);
773
774	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
775	ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
776
777	/* A subsequent read should be serviced by cache */
778	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
779	ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
780	leak(fd);
781	free(contents);
782}
783
784/* Reading with sendfile should work (though it obviously won't be 0-copy) */
785TEST_F(Read, sendfile)
786{
787	const char FULLPATH[] = "mountpoint/some_file.txt";
788	const char RELPATH[] = "some_file.txt";
789	const char *CONTENTS = "abcdefgh";
790	uint64_t ino = 42;
791	int fd;
792	size_t bufsize = strlen(CONTENTS);
793	uint8_t buf[bufsize];
794	int sp[2];
795	off_t sbytes;
796
797	expect_lookup(RELPATH, ino, bufsize);
798	expect_open(ino, 0, 1);
799	EXPECT_CALL(*m_mock, process(
800		ResultOf([=](auto in) {
801			return (in.header.opcode == FUSE_READ &&
802				in.header.nodeid == ino &&
803				in.body.read.fh == Read::FH &&
804				in.body.read.offset == 0 &&
805				in.body.read.size == bufsize);
806		}, Eq(true)),
807		_)
808	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
809		out.header.len = sizeof(struct fuse_out_header) + bufsize;
810		memmove(out.body.bytes, CONTENTS, bufsize);
811	})));
812
813	ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
814		<< strerror(errno);
815	fd = open(FULLPATH, O_RDONLY);
816	ASSERT_LE(0, fd) << strerror(errno);
817
818	ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
819		<< strerror(errno);
820	ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
821		<< strerror(errno);
822	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
823
824	close(sp[1]);
825	close(sp[0]);
826	leak(fd);
827}
828
829/* sendfile should fail gracefully if fuse declines the read */
830/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
831TEST_F(Read, sendfile_eio)
832{
833	const char FULLPATH[] = "mountpoint/some_file.txt";
834	const char RELPATH[] = "some_file.txt";
835	const char *CONTENTS = "abcdefgh";
836	uint64_t ino = 42;
837	int fd;
838	ssize_t bufsize = strlen(CONTENTS);
839	int sp[2];
840	off_t sbytes;
841
842	expect_lookup(RELPATH, ino, bufsize);
843	expect_open(ino, 0, 1);
844	EXPECT_CALL(*m_mock, process(
845		ResultOf([=](auto in) {
846			return (in.header.opcode == FUSE_READ);
847		}, Eq(true)),
848		_)
849	).WillOnce(Invoke(ReturnErrno(EIO)));
850
851	ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
852		<< strerror(errno);
853	fd = open(FULLPATH, O_RDONLY);
854	ASSERT_LE(0, fd) << strerror(errno);
855
856	ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
857
858	close(sp[1]);
859	close(sp[0]);
860	leak(fd);
861}
862
863/*
864 * Sequential reads should use readahead.  And if allowed, large reads should
865 * be clustered.
866 */
867TEST_P(ReadAhead, readahead) {
868	const char FULLPATH[] = "mountpoint/some_file.txt";
869	const char RELPATH[] = "some_file.txt";
870	uint64_t ino = 42;
871	int fd, maxcontig, clustersize;
872	ssize_t bufsize = 4 * m_maxbcachebuf;
873	ssize_t filesize = bufsize;
874	uint64_t len;
875	char *rbuf, *contents;
876	off_t offs;
877
878	contents = (char*)malloc(filesize);
879	ASSERT_NE(nullptr, contents);
880	memset(contents, 'X', filesize);
881	rbuf = (char*)calloc(1, bufsize);
882
883	expect_lookup(RELPATH, ino, filesize);
884	expect_open(ino, 0, 1);
885	maxcontig = m_noclusterr ? m_maxbcachebuf :
886		m_maxbcachebuf + m_maxreadahead;
887	clustersize = MIN(maxcontig, m_maxphys);
888	for (offs = 0; offs < bufsize; offs += clustersize) {
889		len = std::min((size_t)clustersize, (size_t)(filesize - offs));
890		expect_read(ino, offs, len, len, contents + offs);
891	}
892
893	fd = open(FULLPATH, O_RDONLY);
894	ASSERT_LE(0, fd) << strerror(errno);
895
896	/* Set the internal readahead counter to a "large" value */
897	ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
898
899	ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
900	ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
901
902	leak(fd);
903	free(rbuf);
904	free(contents);
905}
906
907INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
908	Values(tuple<bool, int>(false, 0),
909	       tuple<bool, int>(false, 1),
910	       tuple<bool, int>(false, 2),
911	       tuple<bool, int>(false, 3),
912	       tuple<bool, int>(true, 0),
913	       tuple<bool, int>(true, 1),
914	       tuple<bool, int>(true, 2)));
915