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