]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/read.cc
fusefs: annotate deliberate file descriptor leaks in the tests
[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         /* mmap may legitimately try to read more data than is available */
567         EXPECT_CALL(*m_mock, process(
568                 ResultOf([=](auto in) {
569                         return (in.header.opcode == FUSE_READ &&
570                                 in.header.nodeid == ino &&
571                                 in.body.read.fh == Read::FH &&
572                                 in.body.read.offset == 0 &&
573                                 in.body.read.size >= bufsize);
574                 }, Eq(true)),
575                 _)
576         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
577                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
578                 memmove(out.body.bytes, CONTENTS, bufsize);
579         })));
580
581         fd = open(FULLPATH, O_RDONLY);
582         ASSERT_LE(0, fd) << strerror(errno);
583
584         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
585         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
586
587         ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
588
589         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
590         leak(fd);
591 }
592
593 /* 
594  * A read via mmap comes up short, indicating that the file was truncated
595  * server-side.
596  */
597 TEST_F(Read, mmap_eof)
598 {
599         const char FULLPATH[] = "mountpoint/some_file.txt";
600         const char RELPATH[] = "some_file.txt";
601         const char *CONTENTS = "abcdefgh";
602         uint64_t ino = 42;
603         int fd;
604         ssize_t len;
605         size_t bufsize = strlen(CONTENTS);
606         struct stat sb;
607         void *p;
608
609         len = getpagesize();
610
611         expect_lookup(RELPATH, ino, 100000);
612         expect_open(ino, 0, 1);
613         /* mmap may legitimately try to read more data than is available */
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 >= bufsize);
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(NULL, 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         /* Like mmap, sendfile may request more data than is available */
806         EXPECT_CALL(*m_mock, process(
807                 ResultOf([=](auto in) {
808                         return (in.header.opcode == FUSE_READ &&
809                                 in.header.nodeid == ino &&
810                                 in.body.read.fh == Read::FH &&
811                                 in.body.read.offset == 0 &&
812                                 in.body.read.size >= bufsize);
813                 }, Eq(true)),
814                 _)
815         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
816                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
817                 memmove(out.body.bytes, CONTENTS, bufsize);
818         })));
819
820         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
821                 << strerror(errno);
822         fd = open(FULLPATH, O_RDONLY);
823         ASSERT_LE(0, fd) << strerror(errno);
824
825         ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
826                 << strerror(errno);
827         ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
828                 << strerror(errno);
829         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
830
831         close(sp[1]);
832         close(sp[0]);
833         leak(fd);
834 }
835
836 /* sendfile should fail gracefully if fuse declines the read */
837 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
838 TEST_F(Read, DISABLED_sendfile_eio)
839 {
840         const char FULLPATH[] = "mountpoint/some_file.txt";
841         const char RELPATH[] = "some_file.txt";
842         const char *CONTENTS = "abcdefgh";
843         uint64_t ino = 42;
844         int fd;
845         ssize_t bufsize = strlen(CONTENTS);
846         int sp[2];
847         off_t sbytes;
848
849         expect_lookup(RELPATH, ino, bufsize);
850         expect_open(ino, 0, 1);
851         EXPECT_CALL(*m_mock, process(
852                 ResultOf([=](auto in) {
853                         return (in.header.opcode == FUSE_READ);
854                 }, Eq(true)),
855                 _)
856         ).WillOnce(Invoke(ReturnErrno(EIO)));
857
858         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
859                 << strerror(errno);
860         fd = open(FULLPATH, O_RDONLY);
861         ASSERT_LE(0, fd) << strerror(errno);
862
863         ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
864
865         close(sp[1]);
866         close(sp[0]);
867         leak(fd);
868 }
869
870 /*
871  * Sequential reads should use readahead.  And if allowed, large reads should
872  * be clustered.
873  */
874 TEST_P(ReadAhead, readahead) {
875         const char FULLPATH[] = "mountpoint/some_file.txt";
876         const char RELPATH[] = "some_file.txt";
877         uint64_t ino = 42;
878         int fd, maxcontig, clustersize;
879         ssize_t bufsize = 4 * m_maxbcachebuf;
880         ssize_t filesize = bufsize;
881         uint64_t len;
882         char *rbuf, *contents;
883         off_t offs;
884
885         contents = (char*)malloc(filesize);
886         ASSERT_NE(NULL, contents);
887         memset(contents, 'X', filesize);
888         rbuf = (char*)calloc(1, bufsize);
889
890         expect_lookup(RELPATH, ino, filesize);
891         expect_open(ino, 0, 1);
892         maxcontig = m_noclusterr ? m_maxbcachebuf :
893                 m_maxbcachebuf + m_maxreadahead;
894         clustersize = MIN(maxcontig, m_maxphys);
895         for (offs = 0; offs < bufsize; offs += clustersize) {
896                 len = std::min((size_t)clustersize, (size_t)(filesize - offs));
897                 expect_read(ino, offs, len, len, contents + offs);
898         }
899
900         fd = open(FULLPATH, O_RDONLY);
901         ASSERT_LE(0, fd) << strerror(errno);
902
903         /* Set the internal readahead counter to a "large" value */
904         ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
905
906         ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
907         ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
908
909         leak(fd);
910 }
911
912 INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
913         Values(tuple<bool, int>(false, 0),
914                tuple<bool, int>(false, 1),
915                tuple<bool, int>(false, 2),
916                tuple<bool, int>(false, 3),
917                tuple<bool, int>(true, 0),
918                tuple<bool, int>(true, 1),
919                tuple<bool, int>(true, 2)));