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