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