xref: /freebsd/tests/sys/fs/fusefs/read.cc (revision 6419bb52)
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 
33 extern "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 
49 using namespace testing;
50 
51 class Read: public FuseTest {
52 
53 public:
54 void 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 
60 class Read_7_8: public FuseTest {
61 public:
62 virtual void SetUp() {
63 	m_kernel_minor_version = 8;
64 	FuseTest::SetUp();
65 }
66 
67 void 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 
73 class AioRead: public Read {
74 public:
75 virtual 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 
83 class AsyncRead: public AioRead {
84 	virtual void SetUp() {
85 		m_init_flags = FUSE_ASYNC_READ;
86 		AioRead::SetUp();
87 	}
88 };
89 
90 class 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 */
108 TEST_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  */
142 TEST_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  */
222 TEST_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 */
298 TEST_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  */
321 TEST_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  */
354 TEST_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 
379 TEST_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  */
411 TEST_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 */
442 TEST_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  */
478 TEST_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  */
513 TEST_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 
546 TEST_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  */
591 TEST_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  */
641 TEST_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 
672 TEST_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 
699 TEST_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 
722 TEST_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  */
749 TEST_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) */
785 TEST_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 */
831 TEST_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  */
867 TEST_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 
907 INSTANTIATE_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