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/types.h>
35 #include <sys/sysctl.h>
47 * TODO: remove FUSE_WRITE_CACHE definition when upgrading to protocol 7.9.
48 * This bit was actually part of kernel protocol version 7.2, but never
49 * documented until 7.9
51 #ifndef FUSE_WRITE_CACHE
52 #define FUSE_WRITE_CACHE 1
55 using namespace testing;
57 class Write: public FuseTest {
60 const static uint64_t FH = 0xdeadbeef1a7ebabe;
61 void expect_getattr(uint64_t ino, uint64_t size)
63 /* Until the attr cache is working, we may send an additional GETATTR */
64 EXPECT_CALL(*m_mock, process(
65 ResultOf([=](auto in) {
66 return (in->header.opcode == FUSE_GETATTR &&
67 in->header.nodeid == ino);
70 ).WillRepeatedly(Invoke([=](auto in, auto out) {
71 out->header.unique = in->header.unique;
72 SET_OUT_HEADER_LEN(out, attr);
73 out->body.attr.attr.ino = ino; // Must match nodeid
74 out->body.attr.attr.mode = S_IFREG | 0644;
75 out->body.attr.attr.size = size;
76 out->body.attr.attr_valid = UINT64_MAX;
81 void expect_lookup(const char *relpath, uint64_t ino)
83 EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
84 out->header.unique = in->header.unique;
85 SET_OUT_HEADER_LEN(out, entry);
86 out->body.entry.attr.mode = S_IFREG | 0644;
87 out->body.entry.nodeid = ino;
88 out->body.entry.attr_valid = UINT64_MAX;
92 void expect_open(uint64_t ino, uint32_t flags, int times)
94 EXPECT_CALL(*m_mock, process(
95 ResultOf([=](auto in) {
96 return (in->header.opcode == FUSE_OPEN &&
97 in->header.nodeid == ino);
101 .WillRepeatedly(Invoke([=](auto in, auto out) {
102 out->header.unique = in->header.unique;
103 out->header.len = sizeof(out->header);
104 SET_OUT_HEADER_LEN(out, open);
105 out->body.open.fh = Write::FH;
106 out->body.open.open_flags = flags;
110 void expect_read(uint64_t ino, uint64_t offset, uint64_t size,
111 const void *contents)
113 EXPECT_CALL(*m_mock, process(
114 ResultOf([=](auto in) {
115 return (in->header.opcode == FUSE_READ &&
116 in->header.nodeid == ino &&
117 in->body.read.fh == Write::FH &&
118 in->body.read.offset == offset &&
119 in->body.read.size == size);
122 ).WillOnce(Invoke([=](auto in, auto out) {
123 out->header.unique = in->header.unique;
124 out->header.len = sizeof(struct fuse_out_header) + size;
125 memmove(out->body.bytes, contents, size);
126 })).RetiresOnSaturation();
129 void expect_release(uint64_t ino, ProcessMockerT r)
131 EXPECT_CALL(*m_mock, process(
132 ResultOf([=](auto in) {
133 return (in->header.opcode == FUSE_RELEASE &&
134 in->header.nodeid == ino);
137 ).WillRepeatedly(Invoke(r));
140 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize,
141 uint32_t flags, const void *contents)
143 EXPECT_CALL(*m_mock, process(
144 ResultOf([=](auto in) {
145 const char *buf = (const char*)in->body.bytes +
146 sizeof(struct fuse_write_in);
149 if (in->body.write.write_flags & FUSE_WRITE_CACHE)
152 pid_ok = (pid_t)in->header.pid == getpid();
154 return (in->header.opcode == FUSE_WRITE &&
155 in->header.nodeid == ino &&
156 in->body.write.fh == Write::FH &&
157 in->body.write.offset == offset &&
158 in->body.write.size == isize &&
160 in->body.write.write_flags == flags &&
161 0 == bcmp(buf, contents, isize));
164 ).WillOnce(Invoke([=](auto in, auto out) {
165 out->header.unique = in->header.unique;
166 SET_OUT_HEADER_LEN(out, write);
167 out->body.write.size = osize;
173 class AioWrite: public Write {
174 virtual void SetUp() {
175 const char *node = "vfs.aio.enable_unsafe";
177 size_t size = sizeof(val);
179 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
181 // TODO: With GoogleTest 1.8.2, use SKIP instead
183 FAIL() << "vfs.aio.enable_unsafe must be set for this test";
188 /* Tests for the write-through cache mode */
189 class WriteThrough: public Write {
191 virtual void SetUp() {
192 const char *cache_mode_node = "vfs.fuse.data_cache_mode";
193 const char *sync_resize_node = "vfs.fuse.sync_resize";
195 size_t size = sizeof(val);
197 ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
199 // TODO: With GoogleTest 1.8.2, use SKIP instead
201 FAIL() << "vfs.fuse.data_cache_mode must be set to 1 "
202 "(writethrough) for this test";
204 ASSERT_EQ(0, sysctlbyname(sync_resize_node, &val, &size, NULL, 0))
207 FAIL() << "vfs.fuse.sync_resize must be set to 0 for this test."
208 " That sysctl will probably be removed soon.";
215 /* Tests for the writeback cache mode */
216 class WriteBack: public Write {
218 virtual void SetUp() {
219 const char *node = "vfs.fuse.data_cache_mode";
221 size_t size = sizeof(val);
223 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
225 // TODO: With GoogleTest 1.8.2, use SKIP instead
227 FAIL() << "vfs.fuse.data_cache_mode must be set to 2 "
228 "(writeback) for this test";
234 /* AIO writes need to set the header's pid field correctly */
235 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
236 TEST_F(AioWrite, DISABLED_aio_write)
238 const char FULLPATH[] = "mountpoint/some_file.txt";
239 const char RELPATH[] = "some_file.txt";
240 const char *CONTENTS = "abcdefgh";
242 uint64_t offset = 4096;
244 ssize_t bufsize = strlen(CONTENTS);
245 struct aiocb iocb, *piocb;
247 expect_lookup(RELPATH, ino);
248 expect_open(ino, 0, 1);
249 expect_getattr(ino, 0);
250 expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
252 fd = open(FULLPATH, O_WRONLY);
253 EXPECT_LE(0, fd) << strerror(errno);
255 iocb.aio_nbytes = bufsize;
256 iocb.aio_fildes = fd;
257 iocb.aio_buf = (void *)CONTENTS;
258 iocb.aio_offset = offset;
259 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
260 ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
261 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
262 /* Deliberately leak fd. close(2) will be tested in release.cc */
266 * When a file is opened with O_APPEND, we should forward that flag to
267 * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
268 * offset internally. That way we'll work both with filesystems that
269 * understand O_APPEND (and ignore the offset) and filesystems that don't (and
270 * simply use the offset).
272 * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
273 * Open.o_append test.
275 TEST_F(Write, append)
277 const ssize_t BUFSIZE = 9;
278 const char FULLPATH[] = "mountpoint/some_file.txt";
279 const char RELPATH[] = "some_file.txt";
280 const char CONTENTS[BUFSIZE] = "abcdefgh";
283 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
284 * using writeback caching
286 uint64_t initial_offset = m_maxbcachebuf;
289 expect_lookup(RELPATH, ino);
290 expect_open(ino, 0, 1);
291 expect_getattr(ino, initial_offset);
292 expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
294 /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
295 fd = open(FULLPATH, O_RDWR | O_APPEND);
296 EXPECT_LE(0, fd) << strerror(errno);
298 ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
299 /* Deliberately leak fd. close(2) will be tested in release.cc */
302 TEST_F(Write, append_direct_io)
304 const ssize_t BUFSIZE = 9;
305 const char FULLPATH[] = "mountpoint/some_file.txt";
306 const char RELPATH[] = "some_file.txt";
307 const char CONTENTS[BUFSIZE] = "abcdefgh";
309 uint64_t initial_offset = 4096;
312 expect_lookup(RELPATH, ino);
313 expect_open(ino, FOPEN_DIRECT_IO, 1);
314 expect_getattr(ino, initial_offset);
315 expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
317 fd = open(FULLPATH, O_WRONLY | O_APPEND);
318 EXPECT_LE(0, fd) << strerror(errno);
320 ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
321 /* Deliberately leak fd. close(2) will be tested in release.cc */
324 /* A direct write should evict any overlapping cached data */
325 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235774 */
326 TEST_F(Write, DISABLED_direct_io_evicts_cache)
328 const char FULLPATH[] = "mountpoint/some_file.txt";
329 const char RELPATH[] = "some_file.txt";
330 const char CONTENTS0[] = "abcdefgh";
331 const char CONTENTS1[] = "ijklmnop";
334 ssize_t bufsize = strlen(CONTENTS0) + 1;
335 char readbuf[bufsize];
337 expect_lookup(RELPATH, ino);
338 expect_open(ino, 0, 1);
339 expect_getattr(ino, bufsize);
340 expect_read(ino, 0, bufsize, CONTENTS0);
341 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
343 fd = open(FULLPATH, O_RDWR);
344 EXPECT_LE(0, fd) << strerror(errno);
347 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
349 // Write directly, evicting cache
350 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
351 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
352 ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
354 // Read again. Cache should be bypassed
355 expect_read(ino, 0, bufsize, CONTENTS1);
356 ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
357 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
358 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
359 ASSERT_STREQ(readbuf, CONTENTS1);
361 /* Deliberately leak fd. close(2) will be tested in release.cc */
365 * When the direct_io option is used, filesystems are allowed to write less
366 * data than requested
368 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */
369 TEST_F(Write, DISABLED_direct_io_short_write)
371 const char FULLPATH[] = "mountpoint/some_file.txt";
372 const char RELPATH[] = "some_file.txt";
373 const char *CONTENTS = "abcdefghijklmnop";
376 ssize_t bufsize = strlen(CONTENTS);
377 ssize_t halfbufsize = bufsize / 2;
378 const char *halfcontents = CONTENTS + halfbufsize;
380 expect_lookup(RELPATH, ino);
381 expect_open(ino, FOPEN_DIRECT_IO, 1);
382 expect_getattr(ino, 0);
383 expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS);
384 expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
387 fd = open(FULLPATH, O_WRONLY);
388 EXPECT_LE(0, fd) << strerror(errno);
390 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
391 /* Deliberately leak fd. close(2) will be tested in release.cc */
395 * An insidious edge case: the filesystem returns a short write, and the
396 * difference between what we requested and what it actually wrote crosses an
397 * iov element boundary
399 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */
400 TEST_F(Write, DISABLED_direct_io_short_write_iov)
402 const char FULLPATH[] = "mountpoint/some_file.txt";
403 const char RELPATH[] = "some_file.txt";
404 const char *CONTENTS0 = "abcdefgh";
405 const char *CONTENTS1 = "ijklmnop";
406 const char *EXPECTED0 = "abcdefghijklmnop";
407 const char *EXPECTED1 = "hijklmnop";
410 ssize_t size0 = strlen(CONTENTS0) - 1;
411 ssize_t size1 = strlen(CONTENTS1) + 1;
412 ssize_t totalsize = size0 + size1;
415 expect_lookup(RELPATH, ino);
416 expect_open(ino, FOPEN_DIRECT_IO, 1);
417 expect_getattr(ino, 0);
418 expect_write(ino, 0, totalsize, size0, 0, EXPECTED0);
419 expect_write(ino, size0, size1, size1, 0, EXPECTED1);
421 fd = open(FULLPATH, O_WRONLY);
422 EXPECT_LE(0, fd) << strerror(errno);
424 iov[0].iov_base = (void*)CONTENTS0;
425 iov[0].iov_len = strlen(CONTENTS0);
426 iov[1].iov_base = (void*)CONTENTS1;
427 iov[1].iov_len = strlen(CONTENTS1);
428 ASSERT_EQ(totalsize, writev(fd, iov, 2)) << strerror(errno);
429 /* Deliberately leak fd. close(2) will be tested in release.cc */
433 * If the kernel cannot be sure which uid, gid, or pid was responsible for a
434 * write, then it must set the FUSE_WRITE_CACHE bit
436 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
437 // TODO: check vfs.fuse.mmap_enable
438 TEST_F(Write, DISABLED_mmap)
440 const char FULLPATH[] = "mountpoint/some_file.txt";
441 const char RELPATH[] = "some_file.txt";
442 const char *CONTENTS = "abcdefgh";
445 ssize_t bufsize = strlen(CONTENTS);
447 uint64_t offset = 10;
449 void *zeros, *expected;
453 zeros = calloc(1, len);
454 ASSERT_NE(NULL, zeros);
455 expected = calloc(1, len);
456 ASSERT_NE(NULL, expected);
457 memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
459 expect_lookup(RELPATH, ino);
460 expect_open(ino, 0, 1);
461 expect_getattr(ino, len);
462 expect_read(ino, 0, len, zeros);
464 * Writes from the pager may or may not be associated with the correct
465 * pid, so they must set FUSE_WRITE_CACHE
467 expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, expected);
468 expect_release(ino, ReturnErrno(0));
470 fd = open(FULLPATH, O_RDWR);
471 EXPECT_LE(0, fd) << strerror(errno);
473 p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
474 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
476 memmove((uint8_t*)p + offset, CONTENTS, bufsize);
478 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
479 close(fd); // Write mmap'd data on close
485 TEST_F(Write, pwrite)
487 const char FULLPATH[] = "mountpoint/some_file.txt";
488 const char RELPATH[] = "some_file.txt";
489 const char *CONTENTS = "abcdefgh";
491 uint64_t offset = 4096;
493 ssize_t bufsize = strlen(CONTENTS);
495 expect_lookup(RELPATH, ino);
496 expect_open(ino, 0, 1);
497 expect_getattr(ino, 0);
498 expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
500 fd = open(FULLPATH, O_WRONLY);
501 EXPECT_LE(0, fd) << strerror(errno);
503 ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
505 /* Deliberately leak fd. close(2) will be tested in release.cc */
510 const char FULLPATH[] = "mountpoint/some_file.txt";
511 const char RELPATH[] = "some_file.txt";
512 const char *CONTENTS = "abcdefgh";
515 ssize_t bufsize = strlen(CONTENTS);
517 expect_lookup(RELPATH, ino);
518 expect_open(ino, 0, 1);
519 expect_getattr(ino, 0);
520 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
522 fd = open(FULLPATH, O_WRONLY);
523 EXPECT_LE(0, fd) << strerror(errno);
525 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
526 /* Deliberately leak fd. close(2) will be tested in release.cc */
529 /* fuse(4) should not issue writes of greater size than the daemon requests */
530 TEST_F(Write, write_large)
532 const char FULLPATH[] = "mountpoint/some_file.txt";
533 const char RELPATH[] = "some_file.txt";
537 ssize_t halfbufsize, bufsize;
539 halfbufsize = m_mock->m_max_write;
540 bufsize = halfbufsize * 2;
541 contents = (int*)malloc(bufsize);
542 ASSERT_NE(NULL, contents);
543 for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
547 expect_lookup(RELPATH, ino);
548 expect_open(ino, 0, 1);
549 expect_getattr(ino, 0);
550 expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents);
551 expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
552 &contents[halfbufsize / sizeof(int)]);
554 fd = open(FULLPATH, O_WRONLY);
555 EXPECT_LE(0, fd) << strerror(errno);
557 ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
558 /* Deliberately leak fd. close(2) will be tested in release.cc */
563 TEST_F(Write, write_nothing)
565 const char FULLPATH[] = "mountpoint/some_file.txt";
566 const char RELPATH[] = "some_file.txt";
567 const char *CONTENTS = "";
572 expect_lookup(RELPATH, ino);
573 expect_open(ino, 0, 1);
574 expect_getattr(ino, 0);
576 fd = open(FULLPATH, O_WRONLY);
577 EXPECT_LE(0, fd) << strerror(errno);
579 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
580 /* Deliberately leak fd. close(2) will be tested in release.cc */
584 * Without direct_io, writes should be committed to cache
586 TEST_F(WriteBack, writeback)
588 const char FULLPATH[] = "mountpoint/some_file.txt";
589 const char RELPATH[] = "some_file.txt";
590 const char *CONTENTS = "abcdefgh";
593 ssize_t bufsize = strlen(CONTENTS);
594 char readbuf[bufsize];
596 expect_lookup(RELPATH, ino);
597 expect_open(ino, 0, 1);
598 expect_getattr(ino, 0);
599 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
601 fd = open(FULLPATH, O_RDWR);
602 EXPECT_LE(0, fd) << strerror(errno);
604 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
606 * A subsequent read should be serviced by cache, without querying the
609 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
610 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
611 /* Deliberately leak fd. close(2) will be tested in release.cc */
615 * With O_DIRECT, writes should be not committed to cache. Admittedly this is
616 * an odd test, because it would be unusual to use O_DIRECT for writes but not
619 TEST_F(WriteBack, o_direct)
621 const char FULLPATH[] = "mountpoint/some_file.txt";
622 const char RELPATH[] = "some_file.txt";
623 const char *CONTENTS = "abcdefgh";
626 ssize_t bufsize = strlen(CONTENTS);
627 char readbuf[bufsize];
629 expect_lookup(RELPATH, ino);
630 expect_open(ino, 0, 1);
631 expect_getattr(ino, 0);
632 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
633 expect_read(ino, 0, bufsize, CONTENTS);
635 fd = open(FULLPATH, O_RDWR | O_DIRECT);
636 EXPECT_LE(0, fd) << strerror(errno);
638 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
639 /* A subsequent read must query the daemon because cache is empty */
640 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
641 ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
642 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
643 /* Deliberately leak fd. close(2) will be tested in release.cc */
647 * Without direct_io, writes should be committed to cache
650 * Disabled because we don't yet implement write-through caching. No bugzilla
651 * entry, because that's a feature request, not a bug.
653 TEST_F(WriteThrough, DISABLED_writethrough)
655 const char FULLPATH[] = "mountpoint/some_file.txt";
656 const char RELPATH[] = "some_file.txt";
657 const char *CONTENTS = "abcdefgh";
660 ssize_t bufsize = strlen(CONTENTS);
661 char readbuf[bufsize];
663 expect_lookup(RELPATH, ino);
664 expect_open(ino, 0, 1);
665 expect_getattr(ino, 0);
666 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
668 fd = open(FULLPATH, O_RDWR);
669 EXPECT_LE(0, fd) << strerror(errno);
671 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
673 * A subsequent read should be serviced by cache, without querying the
676 ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
677 /* Deliberately leak fd. close(2) will be tested in release.cc */
680 /* With writethrough caching, writes update the cached file size */
681 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */
682 TEST_F(WriteThrough, DISABLED_update_file_size)
684 const char FULLPATH[] = "mountpoint/some_file.txt";
685 const char RELPATH[] = "some_file.txt";
686 const char *CONTENTS = "abcdefgh";
690 ssize_t bufsize = strlen(CONTENTS);
692 expect_lookup(RELPATH, ino);
693 expect_open(ino, 0, 1);
694 EXPECT_CALL(*m_mock, process(
695 ResultOf([=](auto in) {
696 return (in->header.opcode == FUSE_GETATTR &&
697 in->header.nodeid == ino);
701 .WillRepeatedly(Invoke([=](auto in, auto out) {
702 out->header.unique = in->header.unique;
703 SET_OUT_HEADER_LEN(out, attr);
704 out->body.attr.attr.ino = ino; // Must match nodeid
705 out->body.attr.attr.mode = S_IFREG | 0644;
706 out->body.attr.attr.size = 0;
707 out->body.attr.attr_valid = UINT64_MAX;
709 expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
711 fd = open(FULLPATH, O_RDWR);
712 EXPECT_LE(0, fd) << strerror(errno);
714 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
715 /* Get cached attributes */
716 ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
717 ASSERT_EQ(bufsize, sb.st_size);
718 /* Deliberately leak fd. close(2) will be tested in release.cc */