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