]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/read.cc
fusefs: correctly handle short reads
[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 = 65536;
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 = 65536;
247         struct aiocb iocb0, iocb1;
248         sem_t sem;
249
250         ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
251
252         expect_lookup(RELPATH, ino, 131072);
253         expect_open(ino, 0, 1);
254         EXPECT_CALL(*m_mock, process(
255                 ResultOf([=](auto in) {
256                         return (in.header.opcode == FUSE_READ &&
257                                 in.header.nodeid == ino &&
258                                 in.body.read.fh == FH &&
259                                 in.body.read.offset == (uint64_t)off0);
260                 }, Eq(true)),
261                 _)
262         ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
263                 sem_post(&sem);
264                 /* Filesystem is slow to respond */
265         }));
266         EXPECT_CALL(*m_mock, process(
267                 ResultOf([=](auto in) {
268                         return (in.header.opcode == FUSE_READ &&
269                                 in.header.nodeid == ino &&
270                                 in.body.read.fh == FH &&
271                                 in.body.read.offset == (uint64_t)off1);
272                 }, Eq(true)),
273                 _)
274         ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
275                 sem_post(&sem);
276                 /* Filesystem is slow to respond */
277         }));
278
279         fd = open(FULLPATH, O_RDONLY);
280         ASSERT_LE(0, fd) << strerror(errno);
281
282         /* 
283          * Submit two AIO read requests, but respond to neither.  Ensure that
284          * we received both.
285          */
286         iocb0.aio_nbytes = bufsize;
287         iocb0.aio_fildes = fd;
288         iocb0.aio_buf = buf0;
289         iocb0.aio_offset = off0;
290         iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
291         ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
292
293         iocb1.aio_nbytes = bufsize;
294         iocb1.aio_fildes = fd;
295         iocb1.aio_buf = buf1;
296         iocb1.aio_offset = off1;
297         iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
298         ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
299
300         /* Wait until both reads have reached the daemon */
301         ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
302         ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
303
304         m_mock->kill_daemon();
305         /* Wait for AIO activity to complete, but ignore errors */
306         (void)aio_waitcomplete(NULL, NULL);
307         
308         /* Deliberately leak fd.  close(2) will be tested in release.cc */
309 }
310
311 /* 0-length reads shouldn't cause any confusion */
312 TEST_F(Read, direct_io_read_nothing)
313 {
314         const char FULLPATH[] = "mountpoint/some_file.txt";
315         const char RELPATH[] = "some_file.txt";
316         uint64_t ino = 42;
317         int fd;
318         uint64_t offset = 100;
319         char buf[80];
320
321         expect_lookup(RELPATH, ino, offset + 1000);
322         expect_open(ino, FOPEN_DIRECT_IO, 1);
323
324         fd = open(FULLPATH, O_RDONLY);
325         ASSERT_LE(0, fd) << strerror(errno);
326
327         ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
328         /* Deliberately leak fd.  close(2) will be tested in release.cc */
329 }
330
331 /* 
332  * With direct_io, reads should not fill the cache.  They should go straight to
333  * the daemon
334  */
335 TEST_F(Read, direct_io_pread)
336 {
337         const char FULLPATH[] = "mountpoint/some_file.txt";
338         const char RELPATH[] = "some_file.txt";
339         const char *CONTENTS = "abcdefgh";
340         uint64_t ino = 42;
341         int fd;
342         uint64_t offset = 100;
343         ssize_t bufsize = strlen(CONTENTS);
344         char buf[bufsize];
345
346         expect_lookup(RELPATH, ino, offset + bufsize);
347         expect_open(ino, FOPEN_DIRECT_IO, 1);
348         expect_read(ino, offset, bufsize, bufsize, CONTENTS);
349
350         fd = open(FULLPATH, O_RDONLY);
351         ASSERT_LE(0, fd) << strerror(errno);
352
353         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
354         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
355         /* Deliberately leak fd.  close(2) will be tested in release.cc */
356 }
357
358 /* 
359  * With direct_io, filesystems are allowed to return less data than is
360  * requested.  fuse(4) should return a short read to userland.
361  */
362 TEST_F(Read, direct_io_short_read)
363 {
364         const char FULLPATH[] = "mountpoint/some_file.txt";
365         const char RELPATH[] = "some_file.txt";
366         const char *CONTENTS = "abcdefghijklmnop";
367         uint64_t ino = 42;
368         int fd;
369         uint64_t offset = 100;
370         ssize_t bufsize = strlen(CONTENTS);
371         ssize_t halfbufsize = bufsize / 2;
372         char buf[bufsize];
373
374         expect_lookup(RELPATH, ino, offset + bufsize);
375         expect_open(ino, FOPEN_DIRECT_IO, 1);
376         expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
377
378         fd = open(FULLPATH, O_RDONLY);
379         ASSERT_LE(0, fd) << strerror(errno);
380
381         ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
382                 << strerror(errno);
383         ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
384         /* Deliberately leak fd.  close(2) will be tested in release.cc */
385 }
386
387 TEST_F(Read, eio)
388 {
389         const char FULLPATH[] = "mountpoint/some_file.txt";
390         const char RELPATH[] = "some_file.txt";
391         const char *CONTENTS = "abcdefgh";
392         uint64_t ino = 42;
393         int fd;
394         ssize_t bufsize = strlen(CONTENTS);
395         char buf[bufsize];
396
397         expect_lookup(RELPATH, ino, bufsize);
398         expect_open(ino, 0, 1);
399         EXPECT_CALL(*m_mock, process(
400                 ResultOf([=](auto in) {
401                         return (in.header.opcode == FUSE_READ);
402                 }, Eq(true)),
403                 _)
404         ).WillOnce(Invoke(ReturnErrno(EIO)));
405
406         fd = open(FULLPATH, O_RDONLY);
407         ASSERT_LE(0, fd) << strerror(errno);
408
409         ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
410         ASSERT_EQ(EIO, errno);
411         /* Deliberately leak fd.  close(2) will be tested in release.cc */
412 }
413
414 /* 
415  * If the server returns a short read when direct io is not in use, that
416  * indicates EOF and we should update the file size.
417  */
418 TEST_F(ReadCacheable, eof)
419 {
420         const char FULLPATH[] = "mountpoint/some_file.txt";
421         const char RELPATH[] = "some_file.txt";
422         const char *CONTENTS = "abcdefghijklmnop";
423         uint64_t ino = 42;
424         int fd;
425         uint64_t offset = 100;
426         ssize_t bufsize = strlen(CONTENTS);
427         ssize_t partbufsize = 3 * bufsize / 4;
428         char buf[bufsize];
429         struct stat sb;
430
431         expect_lookup(RELPATH, ino, offset + bufsize);
432         expect_open(ino, 0, 1);
433         expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
434
435         fd = open(FULLPATH, O_RDONLY);
436         ASSERT_LE(0, fd) << strerror(errno);
437
438         ASSERT_EQ(partbufsize, pread(fd, buf, bufsize, offset))
439                 << strerror(errno);
440         ASSERT_EQ(0, fstat(fd, &sb));
441         EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
442         /* Deliberately leak fd.  close(2) will be tested in release.cc */
443 }
444
445 /* Like ReadCacheable.eof, but causes an entire buffer to be invalidated */
446 TEST_F(ReadCacheable, eof_of_whole_buffer)
447 {
448         const char FULLPATH[] = "mountpoint/some_file.txt";
449         const char RELPATH[] = "some_file.txt";
450         const char *CONTENTS = "abcdefghijklmnop";
451         uint64_t ino = 42;
452         int fd;
453         ssize_t bufsize = strlen(CONTENTS);
454         off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
455         char buf[bufsize];
456         struct stat sb;
457
458         expect_lookup(RELPATH, ino, old_filesize);
459         expect_open(ino, 0, 1);
460         expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
461         expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
462
463         fd = open(FULLPATH, O_RDONLY);
464         ASSERT_LE(0, fd) << strerror(errno);
465
466         /* Cache the third block */
467         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
468                 << strerror(errno);
469         /* Try to read the 2nd block, but it's past EOF */
470         ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
471                 << strerror(errno);
472         ASSERT_EQ(0, fstat(fd, &sb));
473         EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
474         /* Deliberately leak fd.  close(2) will be tested in release.cc */
475 }
476
477 /* 
478  * With the keep_cache option, the kernel may keep its read cache across
479  * multiple open(2)s.
480  */
481 TEST_F(ReadCacheable, keep_cache)
482 {
483         const char FULLPATH[] = "mountpoint/some_file.txt";
484         const char RELPATH[] = "some_file.txt";
485         const char *CONTENTS = "abcdefgh";
486         uint64_t ino = 42;
487         int fd0, fd1;
488         ssize_t bufsize = strlen(CONTENTS);
489         char buf[bufsize];
490
491         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
492         expect_open(ino, FOPEN_KEEP_CACHE, 2);
493         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
494
495         fd0 = open(FULLPATH, O_RDONLY);
496         ASSERT_LE(0, fd0) << strerror(errno);
497         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
498
499         fd1 = open(FULLPATH, O_RDWR);
500         ASSERT_LE(0, fd1) << strerror(errno);
501
502         /*
503          * This read should be serviced by cache, even though it's on the other
504          * file descriptor
505          */
506         ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
507
508         /* Deliberately leak fd0 and fd1. */
509 }
510
511 /* 
512  * Without the keep_cache option, the kernel should drop its read caches on
513  * every open
514  */
515 TEST_F(Read, keep_cache_disabled)
516 {
517         const char FULLPATH[] = "mountpoint/some_file.txt";
518         const char RELPATH[] = "some_file.txt";
519         const char *CONTENTS = "abcdefgh";
520         uint64_t ino = 42;
521         int fd0, fd1;
522         ssize_t bufsize = strlen(CONTENTS);
523         char buf[bufsize];
524
525         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
526         expect_open(ino, 0, 2);
527         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
528
529         fd0 = open(FULLPATH, O_RDONLY);
530         ASSERT_LE(0, fd0) << strerror(errno);
531         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
532
533         fd1 = open(FULLPATH, O_RDWR);
534         ASSERT_LE(0, fd1) << strerror(errno);
535
536         /*
537          * This read should not be serviced by cache, even though it's on the
538          * original file descriptor
539          */
540         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
541         ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
542         ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
543
544         /* Deliberately leak fd0 and fd1. */
545 }
546
547 TEST_F(ReadCacheable, mmap)
548 {
549         const char FULLPATH[] = "mountpoint/some_file.txt";
550         const char RELPATH[] = "some_file.txt";
551         const char *CONTENTS = "abcdefgh";
552         uint64_t ino = 42;
553         int fd;
554         ssize_t len;
555         size_t bufsize = strlen(CONTENTS);
556         void *p;
557
558         len = getpagesize();
559
560         expect_lookup(RELPATH, ino, bufsize);
561         expect_open(ino, 0, 1);
562         /* mmap may legitimately try to read more data than is available */
563         EXPECT_CALL(*m_mock, process(
564                 ResultOf([=](auto in) {
565                         return (in.header.opcode == FUSE_READ &&
566                                 in.header.nodeid == ino &&
567                                 in.body.read.fh == Read::FH &&
568                                 in.body.read.offset == 0 &&
569                                 in.body.read.size >= bufsize);
570                 }, Eq(true)),
571                 _)
572         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
573                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
574                 memmove(out.body.bytes, CONTENTS, bufsize);
575         })));
576
577         fd = open(FULLPATH, O_RDONLY);
578         ASSERT_LE(0, fd) << strerror(errno);
579
580         p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
581         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
582
583         ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
584
585         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
586         /* Deliberately leak fd.  close(2) will be tested in release.cc */
587 }
588
589 /*
590  * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
591  * cache and to straight to the daemon
592  */
593 TEST_F(Read, o_direct)
594 {
595         const char FULLPATH[] = "mountpoint/some_file.txt";
596         const char RELPATH[] = "some_file.txt";
597         const char *CONTENTS = "abcdefgh";
598         uint64_t ino = 42;
599         int fd;
600         ssize_t bufsize = strlen(CONTENTS);
601         char buf[bufsize];
602
603         expect_lookup(RELPATH, ino, bufsize);
604         expect_open(ino, 0, 1);
605         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
606
607         fd = open(FULLPATH, O_RDONLY);
608         ASSERT_LE(0, fd) << strerror(errno);
609
610         // Fill the cache
611         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
612         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
613
614         // Reads with o_direct should bypass the cache
615         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
616         ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
617         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
618         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
619         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
620         
621         /* Deliberately leak fd.  close(2) will be tested in release.cc */
622 }
623
624 TEST_F(Read, pread)
625 {
626         const char FULLPATH[] = "mountpoint/some_file.txt";
627         const char RELPATH[] = "some_file.txt";
628         const char *CONTENTS = "abcdefgh";
629         uint64_t ino = 42;
630         int fd;
631         /* 
632          * Set offset to a maxbcachebuf boundary so we'll be sure what offset
633          * to read from.  Without this, the read might start at a lower offset.
634          */
635         uint64_t offset = m_maxbcachebuf;
636         ssize_t bufsize = strlen(CONTENTS);
637         char buf[bufsize];
638
639         expect_lookup(RELPATH, ino, offset + bufsize);
640         expect_open(ino, 0, 1);
641         expect_read(ino, offset, bufsize, bufsize, CONTENTS);
642
643         fd = open(FULLPATH, O_RDONLY);
644         ASSERT_LE(0, fd) << strerror(errno);
645
646         ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
647         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
648         /* Deliberately leak fd.  close(2) will be tested in release.cc */
649 }
650
651 TEST_F(Read, read)
652 {
653         const char FULLPATH[] = "mountpoint/some_file.txt";
654         const char RELPATH[] = "some_file.txt";
655         const char *CONTENTS = "abcdefgh";
656         uint64_t ino = 42;
657         int fd;
658         ssize_t bufsize = strlen(CONTENTS);
659         char buf[bufsize];
660
661         expect_lookup(RELPATH, ino, bufsize);
662         expect_open(ino, 0, 1);
663         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
664
665         fd = open(FULLPATH, O_RDONLY);
666         ASSERT_LE(0, fd) << strerror(errno);
667
668         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
669         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
670
671         /* Deliberately leak fd.  close(2) will be tested in release.cc */
672 }
673
674 TEST_F(Read_7_8, read)
675 {
676         const char FULLPATH[] = "mountpoint/some_file.txt";
677         const char RELPATH[] = "some_file.txt";
678         const char *CONTENTS = "abcdefgh";
679         uint64_t ino = 42;
680         int fd;
681         ssize_t bufsize = strlen(CONTENTS);
682         char buf[bufsize];
683
684         expect_lookup(RELPATH, ino, bufsize);
685         expect_open(ino, 0, 1);
686         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
687
688         fd = open(FULLPATH, O_RDONLY);
689         ASSERT_LE(0, fd) << strerror(errno);
690
691         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
692         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
693
694         /* Deliberately leak fd.  close(2) will be tested in release.cc */
695 }
696
697 /* 
698  * If cacheing is enabled, the kernel should try to read an entire cache block
699  * at a time.
700  */
701 TEST_F(ReadCacheable, cache_block)
702 {
703         const char FULLPATH[] = "mountpoint/some_file.txt";
704         const char RELPATH[] = "some_file.txt";
705         const char *CONTENTS0 = "abcdefghijklmnop";
706         uint64_t ino = 42;
707         int fd;
708         ssize_t bufsize = 8;
709         ssize_t filesize = m_maxbcachebuf * 2;
710         char *contents;
711         char buf[bufsize];
712         const char *contents1 = CONTENTS0 + bufsize;
713
714         contents = (char*)calloc(1, filesize);
715         ASSERT_NE(NULL, contents);
716         memmove(contents, CONTENTS0, strlen(CONTENTS0));
717
718         expect_lookup(RELPATH, ino, filesize);
719         expect_open(ino, 0, 1);
720         expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
721                 contents);
722
723         fd = open(FULLPATH, O_RDONLY);
724         ASSERT_LE(0, fd) << strerror(errno);
725
726         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
727         ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
728
729         /* A subsequent read should be serviced by cache */
730         ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
731         ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
732         /* Deliberately leak fd.  close(2) will be tested in release.cc */
733 }
734
735 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
736 TEST_F(ReadCacheable, sendfile)
737 {
738         const char FULLPATH[] = "mountpoint/some_file.txt";
739         const char RELPATH[] = "some_file.txt";
740         const char *CONTENTS = "abcdefgh";
741         uint64_t ino = 42;
742         int fd;
743         size_t bufsize = strlen(CONTENTS);
744         char buf[bufsize];
745         int sp[2];
746         off_t sbytes;
747
748         expect_lookup(RELPATH, ino, bufsize);
749         expect_open(ino, 0, 1);
750         /* Like mmap, sendfile may request more data than is available */
751         EXPECT_CALL(*m_mock, process(
752                 ResultOf([=](auto in) {
753                         return (in.header.opcode == FUSE_READ &&
754                                 in.header.nodeid == ino &&
755                                 in.body.read.fh == Read::FH &&
756                                 in.body.read.offset == 0 &&
757                                 in.body.read.size >= bufsize);
758                 }, Eq(true)),
759                 _)
760         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
761                 out.header.len = sizeof(struct fuse_out_header) + bufsize;
762                 memmove(out.body.bytes, CONTENTS, bufsize);
763         })));
764
765         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
766                 << strerror(errno);
767         fd = open(FULLPATH, O_RDONLY);
768         ASSERT_LE(0, fd) << strerror(errno);
769
770         ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
771                 << strerror(errno);
772         ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
773                 << strerror(errno);
774         ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
775
776         close(sp[1]);
777         close(sp[0]);
778         /* Deliberately leak fd.  close(2) will be tested in release.cc */
779 }
780
781 /* sendfile should fail gracefully if fuse declines the read */
782 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
783 TEST_F(ReadCacheable, DISABLED_sendfile_eio)
784 {
785         const char FULLPATH[] = "mountpoint/some_file.txt";
786         const char RELPATH[] = "some_file.txt";
787         const char *CONTENTS = "abcdefgh";
788         uint64_t ino = 42;
789         int fd;
790         ssize_t bufsize = strlen(CONTENTS);
791         int sp[2];
792         off_t sbytes;
793
794         expect_lookup(RELPATH, ino, bufsize);
795         expect_open(ino, 0, 1);
796         EXPECT_CALL(*m_mock, process(
797                 ResultOf([=](auto in) {
798                         return (in.header.opcode == FUSE_READ);
799                 }, Eq(true)),
800                 _)
801         ).WillOnce(Invoke(ReturnErrno(EIO)));
802
803         ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
804                 << strerror(errno);
805         fd = open(FULLPATH, O_RDONLY);
806         ASSERT_LE(0, fd) << strerror(errno);
807
808         ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
809
810         close(sp[1]);
811         close(sp[0]);
812         /* Deliberately leak fd.  close(2) will be tested in release.cc */
813 }
814
815 /*
816  * Sequential reads should use readahead.  And if allowed, large reads should
817  * be clustered.
818  */
819 TEST_P(ReadAhead, readahead) {
820         const char FULLPATH[] = "mountpoint/some_file.txt";
821         const char RELPATH[] = "some_file.txt";
822         uint64_t ino = 42;
823         int fd, maxcontig, clustersize;
824         ssize_t bufsize = 4 * m_maxbcachebuf;
825         ssize_t filesize = bufsize;
826         uint64_t len;
827         char *rbuf, *contents;
828         off_t offs;
829
830         contents = (char*)malloc(filesize);
831         ASSERT_NE(NULL, contents);
832         memset(contents, 'X', filesize);
833         rbuf = (char*)calloc(1, bufsize);
834
835         expect_lookup(RELPATH, ino, filesize);
836         expect_open(ino, 0, 1);
837         maxcontig = m_noclusterr ? m_maxbcachebuf :
838                                    m_maxbcachebuf + (int)get<1>(GetParam());
839         clustersize = MIN(maxcontig, MAXPHYS);
840         for (offs = 0; offs < bufsize; offs += clustersize) {
841                 len = std::min((size_t)clustersize, (size_t)(filesize - offs));
842                 expect_read(ino, offs, len, len, contents + offs);
843         }
844
845         fd = open(FULLPATH, O_RDONLY);
846         ASSERT_LE(0, fd) << strerror(errno);
847
848         /* Set the internal readahead counter to a "large" value */
849         ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
850
851         ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
852         ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
853
854         /* Deliberately leak fd.  close(2) will be tested in release.cc */
855 }
856
857 INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
858         Values(tuple<bool, int>(false, 0u),
859                tuple<bool, int>(false, 0x10000),
860                tuple<bool, int>(false, 0x20000),
861                tuple<bool, int>(false, 0x30000),
862                tuple<bool, int>(true, 0u),
863                tuple<bool, int>(true, 0x10000),
864                tuple<bool, int>(true, 0x20000)));