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