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