]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/read.cc
byacc: Update to 20230201.
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / read.cc
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 extern "C" {
34 #include <sys/param.h>
35 #include <sys/mman.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
38 #include <sys/uio.h>
39
40 #include <aio.h>
41 #include <fcntl.h>
42 #include <semaphore.h>
43 #include <setjmp.h>
44 #include <signal.h>
45 #include <unistd.h>
46 }
47
48 #include "mockfs.hh"
49 #include "utils.hh"
50
51 using namespace testing;
52
53 class Read: public FuseTest {
54
55 public:
56 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
57 {
58         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
59 }
60 };
61
62 class Read_7_8: public FuseTest {
63 public:
64 virtual void SetUp() {
65         m_kernel_minor_version = 8;
66         FuseTest::SetUp();
67 }
68
69 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
70 {
71         FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
72 }
73 };
74
75 class AioRead: public Read {
76 public:
77 virtual void SetUp() {
78         if (!is_unsafe_aio_enabled())
79                 GTEST_SKIP() <<
80                         "vfs.aio.enable_unsafe must be set for this test";
81         FuseTest::SetUp();
82 }
83 };
84
85 class AsyncRead: public AioRead {
86         virtual void SetUp() {
87                 m_init_flags = FUSE_ASYNC_READ;
88                 AioRead::SetUp();
89         }
90 };
91
92 class ReadAhead: public Read,
93                  public WithParamInterface<tuple<bool, int>>
94 {
95         virtual void SetUp() {
96                 int val;
97                 const char *node = "vfs.maxbcachebuf";
98                 size_t size = sizeof(val);
99                 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
100                         << strerror(errno);
101
102                 m_maxreadahead = val * get<1>(GetParam());
103                 m_noclusterr = get<0>(GetParam());
104                 Read::SetUp();
105         }
106 };
107
108 class ReadNoatime: public Read {
109         virtual void SetUp() {
110                 m_noatime = true;
111                 Read::SetUp();
112         }
113 };
114
115 class ReadSigbus: public Read
116 {
117 public:
118 static jmp_buf s_jmpbuf;
119 static void *s_si_addr;
120
121 void TearDown() {
122         struct sigaction sa;
123
124         bzero(&sa, sizeof(sa));
125         sa.sa_handler = SIG_DFL;
126         sigaction(SIGBUS, &sa, NULL);
127
128         FuseTest::TearDown();
129 }
130
131 };
132
133 static void
134 handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) {
135         ReadSigbus::s_si_addr = info->si_addr;
136         longjmp(ReadSigbus::s_jmpbuf, 1);
137 }
138
139 jmp_buf ReadSigbus::s_jmpbuf;
140 void *ReadSigbus::s_si_addr;
141
142 class TimeGran: public Read, public WithParamInterface<unsigned> {
143 public:
144 virtual void SetUp() {
145         m_time_gran = 1 << GetParam();
146         Read::SetUp();
147 }
148 };
149
150 /* AIO reads need to set the header's pid field correctly */
151 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
152 TEST_F(AioRead, aio_read)
153 {
154         const char FULLPATH[] = "mountpoint/some_file.txt";
155         const char RELPATH[] = "some_file.txt";
156         const char *CONTENTS = "abcdefgh";
157         uint64_t ino = 42;
158         int fd;
159         ssize_t bufsize = strlen(CONTENTS);
160         uint8_t buf[bufsize];
161         struct aiocb iocb, *piocb;
162
163         expect_lookup(RELPATH, ino, bufsize);
164         expect_open(ino, 0, 1);
165         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
166
167         fd = open(FULLPATH, O_RDONLY);
168         ASSERT_LE(0, fd) << strerror(errno);
169
170         iocb.aio_nbytes = bufsize;
171         iocb.aio_fildes = fd;
172         iocb.aio_buf = buf;
173         iocb.aio_offset = 0;
174         iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
175         ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
176         ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
177         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
178
179         leak(fd);
180 }
181
182 /* 
183  * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
184  * is at most one outstanding read operation per file handle
185  */
186 TEST_F(AioRead, async_read_disabled)
187 {
188         const char FULLPATH[] = "mountpoint/some_file.txt";
189         const char RELPATH[] = "some_file.txt";
190         uint64_t ino = 42;
191         int fd;
192         ssize_t bufsize = 50;
193         char buf0[bufsize], buf1[bufsize];
194         off_t off0 = 0;
195         off_t off1 = m_maxbcachebuf;
196         struct aiocb iocb0, iocb1;
197         volatile sig_atomic_t read_count = 0;
198
199         expect_lookup(RELPATH, ino, 131072);
200         expect_open(ino, 0, 1);
201         EXPECT_CALL(*m_mock, process(
202                 ResultOf([=](auto in) {
203                         return (in.header.opcode == FUSE_READ &&
204                                 in.header.nodeid == ino &&
205                                 in.body.read.fh == FH &&
206                                 in.body.read.offset == (uint64_t)off0);
207                 }, Eq(true)),
208                 _)
209         ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
210                 read_count++;
211                 /* Filesystem is slow to respond */
212         }));
213         EXPECT_CALL(*m_mock, process(
214                 ResultOf([=](auto in) {
215                         return (in.header.opcode == FUSE_READ &&
216                                 in.header.nodeid == ino &&
217                                 in.body.read.fh == FH &&
218                                 in.body.read.offset == (uint64_t)off1);
219                 }, Eq(true)),
220                 _)
221         ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
222                 read_count++;
223                 /* Filesystem is slow to respond */
224         }));
225
226         fd = open(FULLPATH, O_RDONLY);
227         ASSERT_LE(0, fd) << strerror(errno);
228
229         /* 
230          * Submit two AIO read requests, and respond to neither.  If the
231          * filesystem ever gets the second read request, then we failed to
232          * limit outstanding reads.
233          */
234         iocb0.aio_nbytes = bufsize;
235         iocb0.aio_fildes = fd;
236         iocb0.aio_buf = buf0;
237         iocb0.aio_offset = off0;
238         iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
239         ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
240
241         iocb1.aio_nbytes = bufsize;
242         iocb1.aio_fildes = fd;
243         iocb1.aio_buf = buf1;
244         iocb1.aio_offset = off1;
245         iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
246         ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
247
248         /* 
249          * Sleep for awhile to make sure the kernel has had a chance to issue
250          * the second read, even though the first has not yet returned
251          */
252         nap();
253         EXPECT_EQ(read_count, 1);
254         
255         m_mock->kill_daemon();
256         /* Wait for AIO activity to complete, but ignore errors */
257         (void)aio_waitcomplete(NULL, NULL);
258
259         leak(fd);
260 }
261
262 /* 
263  * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
264  * simultaneous read requests on the same file handle.
265  */
266 TEST_F(AsyncRead, async_read)
267 {
268         const char FULLPATH[] = "mountpoint/some_file.txt";
269         const char RELPATH[] = "some_file.txt";
270         uint64_t ino = 42;
271         int fd;
272         ssize_t bufsize = 50;
273         char buf0[bufsize], buf1[bufsize];
274         off_t off0 = 0;
275         off_t off1 = m_maxbcachebuf;
276         off_t fsize = 2 * m_maxbcachebuf;
277         struct aiocb iocb0, iocb1;
278         sem_t sem;
279
280         ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
281
282         expect_lookup(RELPATH, ino, fsize);
283         expect_open(ino, 0, 1);
284         EXPECT_CALL(*m_mock, process(
285                 ResultOf([=](auto in) {
286                         return (in.header.opcode == FUSE_READ &&
287                                 in.header.nodeid == ino &&
288                                 in.body.read.fh == FH &&
289                                 in.body.read.offset == (uint64_t)off0);
290                 }, Eq(true)),
291                 _)
292         ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
293                 sem_post(&sem);
294                 /* Filesystem is slow to respond */
295         }));
296         EXPECT_CALL(*m_mock, process(
297                 ResultOf([=](auto in) {
298                         return (in.header.opcode == FUSE_READ &&
299                                 in.header.nodeid == ino &&
300                                 in.body.read.fh == FH &&
301                                 in.body.read.offset == (uint64_t)off1);
302                 }, Eq(true)),
303                 _)
304         ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
305                 sem_post(&sem);
306                 /* Filesystem is slow to respond */
307         }));
308
309         fd = open(FULLPATH, O_RDONLY);
310         ASSERT_LE(0, fd) << strerror(errno);
311
312         /* 
313          * Submit two AIO read requests, but respond to neither.  Ensure that
314          * we received both.
315          */
316         iocb0.aio_nbytes = bufsize;
317         iocb0.aio_fildes = fd;
318         iocb0.aio_buf = buf0;
319         iocb0.aio_offset = off0;
320         iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
321         ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
322
323         iocb1.aio_nbytes = bufsize;
324         iocb1.aio_fildes = fd;
325         iocb1.aio_buf = buf1;
326         iocb1.aio_offset = off1;
327         iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
328         ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
329
330         /* Wait until both reads have reached the daemon */
331         ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
332         ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
333
334         m_mock->kill_daemon();
335         /* Wait for AIO activity to complete, but ignore errors */
336         (void)aio_waitcomplete(NULL, NULL);
337         
338         leak(fd);
339 }
340
341 /* The kernel should update the cached atime attribute during a read */
342 TEST_F(Read, atime)
343 {
344         const char FULLPATH[] = "mountpoint/some_file.txt";
345         const char RELPATH[] = "some_file.txt";
346         const char *CONTENTS = "abcdefgh";
347         struct stat sb1, sb2;
348         uint64_t ino = 42;
349         int fd;
350         ssize_t bufsize = strlen(CONTENTS);
351         uint8_t buf[bufsize];
352
353         expect_lookup(RELPATH, ino, bufsize);
354         expect_open(ino, 0, 1);
355         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
356
357         fd = open(FULLPATH, O_RDONLY);
358         ASSERT_LE(0, fd) << strerror(errno);
359         ASSERT_EQ(0, fstat(fd, &sb1));
360
361         /* Ensure atime will be different than it was during lookup */
362         nap();
363
364         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
365         ASSERT_EQ(0, fstat(fd, &sb2));
366
367         /* The kernel should automatically update atime during read */
368         EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
369         EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
370         EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
371
372         leak(fd);
373 }
374
375 /* The kernel should update the cached atime attribute during a cached read */
376 TEST_F(Read, atime_cached)
377 {
378         const char FULLPATH[] = "mountpoint/some_file.txt";
379         const char RELPATH[] = "some_file.txt";
380         const char *CONTENTS = "abcdefgh";
381         struct stat sb1, sb2;
382         uint64_t ino = 42;
383         int fd;
384         ssize_t bufsize = strlen(CONTENTS);
385         uint8_t buf[bufsize];
386
387         expect_lookup(RELPATH, ino, bufsize);
388         expect_open(ino, 0, 1);
389         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
390
391         fd = open(FULLPATH, O_RDONLY);
392         ASSERT_LE(0, fd) << strerror(errno);
393
394         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
395         ASSERT_EQ(0, fstat(fd, &sb1));
396
397         /* Ensure atime will be different than it was during the first read */
398         nap();
399
400         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
401         ASSERT_EQ(0, fstat(fd, &sb2));
402
403         /* The kernel should automatically update atime during read */
404         EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
405         EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
406         EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
407
408         leak(fd);
409 }
410
411 /* dirty atime values should be flushed during close */
412 TEST_F(Read, atime_during_close)
413 {
414         const char FULLPATH[] = "mountpoint/some_file.txt";
415         const char RELPATH[] = "some_file.txt";
416         const char *CONTENTS = "abcdefgh";
417         struct stat sb;
418         uint64_t ino = 42;
419         const mode_t newmode = 0755;
420         int fd;
421         ssize_t bufsize = strlen(CONTENTS);
422         uint8_t buf[bufsize];
423
424         expect_lookup(RELPATH, ino, bufsize);
425         expect_open(ino, 0, 1);
426         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
427         EXPECT_CALL(*m_mock, process(
428                 ResultOf([&](auto in) {
429                         uint32_t valid = FATTR_ATIME;
430                         return (in.header.opcode == FUSE_SETATTR &&
431                                 in.header.nodeid == ino &&
432                                 in.body.setattr.valid == valid &&
433                                 (time_t)in.body.setattr.atime ==
434                                         sb.st_atim.tv_sec &&
435                                 (long)in.body.setattr.atimensec ==
436                                         sb.st_atim.tv_nsec);
437                 }, Eq(true)),
438                 _)
439         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
440                 SET_OUT_HEADER_LEN(out, attr);
441                 out.body.attr.attr.ino = ino;
442                 out.body.attr.attr.mode = S_IFREG | newmode;
443         })));
444         expect_flush(ino, 1, ReturnErrno(0));
445         expect_release(ino, FuseTest::FH);
446
447         fd = open(FULLPATH, O_RDONLY);
448         ASSERT_LE(0, fd) << strerror(errno);
449
450         /* Ensure atime will be different than during lookup */
451         nap();
452
453         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
454         ASSERT_EQ(0, fstat(fd, &sb));
455
456         close(fd);
457 }
458
459 /* A cached atime should be flushed during FUSE_SETATTR */
460 TEST_F(Read, atime_during_setattr)
461 {
462         const char FULLPATH[] = "mountpoint/some_file.txt";
463         const char RELPATH[] = "some_file.txt";
464         const char *CONTENTS = "abcdefgh";
465         struct stat sb;
466         uint64_t ino = 42;
467         const mode_t newmode = 0755;
468         int fd;
469         ssize_t bufsize = strlen(CONTENTS);
470         uint8_t buf[bufsize];
471
472         expect_lookup(RELPATH, ino, bufsize);
473         expect_open(ino, 0, 1);
474         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
475         EXPECT_CALL(*m_mock, process(
476                 ResultOf([&](auto in) {
477                         uint32_t valid = FATTR_MODE | FATTR_ATIME;
478                         return (in.header.opcode == FUSE_SETATTR &&
479                                 in.header.nodeid == ino &&
480                                 in.body.setattr.valid == valid &&
481                                 (time_t)in.body.setattr.atime ==
482                                         sb.st_atim.tv_sec &&
483                                 (long)in.body.setattr.atimensec ==
484                                         sb.st_atim.tv_nsec);
485                 }, Eq(true)),
486                 _)
487         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
488                 SET_OUT_HEADER_LEN(out, attr);
489                 out.body.attr.attr.ino = ino;
490                 out.body.attr.attr.mode = S_IFREG | newmode;
491         })));
492
493         fd = open(FULLPATH, O_RDONLY);
494         ASSERT_LE(0, fd) << strerror(errno);
495
496         /* Ensure atime will be different than during lookup */
497         nap();
498
499         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
500         ASSERT_EQ(0, fstat(fd, &sb));
501         ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
502
503         leak(fd);
504 }
505
506 /* 0-length reads shouldn't cause any confusion */
507 TEST_F(Read, direct_io_read_nothing)
508 {
509         const char FULLPATH[] = "mountpoint/some_file.txt";
510         const char RELPATH[] = "some_file.txt";
511         uint64_t ino = 42;
512         int fd;
513         uint64_t offset = 100;
514         char buf[80];
515
516         expect_lookup(RELPATH, ino, offset + 1000);
517         expect_open(ino, FOPEN_DIRECT_IO, 1);
518
519         fd = open(FULLPATH, O_RDONLY);
520         ASSERT_LE(0, fd) << strerror(errno);
521
522         ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
523         leak(fd);
524 }
525
526 /* 
527  * With direct_io, reads should not fill the cache.  They should go straight to
528  * the daemon
529  */
530 TEST_F(Read, direct_io_pread)
531 {
532         const char FULLPATH[] = "mountpoint/some_file.txt";
533         const char RELPATH[] = "some_file.txt";
534         const char *CONTENTS = "abcdefgh";
535         uint64_t ino = 42;
536         int fd;
537         uint64_t offset = 100;
538         ssize_t bufsize = strlen(CONTENTS);
539         uint8_t buf[bufsize];
540
541         expect_lookup(RELPATH, ino, offset + bufsize);
542         expect_open(ino, FOPEN_DIRECT_IO, 1);
543         expect_read(ino, offset, bufsize, bufsize, CONTENTS);
544
545         fd = open(FULLPATH, O_RDONLY);
546         ASSERT_LE(0, fd) << strerror(errno);
547
548         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
549         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
550
551         // With FOPEN_DIRECT_IO, the cache should be bypassed.  The server will
552         // get a 2nd read request.
553         expect_read(ino, offset, bufsize, bufsize, CONTENTS);
554         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
555         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
556         leak(fd);
557 }
558
559 /* 
560  * With direct_io, filesystems are allowed to return less data than is
561  * requested.  fuse(4) should return a short read to userland.
562  */
563 TEST_F(Read, direct_io_short_read)
564 {
565         const char FULLPATH[] = "mountpoint/some_file.txt";
566         const char RELPATH[] = "some_file.txt";
567         const char *CONTENTS = "abcdefghijklmnop";
568         uint64_t ino = 42;
569         int fd;
570         uint64_t offset = 100;
571         ssize_t bufsize = strlen(CONTENTS);
572         ssize_t halfbufsize = bufsize / 2;
573         uint8_t buf[bufsize];
574
575         expect_lookup(RELPATH, ino, offset + bufsize);
576         expect_open(ino, FOPEN_DIRECT_IO, 1);
577         expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
578
579         fd = open(FULLPATH, O_RDONLY);
580         ASSERT_LE(0, fd) << strerror(errno);
581
582         ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
583                 << strerror(errno);
584         ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
585         leak(fd);
586 }
587
588 TEST_F(Read, eio)
589 {
590         const char FULLPATH[] = "mountpoint/some_file.txt";
591         const char RELPATH[] = "some_file.txt";
592         const char *CONTENTS = "abcdefgh";
593         uint64_t ino = 42;
594         int fd;
595         ssize_t bufsize = strlen(CONTENTS);
596         uint8_t buf[bufsize];
597
598         expect_lookup(RELPATH, ino, bufsize);
599         expect_open(ino, 0, 1);
600         EXPECT_CALL(*m_mock, process(
601                 ResultOf([=](auto in) {
602                         return (in.header.opcode == FUSE_READ);
603                 }, Eq(true)),
604                 _)
605         ).WillOnce(Invoke(ReturnErrno(EIO)));
606
607         fd = open(FULLPATH, O_RDONLY);
608         ASSERT_LE(0, fd) << strerror(errno);
609
610         ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
611         ASSERT_EQ(EIO, errno);
612         leak(fd);
613 }
614
615 /* 
616  * If the server returns a short read when direct io is not in use, that
617  * indicates EOF, because of a server-side truncation.  We should invalidate
618  * all cached attributes.  We may update the file size, 
619  */
620 TEST_F(Read, eof)
621 {
622         const char FULLPATH[] = "mountpoint/some_file.txt";
623         const char RELPATH[] = "some_file.txt";
624         const char *CONTENTS = "abcdefghijklmnop";
625         uint64_t ino = 42;
626         int fd;
627         uint64_t offset = 100;
628         ssize_t bufsize = strlen(CONTENTS);
629         ssize_t partbufsize = 3 * bufsize / 4;
630         ssize_t r;
631         uint8_t buf[bufsize];
632         struct stat sb;
633
634         expect_lookup(RELPATH, ino, offset + bufsize);
635         expect_open(ino, 0, 1);
636         expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
637         expect_getattr(ino, offset + partbufsize);
638
639         fd = open(FULLPATH, O_RDONLY);
640         ASSERT_LE(0, fd) << strerror(errno);
641
642         r = pread(fd, buf, bufsize, offset);
643         ASSERT_LE(0, r) << strerror(errno);
644         EXPECT_EQ(partbufsize, r) << strerror(errno);
645         ASSERT_EQ(0, fstat(fd, &sb));
646         EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
647         leak(fd);
648 }
649
650 /* Like Read.eof, but causes an entire buffer to be invalidated */
651 TEST_F(Read, eof_of_whole_buffer)
652 {
653         const char FULLPATH[] = "mountpoint/some_file.txt";
654         const char RELPATH[] = "some_file.txt";
655         const char *CONTENTS = "abcdefghijklmnop";
656         uint64_t ino = 42;
657         int fd;
658         ssize_t bufsize = strlen(CONTENTS);
659         off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
660         uint8_t buf[bufsize];
661         struct stat sb;
662
663         expect_lookup(RELPATH, ino, old_filesize);
664         expect_open(ino, 0, 1);
665         expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
666         expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
667         expect_getattr(ino, m_maxbcachebuf);
668
669         fd = open(FULLPATH, O_RDONLY);
670         ASSERT_LE(0, fd) << strerror(errno);
671
672         /* Cache the third block */
673         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
674                 << strerror(errno);
675         /* Try to read the 2nd block, but it's past EOF */
676         ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
677                 << strerror(errno);
678         ASSERT_EQ(0, fstat(fd, &sb));
679         EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
680         leak(fd);
681 }
682
683 /* 
684  * With the keep_cache option, the kernel may keep its read cache across
685  * multiple open(2)s.
686  */
687 TEST_F(Read, keep_cache)
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 fd0, fd1;
694         ssize_t bufsize = strlen(CONTENTS);
695         uint8_t buf[bufsize];
696
697         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
698         expect_open(ino, FOPEN_KEEP_CACHE, 2);
699         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
700
701         fd0 = open(FULLPATH, O_RDONLY);
702         ASSERT_LE(0, fd0) << strerror(errno);
703         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
704
705         fd1 = open(FULLPATH, O_RDWR);
706         ASSERT_LE(0, fd1) << strerror(errno);
707
708         /*
709          * This read should be serviced by cache, even though it's on the other
710          * file descriptor
711          */
712         ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
713
714         leak(fd0);
715         leak(fd1);
716 }
717
718 /* 
719  * Without the keep_cache option, the kernel should drop its read caches on
720  * every open
721  */
722 TEST_F(Read, keep_cache_disabled)
723 {
724         const char FULLPATH[] = "mountpoint/some_file.txt";
725         const char RELPATH[] = "some_file.txt";
726         const char *CONTENTS = "abcdefgh";
727         uint64_t ino = 42;
728         int fd0, fd1;
729         ssize_t bufsize = strlen(CONTENTS);
730         uint8_t buf[bufsize];
731
732         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
733         expect_open(ino, 0, 2);
734         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
735
736         fd0 = open(FULLPATH, O_RDONLY);
737         ASSERT_LE(0, fd0) << strerror(errno);
738         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
739
740         fd1 = open(FULLPATH, O_RDWR);
741         ASSERT_LE(0, fd1) << strerror(errno);
742
743         /*
744          * This read should not be serviced by cache, even though it's on the
745          * original file descriptor
746          */
747         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
748         ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
749         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
750
751         leak(fd0);
752         leak(fd1);
753 }
754
755 TEST_F(Read, mmap)
756 {
757         const char FULLPATH[] = "mountpoint/some_file.txt";
758         const char RELPATH[] = "some_file.txt";
759         const char *CONTENTS = "abcdefgh";
760         uint64_t ino = 42;
761         int fd;
762         ssize_t len;
763         size_t bufsize = strlen(CONTENTS);
764         void *p;
765
766         len = getpagesize();
767
768         expect_lookup(RELPATH, ino, bufsize);
769         expect_open(ino, 0, 1);
770         EXPECT_CALL(*m_mock, process(
771                 ResultOf([=](auto in) {
772                         return (in.header.opcode == FUSE_READ &&
773                                 in.header.nodeid == ino &&
774                                 in.body.read.fh == Read::FH &&
775                                 in.body.read.offset == 0 &&
776                                 in.body.read.size == bufsize);
777                 }, Eq(true)),
778                 _)
779         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
780                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
781                 memmove(out.body.bytes, CONTENTS, bufsize);
782         })));
783
784         fd = open(FULLPATH, O_RDONLY);
785         ASSERT_LE(0, fd) << strerror(errno);
786
787         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
788         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
789
790         ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
791
792         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
793         leak(fd);
794 }
795
796 /*
797  * The kernel should not update the cached atime attribute during a read, if
798  * MNT_NOATIME is used.
799  */
800 TEST_F(ReadNoatime, atime)
801 {
802         const char FULLPATH[] = "mountpoint/some_file.txt";
803         const char RELPATH[] = "some_file.txt";
804         const char *CONTENTS = "abcdefgh";
805         struct stat sb1, sb2;
806         uint64_t ino = 42;
807         int fd;
808         ssize_t bufsize = strlen(CONTENTS);
809         uint8_t buf[bufsize];
810
811         expect_lookup(RELPATH, ino, bufsize);
812         expect_open(ino, 0, 1);
813         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
814
815         fd = open(FULLPATH, O_RDONLY);
816         ASSERT_LE(0, fd) << strerror(errno);
817         ASSERT_EQ(0, fstat(fd, &sb1));
818
819         nap();
820
821         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
822         ASSERT_EQ(0, fstat(fd, &sb2));
823
824         /* The kernel should not update atime during read */
825         EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
826         EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
827         EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
828
829         leak(fd);
830 }
831
832 /*
833  * The kernel should not update the cached atime attribute during a cached
834  * read, if MNT_NOATIME is used.
835  */
836 TEST_F(ReadNoatime, atime_cached)
837 {
838         const char FULLPATH[] = "mountpoint/some_file.txt";
839         const char RELPATH[] = "some_file.txt";
840         const char *CONTENTS = "abcdefgh";
841         struct stat sb1, sb2;
842         uint64_t ino = 42;
843         int fd;
844         ssize_t bufsize = strlen(CONTENTS);
845         uint8_t buf[bufsize];
846
847         expect_lookup(RELPATH, ino, bufsize);
848         expect_open(ino, 0, 1);
849         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
850
851         fd = open(FULLPATH, O_RDONLY);
852         ASSERT_LE(0, fd) << strerror(errno);
853
854         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
855         ASSERT_EQ(0, fstat(fd, &sb1));
856
857         nap();
858
859         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
860         ASSERT_EQ(0, fstat(fd, &sb2));
861
862         /* The kernel should automatically update atime during read */
863         EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
864         EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
865         EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
866
867         leak(fd);
868 }
869
870 /* Read of an mmap()ed file fails */
871 TEST_F(ReadSigbus, mmap_eio)
872 {
873         const char FULLPATH[] = "mountpoint/some_file.txt";
874         const char RELPATH[] = "some_file.txt";
875         const char *CONTENTS = "abcdefgh";
876         struct sigaction sa;
877         uint64_t ino = 42;
878         int fd;
879         ssize_t len;
880         size_t bufsize = strlen(CONTENTS);
881         void *p;
882
883         len = getpagesize();
884
885         expect_lookup(RELPATH, ino, bufsize);
886         expect_open(ino, 0, 1);
887         EXPECT_CALL(*m_mock, process(
888                 ResultOf([=](auto in) {
889                         return (in.header.opcode == FUSE_READ &&
890                                 in.header.nodeid == ino &&
891                                 in.body.read.fh == Read::FH);
892                 }, Eq(true)),
893                 _)
894         ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
895
896         fd = open(FULLPATH, O_RDONLY);
897         ASSERT_LE(0, fd) << strerror(errno);
898
899         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
900         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
901
902         /* Accessing the mapped page should return SIGBUS.  */
903
904         bzero(&sa, sizeof(sa));
905         sa.sa_handler = SIG_DFL;
906         sa.sa_sigaction = handle_sigbus;
907         sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
908         ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
909         if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
910                 atomic_signal_fence(std::memory_order::memory_order_seq_cst);
911                 volatile char x __unused = *(volatile char*)p;
912                 FAIL() << "shouldn't get here";
913         }
914
915         ASSERT_EQ(p, ReadSigbus::s_si_addr);
916         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
917         leak(fd);
918 }
919
920 /* 
921  * A read via mmap comes up short, indicating that the file was truncated
922  * server-side.
923  */
924 TEST_F(Read, mmap_eof)
925 {
926         const char FULLPATH[] = "mountpoint/some_file.txt";
927         const char RELPATH[] = "some_file.txt";
928         const char *CONTENTS = "abcdefgh";
929         uint64_t ino = 42;
930         int fd;
931         ssize_t len;
932         size_t bufsize = strlen(CONTENTS);
933         struct stat sb;
934         void *p;
935
936         len = getpagesize();
937
938         expect_lookup(RELPATH, ino, m_maxbcachebuf);
939         expect_open(ino, 0, 1);
940         EXPECT_CALL(*m_mock, process(
941                 ResultOf([=](auto in) {
942                         return (in.header.opcode == FUSE_READ &&
943                                 in.header.nodeid == ino &&
944                                 in.body.read.fh == Read::FH &&
945                                 in.body.read.offset == 0 &&
946                                 in.body.read.size == (uint32_t)m_maxbcachebuf);
947                 }, Eq(true)),
948                 _)
949         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
950                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
951                 memmove(out.body.bytes, CONTENTS, bufsize);
952         })));
953         expect_getattr(ino, bufsize);
954
955         fd = open(FULLPATH, O_RDONLY);
956         ASSERT_LE(0, fd) << strerror(errno);
957
958         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
959         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
960
961         /* The file size should be automatically truncated */
962         ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
963         ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
964         EXPECT_EQ((off_t)bufsize, sb.st_size);
965
966         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
967         leak(fd);
968 }
969
970 /*
971  * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation.  This
972  * almost certainly indicates a buggy FUSE server, and our goal should be not
973  * to panic.  Instead, generate SIGBUS.
974  */
975 TEST_F(ReadSigbus, mmap_getblksz_fail)
976 {
977         const char FULLPATH[] = "mountpoint/some_file.txt";
978         const char RELPATH[] = "some_file.txt";
979         const char *CONTENTS = "abcdefgh";
980         struct sigaction sa;
981         Sequence seq;
982         uint64_t ino = 42;
983         int fd;
984         ssize_t len;
985         size_t bufsize = strlen(CONTENTS);
986         mode_t mode = S_IFREG | 0644;
987         void *p;
988
989         len = getpagesize();
990
991         FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0);
992         /* Expect two GETATTR calls that succeed, followed by one that fail. */
993         EXPECT_CALL(*m_mock, process(
994                 ResultOf([=](auto in) {
995                         return (in.header.opcode == FUSE_GETATTR &&
996                                 in.header.nodeid == ino);
997                 }, Eq(true)),
998                 _)
999         ).Times(2)
1000         .InSequence(seq)
1001         .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
1002                 SET_OUT_HEADER_LEN(out, attr);
1003                 out.body.attr.attr.ino = ino;
1004                 out.body.attr.attr.mode = mode;
1005                 out.body.attr.attr.size = bufsize;
1006                 out.body.attr.attr_valid = 0;
1007         })));
1008         EXPECT_CALL(*m_mock, process(
1009                 ResultOf([=](auto in) {
1010                         return (in.header.opcode == FUSE_GETATTR &&
1011                                 in.header.nodeid == ino);
1012                 }, Eq(true)),
1013                 _)
1014         ).InSequence(seq)
1015         .WillRepeatedly(Invoke(ReturnErrno(EIO)));
1016         expect_open(ino, 0, 1);
1017         EXPECT_CALL(*m_mock, process(
1018                 ResultOf([=](auto in) {
1019                         return (in.header.opcode == FUSE_READ);
1020                 }, Eq(true)),
1021                 _)
1022         ).Times(0);
1023
1024         fd = open(FULLPATH, O_RDONLY);
1025         ASSERT_LE(0, fd) << strerror(errno);
1026
1027         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1028         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1029
1030         /* Accessing the mapped page should return SIGBUS.  */
1031         bzero(&sa, sizeof(sa));
1032         sa.sa_handler = SIG_DFL;
1033         sa.sa_sigaction = handle_sigbus;
1034         sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
1035         ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
1036         if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
1037                 atomic_signal_fence(std::memory_order::memory_order_seq_cst);
1038                 volatile char x __unused = *(volatile char*)p;
1039                 FAIL() << "shouldn't get here";
1040         }
1041
1042         ASSERT_EQ(p, ReadSigbus::s_si_addr);
1043         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1044         leak(fd);
1045 }
1046
1047 /*
1048  * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
1049  * cache and to straight to the daemon
1050  */
1051 TEST_F(Read, o_direct)
1052 {
1053         const char FULLPATH[] = "mountpoint/some_file.txt";
1054         const char RELPATH[] = "some_file.txt";
1055         const char *CONTENTS = "abcdefgh";
1056         uint64_t ino = 42;
1057         int fd;
1058         ssize_t bufsize = strlen(CONTENTS);
1059         uint8_t buf[bufsize];
1060
1061         expect_lookup(RELPATH, ino, bufsize);
1062         expect_open(ino, 0, 1);
1063         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1064
1065         fd = open(FULLPATH, O_RDONLY);
1066         ASSERT_LE(0, fd) << strerror(errno);
1067
1068         // Fill the cache
1069         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1070         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1071
1072         // Reads with o_direct should bypass the cache
1073         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1074         ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1075         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1076         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1077         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1078
1079         leak(fd);
1080 }
1081
1082 TEST_F(Read, pread)
1083 {
1084         const char FULLPATH[] = "mountpoint/some_file.txt";
1085         const char RELPATH[] = "some_file.txt";
1086         const char *CONTENTS = "abcdefgh";
1087         uint64_t ino = 42;
1088         int fd;
1089         /* 
1090          * Set offset to a maxbcachebuf boundary so we'll be sure what offset
1091          * to read from.  Without this, the read might start at a lower offset.
1092          */
1093         uint64_t offset = m_maxbcachebuf;
1094         ssize_t bufsize = strlen(CONTENTS);
1095         uint8_t buf[bufsize];
1096
1097         expect_lookup(RELPATH, ino, offset + bufsize);
1098         expect_open(ino, 0, 1);
1099         expect_read(ino, offset, bufsize, bufsize, CONTENTS);
1100
1101         fd = open(FULLPATH, O_RDONLY);
1102         ASSERT_LE(0, fd) << strerror(errno);
1103
1104         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
1105         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1106         leak(fd);
1107 }
1108
1109 TEST_F(Read, read)
1110 {
1111         const char FULLPATH[] = "mountpoint/some_file.txt";
1112         const char RELPATH[] = "some_file.txt";
1113         const char *CONTENTS = "abcdefgh";
1114         uint64_t ino = 42;
1115         int fd;
1116         ssize_t bufsize = strlen(CONTENTS);
1117         uint8_t buf[bufsize];
1118
1119         expect_lookup(RELPATH, ino, bufsize);
1120         expect_open(ino, 0, 1);
1121         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1122
1123         fd = open(FULLPATH, O_RDONLY);
1124         ASSERT_LE(0, fd) << strerror(errno);
1125
1126         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1127         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1128
1129         leak(fd);
1130 }
1131
1132 TEST_F(Read_7_8, read)
1133 {
1134         const char FULLPATH[] = "mountpoint/some_file.txt";
1135         const char RELPATH[] = "some_file.txt";
1136         const char *CONTENTS = "abcdefgh";
1137         uint64_t ino = 42;
1138         int fd;
1139         ssize_t bufsize = strlen(CONTENTS);
1140         uint8_t buf[bufsize];
1141
1142         expect_lookup(RELPATH, ino, bufsize);
1143         expect_open(ino, 0, 1);
1144         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1145
1146         fd = open(FULLPATH, O_RDONLY);
1147         ASSERT_LE(0, fd) << strerror(errno);
1148
1149         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1150         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1151
1152         leak(fd);
1153 }
1154
1155 /* 
1156  * If cacheing is enabled, the kernel should try to read an entire cache block
1157  * at a time.
1158  */
1159 TEST_F(Read, cache_block)
1160 {
1161         const char FULLPATH[] = "mountpoint/some_file.txt";
1162         const char RELPATH[] = "some_file.txt";
1163         const char *CONTENTS0 = "abcdefghijklmnop";
1164         uint64_t ino = 42;
1165         int fd;
1166         ssize_t bufsize = 8;
1167         ssize_t filesize = m_maxbcachebuf * 2;
1168         char *contents;
1169         char buf[bufsize];
1170         const char *contents1 = CONTENTS0 + bufsize;
1171
1172         contents = (char*)calloc(1, filesize);
1173         ASSERT_NE(nullptr, contents);
1174         memmove(contents, CONTENTS0, strlen(CONTENTS0));
1175
1176         expect_lookup(RELPATH, ino, filesize);
1177         expect_open(ino, 0, 1);
1178         expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
1179                 contents);
1180
1181         fd = open(FULLPATH, O_RDONLY);
1182         ASSERT_LE(0, fd) << strerror(errno);
1183
1184         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1185         ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
1186
1187         /* A subsequent read should be serviced by cache */
1188         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1189         ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
1190         leak(fd);
1191         free(contents);
1192 }
1193
1194 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
1195 TEST_F(Read, sendfile)
1196 {
1197         const char FULLPATH[] = "mountpoint/some_file.txt";
1198         const char RELPATH[] = "some_file.txt";
1199         const char *CONTENTS = "abcdefgh";
1200         uint64_t ino = 42;
1201         int fd;
1202         size_t bufsize = strlen(CONTENTS);
1203         uint8_t buf[bufsize];
1204         int sp[2];
1205         off_t sbytes;
1206
1207         expect_lookup(RELPATH, ino, bufsize);
1208         expect_open(ino, 0, 1);
1209         EXPECT_CALL(*m_mock, process(
1210                 ResultOf([=](auto in) {
1211                         return (in.header.opcode == FUSE_READ &&
1212                                 in.header.nodeid == ino &&
1213                                 in.body.read.fh == Read::FH &&
1214                                 in.body.read.offset == 0 &&
1215                                 in.body.read.size == bufsize);
1216                 }, Eq(true)),
1217                 _)
1218         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1219                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
1220                 memmove(out.body.bytes, CONTENTS, bufsize);
1221         })));
1222
1223         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
1224                 << strerror(errno);
1225         fd = open(FULLPATH, O_RDONLY);
1226         ASSERT_LE(0, fd) << strerror(errno);
1227
1228         ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
1229                 << strerror(errno);
1230         ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
1231                 << strerror(errno);
1232         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1233
1234         close(sp[1]);
1235         close(sp[0]);
1236         leak(fd);
1237 }
1238
1239 /* sendfile should fail gracefully if fuse declines the read */
1240 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
1241 TEST_F(Read, sendfile_eio)
1242 {
1243         const char FULLPATH[] = "mountpoint/some_file.txt";
1244         const char RELPATH[] = "some_file.txt";
1245         const char *CONTENTS = "abcdefgh";
1246         uint64_t ino = 42;
1247         int fd;
1248         ssize_t bufsize = strlen(CONTENTS);
1249         int sp[2];
1250         off_t sbytes;
1251
1252         expect_lookup(RELPATH, ino, bufsize);
1253         expect_open(ino, 0, 1);
1254         EXPECT_CALL(*m_mock, process(
1255                 ResultOf([=](auto in) {
1256                         return (in.header.opcode == FUSE_READ);
1257                 }, Eq(true)),
1258                 _)
1259         ).WillOnce(Invoke(ReturnErrno(EIO)));
1260
1261         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
1262                 << strerror(errno);
1263         fd = open(FULLPATH, O_RDONLY);
1264         ASSERT_LE(0, fd) << strerror(errno);
1265
1266         ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
1267
1268         close(sp[1]);
1269         close(sp[0]);
1270         leak(fd);
1271 }
1272
1273 /*
1274  * Sequential reads should use readahead.  And if allowed, large reads should
1275  * be clustered.
1276  */
1277 TEST_P(ReadAhead, readahead) {
1278         const char FULLPATH[] = "mountpoint/some_file.txt";
1279         const char RELPATH[] = "some_file.txt";
1280         uint64_t ino = 42;
1281         int fd, maxcontig, clustersize;
1282         ssize_t bufsize = 4 * m_maxbcachebuf;
1283         ssize_t filesize = bufsize;
1284         uint64_t len;
1285         char *rbuf, *contents;
1286         off_t offs;
1287
1288         contents = (char*)malloc(filesize);
1289         ASSERT_NE(nullptr, contents);
1290         memset(contents, 'X', filesize);
1291         rbuf = (char*)calloc(1, bufsize);
1292
1293         expect_lookup(RELPATH, ino, filesize);
1294         expect_open(ino, 0, 1);
1295         maxcontig = m_noclusterr ? m_maxbcachebuf :
1296                 m_maxbcachebuf + m_maxreadahead;
1297         clustersize = MIN(maxcontig, m_maxphys);
1298         for (offs = 0; offs < bufsize; offs += clustersize) {
1299                 len = std::min((size_t)clustersize, (size_t)(filesize - offs));
1300                 expect_read(ino, offs, len, len, contents + offs);
1301         }
1302
1303         fd = open(FULLPATH, O_RDONLY);
1304         ASSERT_LE(0, fd) << strerror(errno);
1305
1306         /* Set the internal readahead counter to a "large" value */
1307         ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
1308
1309         ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
1310         ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
1311
1312         leak(fd);
1313         free(rbuf);
1314         free(contents);
1315 }
1316
1317 INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
1318         Values(tuple<bool, int>(false, 0),
1319                tuple<bool, int>(false, 1),
1320                tuple<bool, int>(false, 2),
1321                tuple<bool, int>(false, 3),
1322                tuple<bool, int>(true, 0),
1323                tuple<bool, int>(true, 1),
1324                tuple<bool, int>(true, 2)));
1325
1326 /* fuse_init_out.time_gran controls the granularity of timestamps */
1327 TEST_P(TimeGran, atime_during_setattr)
1328 {
1329         const char FULLPATH[] = "mountpoint/some_file.txt";
1330         const char RELPATH[] = "some_file.txt";
1331         const char *CONTENTS = "abcdefgh";
1332         ssize_t bufsize = strlen(CONTENTS);
1333         uint8_t buf[bufsize];
1334         uint64_t ino = 42;
1335         const mode_t newmode = 0755;
1336         int fd;
1337
1338         expect_lookup(RELPATH, ino, bufsize);
1339         expect_open(ino, 0, 1);
1340         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1341         EXPECT_CALL(*m_mock, process(
1342                 ResultOf([=](auto in) {
1343                         uint32_t valid = FATTR_MODE | FATTR_ATIME;
1344                         return (in.header.opcode == FUSE_SETATTR &&
1345                                 in.header.nodeid == ino &&
1346                                 in.body.setattr.valid == valid &&
1347                                 in.body.setattr.atimensec % m_time_gran == 0);
1348                 }, Eq(true)),
1349                 _)
1350         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1351                 SET_OUT_HEADER_LEN(out, attr);
1352                 out.body.attr.attr.ino = ino;
1353                 out.body.attr.attr.mode = S_IFREG | newmode;
1354         })));
1355
1356         fd = open(FULLPATH, O_RDWR);
1357         ASSERT_LE(0, fd) << strerror(errno);
1358
1359         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1360         ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1361
1362         leak(fd);
1363 }
1364
1365 INSTANTIATE_TEST_CASE_P(TG, TimeGran, Range(0u, 10u));