2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/param.h>
34 #include <sys/resource.h>
36 #include <sys/sysctl.h>
49 using namespace testing;
51 class Write: public FuseTest {
54 static sig_atomic_t s_sigxfsz;
64 bzero(&sa, sizeof(sa));
65 sa.sa_handler = SIG_DFL;
66 sigaction(SIGXFSZ, &sa, NULL);
71 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
73 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
76 void expect_release(uint64_t ino, ProcessMockerT r)
78 EXPECT_CALL(*m_mock, process(
79 ResultOf([=](auto in) {
80 return (in.header.opcode == FUSE_RELEASE &&
81 in.header.nodeid == ino);
84 ).WillRepeatedly(Invoke(r));
87 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
88 uint64_t osize, const void *contents)
90 FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
93 /* Expect a write that may or may not come, depending on the cache mode */
94 void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
97 EXPECT_CALL(*m_mock, process(
98 ResultOf([=](auto in) {
99 const char *buf = (const char*)in.body.bytes +
100 sizeof(struct fuse_write_in);
102 return (in.header.opcode == FUSE_WRITE &&
103 in.header.nodeid == ino &&
104 in.body.write.offset == offset &&
105 in.body.write.size == size &&
106 0 == bcmp(buf, contents, size));
110 .WillRepeatedly(Invoke(
111 ReturnImmediate([=](auto in __unused, auto& out) {
112 SET_OUT_HEADER_LEN(out, write);
113 out.body.write.size = size;
120 class WriteCacheable: public Write {
122 virtual void SetUp() {
123 const char *node = "vfs.fusefs.data_cache_mode";
125 size_t size = sizeof(val);
129 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
133 "fusefs data caching must be enabled for this test";
137 sig_atomic_t Write::s_sigxfsz = 0;
139 class Write_7_8: public FuseTest {
142 virtual void SetUp() {
143 m_kernel_minor_version = 8;
147 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
149 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
154 class AioWrite: public Write {
155 virtual void SetUp() {
156 const char *node = "vfs.aio.enable_unsafe";
158 size_t size = sizeof(val);
162 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
166 "vfs.aio.enable_unsafe must be set for this test";
170 /* Tests for the write-through cache mode */
171 class WriteThrough: public Write {
173 virtual void SetUp() {
174 const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
176 size_t size = sizeof(val);
182 ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
185 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
186 "(writethrough) for this test";
189 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
190 uint64_t osize, const void *contents)
192 FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
197 /* Tests for the writeback cache mode */
198 class WriteBack: public Write {
200 virtual void SetUp() {
201 const char *node = "vfs.fusefs.data_cache_mode";
203 size_t size = sizeof(val);
209 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
212 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
213 "(writeback) for this test";
216 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
217 uint64_t osize, const void *contents)
219 FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
224 class WriteBackAsync: public WriteBack {
226 virtual void SetUp() {
232 /* Tests for clustered writes with WriteBack cacheing */
233 class WriteCluster: public WriteBack {
235 virtual void SetUp() {
236 if (MAXPHYS < 2 * DFLTPHYS)
237 GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
240 m_maxwrite = MAXPHYS;
245 void sigxfsz_handler(int __unused sig) {
246 Write::s_sigxfsz = 1;
249 /* AIO writes need to set the header's pid field correctly */
250 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
251 TEST_F(AioWrite, DISABLED_aio_write)
253 const char FULLPATH[] = "mountpoint/some_file.txt";
254 const char RELPATH[] = "some_file.txt";
255 const char *CONTENTS = "abcdefgh";
257 uint64_t offset = 4096;
259 ssize_t bufsize = strlen(CONTENTS);
260 struct aiocb iocb, *piocb;
262 expect_lookup(RELPATH, ino, 0);
263 expect_open(ino, 0, 1);
264 expect_write(ino, offset, bufsize, bufsize, CONTENTS);
266 fd = open(FULLPATH, O_WRONLY);
267 EXPECT_LE(0, fd) << strerror(errno);
269 iocb.aio_nbytes = bufsize;
270 iocb.aio_fildes = fd;
271 iocb.aio_buf = (void *)CONTENTS;
272 iocb.aio_offset = offset;
273 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
274 ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
275 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
276 /* Deliberately leak fd. close(2) will be tested in release.cc */
280 * When a file is opened with O_APPEND, we should forward that flag to
281 * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
282 * offset internally. That way we'll work both with filesystems that
283 * understand O_APPEND (and ignore the offset) and filesystems that don't (and
284 * simply use the offset).
286 * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
287 * Open.o_append test.
289 TEST_F(Write, append)
291 const ssize_t BUFSIZE = 9;
292 const char FULLPATH[] = "mountpoint/some_file.txt";
293 const char RELPATH[] = "some_file.txt";
294 const char CONTENTS[BUFSIZE] = "abcdefgh";
297 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
298 * using writeback caching
300 uint64_t initial_offset = m_maxbcachebuf;
303 expect_lookup(RELPATH, ino, initial_offset);
304 expect_open(ino, 0, 1);
305 expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
307 /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
308 fd = open(FULLPATH, O_RDWR | O_APPEND);
309 EXPECT_LE(0, fd) << strerror(errno);
311 ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
312 /* Deliberately leak fd. close(2) will be tested in release.cc */
315 /* If a file is cached, then appending to the end should not cause a read */
316 TEST_F(Write, append_to_cached)
318 const ssize_t BUFSIZE = 9;
319 const char FULLPATH[] = "mountpoint/some_file.txt";
320 const char RELPATH[] = "some_file.txt";
321 char *oldcontents, *oldbuf;
322 const char CONTENTS[BUFSIZE] = "abcdefgh";
325 * Set offset in between maxbcachebuf boundary to test buffer handling
327 uint64_t oldsize = m_maxbcachebuf / 2;
330 oldcontents = (char*)calloc(1, oldsize);
331 ASSERT_NE(NULL, oldcontents) << strerror(errno);
332 oldbuf = (char*)malloc(oldsize);
333 ASSERT_NE(NULL, oldbuf) << strerror(errno);
335 expect_lookup(RELPATH, ino, oldsize);
336 expect_open(ino, 0, 1);
337 expect_read(ino, 0, oldsize, oldsize, oldcontents);
338 maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
340 /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
341 fd = open(FULLPATH, O_RDWR | O_APPEND);
342 EXPECT_LE(0, fd) << strerror(errno);
344 /* Read the old data into the cache */
345 ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
348 /* Write the new data. There should be no more read operations */
349 ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
350 /* Deliberately leak fd. close(2) will be tested in release.cc */
353 TEST_F(Write, append_direct_io)
355 const ssize_t BUFSIZE = 9;
356 const char FULLPATH[] = "mountpoint/some_file.txt";
357 const char RELPATH[] = "some_file.txt";
358 const char CONTENTS[BUFSIZE] = "abcdefgh";
360 uint64_t initial_offset = 4096;
363 expect_lookup(RELPATH, ino, initial_offset);
364 expect_open(ino, FOPEN_DIRECT_IO, 1);
365 expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
367 fd = open(FULLPATH, O_WRONLY | O_APPEND);
368 EXPECT_LE(0, fd) << strerror(errno);
370 ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
371 /* Deliberately leak fd. close(2) will be tested in release.cc */
374 /* A direct write should evict any overlapping cached data */
375 TEST_F(Write, direct_io_evicts_cache)
377 const char FULLPATH[] = "mountpoint/some_file.txt";
378 const char RELPATH[] = "some_file.txt";
379 const char CONTENTS0[] = "abcdefgh";
380 const char CONTENTS1[] = "ijklmnop";
383 ssize_t bufsize = strlen(CONTENTS0) + 1;
384 char readbuf[bufsize];
386 expect_lookup(RELPATH, ino, bufsize);
387 expect_open(ino, 0, 1);
388 expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
389 expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
391 fd = open(FULLPATH, O_RDWR);
392 EXPECT_LE(0, fd) << strerror(errno);
395 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
397 // Write directly, evicting cache
398 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
399 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
400 ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
402 // Read again. Cache should be bypassed
403 expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
404 ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
405 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
406 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
407 ASSERT_STREQ(readbuf, CONTENTS1);
409 /* Deliberately leak fd. close(2) will be tested in release.cc */
413 * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
414 * allowed to return a short write for that file handle. However, if it does
415 * then we should still do our darndest to handle it by resending the unwritten
418 TEST_F(Write, indirect_io_short_write)
420 const char FULLPATH[] = "mountpoint/some_file.txt";
421 const char RELPATH[] = "some_file.txt";
422 const char *CONTENTS = "abcdefghijklmnop";
425 ssize_t bufsize = strlen(CONTENTS);
426 ssize_t bufsize0 = 11;
427 ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
428 const char *contents1 = CONTENTS + bufsize0;
430 expect_lookup(RELPATH, ino, 0);
431 expect_open(ino, 0, 1);
432 expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
433 expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
435 fd = open(FULLPATH, O_WRONLY);
436 EXPECT_LE(0, fd) << strerror(errno);
438 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
439 /* Deliberately leak fd. close(2) will be tested in release.cc */
443 * When the direct_io option is used, filesystems are allowed to write less
444 * data than requested. We should return the short write to userland.
446 TEST_F(Write, direct_io_short_write)
448 const char FULLPATH[] = "mountpoint/some_file.txt";
449 const char RELPATH[] = "some_file.txt";
450 const char *CONTENTS = "abcdefghijklmnop";
453 ssize_t bufsize = strlen(CONTENTS);
454 ssize_t halfbufsize = bufsize / 2;
456 expect_lookup(RELPATH, ino, 0);
457 expect_open(ino, FOPEN_DIRECT_IO, 1);
458 expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
460 fd = open(FULLPATH, O_WRONLY);
461 EXPECT_LE(0, fd) << strerror(errno);
463 ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
464 /* Deliberately leak fd. close(2) will be tested in release.cc */
468 * An insidious edge case: the filesystem returns a short write, and the
469 * difference between what we requested and what it actually wrote crosses an
470 * iov element boundary
472 TEST_F(Write, direct_io_short_write_iov)
474 const char FULLPATH[] = "mountpoint/some_file.txt";
475 const char RELPATH[] = "some_file.txt";
476 const char *CONTENTS0 = "abcdefgh";
477 const char *CONTENTS1 = "ijklmnop";
478 const char *EXPECTED0 = "abcdefghijklmnop";
481 ssize_t size0 = strlen(CONTENTS0) - 1;
482 ssize_t size1 = strlen(CONTENTS1) + 1;
483 ssize_t totalsize = size0 + size1;
486 expect_lookup(RELPATH, ino, 0);
487 expect_open(ino, FOPEN_DIRECT_IO, 1);
488 expect_write(ino, 0, totalsize, size0, EXPECTED0);
490 fd = open(FULLPATH, O_WRONLY);
491 EXPECT_LE(0, fd) << strerror(errno);
493 iov[0].iov_base = (void*)CONTENTS0;
494 iov[0].iov_len = strlen(CONTENTS0);
495 iov[1].iov_base = (void*)CONTENTS1;
496 iov[1].iov_len = strlen(CONTENTS1);
497 ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
498 /* Deliberately leak fd. close(2) will be tested in release.cc */
501 /* fusefs should respect RLIMIT_FSIZE */
502 TEST_F(Write, rlimit_fsize)
504 const char FULLPATH[] = "mountpoint/some_file.txt";
505 const char RELPATH[] = "some_file.txt";
506 const char *CONTENTS = "abcdefgh";
508 ssize_t bufsize = strlen(CONTENTS);
509 off_t offset = 1'000'000'000;
513 expect_lookup(RELPATH, ino, 0);
514 expect_open(ino, 0, 1);
516 rl.rlim_cur = offset;
517 rl.rlim_max = 10 * offset;
518 ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
519 ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
521 fd = open(FULLPATH, O_WRONLY);
523 EXPECT_LE(0, fd) << strerror(errno);
525 ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
526 EXPECT_EQ(EFBIG, errno);
527 EXPECT_EQ(1, s_sigxfsz);
528 /* Deliberately leak fd. close(2) will be tested in release.cc */
532 * If the kernel cannot be sure which uid, gid, or pid was responsible for a
533 * write, then it must set the FUSE_WRITE_CACHE bit
535 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
536 TEST_F(WriteCacheable, mmap)
538 const char FULLPATH[] = "mountpoint/some_file.txt";
539 const char RELPATH[] = "some_file.txt";
540 const char *CONTENTS = "abcdefgh";
543 ssize_t bufsize = strlen(CONTENTS);
545 uint64_t offset = 10;
547 void *zeros, *expected;
551 zeros = calloc(1, len);
552 ASSERT_NE(NULL, zeros);
553 expected = calloc(1, len);
554 ASSERT_NE(NULL, expected);
555 memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
557 expect_lookup(RELPATH, ino, len);
558 expect_open(ino, 0, 1);
559 expect_read(ino, 0, len, len, zeros);
561 * Writes from the pager may or may not be associated with the correct
562 * pid, so they must set FUSE_WRITE_CACHE.
564 FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
565 expect_flush(ino, 1, ReturnErrno(0));
566 expect_release(ino, ReturnErrno(0));
568 fd = open(FULLPATH, O_RDWR);
569 EXPECT_LE(0, fd) << strerror(errno);
571 p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
572 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
574 memmove((uint8_t*)p + offset, CONTENTS, bufsize);
576 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
577 close(fd); // Write mmap'd data on close
583 TEST_F(WriteThrough, pwrite)
585 const char FULLPATH[] = "mountpoint/some_file.txt";
586 const char RELPATH[] = "some_file.txt";
587 const char *CONTENTS = "abcdefgh";
589 uint64_t offset = 65536;
591 ssize_t bufsize = strlen(CONTENTS);
593 expect_lookup(RELPATH, ino, 0);
594 expect_open(ino, 0, 1);
595 expect_write(ino, offset, bufsize, bufsize, CONTENTS);
597 fd = open(FULLPATH, O_WRONLY);
598 EXPECT_LE(0, fd) << strerror(errno);
600 ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
602 /* Deliberately leak fd. close(2) will be tested in release.cc */
607 const char FULLPATH[] = "mountpoint/some_file.txt";
608 const char RELPATH[] = "some_file.txt";
609 const char *CONTENTS = "abcdefgh";
612 ssize_t bufsize = strlen(CONTENTS);
614 expect_lookup(RELPATH, ino, 0);
615 expect_open(ino, 0, 1);
616 expect_write(ino, 0, bufsize, bufsize, CONTENTS);
618 fd = open(FULLPATH, O_WRONLY);
619 EXPECT_LE(0, fd) << strerror(errno);
621 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
622 /* Deliberately leak fd. close(2) will be tested in release.cc */
625 /* fuse(4) should not issue writes of greater size than the daemon requests */
626 TEST_F(Write, write_large)
628 const char FULLPATH[] = "mountpoint/some_file.txt";
629 const char RELPATH[] = "some_file.txt";
633 ssize_t halfbufsize, bufsize;
635 halfbufsize = m_mock->m_maxwrite;
636 bufsize = halfbufsize * 2;
637 contents = (int*)malloc(bufsize);
638 ASSERT_NE(NULL, contents);
639 for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
643 expect_lookup(RELPATH, ino, 0);
644 expect_open(ino, 0, 1);
645 expect_write(ino, 0, halfbufsize, halfbufsize, contents);
646 maybe_expect_write(ino, halfbufsize, halfbufsize,
647 &contents[halfbufsize / sizeof(int)]);
649 fd = open(FULLPATH, O_WRONLY);
650 EXPECT_LE(0, fd) << strerror(errno);
652 ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
653 /* Deliberately leak fd. close(2) will be tested in release.cc */
658 TEST_F(Write, write_nothing)
660 const char FULLPATH[] = "mountpoint/some_file.txt";
661 const char RELPATH[] = "some_file.txt";
662 const char *CONTENTS = "";
667 expect_lookup(RELPATH, ino, 0);
668 expect_open(ino, 0, 1);
670 fd = open(FULLPATH, O_WRONLY);
671 EXPECT_LE(0, fd) << strerror(errno);
673 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
674 /* Deliberately leak fd. close(2) will be tested in release.cc */
677 TEST_F(Write_7_8, write)
679 const char FULLPATH[] = "mountpoint/some_file.txt";
680 const char RELPATH[] = "some_file.txt";
681 const char *CONTENTS = "abcdefgh";
684 ssize_t bufsize = strlen(CONTENTS);
686 expect_lookup(RELPATH, ino, 0);
687 expect_open(ino, 0, 1);
688 expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
690 fd = open(FULLPATH, O_WRONLY);
691 EXPECT_LE(0, fd) << strerror(errno);
693 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
694 /* Deliberately leak fd. close(2) will be tested in release.cc */
697 /* In writeback mode, dirty data should be written on close */
698 TEST_F(WriteBackAsync, close)
700 const char FULLPATH[] = "mountpoint/some_file.txt";
701 const char RELPATH[] = "some_file.txt";
702 const char *CONTENTS = "abcdefgh";
705 ssize_t bufsize = strlen(CONTENTS);
707 expect_lookup(RELPATH, ino, 0);
708 expect_open(ino, 0, 1);
709 expect_write(ino, 0, bufsize, bufsize, CONTENTS);
710 EXPECT_CALL(*m_mock, process(
711 ResultOf([=](auto in) {
712 return (in.header.opcode == FUSE_SETATTR);
715 ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
716 SET_OUT_HEADER_LEN(out, attr);
717 out.body.attr.attr.ino = ino; // Must match nodeid
719 expect_flush(ino, 1, ReturnErrno(0));
720 expect_release(ino, ReturnErrno(0));
722 fd = open(FULLPATH, O_RDWR);
723 ASSERT_LE(0, fd) << strerror(errno);
725 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
729 /* In writeback mode, adjacent writes will be clustered together */
730 TEST_F(WriteCluster, clustering)
732 const char FULLPATH[] = "mountpoint/some_file.txt";
733 const char RELPATH[] = "some_file.txt";
737 ssize_t bufsize = 65536;
738 off_t filesize = 327680;
740 wbuf = malloc(bufsize);
741 ASSERT_NE(NULL, wbuf) << strerror(errno);
742 memset(wbuf, 'X', bufsize);
743 wbuf2x = malloc(2 * bufsize);
744 ASSERT_NE(NULL, wbuf2x) << strerror(errno);
745 memset(wbuf2x, 'X', 2 * bufsize);
747 expect_lookup(RELPATH, ino, filesize);
748 expect_open(ino, 0, 1);
750 * Writes of bufsize-bytes each should be clustered into greater sizes.
751 * The amount of clustering is adaptive, so the first write actually
752 * issued will be 2x bufsize and subsequent writes may be larger
754 expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
755 expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
756 expect_flush(ino, 1, ReturnErrno(0));
757 expect_release(ino, ReturnErrno(0));
759 fd = open(FULLPATH, O_RDWR);
760 ASSERT_LE(0, fd) << strerror(errno);
762 for (i = 0; i < 4; i++) {
763 ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
770 * When clustering writes, an I/O error to any of the cluster's children should
771 * not panic the system on unmount
774 * Disabled because it panics.
775 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
777 TEST_F(WriteCluster, DISABLED_cluster_write_err)
779 const char FULLPATH[] = "mountpoint/some_file.txt";
780 const char RELPATH[] = "some_file.txt";
784 ssize_t bufsize = 65536;
785 off_t filesize = 262144;
787 wbuf = malloc(bufsize);
788 ASSERT_NE(NULL, wbuf) << strerror(errno);
789 memset(wbuf, 'X', bufsize);
791 expect_lookup(RELPATH, ino, filesize);
792 expect_open(ino, 0, 1);
793 EXPECT_CALL(*m_mock, process(
794 ResultOf([=](auto in) {
795 return (in.header.opcode == FUSE_WRITE);
798 ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
799 expect_flush(ino, 1, ReturnErrno(0));
800 expect_release(ino, ReturnErrno(0));
802 fd = open(FULLPATH, O_RDWR);
803 ASSERT_LE(0, fd) << strerror(errno);
805 for (i = 0; i < 3; i++) {
806 ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
813 * In writeback mode, writes to an O_WRONLY file could trigger reads from the
814 * server. The FUSE protocol explicitly allows that.
816 TEST_F(WriteBack, rmw)
818 const char FULLPATH[] = "mountpoint/some_file.txt";
819 const char RELPATH[] = "some_file.txt";
820 const char *CONTENTS = "abcdefgh";
821 const char *INITIAL = "XXXXXXXXXX";
826 ssize_t bufsize = strlen(CONTENTS);
828 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
829 expect_open(ino, 0, 1);
830 expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
831 maybe_expect_write(ino, offset, bufsize, CONTENTS);
833 fd = open(FULLPATH, O_WRONLY);
834 EXPECT_LE(0, fd) << strerror(errno);
836 ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
838 /* Deliberately leak fd. close(2) will be tested in release.cc */
842 * Without direct_io, writes should be committed to cache
844 TEST_F(WriteBack, cache)
846 const char FULLPATH[] = "mountpoint/some_file.txt";
847 const char RELPATH[] = "some_file.txt";
848 const char *CONTENTS = "abcdefgh";
851 ssize_t bufsize = strlen(CONTENTS);
852 char readbuf[bufsize];
854 expect_lookup(RELPATH, ino, 0);
855 expect_open(ino, 0, 1);
856 expect_write(ino, 0, bufsize, bufsize, CONTENTS);
858 fd = open(FULLPATH, O_RDWR);
859 EXPECT_LE(0, fd) << strerror(errno);
861 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
863 * A subsequent read should be serviced by cache, without querying the
866 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
867 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
868 /* Deliberately leak fd. close(2) will be tested in release.cc */
872 * With O_DIRECT, writes should be not committed to cache. Admittedly this is
873 * an odd test, because it would be unusual to use O_DIRECT for writes but not
876 TEST_F(WriteBack, o_direct)
878 const char FULLPATH[] = "mountpoint/some_file.txt";
879 const char RELPATH[] = "some_file.txt";
880 const char *CONTENTS = "abcdefgh";
883 ssize_t bufsize = strlen(CONTENTS);
884 char readbuf[bufsize];
886 expect_lookup(RELPATH, ino, 0);
887 expect_open(ino, 0, 1);
888 FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
890 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
892 fd = open(FULLPATH, O_RDWR | O_DIRECT);
893 EXPECT_LE(0, fd) << strerror(errno);
895 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
896 /* A subsequent read must query the daemon because cache is empty */
897 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
898 ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
899 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
900 /* Deliberately leak fd. close(2) will be tested in release.cc */
904 * When mounted with -o async, the writeback cache mode should delay writes
906 TEST_F(WriteBackAsync, delay)
908 const char FULLPATH[] = "mountpoint/some_file.txt";
909 const char RELPATH[] = "some_file.txt";
910 const char *CONTENTS = "abcdefgh";
913 ssize_t bufsize = strlen(CONTENTS);
915 expect_lookup(RELPATH, ino, 0);
916 expect_open(ino, 0, 1);
917 /* Write should be cached, but FUSE_WRITE shouldn't be sent */
918 EXPECT_CALL(*m_mock, process(
919 ResultOf([=](auto in) {
920 return (in.header.opcode == FUSE_WRITE);
925 fd = open(FULLPATH, O_RDWR);
926 EXPECT_LE(0, fd) << strerror(errno);
928 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
930 /* Don't close the file because that would flush the cache */
934 * Without direct_io, writes should be committed to cache
936 TEST_F(WriteThrough, writethrough)
938 const char FULLPATH[] = "mountpoint/some_file.txt";
939 const char RELPATH[] = "some_file.txt";
940 const char *CONTENTS = "abcdefgh";
943 ssize_t bufsize = strlen(CONTENTS);
944 char readbuf[bufsize];
946 expect_lookup(RELPATH, ino, 0);
947 expect_open(ino, 0, 1);
948 expect_write(ino, 0, bufsize, bufsize, CONTENTS);
950 fd = open(FULLPATH, O_RDWR);
951 EXPECT_LE(0, fd) << strerror(errno);
953 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
955 * A subsequent read should be serviced by cache, without querying the
958 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
959 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
960 /* Deliberately leak fd. close(2) will be tested in release.cc */
963 /* With writethrough caching, writes update the cached file size */
964 TEST_F(WriteThrough, update_file_size)
966 const char FULLPATH[] = "mountpoint/some_file.txt";
967 const char RELPATH[] = "some_file.txt";
968 const char *CONTENTS = "abcdefgh";
972 ssize_t bufsize = strlen(CONTENTS);
974 expect_lookup(RELPATH, ino, 0);
975 expect_open(ino, 0, 1);
976 expect_write(ino, 0, bufsize, bufsize, CONTENTS);
978 fd = open(FULLPATH, O_RDWR);
979 EXPECT_LE(0, fd) << strerror(errno);
981 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
982 /* Get cached attributes */
983 ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
984 ASSERT_EQ(bufsize, sb.st_size);
985 /* Deliberately leak fd. close(2) will be tested in release.cc */