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